From 26a933307b6c95d8c6158e43eaf6a1187478c805 Mon Sep 17 00:00:00 2001 From: mrq Date: Sat, 9 Jul 2022 19:45:00 -0500 Subject: [PATCH] Commit for 2022.07.09 19-45-23.7z --- Makefile | 33 +- bin/data/config.json | 23 +- bin/data/entities/model.json | 29 +- bin/data/entities/player.json | 58 +- bin/data/scenes/cornell/player.json | 21 +- bin/data/scenes/mcdonalds/door.json | 2 +- bin/data/scenes/mcdonalds/mcdonalds.json | 12 +- bin/data/scenes/mcdonalds/player.json | 13 +- bin/data/scenes/mcdonalds/scene.json | 4 +- bin/data/scenes/sh2_mcdonalds/door.json | 2 +- bin/data/scenes/sh2_mcdonalds/player.json | 11 - bin/data/scenes/sh2_mcdonalds/prop.json | 2 +- .../scenes/sh2_mcdonalds/sh2_mcdonalds.json | 12 +- bin/data/scenes/sm64/loading.json | 32 + bin/data/scenes/sm64/player.json | 19 + bin/data/scenes/sm64/scene.json | 89 ++ bin/data/scenes/sm64/sm64.json | 35 + bin/data/scenes/ss2/lift.json | 2 +- bin/data/scenes/ss2/medsci.json | 10 +- bin/data/scenes/ss2/player.json | 16 +- bin/data/shaders/common/vxgi.h | 2 +- bin/data/shaders/raytrace/shader.ray-gen.glsl | 13 +- bin/dreamcast/config.json | 2 +- client/client/ext.cpp | 4 +- client/main.cpp | 1 + engine/inc/uf/engine/graph/graph.h | 2 - engine/inc/uf/ext/opengl/opengl.h | 1 - engine/inc/uf/ext/reactphysics/reactphysics.h | 3 +- engine/inc/uf/ext/vulkan/vulkan.h | 1 - engine/inc/uf/macros.h | 9 +- engine/inc/uf/utils/io/fmt.h | 5 - engine/inc/uf/utils/math/vector/simd.inl | 29 +- engine/inc/uf/utils/memory/key_map.h | 9 + engine/inc/uf/utils/thread/thread.h | 2 +- .../win64/{ => other}/discord_game_sdk.bundle | Bin .../lib/win64/{ => other}/openvr_api.dll.sig | Bin engine/lib/win64/{ => other}/openvr_api.pdb | Bin engine/lib/win64/zig/libalut.pdb | Bin 0 -> 249856 bytes engine/src/engine/asset/asset.cpp | 4 +- engine/src/engine/graph/decode.cpp | 51 +- engine/src/engine/graph/encode.cpp | 10 +- engine/src/engine/graph/graph.cpp | 761 ++++++++++-------- engine/src/engine/object/behavior.cpp | 32 +- engine/src/engine/object/object.cpp | 16 - engine/src/ext/gltf/gltf.cpp | 36 +- engine/src/ext/gltf/processPrimitives.inl | 164 +++- engine/src/ext/gltf/processPrimitives2.inl | 214 ----- engine/src/ext/imgui/imgui.cpp | 2 +- engine/src/ext/lua/lua.cpp | 2 +- engine/src/ext/lua/usertypes/object.cpp | 4 +- engine/src/ext/lua/usertypes/quaternion.cpp | 2 +- engine/src/ext/lua/usertypes/vector.cpp | 4 +- engine/src/ext/opengl/commands.cpp | 12 +- engine/src/ext/opengl/opengl.cpp | 1 - engine/src/ext/reactphysics/reactphysics.cpp | 112 ++- engine/src/ext/vulkan/graphic.cpp | 3 +- engine/src/ext/vulkan/vulkan.cpp | 3 +- engine/src/utils/hook/hook.cpp | 2 +- engine/src/utils/http/http.cpp | 2 +- engine/src/utils/serialize/serializer.cpp | 13 +- ext/behaviors/player/behavior.cpp | 90 ++- ext/behaviors/player/behavior.h | 5 + ext/behaviors/raytrace/behavior.cpp | 32 +- ext/behaviors/raytrace/behavior.h | 2 + ext/gui/behavior.cpp | 16 +- ext/gui/manager/behavior.cpp | 4 +- ext/main.cpp | 43 +- makefiles/win64.clang.make | 5 +- makefiles/win64.gcc.make | 2 +- makefiles/win64.zig.make | 8 + program.sh | 4 +- 71 files changed, 1237 insertions(+), 932 deletions(-) create mode 100644 bin/data/scenes/sm64/loading.json create mode 100644 bin/data/scenes/sm64/player.json create mode 100644 bin/data/scenes/sm64/scene.json create mode 100644 bin/data/scenes/sm64/sm64.json rename engine/lib/win64/{ => other}/discord_game_sdk.bundle (100%) rename engine/lib/win64/{ => other}/openvr_api.dll.sig (100%) rename engine/lib/win64/{ => other}/openvr_api.pdb (100%) create mode 100644 engine/lib/win64/zig/libalut.pdb delete mode 100644 engine/src/ext/gltf/processPrimitives2.inl create mode 100644 makefiles/win64.zig.make diff --git a/Makefile b/Makefile index 4d94815d..9a53c79e 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,7 @@ BIN_DIR += ./bin ENGINE_SRC_DIR += ./engine/src ENGINE_INC_DIR += ./engine/inc ENGINE_LIB_DIR += ./engine/lib +DEP_SRC_DIR += ./dep/src EXT_SRC_DIR += ./ext CLIENT_SRC_DIR += ./client @@ -49,25 +50,29 @@ SPV_OPTIMIZER += $(VULKAN_SDK_PATH)/Bin/spirv-opt INC_DIR += $(ENGINE_INC_DIR) LIB_DIR += $(ENGINE_LIB_DIR) -INCS += -I$(ENGINE_INC_DIR) -I$(INC_DIR) -I./dep/ #-I/mingw64/include -LIBS += -L$(ENGINE_LIB_DIR) -L$(LIB_DIR) -L$(LIB_DIR)/$(ARCH) -L$(LIB_DIR)/$(PREFIX_PATH) +INCS += -I$(ENGINE_INC_DIR) -I./dep/include/ #-I/mingw64/include/ +LIBS += -L$(ENGINE_LIB_DIR) -L$(LIB_DIR)/$(PREFIX_PATH) -L$(LIB_DIR)/$(ARCH)/$(CC) -L$(LIB_DIR)/$(ARCH) #-L/mingw64/lib/ LINKS += $(UF_LIBS) $(EXT_LIBS) $(DEPS) DEPS += ifneq (,$(findstring win64,$(ARCH))) - REQ_DEPS += $(RENDERER) json:nlohmann png zlib openal ogg freetype curl luajit reactphysics meshoptimizer xatlas simd ctti gltf imgui fmt # ncurses openvr draco discord bullet ultralight-ux + ifneq (,$(findstring zig,$(CC))) + REQ_DEPS += $(RENDERER) json:nlohmann png zlib luajit reactphysics meshoptimizer xatlas simd ctti gltf imgui fmt curl freetype openal ogg # ncurses openvr draco discord bullet ultralight-ux + else + REQ_DEPS += $(RENDERER) json:nlohmann png zlib luajit reactphysics meshoptimizer xatlas simd ctti gltf imgui fmt curl freetype openal ogg # ncurses openvr draco discord bullet ultralight-ux + endif FLAGS += -DUF_ENV_WINDOWS -DUF_ENV_WIN64 -DWIN32_LEAN_AND_MEAN DEPS += -lgdi32 -ldwmapi LINKS += #-Wl,-subsystem,windows else ifneq (,$(findstring dreamcast,$(ARCH))) FLAGS += -DUF_ENV_DREAMCAST - REQ_DEPS += simd opengl gldc json:nlohmann reactphysics png zlib ctti lua fmt # lua ogg openal aldc gltf freetype bullet meshoptimizer draco luajit ultralight-ux ncurses curl openvr discord + REQ_DEPS += simd opengl gldc json:nlohmann reactphysics png zlib ctti lua fmt # ogg openal aldc gltf freetype bullet meshoptimizer draco luajit ultralight-ux ncurses curl openvr discord endif ifneq (,$(findstring vulkan,$(REQ_DEPS))) FLAGS += -DVK_USE_PLATFORM_WIN32_KHR -DUF_USE_VULKAN - DEPS += -lvulkan -lspirv-cross #-lVulkanMemoryAllocator - INCS += -I$(VULKAN_SDK_PATH)/include + DEPS += -lvulkan -lspirv-cross-core -lspirv-cross-cpp #-lVulkanMemoryAllocator + INCS += -I$(VULKAN_SDK_PATH)/include -I./dep/include/spirv_cross/ LIBS += -L$(VULKAN_SDK_PATH)/Lib endif ifneq (,$(findstring opengl,$(REQ_DEPS))) @@ -100,8 +105,8 @@ ifneq (,$(findstring fmt,$(REQ_DEPS))) endif ifneq (,$(findstring imgui,$(REQ_DEPS))) FLAGS += -DUF_USE_IMGUI - INCS += -I./dep/imgui/ - INCS += -I./dep/imgui/backends + INCS += -I./dep/include/imgui/ + INCS += -I./dep/include/imgui/backends endif ifneq (,$(findstring json,$(REQ_DEPS))) FLAGS += -DUF_USE_JSON @@ -222,7 +227,11 @@ endif # SRCS_DLL += $(wildcard $(ENGINE_SRC_DIR)/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*/*/*.cpp) #SRCS_DLL += $(wildcard $(ENGINE_SRC_DIR)/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*/*/*.cpp) -SRCS_DLL := $(shell find $(ENGINE_SRC_DIR) -name "*.cpp") $(shell find $(EXT_SRC_DIR) -name "*.cpp") +ifneq (,$(findstring zig,$(CC))) + SRCS_DLL := $(shell find $(ENGINE_SRC_DIR) -name "*.cpp") $(shell find $(EXT_SRC_DIR) -name "*.cpp") $(shell find $(DEP_SRC_DIR) -name "*.cpp") $(shell find ./dep/zig/src/ -name "*.cpp") +else + SRCS_DLL := $(shell find $(ENGINE_SRC_DIR) -name "*.cpp") $(shell find $(EXT_SRC_DIR) -name "*.cpp") $(shell find $(DEP_SRC_DIR) -name "*.cpp") +endif OBJS_DLL += $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_DLL)) BASE_DLL += lib$(LIB_NAME) IM_DLL += $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_DLL).$(TARGET_LIB_EXTENSION).a @@ -234,8 +243,8 @@ EXT_DEPS += -l$(LIB_NAME) $(DEPS) EXT_LINKS += $(UF_LIBS) $(EXT_LIBS) $(EXT_DEPS) EXT_LIB_DIR += $(ENGINE_LIB_DIR)/$(ARCH) -EXT_INCS += -I$(ENGINE_INC_DIR) -I$(EXT_INC_DIR) -I/mingw64/include -EXT_LIBS += -L$(ENGINE_LIB_DIR) -L$(EXT_LIB_DIR) -L$(EXT_LIB_DIR)/$(ARCH) -L$(EXT_LIB_DIR)/$(PREFIX_PATH) -L/mingw64/lib +EXT_INCS += $(INCS) +EXT_LIBS += $(LIBS) #SRCS_EXT_DLL += $(wildcard $(EXT_SRC_DIR)/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*/*/*.cpp) SRCS_EXT_DLL := $(shell find $(EXT_SRC_DIR) -name "*.cpp") @@ -377,4 +386,6 @@ backup: make CC=gcc RENDERER=vulkan clean make CC=clang RENDERER=opengl clean make CC=clang RENDERER=vulkan clean + make CC=zig RENDERER=opengl clean + make CC=zig RENDERER=vulkan clean $(7Z) a -r ../misc/backups/$(shell date +"%Y.%m.%d\ %H-%M-%S").7z . \ No newline at end of file diff --git a/bin/data/config.json b/bin/data/config.json index c1d56d89..13529a97 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -1,7 +1,7 @@ { "engine": { "scenes": { - "start": "SH2_McDonalds", + "start": "McDonalds", "meshes": { "interleaved": false }, "matrix": { "reverseInfinite": true }, "lights": { "enabled": true, @@ -16,9 +16,9 @@ }, "textures": { "max": { - "2D": 512, - "cube": 128, - "3D": 64 + "2D": 1024, + "cube": 1024, + "3D": 128 } }, "vxgi": { @@ -38,6 +38,8 @@ } }, "rt": { + "size": [ 960, 540 ], + "filter": "NEAREST", "defaultRayBounds": [ 0.001, 1024.0 ], "alphaTestOffset": 0.001, "samples": 1, @@ -90,6 +92,7 @@ }, "invariant": { // "multithreaded recording": true + "deferred alias output to swapchain": false, "defaultStageBuffers": true }, "pipelines": { @@ -98,7 +101,7 @@ "vxgi": true, "culling": true, "bloom": false, - "rt": false + "rt": true }, "formats": { "depth": "D32_SFLOAT", @@ -288,13 +291,13 @@ "cursor" : { "visible" : true, "center" : false, - "sensitivity": [ 1.25, 1.25 ], - "smoothing": [ 10, 10 ] + "sensitivity": [ 0.75, 0.75 ], + "smoothing": [ 4, 4 ] }, - "mode" : "borderless", // fullscreen, borderless, windowed + "mode" : "windowed", // fullscreen, borderless, windowed "icon" : "./data/textures/icon.png", - "size" : [ 1920, 1080 ], - // "size" : [ 1280, 720 ], + // "size" : [ 1920, 1080 ], + "size" : [ 1280, 720 ], // "size" : [ 960, 540 ], // "size" : [ 640, 480 ], // "size" : [ 256, 224 ], diff --git a/bin/data/entities/model.json b/bin/data/entities/model.json index b33e2187..655c61d4 100644 --- a/bin/data/entities/model.json +++ b/bin/data/entities/model.json @@ -12,8 +12,16 @@ "model": { "debug": { "no cleanup": false, - "print tree": false, - "print stats": false + "print": { + "tree": false, + "stats": false, + "meshes": false, + "materials": false, + "textures": false + } + }, + "sanitizer": { + "winding order": true }, "exporter": { "enabled": true, @@ -24,14 +32,9 @@ "precision": 4, "combined": false, "encode buffers": true, - "unwrap": true, + "unwrap": true, // "tagged", // "unwrap": "tagged", - "optimize": "tagged", - // "optimize": true, - "quit": true, - "mesh": { - // "print": true - } + "quit": true }, "baking": { "enabled": true, @@ -55,8 +58,11 @@ "rotateCharts": true } }, - // "filter": "NEAREST", - "lightmap": "auto", + "renderer": { + "front face": "ccw", + "cull mode": "back", + "filter": "linear" + }, "flags": { "ATLAS": false, "INVERT": false, @@ -68,6 +74,7 @@ "NORMALS": false }, "lights": { + "lightmap": "auto", // "disable if lightmapped": false, "shadows": true } diff --git a/bin/data/entities/player.json b/bin/data/entities/player.json index c508710d..0cc23dc8 100644 --- a/bin/data/entities/player.json +++ b/bin/data/entities/player.json @@ -14,36 +14,10 @@ "scale": [ 1, 1, 1 ] }, "assets": [ - { "filename": "./playerModel.json", "delay": 1.0, "single threaded": true }, + "./playerModel.json", "./hands.json" ], "system": { - "physics": { - "movement": { - "walk": 1, - "move": 8, - "run": 16, - "rotate": 1.5, - "air": 0.1, - - "crouch": 1, - "jump": [ 0, 4, 0 ], - "look": 0.75 - }, - - "gravity": [ 0, -9.81, 0 ], - "inertia": [ 0, 0, 0 ], - - "type": "capsule", - "radius": 1, - "height": 2, - - "mass": 100, - "friction": 0.8, - "restitution": 0.0, - - "shared": false - }, "hot reload": { "enabled": true } @@ -74,6 +48,36 @@ ] } }, + "movement": { + "walk": 1, + "move": 4, + "run": 16, + "rotate": 1.5, + "air": 0.1, + + "crouch": 1, + "jump": [ 0, 4, 0 ], + "look": 1, + "floored": { + "feet": [ 0, -1.5, 0 ], + "floor": [ 0, -0.5, 0 ], + "print": false + } + }, + "physics": { + "gravity": [ 0, -9.81, 0 ], + "inertia": [ 0, 0, 0 ], + + "type": "capsule", + "radius": 1, + "height": 2, + + "mass": 100, + "friction": 0.95, + "restitution": 0.0, + + "shared": false + }, "camera": { "offset": [ 0, 0, 0 ], "position" : [ 0, 1.8, 0 ], diff --git a/bin/data/scenes/cornell/player.json b/bin/data/scenes/cornell/player.json index 72aafad1..ddfc0fa3 100644 --- a/bin/data/scenes/cornell/player.json +++ b/bin/data/scenes/cornell/player.json @@ -3,25 +3,10 @@ "assets": [ // { "filename": "/gui/hud/hud.json", "delay": 0 } ], - "system": { - "physics": { - "gravity": [ 0, 0, 0 ] - }, - "hot reload": { - "enabled": true - } - }, "metadata": { - "overlay": { - "floating": true - }, - "collider": false, - "light": { - "should": false, - "color": [1, 1, 1], - "position": [ 0, 2.5, 0 ], - "power": 1, - "radius": [0.001, 32] + "physics": { + "collider": false, + "gravity": [ 0, 0, 0 ] } } } \ No newline at end of file diff --git a/bin/data/scenes/mcdonalds/door.json b/bin/data/scenes/mcdonalds/door.json index 41caf066..4da4924a 100644 --- a/bin/data/scenes/mcdonalds/door.json +++ b/bin/data/scenes/mcdonalds/door.json @@ -1,6 +1,6 @@ { "assets": ["./scripts/door.lua"], - "system": { + "metadata": { "physics": { "type": "bounding box", "recenter": true diff --git a/bin/data/scenes/mcdonalds/mcdonalds.json b/bin/data/scenes/mcdonalds/mcdonalds.json index 7b9c9e15..c30e80dd 100644 --- a/bin/data/scenes/mcdonalds/mcdonalds.json +++ b/bin/data/scenes/mcdonalds/mcdonalds.json @@ -15,13 +15,17 @@ "metadata": { "model": { "baking": { - "enabled": false, + "enabled": true, "resolution": 2048, "settings": { "useInputMeshUvs": false } }, - // "cull mode": "none", + "renderer": { + "front face": "cw", + "cull mode": "back", + "filter": "linear" + }, "tags": { "/^worldspawn/": { "physics": { "type": "mesh", "static": true }, @@ -49,7 +53,9 @@ "func_physbox_5212": { "action": "load", "payload": { "import": "./door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } }, "func_physbox_5548": { "action": "load", "payload": { "import": "./door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } }, - "func_physbox_5931": { "action": "load", "payload": { "import": "./door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } } + "func_physbox_5931": { "action": "load", "payload": { "import": "./door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } }, + + "tools/toolsnodraw": { "material": { "base": [0, 0, 0, 0] } } } } } diff --git a/bin/data/scenes/mcdonalds/player.json b/bin/data/scenes/mcdonalds/player.json index dec26007..e2c6058c 100644 --- a/bin/data/scenes/mcdonalds/player.json +++ b/bin/data/scenes/mcdonalds/player.json @@ -3,17 +3,8 @@ "assets": [ // { "filename": "/gui/hud/hud.json", "delay": 0 } ], + "system": { + }, "metadata": { - "overlay": { - "floating": true - }, - "collider": true, - "light": { - "should": false, - "color": [1, 1, 1], - "position": [ 0, 2.5, 0 ], - "power": 1, - "radius": [0.001, 32] - } } } \ No newline at end of file diff --git a/bin/data/scenes/mcdonalds/scene.json b/bin/data/scenes/mcdonalds/scene.json index 314507fe..c949a3cd 100644 --- a/bin/data/scenes/mcdonalds/scene.json +++ b/bin/data/scenes/mcdonalds/scene.json @@ -36,8 +36,8 @@ "exposure": 1.0, "gamma": 1.0, "brightnessThreshold": 1.2, - "ambient": [ 0, 0, 0 ], - // "ambient": [ 0.025, 0.025, 0.025 ], + // "ambient": [ 0, 0, 0 ], + "ambient": [ 0.025, 0.025, 0.025 ], // "ambient": [ 0.075, 0.075, 0.075 ], // "ambient": [ 0.1, 0.1, 0.1 ], // "ambient": [ 0.4, 0.4, 0.4 ], diff --git a/bin/data/scenes/sh2_mcdonalds/door.json b/bin/data/scenes/sh2_mcdonalds/door.json index 1f268ed9..8ba9c58c 100644 --- a/bin/data/scenes/sh2_mcdonalds/door.json +++ b/bin/data/scenes/sh2_mcdonalds/door.json @@ -1,6 +1,6 @@ { "assets": ["./scripts/door.lua"], - "system": { + "metadata": { "physics": { "mass": 0, "inertia": [0, 0, 0], diff --git a/bin/data/scenes/sh2_mcdonalds/player.json b/bin/data/scenes/sh2_mcdonalds/player.json index 18145681..f41a4d75 100644 --- a/bin/data/scenes/sh2_mcdonalds/player.json +++ b/bin/data/scenes/sh2_mcdonalds/player.json @@ -5,16 +5,5 @@ "./playerLight.json" ], "metadata": { - "overlay": { - "floating": true - }, - "collider": true, - "light": { - "should": false, - "color": [1, 1, 1], - "position": [ 0, 2.5, 0 ], - "power": 1, - "radius": [0.001, 32] - } } } \ No newline at end of file diff --git a/bin/data/scenes/sh2_mcdonalds/prop.json b/bin/data/scenes/sh2_mcdonalds/prop.json index 2d33ce82..47b31635 100644 --- a/bin/data/scenes/sh2_mcdonalds/prop.json +++ b/bin/data/scenes/sh2_mcdonalds/prop.json @@ -1,6 +1,6 @@ { "assets": [], - "system": { + "metadata": { "physics": { "mass": 0, "inertia": [0, 0, 0], diff --git a/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json b/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json index 3143edd2..c7802fbe 100644 --- a/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json +++ b/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json @@ -8,8 +8,6 @@ ], "metadata": { "model": { - "cull mode": "none", - "alpha mode": "BLEND", "baking": { "enabled": true, "resolution": 1024, @@ -17,12 +15,18 @@ "useInputMeshUvs": false } }, + "renderer": { + "front face": "cw", + "cull mode": "back", + "filter": "linear" + }, "tags": { "/^worldspawn/": { "physics": { "type": "mesh", "static": true }, "grid": { "size": [3,1,3], "epsilon": 1.0, "cleanup": true, "print": true } }, "/^cb$/": { + "physics": { "type": "mesh", "static": true }, "grid": { "size": [6,1,6], "epsilon": 1.0, "cleanup": true, "print": true } }, // "worldspawn_sh2": { "physics": { "type": "mesh", "static": true } }, @@ -101,7 +105,9 @@ "prop_physics_override_5810": { "action": "load", "payload": { "import": "./prop.json" } }, "prop_physics_override_5814": { "action": "load", "payload": { "import": "./prop.json" } }, "prop_physics_override_5822": { "action": "load", "payload": { "import": "./prop.json" } }, - "prop_physics_override_5824": { "action": "load", "payload": { "import": "./prop.json" } } + "prop_physics_override_5824": { "action": "load", "payload": { "import": "./prop.json" } }, + + "tools/toolsnodraw": { "material": { "base": [0, 0, 0, 0] } } } } } diff --git a/bin/data/scenes/sm64/loading.json b/bin/data/scenes/sm64/loading.json new file mode 100644 index 00000000..0ec6c59c --- /dev/null +++ b/bin/data/scenes/sm64/loading.json @@ -0,0 +1,32 @@ +{ + "name": "Gui: Loading", + "type": "Object", + "behaviors": [ + // "GuiBehavior" + ], + "assets": [ + { "filename": "./mcdonalds.json", "delay": 0 } + ], + "ignore": false, + "transform": { + "position": [ -0.830591, -0.699509, 0 ], + "rotation": { + "axis": [ 1, 0, 0 ], + "angle": 0 + }, + "scale": [ 0.258737, 0.115371, 1 ] + }, + "metadata": { + "uv": [ 0, 0, 1, 1 ], + "color": [ 1, 1, 1, 0.1 ], + "location": "", + "scaling": "relative", + "text settings": { + "stroke": [ 1, 0.749, 0.368, 1 ], + "color": [ 1, 0.749, 0.368, 1 ], + + "string": "Loading...", + "string1": "コマンド" + } + } +} \ No newline at end of file diff --git a/bin/data/scenes/sm64/player.json b/bin/data/scenes/sm64/player.json new file mode 100644 index 00000000..f56e6fd8 --- /dev/null +++ b/bin/data/scenes/sm64/player.json @@ -0,0 +1,19 @@ +{ + "import": "/player.json", + "assets": [ + // { "filename": "/gui/hud/hud.json", "delay": 0 } + ], + "metadata": { + "movement": { + "jump": [0, 1, 0], + "floored": { + "feet": [ 0, 0, 0 ], + "floor": [ 0, -0.8, 0 ] + } + }, + "physics": { + "radius": 0.25, + "height": 0.25 + } + } +} \ No newline at end of file diff --git a/bin/data/scenes/sm64/scene.json b/bin/data/scenes/sm64/scene.json new file mode 100644 index 00000000..dc7f523a --- /dev/null +++ b/bin/data/scenes/sm64/scene.json @@ -0,0 +1,89 @@ +{ + "import": "/scene.json", + "assets": [ + // "./loading.json", + "./sm64.json" + ], + "system": { + "hot reload": { + "enabled": true + }, + "renderer": { + "shader": { + "mode": 1, + "scalar": 16, + "parameters": [ 0, 0, 0, "time" ], + "frame accumulate limit": 0 + }, + "clear values": [ + [ 1, 1, 1, 0 ] + ] + } + }, + "metadata": { + "menus": { + "pause": "/gui/pause/menu.json" + }, + "bloom": { + "scale": 6.0, + "strength": 0.125, + "sigma": 0.125, + "samples": 8 + }, + "light": { + "exposure": 1.0, + "gamma": 1.0, + "brightnessThreshold": 1.2, + // "ambient": [ 0, 0, 0 ], + // "ambient": [ 0.025, 0.025, 0.025 ], + // "ambient": [ 0.075, 0.075, 0.075 ], + // "ambient": [ 0.1, 0.1, 0.1 ], + "ambient": [ 0.4, 0.4, 0.4 ], + // "ambient": [ 0.8, 0.8, 0.8 ], + + "fog-": { + "color": [ 0.5, 0.5, 0.5 ], + "range": [ 16, 32 ], + "step scale": 2, + "absorbtion": 0.01, + "density": { + "threshold": 0.35, + "multiplier": 5.0, + "scale": 25.0, + "offset": [0, -1, 1], + "timescale": 8 + } + }, + "should": true, + "shadows": true + }, + "noise": { + "size": [ 32, 32, 32 ] + }, + "vxgi": { + /* + "limiter": 1, + "size": 96, + "dispatch": 8, + "cascades": 2, + "cascadePower": 4, + "voxelizeScale": 1, + "shadows": 0, + */ + "occlusionFalloff": 2, + "traceStartOffsetFactor": 1, + "granularity": 2, + "extents": { + "min": [ -1.5, -1.5, -1.5 ], + "max": [ 1.5, 1.5, 1.5 ] + } + }, + "rt": { + "defaultRayBounds": [ 0.001, 1024.0 ], + "alphaTestOffset": 1.125, + "samples": 1, + "paths": 1, + "frameAccumulationMinimum": 0 + } + } +} \ No newline at end of file diff --git a/bin/data/scenes/sm64/sm64.json b/bin/data/scenes/sm64/sm64.json new file mode 100644 index 00000000..8109fd6c --- /dev/null +++ b/bin/data/scenes/sm64/sm64.json @@ -0,0 +1,35 @@ +{ + "import": "/model.json", + "assets": [ + // { "filename": "./models/sm64_bbb.glb" } + { "filename": "./models/sm64_bbb/graph.json" } + ], + "metadata": { + "model": { + "baking": { + "enabled": true, + "resolution": 2048, + "settings": { + "useInputMeshUvs": false + } + }, + "renderer": { + "front face": "cw", + "cull mode": "none", + "filter": "nearest" + }, + "tags": { + "/^worldspawn/": { + "physics": { "type": "mesh", "static": true }, + "grid": { "size": [3,1,3], "epsilon": 1.0, "cleanup": true, "print": true }, + // "optimize mesh": { "simplify": 0 }, + "unwrap mesh": true + }, + "info_player_spawn": { "action": "attach", "filename": "./player.json" }, + + "Material.071_574B138E_c.bmp": { "material": { "modeAlpha": "BLEND" } }, + "Material.070_41A41EE3_c.bmp": { "material": { "modeAlpha": "BLEND" } } + } + } + } +} \ No newline at end of file diff --git a/bin/data/scenes/ss2/lift.json b/bin/data/scenes/ss2/lift.json index 4fa0e75d..d94f0d54 100644 --- a/bin/data/scenes/ss2/lift.json +++ b/bin/data/scenes/ss2/lift.json @@ -1,6 +1,6 @@ { "assets": [ "./scripts/lift.lua" ], - "system": { + "metadata": { "physics": { "type": "bounding box", "recenter": true, diff --git a/bin/data/scenes/ss2/medsci.json b/bin/data/scenes/ss2/medsci.json index 2c02a9ba..c940d22c 100644 --- a/bin/data/scenes/ss2/medsci.json +++ b/bin/data/scenes/ss2/medsci.json @@ -5,7 +5,7 @@ // { "filename": "./test.json", "delay": 1 }, // { "filename": "./models/tiny_msci.glb" } - { "filename": "./models/tiny_msci/graph.json" } + // { "filename": "./models/tiny_msci/graph.json" } // { "filename": "./models/micro_sci.glb" } // { "filename": "./models/micro_sci/graph.json" } @@ -14,7 +14,7 @@ // { "filename": "./models/msci/graph.json" } // { "filename": "./models/medsci.glb" } - // { "filename": "./models/medsci/graph.json" } + { "filename": "./models/medsci/graph.json" } ], "metadata": { "model": { @@ -22,7 +22,11 @@ "enabled": true, "resolution": 1024 }, - "filter": "LINEAR", + "renderer": { + "front face": "cw", + "cull mode": "back", + "filter": "linear" + }, "tags": { "/^worldspawn/": { "physics": { "type": "mesh", "static": true }, diff --git a/bin/data/scenes/ss2/player.json b/bin/data/scenes/ss2/player.json index 49c574ca..1c608815 100644 --- a/bin/data/scenes/ss2/player.json +++ b/bin/data/scenes/ss2/player.json @@ -12,16 +12,12 @@ "scale": [ 1, 1, 1 ] }, "metadata": { - "overlay": { - "floating": true - }, - "collider": true, - "light": { - "should": false, - "color": [1, 1, 1], - "position": [ 0, 2.5, 0 ], - "power": 1, - "radius": [0.001, 32] + "movement": { + "floored": { + "feet": [ 0, -1.5, 0 ], + "floor": [ 0, -0.5, 0 ], + "print": false + } } } } \ No newline at end of file diff --git a/bin/data/shaders/common/vxgi.h b/bin/data/shaders/common/vxgi.h index 3dae4fc3..2b3b6605 100644 --- a/bin/data/shaders/common/vxgi.h +++ b/bin/data/shaders/common/vxgi.h @@ -135,7 +135,7 @@ void indirectLighting() { const float DIFFUSE_INDIRECT_FACTOR = 1.0f / float(CONES_COUNT); const float SPECULAR_CONE_APERTURE = clamp(tan(PI * 0.5f * surface.material.roughness), 0.0174533f, PI); // tan( R * PI * 0.5f * 0.1f ); - const float SPECULAR_INDIRECT_FACTOR = (1.0 - surface.material.metallic) * 0.5; // 1.0f; + const float SPECULAR_INDIRECT_FACTOR = (1.0 - surface.material.metallic) * 0.25; // 1.0f; vec4 indirectDiffuse = vec4(0); vec4 indirectSpecular = vec4(0); diff --git a/bin/data/shaders/raytrace/shader.ray-gen.glsl b/bin/data/shaders/raytrace/shader.ray-gen.glsl index 2efb2241..7bc973d8 100644 --- a/bin/data/shaders/raytrace/shader.ray-gen.glsl +++ b/bin/data/shaders/raytrace/shader.ray-gen.glsl @@ -6,8 +6,8 @@ #pragma shader_stage(raygen) layout (constant_id = 0) const uint PASSES = 2; layout (constant_id = 1) const uint TEXTURES = 512; -layout (constant_id = 2) const uint CUBEMAPS = 128; -layout (constant_id = 3) const uint CASCADES = 4; +layout (constant_id = 2) const uint CUBEMAPS = 8; +layout (constant_id = 3) const uint CASCADES = 1; #define COMPUTE 1 #define PBR 1 @@ -70,17 +70,18 @@ layout (std140, binding = 6) readonly buffer Lights { layout (binding = 7) uniform sampler2D samplerTextures[TEXTURES]; layout (binding = 8) uniform samplerCube samplerCubemaps[CUBEMAPS]; - +layout (binding = 9) uniform sampler3D samplerNoise; #if VXGI - layout (binding = 14) uniform usampler3D voxelId[CASCADES]; - layout (binding = 15) uniform sampler3D voxelNormal[CASCADES]; - layout (binding = 16) uniform sampler3D voxelRadiance[CASCADES]; + layout (binding = 10) uniform usampler3D voxelId[CASCADES]; + layout (binding = 11) uniform sampler3D voxelNormal[CASCADES]; + layout (binding = 12) uniform sampler3D voxelRadiance[CASCADES]; #endif layout (location = 0) rayPayloadEXT RayTracePayload payload; #include "../common/functions.h" #include "../common/light.h" +#include "../common/fog.h" #if VXGI #include "../common/vxgi.h" #endif diff --git a/bin/dreamcast/config.json b/bin/dreamcast/config.json index c674c0b2..df8ede35 100644 --- a/bin/dreamcast/config.json +++ b/bin/dreamcast/config.json @@ -1,7 +1,7 @@ { "engine": { "scenes": { - "start": "SS2", + "start": "SM64", "meshes": { "interleaved": false }, "matrix": { "reverseInfinite": false }, "lights": { "enabled": false, diff --git a/client/client/ext.cpp b/client/client/ext.cpp index c45ae933..c865b3d7 100644 --- a/client/client/ext.cpp +++ b/client/client/ext.cpp @@ -17,7 +17,6 @@ uf::Window client::window; uf::Serializer client::config; void client::initialize() { - uf::IoStream::ncurses = true; uf::renderer::device.window = &client::window; ext::load(); @@ -111,6 +110,9 @@ void client::initialize() { #if UF_ENV_DREAMCAST client::window.pollEvents(); #endif + + { + } } void client::tick() { diff --git a/client/main.cpp b/client/main.cpp index 310e3d2d..d20ab961 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -67,6 +67,7 @@ int main(int argc, char** argv){ if ( timer.elapsed().asDouble() >= next ) { // UF_MSG_INFO("Waiting for " << ( client::ready ? "client" : "extension / engine" ) << " to initialize... Retrying in " << next << " seconds."); + UF_MSG_INFO("Waiting for {} to initialize; retrying in {} seconds", ( client::ready ? "client" : "extension / engine" ), next); next *= 2; } } diff --git a/engine/inc/uf/engine/graph/graph.h b/engine/inc/uf/engine/graph/graph.h index 4e15e9f3..87e89f4c 100644 --- a/engine/inc/uf/engine/graph/graph.h +++ b/engine/inc/uf/engine/graph/graph.h @@ -61,8 +61,6 @@ namespace pod { // Local storage, used for save/load struct Storage { - - uf::stl::KeyMap instances; uf::stl::KeyMap instanceAddresses; uf::stl::KeyMap> primitives; diff --git a/engine/inc/uf/ext/opengl/opengl.h b/engine/inc/uf/ext/opengl/opengl.h index 84895359..4a75e63d 100644 --- a/engine/inc/uf/ext/opengl/opengl.h +++ b/engine/inc/uf/ext/opengl/opengl.h @@ -38,7 +38,6 @@ namespace ext { extern UF_API bool multithreadedRecording; extern UF_API uf::stl::string deferredMode; - extern UF_API bool deferredReconstructPosition; extern UF_API bool deferredAliasOutputToSwapchain; extern UF_API bool deferredSampling; extern UF_API bool multiview; diff --git a/engine/inc/uf/ext/reactphysics/reactphysics.h b/engine/inc/uf/ext/reactphysics/reactphysics.h index 71beb221..f0361cea 100644 --- a/engine/inc/uf/ext/reactphysics/reactphysics.h +++ b/engine/inc/uf/ext/reactphysics/reactphysics.h @@ -104,8 +104,7 @@ namespace ext { // ray casting float UF_API rayCast( const pod::Vector3f&, const pod::Vector3f& ); - float UF_API rayCast( const pod::Vector3f&, const pod::Vector3f&, size_t& ); - float UF_API rayCast( const pod::Vector3f&, const pod::Vector3f&, uf::Object*& ); + float UF_API rayCast( const pod::Vector3f&, const pod::Vector3f&, uf::Object*, uf::Object*& ); // allows noclip void UF_API activateCollision( pod::PhysicsState&, bool = true ); diff --git a/engine/inc/uf/ext/vulkan/vulkan.h b/engine/inc/uf/ext/vulkan/vulkan.h index 1979e85b..12a3358a 100644 --- a/engine/inc/uf/ext/vulkan/vulkan.h +++ b/engine/inc/uf/ext/vulkan/vulkan.h @@ -75,7 +75,6 @@ namespace ext { extern UF_API bool multithreadedRecording; extern UF_API uf::stl::string deferredMode; - extern UF_API bool deferredReconstructPosition; extern UF_API bool deferredAliasOutputToSwapchain; extern UF_API bool deferredSampling; extern UF_API bool multiview; diff --git a/engine/inc/uf/macros.h b/engine/inc/uf/macros.h index d45eeaa9..da0b998b 100644 --- a/engine/inc/uf/macros.h +++ b/engine/inc/uf/macros.h @@ -27,7 +27,7 @@ #if UF_USE_FMT #define UF_MSG(CATEGORY, ...) uf::io::log(CATEGORY, __FILE__, __FUNCTION__, __LINE__, ::fmt::format(__VA_ARGS__)); #else - #define UF_MSG(CATEGORY, ...) uf::io::log(CATEGORY, __FILE__, __FUNCTION__, __LINE__, #__VA_ARGS__); // std::cout << "[" << CATEGORY << "] [" << __FILE__ << ":" << __FUNCTION__ << "@" << __LINE__ << "]" << std::endl;; + #define UF_MSG(CATEGORY, ...) uf::io::log(CATEGORY, __FILE__, __FUNCTION__, __LINE__, #__VA_ARGS__); #endif #define UF_MSG_DEBUG(...) if (UF_DEBUG) UF_MSG("DEBUG", __VA_ARGS__); @@ -45,7 +45,8 @@ #define UF_NO_EXCEPTIONS 0 #define UF_EXCEPTIONS 1 #define UF_EXCEPTION(...) {\ - throw std::runtime_error(UF_MSG_ERROR(__VA_ARGS__));\ + auto msg = UF_MSG_ERROR(__VA_ARGS__);\ + throw std::runtime_error(msg);\ } #endif @@ -81,7 +82,7 @@ TIMER_TRACE_PREV = TIMER_TRACE_CUR;\ } -#define UF_TIMER_MULTITRACE_END(X) UF_MSG_DEBUG(X); +#define UF_TIMER_MULTITRACE_END(...) UF_MSG_DEBUG(__VA_ARGS__); #include #define TYPE_SANITIZE(T) std::remove_cv_t> @@ -108,7 +109,7 @@ #define ALIGNED_SIZE(V, A) ((V + A - 1) & ~(A - 1)) -#define UF_MSG_PEEK(X) #X << ": " << X +#define UF_MSG_PEEK(X) #X"="+std::to_string(X) #if UF_ENV_DREAMCAST #define DC_STATS() {\ diff --git a/engine/inc/uf/utils/io/fmt.h b/engine/inc/uf/utils/io/fmt.h index b7217426..df1f59a8 100644 --- a/engine/inc/uf/utils/io/fmt.h +++ b/engine/inc/uf/utils/io/fmt.h @@ -3,12 +3,7 @@ #if UF_USE_FMT #if UF_ENV_DREAMCAST #define FMT_HEADER_ONLY - // #include - - // #include - // #include #include - // #include #else // #include #endif diff --git a/engine/inc/uf/utils/math/vector/simd.inl b/engine/inc/uf/utils/math/vector/simd.inl index d7a5e6c3..2c00d536 100644 --- a/engine/inc/uf/utils/math/vector/simd.inl +++ b/engine/inc/uf/utils/math/vector/simd.inl @@ -139,8 +139,11 @@ inline uf::simd::value /*UF_API*/ uf::simd::sqrt( uf::simd::value } inline float /*UF_API*/ uf::simd::dot( uf::simd::value x, uf::simd::value y ) { #if SSE_INSTR_SET >= 5 + float res; __m128 result = _mm_dp_ps(x, y, 0xFF); - return uf::simd::vector( result )[0]; + _mm_store_ss(&res, result); + return res; +// return uf::simd::vector( result )[0]; #elif SSE_INSTR_SET >= 3 __m128 mulRes = _mm_mul_ps(x, y); __m128 shufReg = _mm_movehdup_ps(mulRes); @@ -155,13 +158,13 @@ inline float /*UF_API*/ uf::simd::dot( uf::simd::value x, uf::simd::value inline uf::simd::value /*UF_API*/ uf::simd::load( const int32_t* f ) { #if SSE_INSTR_SET >= 3 #if UF_VECTOR_ALIGNED - return _mm_load_si128(f); + return _mm_load_si128((__m128i*) f); #else - if ( uf::aligned(f, 16) ) return _mm_load_si128(f); + if ( uf::aligned(f, 16) ) return _mm_load_si128((__m128i*) f); alignas(16) int32_t s[4]; memcpy( &s[0], f, sizeof(int32_t) * 4 ); - return _mm_load_si128(s); + return _mm_load_si128((__m128i*) s); #endif #else return uf::simd::value( f[0], f[1], f[2], f[3] ); @@ -170,12 +173,12 @@ inline uf::simd::value /*UF_API*/ uf::simd::load( const int32_t* f ) { inline void /*UF_API*/ uf::simd::store( uf::simd::value v, int32_t* f ) { #if SSE_INSTR_SET >= 3 #if UF_VECTOR_ALIGNED - return _mm_store_si128(f, v); + return _mm_store_si128((__m128i*) f, v); #else - if ( uf::aligned(f, 16) ) return _mm_store_si128(f, v); + if ( uf::aligned(f, 16) ) return _mm_store_si128((__m128i*) f, v); alignas(16) int32_t s[4]; - _mm_store_si128(&s[0], v); + _mm_store_si128((__m128i*) &s[0], v); memcpy( f, &s[0], sizeof(int32_t) * 4 ); #endif #else @@ -246,13 +249,13 @@ inline int32_t /*UF_API*/ uf::simd::dot( uf::simd::value x, uf::simd::v inline uf::simd::value /*UF_API*/ uf::simd::load( const uint32_t* f ) { #if SSE_INSTR_SET >= 3 #if UF_VECTOR_ALIGNED - return _mm_load_si128(f); + return _mm_load_si128((__m128i*) f); #else - if ( uf::aligned(f, 16) ) return _mm_load_si128(f); + if ( uf::aligned(f, 16) ) return _mm_load_si128((__m128i*) f); alignas(16) uint32_t s[4]; memcpy( &s[0], f, sizeof(uint32_t) * 4 ); - return _mm_load_si128(s); + return _mm_load_si128((__m128i*) &s[0]); #endif #else return uf::simd::value( f[0], f[1], f[2], f[3] ); @@ -261,12 +264,12 @@ inline uf::simd::value /*UF_API*/ uf::simd::load( const uint32_t* f ) inline void /*UF_API*/ uf::simd::store( uf::simd::value v, uint32_t* f ) { #if SSE_INSTR_SET >= 3 #if UF_VECTOR_ALIGNED - return _mm_store_si128(f, v); + return _mm_store_si128((__m128i*) f, v); #else - if ( uf::aligned(f, 16) ) return _mm_store_si128(f, v); + if ( uf::aligned(f, 16) ) return _mm_store_si128((__m128i*) f, v); alignas(16) uint32_t s[4]; - _mm_store_si128(&s[0], v); + _mm_store_si128((__m128i*) &s[0], v); memcpy( f, &s[0], sizeof(uint32_t) * 4 ); #endif #else diff --git a/engine/inc/uf/utils/memory/key_map.h b/engine/inc/uf/utils/memory/key_map.h index 27f1c2d9..cfefa373 100644 --- a/engine/inc/uf/utils/memory/key_map.h +++ b/engine/inc/uf/utils/memory/key_map.h @@ -15,8 +15,10 @@ namespace uf { uf::stl::unordered_map indices; T& operator[]( const Key& key ); + void reserve( size_t i ); uf::stl::vector flatten() const; + void clear(); }; } } @@ -38,6 +40,13 @@ void uf::stl::KeyMap::reserve( size_t i ) { map.reserve(i); } +template +void uf::stl::KeyMap::clear() { + keys.clear(); + indices.clear(); + map.clear(); +} + template uf::stl::vector uf::stl::KeyMap::flatten() const { uf::stl::vector res; res.reserve(keys.size()); diff --git a/engine/inc/uf/utils/thread/thread.h b/engine/inc/uf/utils/thread/thread.h index 86fe582c..6a1e9eca 100644 --- a/engine/inc/uf/utils/thread/thread.h +++ b/engine/inc/uf/utils/thread/thread.h @@ -91,7 +91,7 @@ namespace uf { pod::Thread& UF_API get( const uf::stl::string& ); bool UF_API isMain(); - pod::Thread& currentThread(); + pod::Thread& UF_API currentThread(); /* Acts on thread */ void UF_API start( pod::Thread& ); diff --git a/engine/lib/win64/discord_game_sdk.bundle b/engine/lib/win64/other/discord_game_sdk.bundle similarity index 100% rename from engine/lib/win64/discord_game_sdk.bundle rename to engine/lib/win64/other/discord_game_sdk.bundle diff --git a/engine/lib/win64/openvr_api.dll.sig b/engine/lib/win64/other/openvr_api.dll.sig similarity index 100% rename from engine/lib/win64/openvr_api.dll.sig rename to engine/lib/win64/other/openvr_api.dll.sig diff --git a/engine/lib/win64/openvr_api.pdb b/engine/lib/win64/other/openvr_api.pdb similarity index 100% rename from engine/lib/win64/openvr_api.pdb rename to engine/lib/win64/other/openvr_api.pdb diff --git a/engine/lib/win64/zig/libalut.pdb b/engine/lib/win64/zig/libalut.pdb new file mode 100644 index 0000000000000000000000000000000000000000..1ba425e91a977ca311cf57532485e27a03a785de GIT binary patch literal 249856 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 literal 0 HcmV?d00001 diff --git a/engine/src/engine/asset/asset.cpp b/engine/src/engine/asset/asset.cpp index c876433d..bc8e1f8a 100644 --- a/engine/src/engine/asset/asset.cpp +++ b/engine/src/engine/asset/asset.cpp @@ -264,8 +264,8 @@ uf::stl::string uf::Asset::load(const uf::Asset::Payload& payload ) { asset = uf::graph::load( filename, metadata[payload.filename] ); uf::graph::process( asset ); #if !UF_ENV_DREAMCAST - if ( asset.metadata["debug"]["print stats"].as() ) UF_MSG_INFO("{}", uf::graph::stats( asset )); - if ( asset.metadata["debug"]["print tree"].as() ) UF_MSG_INFO("{}", uf::graph::print( asset )); + if ( asset.metadata["debug"]["print"]["stats"].as() ) UF_MSG_INFO("{}", uf::graph::stats( asset )); + if ( asset.metadata["debug"]["print"]["tree"].as() ) UF_MSG_INFO("{}", uf::graph::print( asset )); #endif if ( !asset.metadata["debug"]["no cleanup"].as() ) uf::graph::cleanup( asset ); } break; diff --git a/engine/src/engine/graph/decode.cpp b/engine/src/engine/graph/decode.cpp index aad34557..58352ed7 100644 --- a/engine/src/engine/graph/decode.cpp +++ b/engine/src/engine/graph/decode.cpp @@ -259,6 +259,44 @@ namespace { mesh.updateDescriptor(); + #if 0 + // swap winding order + if ( graph.metadata["decode"]["invert winding order"].as() ) { + if ( mesh.index.count ) { + uf::Mesh::Attribute indexAttribute = mesh.index.attributes.front(); + size_t tri[3]; + for ( size_t i = 0; i < mesh.index.count / 3; ++i ) { + switch ( mesh.index.size ) { + case sizeof(uint8_t): { + uint8_t* pointer = (uint8_t*) (indexAttribute.pointer); + tri[0] = pointer[i * 3 + 0]; + tri[2] = pointer[i * 3 + 2]; + + pointer[i * 3 + 0] = tri[2]; + pointer[i * 3 + 2] = tri[0]; + } break; + case sizeof(uint16_t): { + uint16_t* pointer = (uint16_t*) (indexAttribute.pointer); + tri[0] = pointer[i * 3 + 0]; + tri[2] = pointer[i * 3 + 2]; + + pointer[i * 3 + 0] = tri[2]; + pointer[i * 3 + 2] = tri[0]; + } break; + case sizeof(uint32_t): { + uint32_t* pointer = (uint32_t*) (indexAttribute.pointer); + tri[0] = pointer[i * 3 + 0]; + tri[2] = pointer[i * 3 + 2]; + + pointer[i * 3 + 0] = tri[2]; + pointer[i * 3 + 2] = tri[0]; + } break; + } + } + } + } + #endif + return mesh; } @@ -302,7 +340,18 @@ pod::Graph uf::graph::load( const uf::stl::string& filename, const uf::Serialize serializer.readFromFile( filename ); // load metadata graph.name = filename; //serializer["name"].as(); - graph.metadata = metadata; // serializer["metadata"]; +// graph.metadata = metadata; // serializer["metadata"]; + +// UF_MSG_DEBUG("A: {}", serializer["metadata"].dump(1, '\t')); +// UF_MSG_DEBUG("B: {}", metadata.dump(1, '\t')); +#if 0 + graph.metadata = serializer["metadata"]; + graph.metadata.merge( metadata, false ); +#else + graph.metadata = metadata; + graph.metadata.merge( serializer["metadata"], true ); +#endif +// UF_MSG_DEBUG("C: {}", graph.metadata.dump(1, '\t')); #if UF_GRAPH_LOAD_MULTITHREAD auto tasks = uf::thread::schedule(true); diff --git a/engine/src/engine/graph/encode.cpp b/engine/src/engine/graph/encode.cpp index 873ae481..c851b83e 100644 --- a/engine/src/engine/graph/encode.cpp +++ b/engine/src/engine/graph/encode.cpp @@ -250,15 +250,7 @@ uf::stl::string uf::graph::save( const pod::Graph& graph, const uf::stl::string& if ( settings.compression == "auto" ) settings.compression = ext::json::PREFERRED_COMPRESSION; if ( !settings.combined ) uf::io::mkdir(directory); -#if UF_USE_XATLAS -/* - if ( settings.unwrap ) { - pod::Graph& g = const_cast(graph); - auto size = ext::xatlas::unwrap( g ); - serializer["wrapped"] = uf::vector::encode( size ); - } -*/ -#endif + serializer["metadata"] = graph.metadata; #if UF_GRAPH_LOAD_MULTITHREAD auto tasks = uf::thread::schedule(true); diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index b309ca4e..e484185d 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -12,6 +12,20 @@ namespace { bool newGraphAdded = true; + + ext::json::Value findTag( const uf::stl::string& tagName, const ext::json::Value& tags ) { + ext::json::Value tag = ext::json::null(); + ext::json::forEach( tags, [&]( const uf::stl::string& key, const ext::json::Value& value ) { + if ( uf::string::isRegex( key ) ) { + if ( !uf::string::matched( tagName, key ) ) return; + } else if ( tagName != key ) return; + tag = value; + }); + return tag; + } + ext::json::Value findTag( const uf::stl::string& tagName, pod::Graph& graph ) { + return findTag( tagName, graph.metadata["tags"] ); + } } pod::Graph::Storage uf::graph::storage; @@ -64,113 +78,181 @@ pod::Node* uf::graph::find( pod::Graph& graph, const uf::stl::string& name ) { } void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::Mesh& mesh ) { - auto& scene = uf::scene::getCurrentScene(); - auto& sceneTextures = scene.getComponent(); - auto& sceneMetadataJson = scene.getComponent(); - - auto& graphic = entity.getComponent(); - graphic.initialize(); - graphic.initializeMesh( mesh ); + auto& scene = uf::scene::getCurrentScene(); + auto& sceneTextures = scene.getComponent(); + auto& sceneMetadataJson = scene.getComponent(); + + auto& graphic = entity.getComponent(); + graphic.initialize(); + graphic.initializeMesh( mesh ); - graphic.device = &uf::renderer::device; - graphic.material.device = &uf::renderer::device; - graphic.descriptor.frontFace = uf::renderer::enums::Face::CCW; - graphic.descriptor.cullMode = !(graph.metadata["flags"]["INVERT"].as()) ? uf::renderer::enums::CullMode::BACK : uf::renderer::enums::CullMode::FRONT; - if ( graph.metadata["cull mode"].is() ) { - const auto mode = graph.metadata["cull mode"].as(); - 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; + graphic.device = &uf::renderer::device; + graphic.material.device = &uf::renderer::device; + graphic.descriptor.frontFace = uf::renderer::enums::Face::CCW; + graphic.descriptor.cullMode = !(graph.metadata["flags"]["INVERT"].as()) ? uf::renderer::enums::CullMode::BACK : uf::renderer::enums::CullMode::FRONT; + + auto tag = ::findTag( entity.getName(), graph ); + if ( !ext::json::isObject( tag ) ) { + tag["renderer"] = graph.metadata["renderer"]; + } + + if ( tag["renderer"]["front face"].is() ) { + const auto mode = uf::string::lowercase( tag["renderer"]["front face"].as() ); + if ( mode == "cw" ) graphic.descriptor.frontFace = uf::renderer::enums::Face::CW; + else if ( mode == "ccw" ) graphic.descriptor.frontFace = uf::renderer::enums::Face::CCW; + else UF_MSG_WARNING("Invalid Face enum string specified: {}", mode); + } + if ( tag["renderer"]["cull mode"].is() ) { + const auto mode = uf::string::lowercase( tag["renderer"]["cull mode"].as() ); + 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; + else UF_MSG_WARNING("Invalid CullMode enum string specified: {}", mode); + } + + { + // for ( auto& s : graph.samplers ) graphic.material.samplers.emplace_back( uf::graph::storage.samplers.map[s] ); + // for ( auto pair : uf::graph::storage.samplers.map ) graphic.material.samplers.emplace_back( pair.second ); + // for ( auto& key : uf::graph::storage.samplers.keys ) graphic.material.samplers.emplace_back( uf::graph::storage.samplers.map[key] ); + + // for ( auto& i : graph.images ) graphic.material.textures.emplace_back().aliasTexture( uf::graph::storage.texture2Ds.map[i] ); + // for ( auto pair : uf::graph::storage.texture2Ds.map ) graphic.material.textures.emplace_back().aliasTexture( pair.second ); + for ( auto& key : uf::graph::storage.texture2Ds.keys ) graphic.material.textures.emplace_back().aliasTexture( uf::graph::storage.texture2Ds.map[key] ); + + // bind scene's voxel texture + if ( uf::renderer::settings::pipelines::vxgi ) { + auto& scene = uf::scene::getCurrentScene(); + auto& sceneTextures = scene.getComponent(); + for ( auto& t : sceneTextures.voxels.id ) graphic.material.textures.emplace_back().aliasTexture(t); + for ( auto& t : sceneTextures.voxels.normal ) graphic.material.textures.emplace_back().aliasTexture(t); + for ( auto& t : sceneTextures.voxels.uv ) graphic.material.textures.emplace_back().aliasTexture(t); + for ( auto& t : sceneTextures.voxels.radiance ) graphic.material.textures.emplace_back().aliasTexture(t); + for ( auto& t : sceneTextures.voxels.depth ) graphic.material.textures.emplace_back().aliasTexture(t); } - { - // for ( auto& s : graph.samplers ) graphic.material.samplers.emplace_back( uf::graph::storage.samplers.map[s] ); - // for ( auto pair : uf::graph::storage.samplers.map ) graphic.material.samplers.emplace_back( pair.second ); - // for ( auto& key : uf::graph::storage.samplers.keys ) graphic.material.samplers.emplace_back( uf::graph::storage.samplers.map[key] ); + } + + uf::stl::string root = uf::io::directory( graph.name ); + size_t texture2Ds = 0; + size_t texture3Ds = 0; + for ( auto& texture : graphic.material.textures ) { + if ( texture.width > 1 && texture.height > 1 && texture.depth == 1 && texture.layers == 1 ) ++texture2Ds; + else if ( texture.width > 1 && texture.height > 1 && texture.depth > 1 && texture.layers == 1 ) ++texture3Ds; + } - // for ( auto& i : graph.images ) graphic.material.textures.emplace_back().aliasTexture( uf::graph::storage.texture2Ds.map[i] ); - // for ( auto pair : uf::graph::storage.texture2Ds.map ) graphic.material.textures.emplace_back().aliasTexture( pair.second ); - for ( auto& key : uf::graph::storage.texture2Ds.keys ) graphic.material.textures.emplace_back().aliasTexture( uf::graph::storage.texture2Ds.map[key] ); + // standard pipeline + uf::stl::string vertexShaderFilename = graph.metadata["shaders"]["vertex"].as("/graph/base.vert.spv"); { + std::pair settings[] = { + { graph.metadata["flags"]["SKINNED"].as(), "skinned.vert" }, + { !graph.metadata["flags"]["SEPARATE"].as(), "instanced.vert" }, + }; + FOR_ARRAY(settings) if ( settings[i].first ) vertexShaderFilename = uf::string::replace( vertexShaderFilename, "vert", settings[i].second ); + vertexShaderFilename = entity.resolveURI( vertexShaderFilename, root ); + } + uf::stl::string geometryShaderFilename = graph.metadata["shaders"]["geometry"].as(""); + uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["fragment"].as("/graph/base.frag.spv"); { + std::pair settings[] = { + { uf::renderer::settings::invariant::deferredSampling, "deferredSampling.frag" }, + }; + FOR_ARRAY(settings) if ( settings[i].first ) fragmentShaderFilename = uf::string::replace( fragmentShaderFilename, "frag", settings[i].second ); + fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); + } + { + graphic.material.metadata.autoInitializeUniformBuffers = false; + graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX); + graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT); + graphic.material.metadata.autoInitializeUniformBuffers = true; + } - // bind scene's voxel texture - if ( uf::renderer::settings::pipelines::vxgi ) { - auto& scene = uf::scene::getCurrentScene(); - auto& sceneTextures = scene.getComponent(); - for ( auto& t : sceneTextures.voxels.id ) graphic.material.textures.emplace_back().aliasTexture(t); - for ( auto& t : sceneTextures.voxels.normal ) graphic.material.textures.emplace_back().aliasTexture(t); - for ( auto& t : sceneTextures.voxels.uv ) graphic.material.textures.emplace_back().aliasTexture(t); - for ( auto& t : sceneTextures.voxels.radiance ) graphic.material.textures.emplace_back().aliasTexture(t); - for ( auto& t : sceneTextures.voxels.depth ) graphic.material.textures.emplace_back().aliasTexture(t); + uf::renderer::Buffer* indirect = NULL; + for ( auto& buffer : graphic.buffers ) if ( !indirect && buffer.usage & uf::renderer::enums::Buffer::INDIRECT ) indirect = &buffer; + + { + auto& shader = graphic.material.getShader("vertex"); + + shader.buffers.emplace_back( uf::graph::storage.buffers.camera.alias() ); +// // shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); + #if UF_USE_VULKAN + shader.buffers.emplace_back( indirect->alias() ); + #endif + shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.joint.alias() ); + #if UF_USE_VULKAN + uint32_t maxPasses = 6; + uint32_t* specializationConstants = (uint32_t*) (void*) shader.specializationConstants; + for ( auto pair : shader.metadata.definitions.specializationConstants ) { + auto& sc = pair.second; + if ( sc.name == "PASSES" ) sc.value.ui = (specializationConstants[sc.index] = maxPasses); + } + #endif + } + { + auto& shader = graphic.material.getShader("fragment"); + + shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.instanceAddresses.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.material.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.texture.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.light.alias() ); + + #if UF_USE_VULKAN + uint32_t maxTextures = graph.textures.size(); // texture2Ds; + + uint32_t* specializationConstants = (uint32_t*) (void*) shader.specializationConstants; + for ( auto pair : shader.metadata.definitions.specializationConstants ) { + auto& sc = pair.second; + if ( sc.name == "TEXTURES" ) sc.value.ui = (specializationConstants[sc.index] = maxTextures); + } + for ( auto pair : shader.metadata.definitions.textures ) { + auto& tx = pair.second; + for ( auto& layout : shader.descriptorSetLayoutBindings ) { + if ( layout.binding != tx.binding ) continue; + if ( tx.name == "samplerTextures" ) layout.descriptorCount = maxTextures; } } - - uf::stl::string root = uf::io::directory( graph.name ); - size_t texture2Ds = 0; - size_t texture3Ds = 0; - for ( auto& texture : graphic.material.textures ) { - if ( texture.width > 1 && texture.height > 1 && texture.depth == 1 && texture.layers == 1 ) ++texture2Ds; - else if ( texture.width > 1 && texture.height > 1 && texture.depth > 1 && texture.layers == 1 ) ++texture3Ds; - } - - // standard pipeline - uf::stl::string vertexShaderFilename = graph.metadata["shaders"]["vertex"].as("/graph/base.vert.spv"); { - std::pair settings[] = { - { graph.metadata["flags"]["SKINNED"].as(), "skinned.vert" }, - { !graph.metadata["flags"]["SEPARATE"].as(), "instanced.vert" }, - }; - FOR_ARRAY(settings) if ( settings[i].first ) vertexShaderFilename = uf::string::replace( vertexShaderFilename, "vert", settings[i].second ); - vertexShaderFilename = entity.resolveURI( vertexShaderFilename, root ); - } - uf::stl::string geometryShaderFilename = graph.metadata["shaders"]["geometry"].as(""); - uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["fragment"].as("/graph/base.frag.spv"); { - std::pair settings[] = { - { uf::renderer::settings::invariant::deferredSampling, "deferredSampling.frag" }, - }; - FOR_ARRAY(settings) if ( settings[i].first ) fragmentShaderFilename = uf::string::replace( fragmentShaderFilename, "frag", settings[i].second ); - fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); - } - { - graphic.material.metadata.autoInitializeUniformBuffers = false; - graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX); - graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT); - graphic.material.metadata.autoInitializeUniformBuffers = true; - } - + #endif + } +#if UF_USE_VULKAN + // culling pipeline + if ( uf::renderer::settings::pipelines::culling ) { 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("/graph/cull.comp.spv"); + { + graphic.material.metadata.autoInitializeUniformBuffers = false; + compShaderFilename = entity.resolveURI( compShaderFilename, root ); + graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, uf::renderer::settings::pipelines::names::culling); + graphic.material.metadata.autoInitializeUniformBuffers = true; + } + graphic.descriptor.inputs.dispatch = { graphic.descriptor.inputs.indirect.count, 1, 1 }; - { - auto& shader = graphic.material.getShader("vertex"); + auto& shader = graphic.material.getShader("compute", uf::renderer::settings::pipelines::names::culling); shader.buffers.emplace_back( uf::graph::storage.buffers.camera.alias() ); - // // shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); #if UF_USE_VULKAN shader.buffers.emplace_back( indirect->alias() ); #endif shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.joint.alias() ); - #if UF_USE_VULKAN - uint32_t maxPasses = 6; - uint32_t* specializationConstants = (uint32_t*) (void*) shader.specializationConstants; - for ( auto pair : shader.metadata.definitions.specializationConstants ) { - auto& sc = pair.second; - if ( sc.name == "PASSES" ) sc.value.ui = (specializationConstants[sc.index] = maxPasses); - } - #endif } + } + if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) { + geometryShaderFilename = entity.resolveURI( geometryShaderFilename, root ); + graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY); + } + // depth only pipeline + { + graphic.material.metadata.autoInitializeUniformBuffers = false; + uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["vertex"].as("/graph/depth.frag.spv"); + fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); + graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, "depth"); + graphic.material.metadata.autoInitializeUniformBuffers = true; { - auto& shader = graphic.material.getShader("fragment"); - - shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.instanceAddresses.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.material.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.texture.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.light.alias() ); - - #if UF_USE_VULKAN uint32_t maxTextures = graph.textures.size(); // texture2Ds; + auto& shader = graphic.material.getShader("fragment", "depth"); uint32_t* specializationConstants = (uint32_t*) (void*) shader.specializationConstants; for ( auto pair : shader.metadata.definitions.specializationConstants ) { auto& sc = pair.second; @@ -183,239 +265,185 @@ void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::M if ( tx.name == "samplerTextures" ) layout.descriptorCount = maxTextures; } } - #endif + + shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.instanceAddresses.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.material.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.texture.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.light.alias() ); } - #if UF_USE_VULKAN - // culling pipeline - if ( uf::renderer::settings::pipelines::culling ) { - 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("/graph/cull.comp.spv"); - { - graphic.material.metadata.autoInitializeUniformBuffers = false; - compShaderFilename = entity.resolveURI( compShaderFilename, root ); - graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, uf::renderer::settings::pipelines::names::culling); - graphic.material.metadata.autoInitializeUniformBuffers = true; - } - graphic.descriptor.inputs.dispatch = { graphic.descriptor.inputs.indirect.count, 1, 1 }; + } + // vxgi pipeline + if ( uf::renderer::settings::pipelines::vxgi ) { + uf::stl::string vertexShaderFilename = graph.metadata["shaders"]["vertex"].as("/graph/base.vert.spv"); + uf::stl::string geometryShaderFilename = graph.metadata["shaders"]["geometry"].as("/graph/voxelize.geom.spv"); + uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["fragment"].as("/graph/voxelize.frag.spv"); - auto& shader = graphic.material.getShader("compute", uf::renderer::settings::pipelines::names::culling); - - shader.buffers.emplace_back( uf::graph::storage.buffers.camera.alias() ); - #if UF_USE_VULKAN - shader.buffers.emplace_back( indirect->alias() ); - #endif - shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); - } + { + fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); + graphic.material.metadata.autoInitializeUniformBuffers = false; + graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, uf::renderer::settings::pipelines::names::vxgi); + graphic.material.metadata.autoInitializeUniformBuffers = true; } if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) { geometryShaderFilename = entity.resolveURI( geometryShaderFilename, root ); - graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY); + graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY, uf::renderer::settings::pipelines::names::vxgi); } - // depth only pipeline + { + uint32_t voxelTypes = 0; + if ( !sceneTextures.voxels.id.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.normal.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.uv.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.radiance.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.depth.empty() ) ++voxelTypes; + + uint32_t maxTextures = texture2Ds; + uint32_t maxCascades = texture3Ds / voxelTypes; + + auto& shader = graphic.material.getShader("fragment", uf::renderer::settings::pipelines::names::vxgi); + uint32_t* specializationConstants = (uint32_t*) (void*) shader.specializationConstants; + for ( auto pair : shader.metadata.definitions.specializationConstants ) { + auto& sc = pair.second; + if ( sc.name == "TEXTURES" ) sc.value.ui = (specializationConstants[sc.index] = maxTextures); + else if ( sc.name == "CASCADES" ) sc.value.ui = (specializationConstants[sc.index] = maxCascades); + } + for ( auto pair : shader.metadata.definitions.textures ) { + auto& tx = pair.second; + for ( auto& layout : shader.descriptorSetLayoutBindings ) { + if ( layout.binding != tx.binding ) continue; + if ( tx.name == "samplerTextures" ) layout.descriptorCount = maxTextures; + else if ( tx.name == "voxelId" ) layout.descriptorCount = maxCascades; + else if ( tx.name == "voxelUv" ) layout.descriptorCount = maxCascades; + else if ( tx.name == "voxelNormal" ) layout.descriptorCount = maxCascades; + else if ( tx.name == "voxelRadiance" ) layout.descriptorCount = maxCascades; + } + } + + shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.instanceAddresses.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.material.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.texture.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.light.alias() ); + } + } + // baking pipeline + if ( graph.metadata["baking"]["enabled"].as() ) { { graphic.material.metadata.autoInitializeUniformBuffers = false; - uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["vertex"].as("/graph/depth.frag.spv"); - fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); - graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, "depth"); + uf::stl::string vertexShaderFilename = uf::io::resolveURI("/graph/baking/bake.vert.spv"); + uf::stl::string geometryShaderFilename = uf::io::resolveURI("/graph/baking/bake.geom.spv"); + uf::stl::string fragmentShaderFilename = uf::io::resolveURI("/graph/baking/bake.frag.spv"); + graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX, "baking"); + graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY, "baking"); + graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, "baking"); graphic.material.metadata.autoInitializeUniformBuffers = true; - { - uint32_t maxTextures = graph.textures.size(); // texture2Ds; - - auto& shader = graphic.material.getShader("fragment", "depth"); - uint32_t* specializationConstants = (uint32_t*) (void*) shader.specializationConstants; - for ( auto pair : shader.metadata.definitions.specializationConstants ) { - auto& sc = pair.second; - if ( sc.name == "TEXTURES" ) sc.value.ui = (specializationConstants[sc.index] = maxTextures); - } - for ( auto pair : shader.metadata.definitions.textures ) { - auto& tx = pair.second; - for ( auto& layout : shader.descriptorSetLayoutBindings ) { - if ( layout.binding != tx.binding ) continue; - if ( tx.name == "samplerTextures" ) layout.descriptorCount = maxTextures; - } - } - - shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.instanceAddresses.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.material.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.texture.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.light.alias() ); - } } - // vxgi pipeline - if ( uf::renderer::settings::pipelines::vxgi ) { - uf::stl::string vertexShaderFilename = graph.metadata["shaders"]["vertex"].as("/graph/base.vert.spv"); - uf::stl::string geometryShaderFilename = graph.metadata["shaders"]["geometry"].as("/graph/voxelize.geom.spv"); - uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["fragment"].as("/graph/voxelize.frag.spv"); - - { - fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); - graphic.material.metadata.autoInitializeUniformBuffers = false; - graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, uf::renderer::settings::pipelines::names::vxgi); - graphic.material.metadata.autoInitializeUniformBuffers = true; - } - if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) { - geometryShaderFilename = entity.resolveURI( geometryShaderFilename, root ); - graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY, uf::renderer::settings::pipelines::names::vxgi); - } - { - uint32_t voxelTypes = 0; - if ( !sceneTextures.voxels.id.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.normal.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.uv.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.radiance.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.depth.empty() ) ++voxelTypes; - - uint32_t maxTextures = texture2Ds; - uint32_t maxCascades = texture3Ds / voxelTypes; - - auto& shader = graphic.material.getShader("fragment", uf::renderer::settings::pipelines::names::vxgi); - uint32_t* specializationConstants = (uint32_t*) (void*) shader.specializationConstants; - for ( auto pair : shader.metadata.definitions.specializationConstants ) { - auto& sc = pair.second; - if ( sc.name == "TEXTURES" ) sc.value.ui = (specializationConstants[sc.index] = maxTextures); - else if ( sc.name == "CASCADES" ) sc.value.ui = (specializationConstants[sc.index] = maxCascades); - } - for ( auto pair : shader.metadata.definitions.textures ) { - auto& tx = pair.second; - for ( auto& layout : shader.descriptorSetLayoutBindings ) { - if ( layout.binding != tx.binding ) continue; - if ( tx.name == "samplerTextures" ) layout.descriptorCount = maxTextures; - else if ( tx.name == "voxelId" ) layout.descriptorCount = maxCascades; - else if ( tx.name == "voxelUv" ) layout.descriptorCount = maxCascades; - else if ( tx.name == "voxelNormal" ) layout.descriptorCount = maxCascades; - else if ( tx.name == "voxelRadiance" ) layout.descriptorCount = maxCascades; - } - } - - shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.instanceAddresses.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.material.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.texture.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.light.alias() ); - } - } - // baking pipeline - if ( graph.metadata["baking"]["enabled"].as() ) { - { - graphic.material.metadata.autoInitializeUniformBuffers = false; - uf::stl::string vertexShaderFilename = uf::io::resolveURI("/graph/baking/bake.vert.spv"); - uf::stl::string geometryShaderFilename = uf::io::resolveURI("/graph/baking/bake.geom.spv"); - uf::stl::string fragmentShaderFilename = uf::io::resolveURI("/graph/baking/bake.frag.spv"); - graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX, "baking"); - graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY, "baking"); - graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, "baking"); - graphic.material.metadata.autoInitializeUniformBuffers = true; - } - { - uint32_t maxPasses = 6; - - auto& shader = graphic.material.getShader("vertex", "baking"); - uint32_t* specializationConstants = (uint32_t*) (void*) shader.specializationConstants; - for ( auto pair : shader.metadata.definitions.specializationConstants ) { - auto& sc = pair.second; - if ( sc.name == "PASSES" ) sc.value.ui = (specializationConstants[sc.index] = maxPasses); - } - - // shader.buffers.emplace_back( uf::graph::storage.buffers.camera.alias() ); - // shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); - #if UF_USE_VULKAN - shader.buffers.emplace_back( indirect->alias() ); - #endif - shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.joint.alias() ); - } - - { - size_t maxTextures = sceneMetadataJson["system"]["config"]["engine"]["scenes"]["textures"]["max"]["2D"].as(512); - size_t maxCubemaps = sceneMetadataJson["system"]["config"]["engine"]["scenes"]["textures"]["max"]["cube"].as(128); - size_t maxTextures3D = sceneMetadataJson["system"]["config"]["engine"]["scenes"]["textures"]["max"]["3D"].as(128); - - auto& shader = graphic.material.getShader("fragment", "baking"); - uint32_t* specializationConstants = (uint32_t*) (void*) shader.specializationConstants; - for ( auto pair : shader.metadata.definitions.specializationConstants ) { - auto& sc = pair.second; - if ( sc.name == "TEXTURES" ) sc.value.ui = (specializationConstants[sc.index] = maxTextures); - else if ( sc.name == "CUBEMAPS" ) sc.value.ui = (specializationConstants[sc.index] = maxCubemaps); - } - for ( auto pair : shader.metadata.definitions.textures ) { - auto& tx = pair.second; - for ( auto& layout : shader.descriptorSetLayoutBindings ) { - if ( layout.binding != tx.binding ) continue; - if ( tx.name == "samplerTextures" ) layout.descriptorCount = maxTextures; - else if ( tx.name == "samplerCubemaps" ) layout.descriptorCount = maxCubemaps; - } - } - - shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.instanceAddresses.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.material.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.texture.alias() ); - shader.buffers.emplace_back( uf::graph::storage.buffers.light.alias() ); - } - } - // rt pipeline - if ( uf::renderer::settings::pipelines::rt ) { - graphic.generateBottomAccelerationStructures(); - } - - // grab addresses { - pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer( mesh.indirect ).data(); - for ( size_t drawID = 0; drawID < mesh.indirect.count; ++drawID ) { - auto& drawCommand = drawCommands[drawID]; - auto instanceID = drawCommand.instanceID; - auto instanceKeyName = std::to_string(instanceID); + uint32_t maxPasses = 6; - if ( uf::graph::storage.instanceAddresses.map.count(instanceKeyName) > 0 ) { - UF_MSG_DEBUG("DUPLICATE INSTANCE ID"); - } + auto& shader = graphic.material.getShader("vertex", "baking"); + uint32_t* specializationConstants = (uint32_t*) (void*) shader.specializationConstants; + for ( auto pair : shader.metadata.definitions.specializationConstants ) { + auto& sc = pair.second; + if ( sc.name == "PASSES" ) sc.value.ui = (specializationConstants[sc.index] = maxPasses); + } - auto& instanceAddresses = uf::graph::storage.instanceAddresses.map[instanceKeyName]; - if ( mesh.vertex.count ) { - if ( mesh.isInterleaved( mesh.vertex ) ) { - instanceAddresses.vertex = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved).getAddress()/* + (drawCommand.vertexID * mesh.vertex.size)*/; - } else { - for ( auto& attribute : graphic.descriptor.inputs.vertex.attributes ) { - if ( attribute.buffer < 0 ) continue; - if ( attribute.descriptor.name == "position" ) instanceAddresses.position = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "uv" ) instanceAddresses.uv = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "color" ) instanceAddresses.color = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "st" ) instanceAddresses.st = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "normal" ) instanceAddresses.normal = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "tangent" ) instanceAddresses.tangent = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "joints" ) instanceAddresses.joints = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "weights" ) instanceAddresses.weights = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "id" ) instanceAddresses.id = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - } - } - } - if ( mesh.index.count ) { - if ( mesh.isInterleaved( mesh.index ) ) instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.interleaved).getAddress(); - else instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.attributes.front().buffer).getAddress(); - } + // shader.buffers.emplace_back( uf::graph::storage.buffers.camera.alias() ); + // shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); + #if UF_USE_VULKAN + shader.buffers.emplace_back( indirect->alias() ); + #endif + shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.joint.alias() ); + } - if ( mesh.indirect.count ) { - if ( mesh.isInterleaved( mesh.indirect ) ) instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.interleaved).getAddress(); - else instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.attributes.front().buffer).getAddress(); + { + size_t maxTextures = sceneMetadataJson["system"]["config"]["engine"]["scenes"]["textures"]["max"]["2D"].as(512); + size_t maxCubemaps = sceneMetadataJson["system"]["config"]["engine"]["scenes"]["textures"]["max"]["cube"].as(128); + size_t maxTextures3D = sceneMetadataJson["system"]["config"]["engine"]["scenes"]["textures"]["max"]["3D"].as(128); - instanceAddresses.drawID = drawID; + auto& shader = graphic.material.getShader("fragment", "baking"); + uint32_t* specializationConstants = (uint32_t*) (void*) shader.specializationConstants; + for ( auto pair : shader.metadata.definitions.specializationConstants ) { + auto& sc = pair.second; + if ( sc.name == "TEXTURES" ) sc.value.ui = (specializationConstants[sc.index] = maxTextures); + else if ( sc.name == "CUBEMAPS" ) sc.value.ui = (specializationConstants[sc.index] = maxCubemaps); + } + for ( auto pair : shader.metadata.definitions.textures ) { + auto& tx = pair.second; + for ( auto& layout : shader.descriptorSetLayoutBindings ) { + if ( layout.binding != tx.binding ) continue; + if ( tx.name == "samplerTextures" ) layout.descriptorCount = maxTextures; + else if ( tx.name == "samplerCubemaps" ) layout.descriptorCount = maxCubemaps; } } + + shader.buffers.emplace_back( uf::graph::storage.buffers.drawCommands.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.instanceAddresses.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.material.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.texture.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.light.alias() ); } - #endif - - // uf::instantiator::bind( "GraphBehavior", entity ); - // uf::instantiator::unbind( "RenderBehavior", entity ); - - graphic.process = true; } + // rt pipeline + if ( uf::renderer::settings::pipelines::rt ) { + graphic.generateBottomAccelerationStructures(); + } + + // grab addresses + { + pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer( mesh.indirect ).data(); + for ( size_t drawID = 0; drawID < mesh.indirect.count; ++drawID ) { + auto& drawCommand = drawCommands[drawID]; + auto instanceID = drawCommand.instanceID; + auto instanceKeyName = std::to_string(instanceID); + + if ( uf::graph::storage.instanceAddresses.map.count(instanceKeyName) > 0 ) { + UF_MSG_DEBUG("DUPLICATE INSTANCE ID"); + } + + auto& instanceAddresses = uf::graph::storage.instanceAddresses.map[instanceKeyName]; + if ( mesh.vertex.count ) { + if ( mesh.isInterleaved( mesh.vertex ) ) { + instanceAddresses.vertex = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved).getAddress()/* + (drawCommand.vertexID * mesh.vertex.size)*/; + } else { + for ( auto& attribute : graphic.descriptor.inputs.vertex.attributes ) { + if ( attribute.buffer < 0 ) continue; + if ( attribute.descriptor.name == "position" ) instanceAddresses.position = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "uv" ) instanceAddresses.uv = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "color" ) instanceAddresses.color = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "st" ) instanceAddresses.st = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "normal" ) instanceAddresses.normal = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "tangent" ) instanceAddresses.tangent = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "joints" ) instanceAddresses.joints = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "weights" ) instanceAddresses.weights = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "id" ) instanceAddresses.id = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + } + } + } + if ( mesh.index.count ) { + if ( mesh.isInterleaved( mesh.index ) ) instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.interleaved).getAddress(); + else instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.attributes.front().buffer).getAddress(); + } + + if ( mesh.indirect.count ) { + if ( mesh.isInterleaved( mesh.indirect ) ) instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.interleaved).getAddress(); + else instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.attributes.front().buffer).getAddress(); + + instanceAddresses.drawID = drawID; + } + } + } +#endif + +// uf::instantiator::bind( "GraphBehavior", entity ); +// uf::instantiator::unbind( "RenderBehavior", entity ); + + graphic.process = true; +} void uf::graph::process( pod::Graph& graph ) { // copy our local storage to global storage @@ -451,11 +479,11 @@ void uf::graph::process( pod::Graph& graph ) { auto& scene = uf::scene::getCurrentScene(); auto& sceneMetadataJson = scene.getComponent(); if ( !sceneMetadataJson["system"]["config"]["engine"]["scenes"]["lights"]["useLightmaps"].as(true) ) { - graph.metadata["lightmap"] = false; + graph.metadata["lights"]["lightmap"] = false; graph.metadata["baking"]["enabled"] = false; } - if ( graph.metadata["lightmap"].is() && graph.metadata["lightmap"].as() == "auto" ) { + if ( graph.metadata["lights"]["lightmap"].is() && graph.metadata["lights"]["lightmap"].as() == "auto" ) { uint32_t mtime = uf::io::mtime( graph.name ); // lightmaps are considered stale if they're older than the graph's source bool stale = false; @@ -469,13 +497,13 @@ void uf::graph::process( pod::Graph& graph ) { break; } } - graph.metadata["lightmap"] = !stale; + graph.metadata["lights"]["lightmap"] = !stale; } graph.metadata["baking"]["layers"] = lightmapCount; - if ( graph.metadata["lightmap"].as() ) for ( auto& pair : filenames ) { + if ( graph.metadata["lights"]["lightmap"].as() ) for ( auto& pair : filenames ) { auto i = pair.first; auto f = uf::io::sanitize( pair.second, uf::io::directory( graph.name ) ); @@ -496,7 +524,7 @@ void uf::graph::process( pod::Graph& graph ) { lightmapIDs[i] = textureID; - graph.metadata["lightmaps"][i] = f; + graph.metadata["lights"]["lightmaps"][i] = f; graph.metadata["baking"]["enabled"] = false; isSrgb[f] = false; @@ -542,11 +570,18 @@ void uf::graph::process( pod::Graph& graph ) { auto& image = uf::graph::storage.images[key]; auto& texture = uf::graph::storage.texture2Ds[key]; if ( !texture.generated() ) { - // bool isLightmap = graph.metadata["lightmapped"].as() == key; - // auto filter = graph.metadata["filter"].as() == "NEAREST" && !isLightmap ? uf::renderer::enums::Filter::NEAREST : uf::renderer::enums::Filter::LINEAR; - // auto filter = uf::renderer::enums::Filter::LINEAR; + auto filter = uf::renderer::enums::Filter::LINEAR; + auto tag = ::findTag( key, graph ); + if ( !ext::json::isObject( tag ) ) { + tag["renderer"] = graph.metadata["renderer"]; + } + if ( tag["renderer"]["filter"].is() ) { + const auto mode = uf::string::lowercase( tag["renderer"]["filter"].as("linear") ); + if ( mode == "linear" ) filter = uf::renderer::enums::Filter::LINEAR; + else if ( mode == "nearest" ) filter = uf::renderer::enums::Filter::NEAREST; + else UF_MSG_WARNING("Invalid Filter enum string specified: {}", mode); + } - auto filter = graph.metadata["filter"].as() == "NEAREST" ? uf::renderer::enums::Filter::NEAREST : uf::renderer::enums::Filter::LINEAR; texture.sampler.descriptor.filter.min = filter; texture.sampler.descriptor.filter.mag = filter; texture.srgb = isSrgb.count(key) == 0 ? false : isSrgb[key]; @@ -681,6 +716,39 @@ void uf::graph::process( pod::Graph& graph ) { } } + // patch materials/textures + for ( auto& name : graph.materials ) { + auto& material = uf::graph::storage.materials[name]; + auto tag = ::findTag( name, graph ); + if ( ext::json::isObject( tag ) ) { + material.colorBase = uf::vector::decode( tag["material"]["base"], material.colorBase); + material.colorEmissive = uf::vector::decode( tag["material"]["emissive"], material.colorEmissive); + material.factorMetallic = tag["material"]["fMetallic"].as(material.factorMetallic); + material.factorRoughness = tag["material"]["fRoughness"].as(material.factorRoughness); + material.factorOcclusion = tag["material"]["fOcclusion"].as(material.factorOcclusion); + material.factorAlphaCutoff = tag["material"]["fAlphaCutoff"].as(material.factorAlphaCutoff); + material.indexAlbedo = tag["material"]["iAlbedo"].as(material.indexAlbedo); + material.indexNormal = tag["material"]["iNormal"].as(material.indexNormal); + material.indexEmissive = tag["material"]["iEmissive"].as(material.indexEmissive); + material.indexOcclusion = tag["material"]["iOcclusion"].as(material.indexOcclusion); + material.indexMetallicRoughness = tag["material"]["iMetallicRoughness"].as(material.indexMetallicRoughness); + + if ( tag["material"]["modeAlpha"].is() ) { + const auto mode = uf::string::lowercase( tag["material"]["modeAlpha"].as() ); + if ( mode == "opaque" ) material.modeAlpha = 0; + else if ( mode == "blend" ) material.modeAlpha = 1; + else if ( mode == "mask" ) material.modeAlpha = 2; + else UF_MSG_WARNING("Invalid AlphaMode enum string specified: {}", mode); + } else { + material.modeAlpha = tag["material"]["modeAlpha"].as(material.modeAlpha); + } + } + } + + if ( graph.metadata["debug"]["print"]["meshes"].as() ) for ( auto& name : graph.meshes ) UF_MSG_DEBUG("Mesh: {}", name); + if ( graph.metadata["debug"]["print"]["materials"].as() ) for ( auto& name : graph.materials ) UF_MSG_DEBUG("Material: {}", name); + if ( graph.metadata["debug"]["print"]["textures"].as() ) for ( auto& name : graph.textures ) UF_MSG_DEBUG("Texture: {}", name); + uf::graph::reload(); // setup combined mesh if requested @@ -756,7 +824,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) // ignore pesky light_Orientation nodes if ( uf::string::split( node.name, "_" ).back() == "Orientation" ) return; // for dreamcast, ignore lights if we're baked - if ( graph.metadata["lightmapped"].as() && graph.metadata["lights"]["disable if lightmapped"].as(true) ) if ( graph.lights.count(node.name) > 0 ) return; + if ( graph.metadata["lights"]["lightmapped"].as() && graph.metadata["lights"]["disable if lightmapped"].as(true) ) if ( graph.lights.count(node.name) > 0 ) return; // create child if requested @@ -775,18 +843,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) // on systems where frametime is very, very important, we can set all static nodes to not tick // tie to tag - ext::json::Value tag = ext::json::null(); -/* - if ( ext::json::isObject( graph.metadata["tags"][node.name] ) ) { - tag = graph.metadata["tags"][node.name]; - } -*/ - ext::json::forEach( graph.metadata["tags"], [&]( const uf::stl::string& key, ext::json::Value& value ) { - if ( uf::string::isRegex( key ) ) { - if ( !uf::string::matched( node.name, key ) ) return; - } else if ( node.name != key ) return; - tag = value; - }); + ext::json::Value tag = ::findTag( node.name, graph ); if ( ext::json::isObject( tag ) ) { if ( tag["ignore"].as() ) return; if ( graph.metadata["baking"]["enabled"].as(false) && !tag["bake"].as(true) ) return; @@ -843,8 +900,13 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) metadataLight[key] = value; }); #if !UF_USE_OPENGL - if ( !(graph.metadata["lightmapped"].as() && !(metadataLight["shadows"].as() || metadataLight["dynamic"].as())) ) { - // { + bool should = false; + if ( !graph.metadata["lights"]["lightmapped"].as() ) { + should = true; + } else if ( metadataLight["shadows"].as() || metadataLight["dynamic"].as() ) { + should = true; + } + if ( should ) { auto& metadataJson = entity.getComponent(); entity.load("/light.json"); // copy @@ -931,8 +993,8 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) { auto phyziks = tag["physics"]; - if ( !ext::json::isObject( phyziks ) ) phyziks = metadataJson["system"]["physics"]; - else metadataJson["system"]["physics"] = phyziks; + if ( !ext::json::isObject( phyziks ) ) phyziks = metadataJson["physics"]; + else metadataJson["physics"] = phyziks; if ( ext::json::isObject( phyziks ) ) { uf::stl::string type = phyziks["type"].as(); @@ -953,8 +1015,8 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) pod::Vector3f center = (max + min) * 0.5f; pod::Vector3f corner = uf::vector::abs(max - min) * 0.5f; - metadataJson["system"]["physics"]["center"] = uf::vector::encode( center ); - metadataJson["system"]["physics"]["corner"] = uf::vector::encode( corner ); + metadataJson["physics"]["center"] = uf::vector::encode( center ); + metadataJson["physics"]["corner"] = uf::vector::encode( corner ); } } } @@ -1130,20 +1192,12 @@ void uf::graph::update( pod::Graph& graph, pod::Node& node ) { } } } + void uf::graph::destroy( pod::Graph& graph ) { #if 0 for ( auto& t : graph.textures ) t.texture.destroy(); for ( auto& m : graph.meshes ) m.destroy(); #endif - - uf::graph::storage.buffers.camera.destroy(); - uf::graph::storage.buffers.drawCommands.destroy(); - uf::graph::storage.buffers.instance.destroy(); - uf::graph::storage.buffers.instanceAddresses.destroy(); - uf::graph::storage.buffers.joint.destroy(); - uf::graph::storage.buffers.material.destroy(); - uf::graph::storage.buffers.texture.destroy(); - uf::graph::storage.buffers.light.destroy(); } void uf::graph::initialize() { @@ -1237,6 +1291,7 @@ void uf::graph::render() { uf::graph::storage.buffers.camera.update( (const void*) &camera.data().viewport, sizeof(pod::Camera::Viewports) ); } void uf::graph::destroy() { + // cleanup graphic handles for ( auto pair : uf::graph::storage.texture2Ds.map ) pair.second.destroy(); for ( auto& t : uf::graph::storage.shadow2Ds ) t.destroy(); for ( auto& t : uf::graph::storage.shadowCubes ) t.destroy(); @@ -1244,6 +1299,34 @@ 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(); + // cleanup storage buffers + uf::graph::storage.buffers.camera.destroy(); + uf::graph::storage.buffers.drawCommands.destroy(); + uf::graph::storage.buffers.instance.destroy(); + uf::graph::storage.buffers.instanceAddresses.destroy(); + uf::graph::storage.buffers.joint.destroy(); + uf::graph::storage.buffers.material.destroy(); + uf::graph::storage.buffers.texture.destroy(); + uf::graph::storage.buffers.light.destroy(); + + // cleanup storage cache + uf::graph::storage.instances.clear(); + uf::graph::storage.instanceAddresses.clear(); + uf::graph::storage.primitives.clear(); + uf::graph::storage.drawCommands.clear(); + uf::graph::storage.meshes.clear(); + uf::graph::storage.images.clear(); + uf::graph::storage.materials.clear(); + uf::graph::storage.textures.clear(); + uf::graph::storage.samplers.clear(); + uf::graph::storage.skins.clear(); + uf::graph::storage.animations.clear(); + uf::graph::storage.atlases.clear(); + uf::graph::storage.joints.clear(); + uf::graph::storage.texture2Ds.clear(); + uf::graph::storage.entities.clear(); + uf::graph::storage.shadow2Ds.clear(); + uf::graph::storage.shadowCubes.clear(); } void uf::graph::reload() { ::newGraphAdded = true; diff --git a/engine/src/engine/object/behavior.cpp b/engine/src/engine/object/behavior.cpp index fbc74dee..3d2e0bbe 100644 --- a/engine/src/engine/object/behavior.cpp +++ b/engine/src/engine/object/behavior.cpp @@ -101,25 +101,25 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) { this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); metadata.deserialize(self, metadataJson); - if ( ext::json::isObject(metadataJson["system"]["physics"]) ) { + if ( ext::json::isObject(metadataJson["physics"]) ) { auto& collider = this->getComponent(); - collider.stats.flags = metadataJson["system"]["physics"]["flags"].as(collider.stats.flags); - collider.stats.mass = metadataJson["system"]["physics"]["mass"].as(collider.stats.mass); - collider.stats.restitution = metadataJson["system"]["physics"]["restitution"].as(collider.stats.restitution); - collider.stats.friction = metadataJson["system"]["physics"]["friction"].as(collider.stats.friction); - collider.stats.inertia = uf::vector::decode( metadataJson["system"]["physics"]["inertia"], collider.stats.inertia ); - collider.stats.gravity = uf::vector::decode( metadataJson["system"]["physics"]["gravity"], collider.stats.gravity ); + collider.stats.flags = metadataJson["physics"]["flags"].as(collider.stats.flags); + collider.stats.mass = metadataJson["physics"]["mass"].as(collider.stats.mass); + collider.stats.restitution = metadataJson["physics"]["restitution"].as(collider.stats.restitution); + collider.stats.friction = metadataJson["physics"]["friction"].as(collider.stats.friction); + collider.stats.inertia = uf::vector::decode( metadataJson["physics"]["inertia"], collider.stats.inertia ); + collider.stats.gravity = uf::vector::decode( metadataJson["physics"]["gravity"], collider.stats.gravity ); - if ( metadataJson["system"]["physics"]["type"].as() == "bounding box" ) { - pod::Vector3f center = uf::vector::decode( metadataJson["system"]["physics"]["center"], pod::Vector3f{} ); - pod::Vector3f corner = uf::vector::decode( metadataJson["system"]["physics"]["corner"], pod::Vector3f{0.5, 0.5, 0.5} ); + if ( metadataJson["physics"]["type"].as() == "bounding box" ) { + pod::Vector3f center = uf::vector::decode( metadataJson["physics"]["center"], pod::Vector3f{} ); + pod::Vector3f corner = uf::vector::decode( metadataJson["physics"]["corner"], pod::Vector3f{0.5, 0.5, 0.5} ); - if ( metadataJson["system"]["physics"]["recenter"].as(true) ) collider.transform.position = (center - transform.position); + if ( metadataJson["physics"]["recenter"].as(true) ) collider.transform.position = (center - transform.position); uf::physics::impl::create( *this, corner ); - } else if ( metadataJson["system"]["physics"]["type"].as() == "capsule" ) { - float radius = metadataJson["system"]["physics"]["radius"].as(); - float height = metadataJson["system"]["physics"]["height"].as(); + } else if ( metadataJson["physics"]["type"].as() == "capsule" ) { + float radius = metadataJson["physics"]["radius"].as(); + float height = metadataJson["physics"]["height"].as(); uf::physics::impl::create( *this, radius, height ); } @@ -128,7 +128,9 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) { void uf::ObjectBehavior::destroy( uf::Object& self ) { auto& metadata = this->getComponent(); for ( auto pair : metadata.hooks.bound ) { - for ( auto id : pair.second ) uf::hooks.removeHook(pair.first, id); + for ( auto id : pair.second ) { + uf::hooks.removeHook(pair.first, id); + } } if ( this->hasComponent() ) { diff --git a/engine/src/engine/object/object.cpp b/engine/src/engine/object/object.cpp index 1d846932..b162a758 100644 --- a/engine/src/engine/object/object.cpp +++ b/engine/src/engine/object/object.cpp @@ -224,22 +224,6 @@ bool uf::Object::load( const uf::Serializer& _json ) { physics.linear.acceleration = uf::vector::decode( json["physics"]["linear"]["acceleration"], physics.linear.acceleration ); physics.rotational.velocity = uf::vector::decode( json["physics"]["rotational"]["velocity"], physics.rotational.velocity ); physics.rotational.acceleration = uf::vector::decode( json["physics"]["rotational"]["acceleration"], physics.rotational.acceleration ); - - /* - if ( ext::json::isArray( json["physics"]["linear"]["velocity"] ) ) - for ( size_t j = 0; j < 3; ++j ) - physics.linear.velocity[j] = json["physics"]["linear"]["velocity"][j].as(); - if ( ext::json::isArray( json["physics"]["linear"]["acceleration"] ) ) - for ( size_t j = 0; j < 3; ++j ) - physics.linear.acceleration[j] = json["physics"]["linear"]["acceleration"][j].as(); - - if ( ext::json::isArray( json["physics"]["rotational"]["velocity"] ) ) - for ( size_t j = 0; j < 4; ++j ) - physics.rotational.velocity[j] = json["physics"]["rotational"]["velocity"][j].as(); - if ( ext::json::isArray( json["physics"]["rotational"]["acceleration"] ) ) - for ( size_t j = 0; j < 4; ++j ) - physics.rotational.acceleration[j] = json["physics"]["rotational"]["acceleration"][j].as(); - */ } } diff --git a/engine/src/ext/gltf/gltf.cpp b/engine/src/ext/gltf/gltf.cpp index ed1e3462..a3733991 100644 --- a/engine/src/ext/gltf/gltf.cpp +++ b/engine/src/ext/gltf/gltf.cpp @@ -155,7 +155,7 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize auto imageID = graph.images.size(); auto keyName = graph.images.emplace_back(/*filename + "/" +*/ i.name); auto& image = /*graph.storage*/uf::graph::storage.images[keyName]; - if ( graph.metadata["exporter"]["image"]["print"].as() ) { + if ( graph.metadata["debug"]["print"]["image"].as() ) { UF_MSG_DEBUG("Image: {}", i.name ); } @@ -169,7 +169,7 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize auto samplerID = graph.samplers.size(); auto keyName = graph.samplers.emplace_back(/*filename + "/" +*/ s.name); auto& sampler = /*graph.storage*/uf::graph::storage.samplers[keyName]; - if ( graph.metadata["exporter"]["sampler"]["print"].as() ) { + if ( graph.metadata["debug"]["print"]["sampler"].as() ) { UF_MSG_DEBUG("Sampler: {}", s.name ); } @@ -189,7 +189,7 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize auto textureID = graph.textures.size(); auto keyName = graph.textures.emplace_back((t.name == "" ? graph.images[t.source] : (/*filename + "/" +*/ t.name))); auto& texture = /*graph.storage*/uf::graph::storage.textures[keyName]; - if ( graph.metadata["exporter"]["texture"]["print"].as() ) { + if ( graph.metadata["debug"]["print"]["texture"].as() ) { UF_MSG_DEBUG("Texture: {}", t.name ); } @@ -206,7 +206,7 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize auto materialID = graph.materials.size(); auto keyName = graph.materials.emplace_back(/*filename + "/" +*/ m.name); auto& material = /*graph.storage*/uf::graph::storage.materials[keyName]; - if ( graph.metadata["exporter"]["material"]["print"].as() ) { + if ( graph.metadata["debug"]["print"]["material"].as() ) { UF_MSG_DEBUG("Material: {}", m.name ); } @@ -233,14 +233,14 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize material.factorOcclusion = m.occlusionTexture.strength; material.factorAlphaCutoff = m.alphaCutoff; - const uf::stl::string mode = graph.metadata["alpha mode"].as(m.alphaMode); - if ( mode == "OPAQUE" ) material.modeAlpha = 0; - else if ( mode == "BLEND" ) material.modeAlpha = 1; - else if ( mode == "MASK" ) material.modeAlpha = 2; - else UF_MSG_WARNING("Unhandled alpha mode: {}", mode); + const auto mode = uf::string::lowercase( graph.metadata["renderer"]["alpha mode"].as(m.alphaMode) ); + if ( mode == "opaque" ) material.modeAlpha = 0; + else if ( mode == "blend" ) material.modeAlpha = 1; + else if ( mode == "mask" ) material.modeAlpha = 2; + else UF_MSG_WARNING("Invalid AlphaMode enum string specified: {}", mode); - if ( m.doubleSided && graph.metadata["cull mode"].as() == "auto" ) { - graph.metadata["cull mode"] = "none"; + if ( m.doubleSided && graph.metadata["renderer"]["cull mode"].as() == "auto" ) { + graph.metadata["renderer"]["cull mode"] = "none"; } } } @@ -253,7 +253,7 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize for ( auto& m : model.meshes ) { auto meshID = graph.meshes.size(); auto keyName = graph.meshes.emplace_back(/*filename + "/" +*/ m.name); - if ( graph.metadata["exporter"]["mesh"]["print"].as() ) { + if ( graph.metadata["debug"]["print"]["mesh"].as() ) { UF_MSG_DEBUG("Mesh: {}", m.name ); } @@ -299,13 +299,13 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize #define UF_GRAPH_MESH_FORMAT uf::graph::mesh::Skinned, uint32_t #define UF_GRAPH_PROCESS_PRIMITIVES_FULL 1 #define UF_GRAPH_GRID 1 - #include "processPrimitives2.inl" + #include "processPrimitives.inl" #undef UF_GRAPH_PROCESS_PRIMITIVES_FULL #undef UF_GRAPH_MESH_FORMAT } else { #define UF_GRAPH_MESH_FORMAT uf::graph::mesh::Base, uint32_t #define UF_GRAPH_PROCESS_PRIMITIVES_FULL 0 - #include "processPrimitives2.inl" + #include "processPrimitives.inl" #undef UF_GRAPH_PROCESS_PRIMITIVES_FULL #undef UF_GRAPH_MESH_FORMAT } @@ -320,7 +320,7 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize auto skinID = graph.skins.size(); auto keyName = graph.skins.emplace_back(/*filename + "/" +*/ s.name); auto& skin = /*graph.storage*/uf::graph::storage.skins[keyName]; - if ( graph.metadata["exporter"]["skin"]["print"].as() ) { + if ( graph.metadata["debug"]["print"]["skin"].as() ) { UF_MSG_DEBUG("Skin: {}", s.name ); } @@ -357,7 +357,7 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize auto animationID = graph.animations.size(); auto keyName = graph.animations.emplace_back(/*filename + "/" +*/ a.name); auto& animation = /*graph.storage*/uf::graph::storage.animations[keyName]; - if ( graph.metadata["exporter"]["animation"]["print"].as() ) { + if ( graph.metadata["debug"]["print"]["animation"].as() ) { UF_MSG_DEBUG("Animation: {}", a.name ); } @@ -417,7 +417,7 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize { for ( auto& l : model.lights ) { auto& light = graph.lights[l.name]; - if ( graph.metadata["exporter"]["light"]["print"].as() ) { + if ( graph.metadata["debug"]["print"]["light"].as() ) { UF_MSG_DEBUG("Light: {}", l.name ); } @@ -524,7 +524,7 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize // graph.metadata["baking"]["enabled"] = false; // disable lightmap loading, 99.999% of the time a previously baked lightmap will not work due to changing STs - graph.metadata["lightmap"] = false; + graph.metadata["lights"]["lightmapped"] = false; } return graph; } diff --git a/engine/src/ext/gltf/processPrimitives.inl b/engine/src/ext/gltf/processPrimitives.inl index e7dd3dd9..6e93b49e 100644 --- a/engine/src/ext/gltf/processPrimitives.inl +++ b/engine/src/ext/gltf/processPrimitives.inl @@ -1,7 +1,20 @@ +struct { + struct { + bool should = true; + size_t corrected{}; + size_t total{}; + } windingOrder; +} sanitizer; + +if ( graph.metadata["sanitizer"]["winding order"].as(true) ) { + sanitizer.windingOrder.should = true; +} + +uf::stl::vector> meshlets; + for ( auto& p : m.primitives ) { - vertices.clear(); - indices.clear(); - auto& primitive = primitives.emplace_back(); + size_t primitiveID = meshlets.size(); + auto& meshlet = meshlets.emplace_back(); struct Attribute { uf::stl::string name = ""; @@ -38,13 +51,13 @@ for ( auto& p : m.primitives ) { auto& view = model.bufferViews[accessor.bufferView]; if ( attribute.name == "POSITION" ) { - vertices.resize(accessor.count); - primitive.instance.bounds.min = pod::Vector3f{ accessor.minValues[0], accessor.minValues[1], accessor.minValues[2] }; - primitive.instance.bounds.max = pod::Vector3f{ accessor.maxValues[0], accessor.maxValues[1], accessor.maxValues[2] }; + meshlet.vertices.resize(accessor.count); + meshlet.primitive.instance.bounds.min = pod::Vector3f{ accessor.minValues[0], accessor.minValues[1], accessor.minValues[2] }; + meshlet.primitive.instance.bounds.max = pod::Vector3f{ accessor.maxValues[0], accessor.maxValues[1], accessor.maxValues[2] }; if ( !(graph.metadata["flags"]["INVERT"].as()) ){ - primitive.instance.bounds.min.x = -primitive.instance.bounds.min.x; - primitive.instance.bounds.max.x = -primitive.instance.bounds.max.x; + meshlet.primitive.instance.bounds.min.x = -meshlet.primitive.instance.bounds.min.x; + meshlet.primitive.instance.bounds.max.x = -meshlet.primitive.instance.bounds.max.x; } } @@ -80,7 +93,7 @@ for ( auto& p : m.primitives ) { } } - for ( size_t i = 0; i < vertices.size(); ++i ) { + for ( size_t i = 0; i < meshlet.vertices.size(); ++i ) { #if 0 #define ITERATE_ATTRIBUTE( name, member )\ memcpy( &vertex.member[0], &attributes[name].buffer[i * attributes[name].components], attributes[name].stride ); @@ -101,7 +114,7 @@ for ( auto& p : m.primitives ) { } #endif - auto& vertex = vertices[i]; + auto& vertex = meshlet.vertices[i]; ITERATE_ATTRIBUTE("POSITION", position, 1); ITERATE_ATTRIBUTE("TEXCOORD_0", uv, 1); ITERATE_ATTRIBUTE("COLOR_0", color, 255.0f); @@ -123,6 +136,9 @@ for ( auto& p : m.primitives ) { vertex.tangent.x = -vertex.tangent.x; #endif } + + vertex.id.x = primitiveID; + vertex.id.y = meshID; } if ( p.indices > -1 ) { @@ -130,9 +146,9 @@ for ( auto& p : m.primitives ) { auto& view = model.bufferViews[accessor.bufferView]; auto& buffer = model.buffers[view.buffer]; - indices.reserve( static_cast(accessor.count) ); + meshlet.indices.reserve( static_cast(accessor.count) ); - #define COPY_INDICES() for (size_t index = 0; index < accessor.count; index++) indices.emplace_back(buf[index]); + #define COPY_INDICES() for (size_t index = 0; index < accessor.count; index++) meshlet.indices.emplace_back(buf[index]); const void* pointer = &(buffer.data[accessor.byteOffset + view.byteOffset]); switch (accessor.componentType) { @@ -155,29 +171,113 @@ for ( auto& p : m.primitives ) { #undef COPY_INDICES } - primitive.instance.materialID = p.material; - primitive.instance.primitiveID = primitives.size() - 1; - primitive.instance.meshID = meshID; - primitive.instance.objectID = 0; + meshlet.primitive.instance.materialID = p.material; + meshlet.primitive.instance.primitiveID = primitiveID; + meshlet.primitive.instance.meshID = meshID; + meshlet.primitive.instance.objectID = 0; - primitive.drawCommand.indices = indices.size(); - primitive.drawCommand.instances = 1; - primitive.drawCommand.indexID = 0; - primitive.drawCommand.vertexID = 0; - primitive.drawCommand.instanceID = 0; - primitive.drawCommand.vertices = vertices.size(); + meshlet.primitive.drawCommand.indices = meshlet.indices.size(); + meshlet.primitive.drawCommand.instances = 1; + meshlet.primitive.drawCommand.indexID = 0; + meshlet.primitive.drawCommand.vertexID = 0; + meshlet.primitive.drawCommand.instanceID = 0; + meshlet.primitive.drawCommand.vertices = meshlet.vertices.size(); - auto& drawCommand = drawCommands.emplace_back(pod::DrawCommand{ - .indices = indices.size(), - .instances = 1, - .indexID = mesh.index.count, - .vertexID = mesh.vertex.count, - .instanceID = 0, + /* detect winding order */ if ( sanitizer.windingOrder.should ) { + if ( !meshlet.indices.empty() ) { + for ( size_t i = 0; i < meshlet.indices.size() / 3; ++i ) { + size_t indices[3] = { + meshlet.indices[i * 3 + 0], + meshlet.indices[i * 3 + 1], + meshlet.indices[i * 3 + 2], + }; + pod::Vector3f triPosition[3] = { + meshlet.vertices[indices[0]].position, + meshlet.vertices[indices[1]].position, + meshlet.vertices[indices[2]].position, + }; + pod::Vector3f normal = meshlet.vertices[indices[0]].normal; + pod::Vector3f geomNormal = uf::vector::normalize( uf::vector::cross((triPosition[0] - triPosition[1]), (triPosition[1] - triPosition[2]))); + + // negative dot = mismatched winding order + if ( uf::vector::dot( normal, geomNormal ) < 0.0f ) { + meshlet.indices[i * 3 + 0] = indices[2]; + meshlet.indices[i * 3 + 2] = indices[0]; + ++sanitizer.windingOrder.corrected; + } + ++sanitizer.windingOrder.total; + } + } else { + for ( size_t i = 0; i < meshlet.vertices.size() / 3; ++i ) { + size_t indices[3] = { + i * 3 + 0, + i * 3 + 1, + i * 3 + 2, + }; + pod::Vector3f triPosition[3] = { + meshlet.vertices[indices[0]].position, + meshlet.vertices[indices[1]].position, + meshlet.vertices[indices[2]].position, + }; + pod::Vector3f normal = meshlet.vertices[indices[0]].normal; + pod::Vector3f geomNormal = uf::vector::normalize( uf::vector::cross((triPosition[0] - triPosition[1]), (triPosition[1] - triPosition[2]))); + + // negative dot = mismatched winding order + if ( uf::vector::dot( normal, geomNormal ) < 0.0f ) { + meshlet.indices[i * 3 + 0] = indices[2]; + meshlet.indices[i * 3 + 2] = indices[0]; + ++sanitizer.windingOrder.corrected; + } + ++sanitizer.windingOrder.total; + } + } + } +} + +if ( sanitizer.windingOrder.should ) { + UF_MSG_DEBUG("Winding order correction: {:.3f}%", ( (float) sanitizer.windingOrder.corrected / (float) sanitizer.windingOrder.total ) * 100.0f ); + if ( sanitizer.windingOrder.corrected * 2 > sanitizer.windingOrder.total ) { + UF_MSG_DEBUG("Consider inverting the front face settings for mesh: {}", m.name); + } +} - .vertices = vertices.size(), - }); +if ( meshgrid.grid.divisions.x > 1 || meshgrid.grid.divisions.y > 1 || meshgrid.grid.divisions.z > 1 ) { + auto partitioned = uf::meshgrid::partition( meshgrid.grid, meshlets, meshgrid.eps ); + if ( meshgrid.print ) UF_MSG_DEBUG( "Draw commands: {}: {} -> {} | Partitions: {} -> {}", m.name, meshlets.size(), partitioned.size(), + (meshgrid.grid.divisions.x * meshgrid.grid.divisions.y * meshgrid.grid.divisions.z), meshgrid.grid.nodes.size() + ); + meshlets = std::move( partitioned ); +} + +{ + size_t indexID = 0; + size_t vertexID = 0; + + mesh.bindIndirect(); + mesh.bind(false); // default to de-interleaved regardless of requirement (makes things easier) - mesh.insertVertices(vertices); - mesh.insertIndices(indices); + for ( auto& meshlet : meshlets ) { + auto& drawCommand = drawCommands.emplace_back(pod::DrawCommand{ + .indices = meshlet.indices.size(), + .instances = 1, + .indexID = indexID, + .vertexID = vertexID, + .instanceID = 0, + .auxID = meshlet.primitive.drawCommand.auxID, + .materialID = meshlet.primitive.drawCommand.materialID, + .vertices = meshlet.vertices.size(), + }); + + primitives.emplace_back( meshlet.primitive ); + + indexID += meshlet.indices.size(); + vertexID += meshlet.vertices.size(); + + mesh.insertVertices(meshlet.vertices); + mesh.insertIndices(meshlet.indices); + } + + mesh.insertIndirects(drawCommands); + mesh.updateDescriptor(); } \ No newline at end of file diff --git a/engine/src/ext/gltf/processPrimitives2.inl b/engine/src/ext/gltf/processPrimitives2.inl deleted file mode 100644 index 4bf11db7..00000000 --- a/engine/src/ext/gltf/processPrimitives2.inl +++ /dev/null @@ -1,214 +0,0 @@ -uf::stl::vector> meshlets; - -for ( auto& p : m.primitives ) { - size_t primitiveID = meshlets.size(); - auto& meshlet = meshlets.emplace_back(); - - struct Attribute { - uf::stl::string name = ""; - size_t components = 0; - size_t length = 0; - size_t stride = 0; - uint8_t* buffer = NULL; - - uf::stl::vector floats; - uf::stl::vector int8s; - uf::stl::vector int16s; - uf::stl::vector int32s; - }; - - uf::stl::unordered_map attributes = { - {"POSITION", {}}, - {"TEXCOORD_0", {}}, - {"COLOR_0", {}}, - {"NORMAL", {}}, -#if UF_GRAPH_PROCESS_PRIMITIVES_FULL - {"TANGENT", {}}, - {"JOINTS_0", {}}, - {"WEIGHTS_0", {}}, -#endif - }; - - for ( auto& kv : attributes ) { - auto& attribute = kv.second; - attribute.name = kv.first; - auto it = p.attributes.find(attribute.name); - if ( it == p.attributes.end() ) continue; - - auto& accessor = model.accessors[it->second]; - auto& view = model.bufferViews[accessor.bufferView]; - - if ( attribute.name == "POSITION" ) { - meshlet.vertices.resize(accessor.count); - meshlet.primitive.instance.bounds.min = pod::Vector3f{ accessor.minValues[0], accessor.minValues[1], accessor.minValues[2] }; - meshlet.primitive.instance.bounds.max = pod::Vector3f{ accessor.maxValues[0], accessor.maxValues[1], accessor.maxValues[2] }; - - if ( !(graph.metadata["flags"]["INVERT"].as()) ){ - meshlet.primitive.instance.bounds.min.x = -meshlet.primitive.instance.bounds.min.x; - meshlet.primitive.instance.bounds.max.x = -meshlet.primitive.instance.bounds.max.x; - } - } - - switch ( accessor.componentType ) { - case TINYGLTF_COMPONENT_TYPE_BYTE: - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: { - auto* buffer = reinterpret_cast(&(model.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset])); - attribute.components = accessor.ByteStride(view) / sizeof(uint8_t); - attribute.length = accessor.count * attribute.components; - attribute.int8s.assign( &buffer[0], &buffer[attribute.length] ); - } break; - case TINYGLTF_COMPONENT_TYPE_SHORT: - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: { - auto* buffer = reinterpret_cast(&(model.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset])); - attribute.components = accessor.ByteStride(view) / sizeof(uint16_t); - attribute.length = accessor.count * attribute.components; - attribute.int16s.assign( &buffer[0], &buffer[attribute.length] ); - } break; - case TINYGLTF_COMPONENT_TYPE_INT: - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: { - auto* buffer = reinterpret_cast(&(model.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset])); - attribute.components = accessor.ByteStride(view) / sizeof(uint32_t); - attribute.length = accessor.count * attribute.components; - attribute.int32s.assign( &buffer[0], &buffer[attribute.length] ); - } break; - case TINYGLTF_COMPONENT_TYPE_FLOAT: { - auto* buffer = reinterpret_cast(&(model.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset])); - attribute.components = accessor.ByteStride(view) / sizeof(float); - attribute.length = accessor.count * attribute.components; - attribute.floats.assign( &buffer[0], &buffer[attribute.length] ); - } break; - default: UF_MSG_ERROR("Unsupported component type"); - } - } - - for ( size_t i = 0; i < meshlet.vertices.size(); ++i ) { - #if 0 - #define ITERATE_ATTRIBUTE( name, member )\ - memcpy( &vertex.member[0], &attributes[name].buffer[i * attributes[name].components], attributes[name].stride ); - #else - #define ITERATE_ATTRIBUTE( name, member, floatScale )\ - if ( !attributes[name].int8s.empty() ) { \ - for ( size_t j = 0; j < attributes[name].components; ++j )\ - vertex.member[j] = attributes[name].int8s[i * attributes[name].components + j];\ - } else if ( !attributes[name].int16s.empty() ) { \ - for ( size_t j = 0; j < attributes[name].components; ++j )\ - vertex.member[j] = attributes[name].int16s[i * attributes[name].components + j];\ - } else if ( !attributes[name].int32s.empty() ) { \ - for ( size_t j = 0; j < attributes[name].components; ++j )\ - vertex.member[j] = attributes[name].int32s[i * attributes[name].components + j];\ - } else if ( !attributes[name].floats.empty() ) { \ - for ( size_t j = 0; j < attributes[name].components; ++j )\ - vertex.member[j] = attributes[name].floats[i * attributes[name].components + j] * floatScale;\ - } - #endif - - auto& vertex = meshlet.vertices[i]; - ITERATE_ATTRIBUTE("POSITION", position, 1); - ITERATE_ATTRIBUTE("TEXCOORD_0", uv, 1); - ITERATE_ATTRIBUTE("COLOR_0", color, 255.0f); - ITERATE_ATTRIBUTE("NORMAL", normal, 1); - #if UF_GRAPH_PROCESS_PRIMITIVES_FULL - ITERATE_ATTRIBUTE("TANGENT", tangent, 1); - ITERATE_ATTRIBUTE("JOINTS_0", joints, 1); - ITERATE_ATTRIBUTE("WEIGHTS_0", weights, 1); - #endif - - #undef ITERATE_ATTRIBUTE - - // required due to reverse-Z projection matrix flipping the X axis as well - // default is to proceed with this - if ( !(graph.metadata["flags"]["INVERT"].as()) ){ - vertex.position.x = -vertex.position.x; - vertex.normal.x = -vertex.normal.x; - #if UF_GRAPH_PROCESS_PRIMITIVES_FULL - vertex.tangent.x = -vertex.tangent.x; - #endif - } - - vertex.id.x = primitiveID; - vertex.id.y = meshID; - } - - if ( p.indices > -1 ) { - auto& accessor = model.accessors[p.indices]; - auto& view = model.bufferViews[accessor.bufferView]; - auto& buffer = model.buffers[view.buffer]; - - meshlet.indices.reserve( static_cast(accessor.count) ); - - #define COPY_INDICES() for (size_t index = 0; index < accessor.count; index++) meshlet.indices.emplace_back(buf[index]); - - const void* pointer = &(buffer.data[accessor.byteOffset + view.byteOffset]); - switch (accessor.componentType) { - case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: { - auto* buf = static_cast( pointer ); - COPY_INDICES() - break; - } - case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: { - auto* buf = static_cast( pointer ); - COPY_INDICES() - break; - } - case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: { - auto* buf = static_cast( pointer ); - COPY_INDICES() - break; - } - } - #undef COPY_INDICES - } - - meshlet.primitive.instance.materialID = p.material; - meshlet.primitive.instance.primitiveID = primitiveID; - meshlet.primitive.instance.meshID = meshID; - meshlet.primitive.instance.objectID = 0; - - meshlet.primitive.drawCommand.indices = meshlet.indices.size(); - meshlet.primitive.drawCommand.instances = 1; - meshlet.primitive.drawCommand.indexID = 0; - meshlet.primitive.drawCommand.vertexID = 0; - meshlet.primitive.drawCommand.instanceID = 0; - meshlet.primitive.drawCommand.vertices = meshlet.vertices.size(); -} - - -if ( meshgrid.grid.divisions.x > 1 || meshgrid.grid.divisions.y > 1 || meshgrid.grid.divisions.z > 1 ) { - auto partitioned = uf::meshgrid::partition( meshgrid.grid, meshlets, meshgrid.eps ); - if ( meshgrid.print ) UF_MSG_DEBUG( "Draw commands: {}: {} -> {} | Partitions: {} -> {}", m.name, meshlets.size(), partitioned.size(), - (meshgrid.grid.divisions.x * meshgrid.grid.divisions.y * meshgrid.grid.divisions.z), meshgrid.grid.nodes.size() - ); - meshlets = std::move( partitioned ); -} - -{ - size_t indexID = 0; - size_t vertexID = 0; - - mesh.bindIndirect(); - mesh.bind(false); // default to de-interleaved regardless of requirement (makes things easier) - - for ( auto& meshlet : meshlets ) { - auto& drawCommand = drawCommands.emplace_back(pod::DrawCommand{ - .indices = meshlet.indices.size(), - .instances = 1, - .indexID = indexID, - .vertexID = vertexID, - .instanceID = 0, - .auxID = meshlet.primitive.drawCommand.auxID, - .materialID = meshlet.primitive.drawCommand.materialID, - .vertices = meshlet.vertices.size(), - }); - - primitives.emplace_back( meshlet.primitive ); - - indexID += meshlet.indices.size(); - vertexID += meshlet.vertices.size(); - - mesh.insertVertices(meshlet.vertices); - mesh.insertIndices(meshlet.indices); - } - - mesh.insertIndirects(drawCommands); - mesh.updateDescriptor(); -} \ No newline at end of file diff --git a/engine/src/ext/imgui/imgui.cpp b/engine/src/ext/imgui/imgui.cpp index 6c647345..0a8ba0fc 100644 --- a/engine/src/ext/imgui/imgui.cpp +++ b/engine/src/ext/imgui/imgui.cpp @@ -91,7 +91,7 @@ namespace { Items.clear(); } - void AddLog(const uf::stl::string &str) { AddLog(str.c_str()); } + void AddLog(const uf::stl::string &str) { AddLog("%s", str.c_str()); } void AddLog(const char *fmt, ...) IM_FMTARGS(2) { // FIXME-OPT char buf[1024]; diff --git a/engine/src/ext/lua/lua.cpp b/engine/src/ext/lua/lua.cpp index 72378539..e16dc1db 100644 --- a/engine/src/ext/lua/lua.cpp +++ b/engine/src/ext/lua/lua.cpp @@ -59,7 +59,7 @@ void ext::lua::onInitialization( const std::function& function ) { namespace binds { namespace hook { - void add( const uf::stl::string& name, const sol::function& function ) { + void add( const uf::stl::string& name, sol::function function ) { uf::hooks.addHook( name, [function](ext::json::Value& json){ sol::table table = ext::lua::state["json"]["decode"]( json.dump() ); auto result = function( table ); diff --git a/engine/src/ext/lua/usertypes/object.cpp b/engine/src/ext/lua/usertypes/object.cpp index fd38eb01..5fdb7444 100644 --- a/engine/src/ext/lua/usertypes/object.cpp +++ b/engine/src/ext/lua/usertypes/object.cpp @@ -87,7 +87,7 @@ namespace binds { self.generateGraph(); return true; } - uf::Object& findByUid( uf::Object& self, const size_t& index ) { + uf::Object& findByUid( uf::Object& self, size_t index ) { auto* pointer = self.findByUid( index ); if ( pointer ) return pointer->as(); static uf::Object null; @@ -123,7 +123,7 @@ namespace binds { uf::Object& getParent( uf::Object& self ){ return self.getParent().as(); } - void addHook( uf::Object& self, const uf::stl::string& name, const sol::function& function ) { + void addHook( uf::Object& self, const uf::stl::string& name, sol::function function ) { self.addHook( name, [function](ext::json::Value& json){ uf::stl::string payload = json.dump(); auto decoded = ext::lua::decode( payload ); diff --git a/engine/src/ext/lua/usertypes/quaternion.cpp b/engine/src/ext/lua/usertypes/quaternion.cpp index 43236454..83d690d5 100644 --- a/engine/src/ext/lua/usertypes/quaternion.cpp +++ b/engine/src/ext/lua/usertypes/quaternion.cpp @@ -3,7 +3,7 @@ #include namespace binds { - float index( const pod::Quaternion<>& self, const size_t& index ) { + float index( const pod::Quaternion<>& self, size_t index ) { return self[index]; } pod::Quaternion<> lookAt( const pod::Vector3f& at, const pod::Vector3f& up ) { diff --git a/engine/src/ext/lua/usertypes/vector.cpp b/engine/src/ext/lua/usertypes/vector.cpp index 587bb756..0036ed8a 100644 --- a/engine/src/ext/lua/usertypes/vector.cpp +++ b/engine/src/ext/lua/usertypes/vector.cpp @@ -4,7 +4,7 @@ namespace binds { namespace v3f { - float index( const pod::Vector3f& self, const size_t& index ) { + float index( const pod::Vector3f& self, size_t index ) { return self[index]; } float distance( pod::Vector3f& self, const pod::Vector3f& other ) { @@ -45,7 +45,7 @@ namespace binds { } } namespace v4f { - float index( const pod::Vector4f& self, const size_t& index ) { + float index( const pod::Vector4f& self, size_t index ) { return self[index]; } float distance( pod::Vector4f& self, const pod::Vector4f& other ) { diff --git a/engine/src/ext/opengl/commands.cpp b/engine/src/ext/opengl/commands.cpp index 7dd57185..f7c6fc6e 100644 --- a/engine/src/ext/opengl/commands.cpp +++ b/engine/src/ext/opengl/commands.cpp @@ -300,7 +300,7 @@ void ext::opengl::CommandBuffer::drawIndexed( const ext::opengl::CommandBuffer:: GL_ERROR_CHECK(glEnable(drawInfo.textures.primary.viewType)); GL_ERROR_CHECK(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); GL_ERROR_CHECK(glBindTexture(drawInfo.textures.primary.viewType, drawInfo.textures.primary.image)); - GL_ERROR_CHECK(glTexCoordPointer(2, GL_FLOAT, drawInfo.attributes.uv.stride, (drawInfo.attributes.uv.pointer + drawInfo.attributes.uv.stride * drawInfo.descriptor.inputs.vertex.first))); + GL_ERROR_CHECK(glTexCoordPointer(2, GL_FLOAT, drawInfo.attributes.uv.stride, (static_cast(drawInfo.attributes.uv.pointer) + drawInfo.attributes.uv.stride * drawInfo.descriptor.inputs.vertex.first))); if ( !(drawInfo.textures.secondary.image && drawInfo.attributes.st.pointer) ) { GL_ERROR_CHECK(glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)); @@ -314,23 +314,23 @@ void ext::opengl::CommandBuffer::drawIndexed( const ext::opengl::CommandBuffer:: GL_ERROR_CHECK(glBindTexture(drawInfo.textures.secondary.viewType, drawInfo.textures.secondary.image)); GL_ERROR_CHECK(glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)); - GL_ERROR_CHECK(glTexCoordPointer(2, GL_FLOAT, drawInfo.attributes.st.stride, (drawInfo.attributes.st.pointer + drawInfo.attributes.st.stride * drawInfo.descriptor.inputs.vertex.first))); + GL_ERROR_CHECK(glTexCoordPointer(2, GL_FLOAT, drawInfo.attributes.st.stride, (static_cast(drawInfo.attributes.st.pointer) + drawInfo.attributes.st.stride * drawInfo.descriptor.inputs.vertex.first))); } } - if ( drawInfo.attributes.normal.pointer ) GL_ERROR_CHECK(glNormalPointer(GL_FLOAT, drawInfo.attributes.normal.stride, (drawInfo.attributes.normal.pointer + drawInfo.attributes.normal.stride * drawInfo.descriptor.inputs.vertex.first))); + if ( drawInfo.attributes.normal.pointer ) GL_ERROR_CHECK(glNormalPointer(GL_FLOAT, drawInfo.attributes.normal.stride, (static_cast(drawInfo.attributes.normal.pointer) + drawInfo.attributes.normal.stride * drawInfo.descriptor.inputs.vertex.first))); if ( drawInfo.attributes.color.pointer ) { GLenum format = GL_UNSIGNED_BYTE; switch ( drawInfo.attributes.color.descriptor.size / drawInfo.attributes.color.descriptor.components ) { case sizeof(uint8_t): format = GL_UNSIGNED_BYTE; break; case sizeof(float): format = GL_FLOAT; break; } - GL_ERROR_CHECK(glColorPointer(drawInfo.attributes.color.descriptor.components, format, drawInfo.attributes.color.stride, (drawInfo.attributes.color.pointer + drawInfo.attributes.color.stride * drawInfo.descriptor.inputs.vertex.first))); + GL_ERROR_CHECK(glColorPointer(drawInfo.attributes.color.descriptor.components, format, drawInfo.attributes.color.stride, (static_cast(drawInfo.attributes.color.pointer) + drawInfo.attributes.color.stride * drawInfo.descriptor.inputs.vertex.first))); } - GL_ERROR_CHECK(glVertexPointer(3, GL_FLOAT, drawInfo.attributes.position.stride, (drawInfo.attributes.position.pointer + drawInfo.attributes.position.stride * drawInfo.descriptor.inputs.vertex.first))); + GL_ERROR_CHECK(glVertexPointer(3, GL_FLOAT, drawInfo.attributes.position.stride, (static_cast(drawInfo.attributes.position.pointer) + drawInfo.attributes.position.stride * drawInfo.descriptor.inputs.vertex.first))); if ( drawInfo.descriptor.inputs.index.count ) { - GL_ERROR_CHECK(glDrawElements(GL_TRIANGLES, drawInfo.descriptor.inputs.index.count, indicesType, (drawInfo.attributes.index.pointer + drawInfo.attributes.index.stride * drawInfo.descriptor.inputs.index.first))); + GL_ERROR_CHECK(glDrawElements(GL_TRIANGLES, drawInfo.descriptor.inputs.index.count, indicesType, (static_cast(drawInfo.attributes.index.pointer) + drawInfo.attributes.index.stride * drawInfo.descriptor.inputs.index.first))); } else { GL_ERROR_CHECK(glDrawArrays(GL_TRIANGLES, drawInfo.descriptor.inputs.vertex.first, drawInfo.descriptor.inputs.vertex.count)); } diff --git a/engine/src/ext/opengl/opengl.cpp b/engine/src/ext/opengl/opengl.cpp index 5cb1ab49..dc5cf3df 100644 --- a/engine/src/ext/opengl/opengl.cpp +++ b/engine/src/ext/opengl/opengl.cpp @@ -47,7 +47,6 @@ bool ext::opengl::settings::invariant::individualPipelines = true; bool ext::opengl::settings::invariant::multithreadedRecording = true; uf::stl::string ext::opengl::settings::invariant::deferredMode = ""; -bool ext::opengl::settings::invariant::deferredReconstructPosition = false; bool ext::opengl::settings::invariant::deferredAliasOutputToSwapchain = false; bool ext::opengl::settings::invariant::deferredSampling = false; diff --git a/engine/src/ext/reactphysics/reactphysics.cpp b/engine/src/ext/reactphysics/reactphysics.cpp index f5ed4555..4fd95a4a 100644 --- a/engine/src/ext/reactphysics/reactphysics.cpp +++ b/engine/src/ext/reactphysics/reactphysics.cpp @@ -20,16 +20,21 @@ namespace { class RaycastCallback : public rp3d::RaycastCallback { public: bool isHit = false; + uf::Object* source = NULL; rp3d::RaycastInfo raycastInfo; virtual rp3d::decimal notifyRaycastHit(const rp3d::RaycastInfo& info) override { if ( !isHit || raycastInfo.hitFraction > info.hitFraction ) { - raycastInfo.body = info.body; - raycastInfo.hitFraction = info.hitFraction; - raycastInfo.collider = info.collider; - raycastInfo.worldNormal = info.worldNormal; - raycastInfo.worldPoint = info.worldPoint; - isHit = true; + if ( info.body->getUserData() == source ) { + + } else { + raycastInfo.body = info.body; + raycastInfo.hitFraction = info.hitFraction; + raycastInfo.collider = info.collider; + raycastInfo.worldNormal = info.worldNormal; + raycastInfo.worldPoint = info.worldPoint; + isHit = true; + } } // return rp3d::decimal(1.0); return raycastInfo.hitFraction; @@ -295,6 +300,9 @@ void ext::reactphysics::attach( pod::PhysicsState& state ) { state.body->enableGravity(false); } + // affects air speed, bad +// state.body->setLinearDamping(state.stats.friction); + auto& material = collider->getMaterial(); material.setBounciness(0); @@ -316,13 +324,18 @@ pod::PhysicsState& ext::reactphysics::create( uf::Object& object, const uf::Mesh uf::Mesh::Input indexInput = mesh.index; uf::Mesh::Attribute vertexAttribute = mesh.vertex.attributes.front(); + uf::Mesh::Attribute normalAttribute = mesh.vertex.attributes.front(); uf::Mesh::Attribute indexAttribute = mesh.index.attributes.front(); - for ( auto& attribute : mesh.vertex.attributes ) if ( attribute.descriptor.name == "position" ) { vertexAttribute = attribute; break; } + for ( auto& attribute : mesh.vertex.attributes ) { + if ( attribute.descriptor.name == "position" ) vertexAttribute = attribute; + if ( attribute.descriptor.name == "normal" ) normalAttribute = attribute; + } UF_ASSERT( vertexAttribute.descriptor.name == "position" ); rp3d::TriangleVertexArray::IndexDataType indexType = rp3d::TriangleVertexArray::IndexDataType::INDEX_INTEGER_TYPE; rp3d::TriangleVertexArray::VertexDataType vertexType = rp3d::TriangleVertexArray::VertexDataType::VERTEX_FLOAT_TYPE; + rp3d::TriangleVertexArray::NormalDataType normalType = rp3d::TriangleVertexArray::NormalDataType::NORMAL_FLOAT_TYPE; switch ( mesh.index.size ) { case sizeof(uint16_t): indexType = rp3d::TriangleVertexArray::IndexDataType::INDEX_SHORT_TYPE; break; case sizeof(uint32_t): indexType = rp3d::TriangleVertexArray::IndexDataType::INDEX_INTEGER_TYPE; break; @@ -334,6 +347,57 @@ pod::PhysicsState& ext::reactphysics::create( uf::Object& object, const uf::Mesh vertexInput = mesh.remapVertexInput( i ); indexInput = mesh.remapIndexInput( i ); + if ( normalAttribute.descriptor.name == "normal" ) { + rMesh->addSubpart(new rp3d::TriangleVertexArray( + vertexInput.count, + (const uint8_t*) (vertexAttribute.pointer) + vertexAttribute.stride * vertexInput.first, + vertexAttribute.stride, + + (const uint8_t*) (normalAttribute.pointer) + normalAttribute.stride * vertexInput.first, + normalAttribute.stride, + + indexInput.count / 3, + (const uint8_t*) (indexAttribute.pointer) + indexAttribute.stride * indexInput.first, + indexAttribute.stride * 3, + + vertexType, + normalType, + indexType + )); + } else { + rMesh->addSubpart(new rp3d::TriangleVertexArray( + vertexInput.count, + (const uint8_t*) (vertexAttribute.pointer) + vertexAttribute.stride * vertexInput.first, + vertexAttribute.stride, + + indexInput.count / 3, + (const uint8_t*) (indexAttribute.pointer) + indexAttribute.stride * indexInput.first, + indexAttribute.stride * 3, + + vertexType, + indexType + )); + } + } + } else { + if ( normalAttribute.descriptor.name == "normal" ) { + rMesh->addSubpart(new rp3d::TriangleVertexArray( + vertexInput.count, + (const uint8_t*) (vertexAttribute.pointer) + vertexAttribute.stride * vertexInput.first, + vertexAttribute.stride, + + (const uint8_t*) (normalAttribute.pointer) + normalAttribute.stride * vertexInput.first, + normalAttribute.stride, + + indexInput.count / 3, + (const uint8_t*) (indexAttribute.pointer) + indexAttribute.stride * indexInput.first, + indexAttribute.stride * 3, + + vertexType, + normalType, + indexType + )); + } else { rMesh->addSubpart(new rp3d::TriangleVertexArray( vertexInput.count, (const uint8_t*) (vertexAttribute.pointer) + vertexAttribute.stride * vertexInput.first, @@ -347,19 +411,6 @@ pod::PhysicsState& ext::reactphysics::create( uf::Object& object, const uf::Mesh indexType )); } - } else { - rMesh->addSubpart(new rp3d::TriangleVertexArray( - vertexInput.count, - (const uint8_t*) (vertexAttribute.pointer) + vertexAttribute.stride * vertexInput.first, - vertexAttribute.stride, - - indexInput.count / 3, - (const uint8_t*) (indexAttribute.pointer) + indexAttribute.stride * indexInput.first, - indexAttribute.stride * 3, - - vertexType, - indexType - )); } auto& state = ext::reactphysics::create( object ); @@ -512,30 +563,17 @@ float ext::reactphysics::rayCast( const pod::Vector3f& center, const pod::Vector return -1; ::RaycastCallback callback; - ::world->raycast( rp3d::Ray( ::convert( center ), ::convert( direction ) ), &callback ); + ::world->raycast( rp3d::Ray( ::convert( center ), ::convert( center + direction ) ), &callback ); if ( !callback.isHit ) return -1; return callback.raycastInfo.hitFraction; } -float ext::reactphysics::rayCast( const pod::Vector3f& center, const pod::Vector3f& direction, size_t& uid ) { +float ext::reactphysics::rayCast( const pod::Vector3f& center, const pod::Vector3f& direction, uf::Object* source, uf::Object*& object ) { if ( !::world ) return -1; ::RaycastCallback callback; - ::world->raycast( rp3d::Ray( ::convert( center ), ::convert( direction ) ), &callback ); - uid = 0; - if ( !callback.isHit ) { - return -1; - } - auto* object = (uf::Object*) callback.raycastInfo.body->getUserData(); - uid = object->getUid(); - return callback.raycastInfo.hitFraction; -} -float ext::reactphysics::rayCast( const pod::Vector3f& center, const pod::Vector3f& direction, uf::Object*& object ) { - if ( !::world ) - return -1; - - ::RaycastCallback callback; - ::world->raycast( rp3d::Ray( ::convert( center ), ::convert( direction ) ), &callback ); + callback.source = source; + ::world->raycast( rp3d::Ray( ::convert( center ), ::convert( center + direction ) ), &callback ); object = NULL; if ( !callback.isHit ) { return -1; diff --git a/engine/src/ext/vulkan/graphic.cpp b/engine/src/ext/vulkan/graphic.cpp index 8b5f4c36..4d9b022d 100644 --- a/engine/src/ext/vulkan/graphic.cpp +++ b/engine/src/ext/vulkan/graphic.cpp @@ -1042,8 +1042,7 @@ void ext::vulkan::Graphic::initializeMesh( uf::Mesh& mesh, bool buffer ) { } } bool ext::vulkan::Graphic::updateMesh( uf::Mesh& mesh ) { - UF_MSG_ERROR("need to fix"); - return false; +// UF_MSG_ERROR("need to fix"); return false; // generate indices if not found // if ( mesh.index.count == 0 ) mesh.generateIndices(); diff --git a/engine/src/ext/vulkan/vulkan.cpp b/engine/src/ext/vulkan/vulkan.cpp index 0db80cf2..a94778a2 100644 --- a/engine/src/ext/vulkan/vulkan.cpp +++ b/engine/src/ext/vulkan/vulkan.cpp @@ -46,8 +46,7 @@ bool ext::vulkan::settings::invariant::individualPipelines = true; bool ext::vulkan::settings::invariant::multithreadedRecording = true; uf::stl::string ext::vulkan::settings::invariant::deferredMode = ""; -bool ext::vulkan::settings::invariant::deferredReconstructPosition = false; -bool ext::vulkan::settings::invariant::deferredAliasOutputToSwapchain = true; +bool ext::vulkan::settings::invariant::deferredAliasOutputToSwapchain = false; bool ext::vulkan::settings::invariant::deferredSampling = true; bool ext::vulkan::settings::invariant::multiview = true; diff --git a/engine/src/utils/hook/hook.cpp b/engine/src/utils/hook/hook.cpp index ca54d584..4b5d6c2b 100644 --- a/engine/src/utils/hook/hook.cpp +++ b/engine/src/utils/hook/hook.cpp @@ -16,7 +16,7 @@ void uf::Hooks::removeHook( const uf::Hooks::name_t& name, size_t uid ) { auto& container = this->m_container[name]; for ( auto it = container.begin(); it != container.end(); ++it ) { if ( it->uid == uid ) { - this->m_container[name].erase(it); + it = container.erase(it); break; } } diff --git a/engine/src/utils/http/http.cpp b/engine/src/utils/http/http.cpp index 3f99734d..3846c89b 100644 --- a/engine/src/utils/http/http.cpp +++ b/engine/src/utils/http/http.cpp @@ -11,7 +11,7 @@ namespace { } } -uf::Http UF_API uf::http::get( const uf::stl::string& url ) { +uf::Http uf::http::get( const uf::stl::string& url ) { uf::Http http; #if UF_USE_CURL diff --git a/engine/src/utils/serialize/serializer.cpp b/engine/src/utils/serialize/serializer.cpp index b8b39a59..0e99559d 100644 --- a/engine/src/utils/serialize/serializer.cpp +++ b/engine/src/utils/serialize/serializer.cpp @@ -160,18 +160,9 @@ void uf::Serializer::merge( const uf::Serializer& other, bool priority ) { update(*this, other); } -void uf::Serializer::import( const uf::Serializer& other ) { -/* - uf::Serializer diff = nlohmann::json::diff(*this, other); - uf::Serializer filtered; - for ( size_t i = 0; i < diff.size(); ++i ) { - if ( diff[i]["op"].as() != "remove" ) - filtered.emplace_back(diff[i]); - } - patch( filtered ); - std::cout << this->dump(1, '\t') << std::endl; -*/ +// only updates if this[key] == null +void uf::Serializer::import( const uf::Serializer& other ) { if ( !ext::json::isObject( *this ) || !ext::json::isObject( other ) ) return; std::function update = [&]( ext::json::Value& a, const ext::json::Value& b ) { // doesn't exist, just copy it diff --git a/ext/behaviors/player/behavior.cpp b/ext/behaviors/player/behavior.cpp index dce82de8..9743e711 100644 --- a/ext/behaviors/player/behavior.cpp +++ b/ext/behaviors/player/behavior.cpp @@ -203,17 +203,26 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { stats.noclipped = metadata.system.noclipped; stats.floored = stats.noclipped; auto& collider = this->getComponent(); - if ( !stats.floored && collider.body && uf::physics::impl::rayCast( transform.position, transform.position - pod::Vector3f{0,1,0} ) >= 0.0f ) stats.floored = true; - else stats.floored |= fabs(physics.linear.velocity.y) < 0.01f; +// if ( !stats.floored && collider.body && uf::physics::impl::rayCast( transform.position, transform.position - pod::Vector3f{0,1,0} ) >= 0.0f ) stats.floored = true; + { + uf::Object* hit = NULL; + pod::Vector3f center = transform.position + metadata.movement.floored.feet; + pod::Vector3f direction = metadata.movement.floored.floor; + float t = 0.0f; + if ( !stats.floored && collider.body && (t = uf::physics::impl::rayCast( center, direction, this, hit )) >= 0.0f ) { + if ( metadata.movement.floored.print ) UF_MSG_DEBUG("Floored: {} | {}", hit->getName(), t); + stats.floored = true; + } + } - TIMER(0.125, keys.use && ) { + TIMER(0.25, keys.use && ) { size_t uid = 0; uf::Object* pointer = NULL; float length = 4.0f; - pod::Vector3f pos = transform.position + cameraTransform.position; - pod::Vector3f dir = uf::vector::normalize( transform.forward + pod::Vector3f{ 0, cameraTransform.forward.y, 0 } ) * length; + pod::Vector3f center = transform.position + cameraTransform.position; + pod::Vector3f direction = uf::vector::normalize( transform.forward + pod::Vector3f{ 0, cameraTransform.forward.y, 0 } ) * length; - float depth = uf::physics::impl::rayCast( pos, pos + dir, pointer ); + float depth = uf::physics::impl::rayCast( center, direction, this, pointer ); if ( pointer ) { ext::json::Value payload; payload["uid"] = this->getUid(); @@ -363,7 +372,8 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { if ( stats.noclipped ) { physics.linear.velocity += target * speed.move; } else { - float delta = collider.body ? uf::physics::impl::timescale : uf::physics::time::delta; + // float delta = collider.body ? uf::physics::impl::timescale : uf::physics::time::delta; + float delta = uf::physics::time::delta; physics.linear.velocity += target * std::clamp( speed.move * factor - uf::vector::dot( physics.linear.velocity, target ), 0.0f, speed.move * 10 * delta ); } @@ -511,8 +521,8 @@ void ext::PlayerBehavior::render( uf::Object& self ){} void ext::PlayerBehavior::destroy( uf::Object& self ){} void ext::PlayerBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer ){ auto& serializerSystem = serializer["system"]; - auto& serializerSystemPhysics = serializerSystem["physics"]; - auto& serializerSystemPhysicsMovement = serializerSystemPhysics["movement"]; + auto& serializerPhysics = serializer["physics"]; + auto& serializerMovement = serializer["movement"]; auto& serializerAudioFootstep = serializer["audio"]["footstep"]; auto& serializerCamera = serializer["camera"]; auto& serializerCameraLimit = serializerCamera["limit"]; @@ -521,15 +531,18 @@ void ext::PlayerBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializerSystem["control"] = /*this->*/system.control; serializerSystem["crouching"] = /*this->*/system.crouching; serializerSystem["noclipped"] = /*this->*/system.noclipped; - serializerSystemPhysics["friction"] = /*this->*/movement.friction; - serializerSystemPhysicsMovement["rotate"] = /*this->*/movement.rotate; - serializerSystemPhysicsMovement["move"] = /*this->*/movement.move; - serializerSystemPhysicsMovement["run"] = /*this->*/movement.run; - serializerSystemPhysicsMovement["walk"] = /*this->*/movement.walk; - serializerSystemPhysicsMovement["air"] = /*this->*/movement.air; - serializerSystemPhysicsMovement["jump"] = uf::vector::encode(/*this->*/movement.jump); - serializerSystemPhysicsMovement["crouch"] = /*this->*/movement.crouch; -// serializerSystemPhysicsMovement["look"] = /*this->*/movement.look; + serializerPhysics["friction"] = /*this->*/movement.friction; + serializerMovement["rotate"] = /*this->*/movement.rotate; + serializerMovement["move"] = /*this->*/movement.move; + serializerMovement["run"] = /*this->*/movement.run; + serializerMovement["walk"] = /*this->*/movement.walk; + serializerMovement["air"] = /*this->*/movement.air; + serializerMovement["jump"] = uf::vector::encode(/*this->*/movement.jump); + serializerMovement["floored"]["feet"] = uf::vector::encode(/*this->*/movement.floored.feet); + serializerMovement["floored"]["floor"] = uf::vector::encode(/*this->*/movement.floored.floor); + serializerMovement["floored"]["print"] = /*this->*/movement.floored.print; + serializerMovement["crouch"] = /*this->*/movement.crouch; +// serializerMovement["look"] = /*this->*/movement.look; serializerAudioFootstep["list"] = /*this->*/audio.footstep.list; serializerAudioFootstep["volume"] = /*this->*/audio.footstep.volume; serializerCamera["invert"] = uf::vector::encode(/*this->*/camera.invert); @@ -540,32 +553,35 @@ void ext::PlayerBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& void ext::PlayerBehavior::Metadata::deserialize( uf::Object& self, uf::Serializer& serializer ){ auto& serializerSystem = serializer["system"]; auto& serializerAudioFootstep = serializer["audio"]["footstep"]; - auto& serializerSystemPhysics = serializerSystem["physics"]; - auto& serializerSystemPhysicsMovement = serializerSystemPhysics["movement"]; + auto& serializerPhysics = serializer["physics"]; + auto& serializerMovement = serializer["movement"]; auto& serializerCamera = serializer["camera"]; auto& serializerCameraLimit = serializerCamera["limit"]; - /*this->*/system.menu = serializerSystem["menu"].as(); - /*this->*/system.control = serializerSystem["control"].as(true); - /*this->*/system.crouching = serializerSystem["crouching"].as(); - /*this->*/system.noclipped = serializerSystem["noclipped"].as(); - /*this->*/movement.friction = serializerSystemPhysics["friction"].as(); - /*this->*/movement.rotate = serializerSystemPhysicsMovement["rotate"].as(); - /*this->*/movement.move = serializerSystemPhysicsMovement["move"].as(); - /*this->*/movement.run = serializerSystemPhysicsMovement["run"].as(); - /*this->*/movement.walk = serializerSystemPhysicsMovement["walk"].as(); - /*this->*/movement.air = serializerSystemPhysicsMovement["air"].as(); - /*this->*/movement.jump = uf::vector::decode(serializerSystemPhysicsMovement["jump"], pod::Vector3f{}); - /*this->*/movement.crouch = serializerSystemPhysicsMovement["crouch"].as(); -// /*this->*/movement.look = serializerSystemPhysicsMovement["look"].as(1.0f); + /*this->*/system.menu = serializerSystem["menu"].as(/*this->*/system.menu); + /*this->*/system.control = serializerSystem["control"].as(/*this->*/system.control); + /*this->*/system.crouching = serializerSystem["crouching"].as(/*this->*/system.crouching); + /*this->*/system.noclipped = serializerSystem["noclipped"].as(/*this->*/system.noclipped); + /*this->*/movement.friction = serializerPhysics["friction"].as(/*this->*/movement.friction); + /*this->*/movement.rotate = serializerMovement["rotate"].as(/*this->*/movement.rotate); + /*this->*/movement.move = serializerMovement["move"].as(/*this->*/movement.move); + /*this->*/movement.run = serializerMovement["run"].as(/*this->*/movement.run); + /*this->*/movement.walk = serializerMovement["walk"].as(/*this->*/movement.walk); + /*this->*/movement.air = serializerMovement["air"].as(/*this->*/movement.air); + /*this->*/movement.jump = uf::vector::decode(serializerMovement["jump"], /*this->*/movement.jump); + /*this->*/movement.floored.feet = uf::vector::decode(serializerMovement["floored"]["feet"], /*this->*/movement.floored.feet); + /*this->*/movement.floored.floor = uf::vector::decode(serializerMovement["floored"]["floor"], /*this->*/movement.floored.floor); + /*this->*/movement.floored.print = serializerMovement["floored"]["print"].as(/*this->*/movement.floored.print); + /*this->*/movement.crouch = serializerMovement["crouch"].as(/*this->*/movement.crouch); +// /*this->*/movement.look = serializerMovement["look"].as(1.0f); ext::json::forEach( serializerAudioFootstep["list"], [&]( const ext::json::Value& value ){ /*this->*/audio.footstep.list.emplace_back(value); }); /*this->*/audio.footstep.volume = serializerAudioFootstep["volume"].as(); - /*this->*/camera.invert = uf::vector::decode( serializerCamera["invert"], pod::Vector3t{false, false, false} ); - /*this->*/camera.limit.current = uf::vector::decode( serializerCameraLimit["current"], pod::Vector3f{NAN, NAN, NAN} ); - /*this->*/camera.limit.min = uf::vector::decode( serializerCameraLimit["minima"], pod::Vector3f{NAN, NAN, NAN} ); - /*this->*/camera.limit.max = uf::vector::decode( serializerCameraLimit["maxima"], pod::Vector3f{NAN, NAN, NAN} ); + /*this->*/camera.invert = uf::vector::decode( serializerCamera["invert"], /*this->*/camera.invert ); + /*this->*/camera.limit.current = uf::vector::decode( serializerCameraLimit["current"], /*this->*/camera.limit.current ); + /*this->*/camera.limit.min = uf::vector::decode( serializerCameraLimit["minima"], /*this->*/camera.limit.min ); + /*this->*/camera.limit.max = uf::vector::decode( serializerCameraLimit["maxima"], /*this->*/camera.limit.max ); } #undef this \ No newline at end of file diff --git a/ext/behaviors/player/behavior.h b/ext/behaviors/player/behavior.h index f1c6083e..22002f3a 100644 --- a/ext/behaviors/player/behavior.h +++ b/ext/behaviors/player/behavior.h @@ -27,6 +27,11 @@ namespace ext { float friction = 0.8f; float air = 1.0f; pod::Vector3f jump = {0,8,0}; + struct { + pod::Vector3f feet = {0,-1.5,0}; + pod::Vector3f floor = {0,-1,0}; + bool print = false; + } floored; } movement; struct { struct { diff --git a/ext/behaviors/raytrace/behavior.cpp b/ext/behaviors/raytrace/behavior.cpp index 8af77eaa..3c8521e3 100644 --- a/ext/behaviors/raytrace/behavior.cpp +++ b/ext/behaviors/raytrace/behavior.cpp @@ -87,11 +87,17 @@ void ext::RayTraceSceneBehavior::tick( uf::Object& self ) { if ( graphic.material.hasShader("ray:gen", uf::renderer::settings::pipelines::names::rt) ) { auto& shader = graphic.material.getShader("ray:gen", uf::renderer::settings::pipelines::names::rt); // + pod::Vector2ui size = metadata.renderer.size; + UF_MSG_DEBUG("Size: {}", uf::vector::toString( size )); + if ( size.x == 0 ) size.x = uf::renderer::settings::width * metadata.renderer.scale; + if ( size.y == 0 ) size.y = uf::renderer::settings::height * metadata.renderer.scale; + UF_MSG_DEBUG("Size: {}", uf::vector::toString( size )); + auto& image = shader.textures.emplace_back(); image.fromBuffers( NULL, 0, uf::renderer::enums::Format::R8G8B8A8_UNORM, - uf::renderer::settings::width * metadata.renderer.scale, uf::renderer::settings::height * metadata.renderer.scale, 1, 1, + size.x, size.y, 1, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL ); @@ -161,8 +167,9 @@ void ext::RayTraceSceneBehavior::tick( uf::Object& self ) { if ( shader.textures.empty() ) { auto& tex = shader.textures.emplace_back(); tex.aliasTexture( image ); - tex.sampler.descriptor.filter.min = VK_FILTER_NEAREST; - tex.sampler.descriptor.filter.mag = VK_FILTER_NEAREST; + + tex.sampler.descriptor.filter.min = metadata.renderer.filter; + tex.sampler.descriptor.filter.mag = metadata.renderer.filter; renderMode.execute = true; renderMode.metadata.limiter.execute = true; @@ -172,11 +179,14 @@ void ext::RayTraceSceneBehavior::tick( uf::Object& self ) { } if ( uf::renderer::states::resized ) { + pod::Vector2ui size = metadata.renderer.size; + if ( size.x == 0 ) size.x = uf::renderer::settings::width * metadata.renderer.scale; + if ( size.y == 0 ) size.y = uf::renderer::settings::height * metadata.renderer.scale; image.destroy(); image.fromBuffers( NULL, 0, uf::renderer::enums::Format::R8G8B8A8_UNORM, - uf::renderer::settings::width * metadata.renderer.scale, uf::renderer::settings::height * metadata.renderer.scale, 1, 1, + size.x, size.y, 1, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL ); @@ -207,7 +217,6 @@ void ext::RayTraceSceneBehavior::Metadata::serialize( uf::Object& self, uf::Seri } void ext::RayTraceSceneBehavior::Metadata::deserialize( uf::Object& self, uf::Serializer& serializer ) { - UF_MSG_DEBUG("Update"); // merge vxgi settings with global settings { const auto& globalSettings = ext::config["engine"]["scenes"]["rt"]; @@ -217,8 +226,17 @@ void ext::RayTraceSceneBehavior::Metadata::deserialize( uf::Object& self, uf::Se } ); } - if ( serializer["size"].is() ) { - /*this->*/renderer.scale = serializer["size"].as(/*this->*/renderer.scale); + if ( serializer["rt"]["filter"].is() ) { + const auto mode = uf::string::lowercase( serializer["rt"]["filter"].as() ); + if ( mode == "nearest" ) /*this->*/renderer.filter = uf::renderer::enums::Filter::NEAREST; + else if ( mode == "linear" ) /*this->*/renderer.filter = uf::renderer::enums::Filter::LINEAR; + else UF_MSG_WARNING("Invalid Filter enum string specified: {}", mode); + } + + if ( serializer["rt"]["size"].is() ) { + /*this->*/renderer.scale = serializer["rt"]["size"].as(/*this->*/renderer.scale); + } else if ( ext::json::isArray( serializer["rt"]["size"] ) ) { + /*this->*/renderer.size = uf::vector::decode( serializer["rt"]["size"], /*this->*/renderer.size ); } else if ( ext::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"].is() ) { /*this->*/renderer.scale = ext::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"].as(/*this->*/renderer.scale); } diff --git a/ext/behaviors/raytrace/behavior.h b/ext/behaviors/raytrace/behavior.h index 296e7904..b3d98fb7 100644 --- a/ext/behaviors/raytrace/behavior.h +++ b/ext/behaviors/raytrace/behavior.h @@ -17,6 +17,8 @@ namespace ext { struct { bool bound = false; float scale = 1; + pod::Vector2ui size{}; + uf::renderer::enums::Filter::type_t filter = uf::renderer::enums::Filter::NEAREST; } renderer; struct { diff --git a/ext/gui/behavior.cpp b/ext/gui/behavior.cpp index 68d1291f..58576436 100644 --- a/ext/gui/behavior.cpp +++ b/ext/gui/behavior.cpp @@ -413,6 +413,16 @@ void ext::Gui::load( const uf::Image& image ) { } } else if ( ext::json::isArray(metadataJson["scaling"]) ) { raidou = uf::vector::decode( metadataJson["scaling"], raidou ); + #if 1 + float factor = 1; + if ( raidou.x == 0 && raidou.y != 0 ) { + factor = raidou.y; + } else if ( raidou.y == 0 && raidou.x != 0 ) { + factor = raidou.x; + } + raidou.x /= factor; + raidou.y /= factor; + #endif modified = true; } if ( modified ) { @@ -531,7 +541,11 @@ void ext::GuiBehavior::initialize( uf::Object& self ) { metadata.clicked = clicked; - if ( clicked ) this->callHook("gui:Clicked.%UID%", payload); + + if ( clicked ) { + UF_MSG_DEBUG("{} clicked", this->getName()); + this->callHook("gui:Clicked.%UID%", payload); + } this->callHook("gui:Mouse.Clicked.%UID%", payload); } ); diff --git a/ext/gui/manager/behavior.cpp b/ext/gui/manager/behavior.cpp index 58af983c..dbe7f19d 100644 --- a/ext/gui/manager/behavior.cpp +++ b/ext/gui/manager/behavior.cpp @@ -38,7 +38,7 @@ ext::gui::Size ext::gui::size = { 0, }, #if UF_ENV_DREAMCAST - .reference = { 960, 720 }, + .reference = { 640, 480 }, #else .reference = { 1920, 1080 }, #endif @@ -67,7 +67,7 @@ void ext::GuiManagerBehavior::initialize( uf::Object& self ) { ext::gui::size.current = uf::vector::decode( ext::config["window"]["size"], pod::Vector2f{} ); this->addHook( "window:Resized", [&](pod::payloads::windowResized& payload){ ext::gui::size.current = payload.window.size; - // ext::gui::size.reference = payload.window.size; + ext::gui::size.reference = payload.window.size; } ); this->addHook( "window:Mouse.Moved", [&](pod::payloads::windowMouseMoved& payload){ bool clicked = false; diff --git a/ext/main.cpp b/ext/main.cpp index 47acde89..186be593 100644 --- a/ext/main.cpp +++ b/ext/main.cpp @@ -416,7 +416,6 @@ void EXT_API ext::initialize() { uf::renderer::settings::invariant::waitOnRenderEnd = configRenderInvariantJson["wait on render end"].as( uf::renderer::settings::invariant::waitOnRenderEnd ); uf::renderer::settings::invariant::individualPipelines = configRenderInvariantJson["individual pipelines"].as( uf::renderer::settings::invariant::individualPipelines ); uf::renderer::settings::invariant::deferredMode = configRenderInvariantJson["deferred mode"].as( uf::renderer::settings::invariant::deferredMode ); - uf::renderer::settings::invariant::deferredReconstructPosition = configRenderInvariantJson["deferred reconstruct position"].as( uf::renderer::settings::invariant::deferredReconstructPosition ); uf::renderer::settings::invariant::deferredAliasOutputToSwapchain = configRenderInvariantJson["deferred alias output to swapchain"].as( uf::renderer::settings::invariant::deferredAliasOutputToSwapchain ); uf::renderer::settings::invariant::deferredSampling = configRenderInvariantJson["deferred sampling"].as( uf::renderer::settings::invariant::deferredSampling ); @@ -606,11 +605,45 @@ void EXT_API ext::initialize() { }); uf::hooks.addHook( "game:Scene.Cleanup", [&](){ - UF_MSG_DEBUG("Cleanup"); - uf::scene::unloadScene(); - uf::physics::terminate(); - uf::physics::initialize(); + + // reset physics world + { + uf::physics::terminate(); + uf::physics::initialize(); + } + // reset graph state + { + // cleanup graphic handles + for ( auto pair : uf::graph::storage.texture2Ds.map ) pair.second.destroy(); + for ( auto& t : uf::graph::storage.shadow2Ds ) t.destroy(); + for ( auto& t : uf::graph::storage.shadowCubes ) t.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(); + + // cleanup storage cache + uf::graph::storage.instances.clear(); + uf::graph::storage.instanceAddresses.clear(); + uf::graph::storage.primitives.clear(); + uf::graph::storage.drawCommands.clear(); + uf::graph::storage.meshes.clear(); + uf::graph::storage.images.clear(); + uf::graph::storage.materials.clear(); + uf::graph::storage.textures.clear(); + uf::graph::storage.samplers.clear(); + uf::graph::storage.skins.clear(); + uf::graph::storage.animations.clear(); + uf::graph::storage.atlases.clear(); + uf::graph::storage.joints.clear(); + uf::graph::storage.texture2Ds.clear(); + uf::graph::storage.entities.clear(); + uf::graph::storage.shadow2Ds.clear(); + uf::graph::storage.shadowCubes.clear(); + } + + uf::renderer::states::rebuild = true; }); uf::hooks.addHook( "system:Quit", [&](ext::json::Value& json){ if ( json["message"].is() ) { diff --git a/makefiles/win64.clang.make b/makefiles/win64.clang.make index 1bd4a23b..16f4c3f4 100644 --- a/makefiles/win64.clang.make +++ b/makefiles/win64.clang.make @@ -2,6 +2,7 @@ ARCH = win64 CDIR = CC = clang CXX = clang++ -OPTIMIZATIONS = -O3 -fstrict-aliasing # -flto +OPTIMIZATIONS = -O3 -fstrict-aliasing -flto WARNINGS = -g -Wall -Wno-pointer-arith -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder-ctor -Wno-ignored-attributes -Wno-c++11-narrowing -Wno-unknown-pragmas -Wno-nullability-completeness -Wno-defaulted-function-deleted -Wno-mismatched-tags -FLAGS += -std=c++17 $(OPTIMIZATIONS) $(WARNINGS) -fcolor-diagnostics -fansi-escape-codes \ No newline at end of file +SANITIZE = -fuse-ld=lld # -fsanitize=address -fno-omit-frame-pointer +FLAGS += -std=c++17 $(OPTIMIZATIONS) $(WARNINGS) $(SANITIZE) -fcolor-diagnostics -fansi-escape-codes \ No newline at end of file diff --git a/makefiles/win64.gcc.make b/makefiles/win64.gcc.make index 88166693..b081d1d4 100644 --- a/makefiles/win64.gcc.make +++ b/makefiles/win64.gcc.make @@ -2,6 +2,6 @@ ARCH = win64 CDIR = CC = gcc CXX = g++ -OPTIMIZATIONS = -O3 -g -fstrict-aliasing -DUF_NO_EXCEPTIONS # -flto +OPTIMIZATIONS = -O3 -g -fstrict-aliasing -DUF_NO_EXCEPTIONS -march=native # -flto WARNINGS = -Wall -Wno-unknown-pragmas -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder -Wno-sign-compare -Wno-unused-but-set-variable -Wno-ignored-attributes -Wno-narrowing -Wno-misleading-indentation FLAGS += -std=c++20 $(OPTIMIZATIONS) $(WARNINGS) -fdiagnostics-color=always \ No newline at end of file diff --git a/makefiles/win64.zig.make b/makefiles/win64.zig.make new file mode 100644 index 00000000..08641e92 --- /dev/null +++ b/makefiles/win64.zig.make @@ -0,0 +1,8 @@ +ARCH = win64 +CDIR = +CC = /opt/zig/zig cc +CXX = /opt/zig/zig c++ +OPTIMIZATIONS = -O3 -fstrict-aliasing #-flto +WARNINGS = -g -Wall -Wno-pointer-arith -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder-ctor -Wno-ignored-attributes -Wno-c++11-narrowing -Wno-unknown-pragmas -Wno-nullability-completeness -Wno-defaulted-function-deleted -Wno-mismatched-tags +SANITIZE = #-fuse-ld=lld -fsanitize=address -fno-omit-frame-pointer +FLAGS += -std=c++17 $(OPTIMIZATIONS) $(WARNINGS) $(SANITIZE) -I./dep/zig/include/ -target x86_64-windows-gnu \ No newline at end of file diff --git a/program.sh b/program.sh index a71b47b3..741760be 100644 --- a/program.sh +++ b/program.sh @@ -6,7 +6,9 @@ ARCH=$(cat ./exe/default/arch) CC=$(cat ./exe/default/cc) RENDERER=$(cat ./exe/default/renderer) -export PATH="$(pwd)/exe/lib/${ARCH}/:$(pwd)/exe/lib/${ARCH}/${CC}/${RENDERER}/:${PATH}" +export PATH="$(pwd)/exe/lib/${ARCH}/:$(pwd)/exe/lib/${ARCH}/${CC}/:$(pwd)/exe/lib/${ARCH}/${CC}/${RENDERER}/:${PATH}" +echo PATH: ${PATH} +echo Executing ./exe/program.${ARCH}.${CC}.${RENDERER}.exe $@ ./exe/program.${ARCH}.${CC}.${RENDERER}.exe $@ tskill program