Merge pull request #10 from harfang3dadmin/main

3.2.2 Release.
This commit is contained in:
astrofra 2022-06-05 14:24:24 +02:00 committed by GitHub
commit cae6343248
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
159 changed files with 12001 additions and 10298 deletions

3
.gitignore vendored
View File

@ -5,3 +5,6 @@ build
**/.vscode/**
/tools/fbx_converter/tests/config.h
/harfang/tests/data_compiled/**
*.egg-info
dist
commit_id

4
.gitmodules vendored
View File

@ -1,7 +1,7 @@
[submodule "extern/openal-soft"]
path = extern/openal-soft
url = https://github.com/kcat/openal-soft.git
branch = 1.21.1
branch = 1.22.0
[submodule "extern/openvr"]
path = extern/openvr
url = https://github.com/ValveSoftware/openvr.git
@ -13,7 +13,7 @@
[submodule "extern/glfw"]
path = extern/glfw
url = https://github.com/glfw/glfw.git
branch = 3.3.6
branch = 3.3.7
[submodule "extern/bgfx/bgfx"]
path = extern/bgfx/bgfx
url = https://github.com/bkaradzic/bgfx

View File

@ -281,6 +281,9 @@ static bool _KeyboardState_Key(hg::KeyboardState *s, hg::Key key) { return s->ke
gen.end_class(keyboard)
bind_signal_T(gen, 'TextInputSignal', 'void', ['const char*'], 'TextInputCallback')
gen.bind_variable('const hg::Signal<void(const char *)> hg::on_text_input', bound_name='OnTextInput')
# gamepad
gen.bind_named_enum('hg::GamepadAxes', ['GA_LeftX', 'GA_LeftY', 'GA_RightX', 'GA_RightY', 'GA_LeftTrigger', 'GA_RightTrigger', 'GA_Count'])
gen.bind_named_enum('hg::GamepadButton', [
@ -641,13 +644,17 @@ def bind_projection(gen):
gen.bind_function('hg::ExtractZRangeFromProjectionMatrix', 'void', ['const hg::Mat44 &m', 'float &znear', 'float &zfar'], {'arg_out': ['znear', 'zfar']})
gen.bind_function('hg::ProjectToClipSpace', 'bool', ['const hg::Mat44 &proj', 'const hg::Vec3 &view', 'hg::Vec3 &clip'], {'arg_out': ['clip']})
gen.bind_function('hg::ProjectOrthoToClipSpace', 'bool', ['const hg::Mat44 &proj', 'const hg::Vec3 &view', 'hg::Vec3 &clip'], {'arg_out': ['clip']})
gen.bind_function('hg::UnprojectFromClipSpace', 'bool', ['const hg::Mat44 &inv_proj', 'const hg::Vec3 &clip', 'hg::Vec3 &view'], {'arg_out': ['view']})
gen.bind_function('hg::UnprojectOrthoFromClipSpace', 'bool', ['const hg::Mat44 &inv_proj', 'const hg::Vec3 &clip', 'hg::Vec3 &view'], {'arg_out': ['view']})
gen.bind_function('hg::ClipSpaceToScreenSpace', 'hg::Vec3', ['const hg::Vec3 &clip', 'const hg::tVec2<float> &resolution'])
gen.bind_function('hg::ScreenSpaceToClipSpace', 'hg::Vec3', ['const hg::Vec3 &screen', 'const hg::tVec2<float> &resolution'])
gen.bind_function('hg::ProjectToScreenSpace', 'bool', ['const hg::Mat44 &proj', 'const hg::Vec3 &view', 'const hg::tVec2<float> &resolution', 'hg::Vec3 &screen'], {'arg_out': ['screen']})
gen.bind_function('hg::ProjectOrthoToScreenSpace', 'bool', ['const hg::Mat44 &proj', 'const hg::Vec3 &view', 'const hg::tVec2<float> &resolution', 'hg::Vec3 &screen'], {'arg_out': ['screen']})
gen.bind_function('hg::UnprojectFromScreenSpace', 'bool', ['const hg::Mat44 &inv_proj', 'const hg::Vec3 &screen', 'const hg::tVec2<float> &resolution', 'hg::Vec3 &view'], {'arg_out': ['view']})
gen.bind_function('hg::UnprojectOrthoFromScreenSpace', 'bool', ['const hg::Mat44 &inv_proj', 'const hg::Vec3 &screen', 'const hg::tVec2<float> &resolution', 'hg::Vec3 &view'], {'arg_out': ['view']})
gen.bind_function('hg::ProjectZToClipSpace', 'float', ['float z', 'const hg::Mat44 &proj'])
@ -1288,7 +1295,7 @@ def bind_scene(gen):
#
environment = gen.begin_class('hg::Scene::Environment')
gen.bind_members(environment, ['hg::Color ambient', 'hg::Color fog_color', 'float fog_near', 'float fog_far', 'hg::TextureRef irradiance_map', 'hg::TextureRef radiance_map', 'hg::TextureRef brdf_map'])
gen.bind_members(environment, ['hg::Color ambient', 'hg::Color fog_color', 'float fog_near', 'float fog_far', 'hg::TextureRef brdf_map'])
gen.end_class(environment)
gen.bind_member(scene, 'hg::Scene::Environment environment')
@ -2542,6 +2549,8 @@ def bind_vertex(gen):
'hg::Color color0', 'hg::Color color1', 'hg::Color color2', 'hg::Color color3'])
gen.end_class(vertex)
gen.bind_function('hg::MakeVertex', 'hg::Vertex', ['const hg::Vec3 &pos', '?const hg::Vec3 &nrm', '?const hg::tVec2<float> &uv0', '?const hg::Color &color0'])
def bind_geometry_builder(gen):
gen.add_include('engine/geometry_builder.h')
@ -2914,7 +2923,14 @@ def bind_math(gen):
gen.bind_function('hg::LinearInterpolate<float>', 'float', ['float y0', 'float y1', 'float t'], bound_name='LinearInterpolate')
gen.bind_function('hg::CosineInterpolate<float>', 'float', ['float y0', 'float y1', 'float t'], bound_name='CosineInterpolate')
gen.bind_function('hg::CubicInterpolate<float>', 'float', ['float y0', 'float y1', 'float y2', 'float y3', 'float t'], bound_name='CubicInterpolate')
gen.insert_binding_code('''
static const float _CubicInterpolateImpl(float y0, float y1, float y2, float y3, float t) { return hg::CubicInterpolate<float>(y0, y1, y2, y3, t); }
static const hg::Vec3 _CubicInterpolateImpl(const hg::Vec3& v0, const hg::Vec3& v1, const hg::Vec3& v2, const hg::Vec3& v3, float t) { return hg::CubicInterpolate<hg::Vec3>(v0, v1, v2, v3, t); }
''')
gen.bind_function_overloads('_CubicInterpolateImpl', [
('float', ['float y0', 'float y1', 'float y2', 'float y3', 'float t'], []),
('hg::Vec3', ['const hg::Vec3& v0', 'const hg::Vec3& v1', 'const hg::Vec3& v2', 'const hg::Vec3& v3', 'float t'], [])
], bound_name='CubicInterpolate')
gen.bind_function('hg::HermiteInterpolate<float>', 'float', ['float y0', 'float y1', 'float y2', 'float y3', 'float t', 'float tension', 'float bias'], bound_name='HermiteInterpolate')
gen.bind_function('hg::ReverseRotationOrder', 'hg::RotationOrder', ['hg::RotationOrder rotation_order'])
@ -3040,8 +3056,7 @@ def bind_math(gen):
])
gen.bind_arithmetic_ops_overloads(vector4, ['*'], [
('hg::Vec4', ['hg::Vec4 &v'], []),
('hg::Vec4', ['float k'], []),
('hg::Vec4', ['const hg::Mat4 &m'], [])
('hg::Vec4', ['float k'], [])
])
gen.bind_inplace_arithmetic_ops_overloads(vector4, ['+=', '-=', '*=', '/='], [
@ -3238,10 +3253,10 @@ def bind_math(gen):
gen.end_class(matrix4)
gen.bind_function('hg::GetRow', 'hg::Vec3', ['const hg::Mat4 &m', 'unsigned int n'])
gen.bind_function('hg::GetColumn', 'hg::Vec4', ['const hg::Mat4 &m', 'unsigned int n'])
gen.bind_function('hg::SetRow', 'void', ['const hg::Mat4 &m', 'unsigned int n', 'const hg::Vec3 &v'])
gen.bind_function('hg::SetColumn', 'void', ['const hg::Mat4 &m', 'unsigned int n', 'const hg::Vec4 &v'])
gen.bind_function('hg::GetRow', 'hg::Vec4', ['const hg::Mat4 &m', 'unsigned int n'])
gen.bind_function('hg::GetColumn', 'hg::Vec3', ['const hg::Mat4 &m', 'unsigned int n'])
gen.bind_function('hg::SetRow', 'void', ['const hg::Mat4 &m', 'unsigned int n', 'const hg::Vec4 &v'])
gen.bind_function('hg::SetColumn', 'void', ['const hg::Mat4 &m', 'unsigned int n', 'const hg::Vec3 &v'])
gen.bind_function('hg::GetX', 'hg::Vec3', ['const hg::Mat4 &m'])
gen.bind_function('hg::GetY', 'hg::Vec3', ['const hg::Mat4 &m'])
@ -3348,9 +3363,7 @@ def bind_math(gen):
gen.bind_arithmetic_ops_overloads(vector3, ['+', '-', '/'], [('hg::Vec3', ['hg::Vec3 &v'], []), ('hg::Vec3', ['float k'], [])])
gen.bind_arithmetic_ops_overloads(vector3, ['*'], [
('hg::Vec3', ['const hg::Vec3 &v'], []),
('hg::Vec3', ['float k'], []),
('hg::Vec3', ['const hg::Mat3 &m'], []),
('hg::Vec3', ['const hg::Mat4 &m'], [])
('hg::Vec3', ['float k'], [])
])
gen.bind_inplace_arithmetic_ops_overloads(vector3, ['+=', '-=', '*=', '/='], [
@ -4324,56 +4337,23 @@ def bind_profiler(gen):
def insert_non_embedded_setup_free_code(gen):
if gen.get_language() == 'CPython':
gen.insert_binding_code('''
// Add the Python interpreter module search paths to the engine default plugins search path
void InitializePluginsDefaultSearchPath() {
if (PyObject *sys_path = PySys_GetObject("path")) {
if (PyList_Check(sys_path)) {
Py_ssize_t n = PyList_Size(sys_path);
for (Py_ssize_t i = 0; i < n; ++i)
if (PyObject *path = PyList_GetItem(sys_path, i))
if (PyObject *tmp = PyUnicode_AsUTF8String(path)) {
std::string path(PyBytes_AsString(tmp));
// hg::g_plugin_system.get().default_search_paths.push_back(path + "/harfang");
}
}
}
#include "foundation/log.h"
#include <iostream>
static void OnHarfangLog(const char *msg, int mask, const char *details, void *user) {
if (mask & hg::LL_Error)
PyErr_SetString(PyExc_RuntimeError, msg);
else if (mask & hg::LL_Warning)
PyErr_WarnEx(PyExc_Warning, msg, 1);
else
std::cout << msg << std::endl;
}
\n''')
static void InstallLogHook() { hg::set_log_hook(OnHarfangLog, nullptr); }
''')
elif gen.get_language() == 'Lua':
gen.insert_binding_code('''
#include "foundation/string.h"
// Add the Lua interpreter package.cpath to the engine default plugins search path
static void InitializePluginsDefaultSearchPath(lua_State *L) {
lua_getglobal(L, "package");
lua_getfield(L, -1, "cpath");
std::string package_cpath = lua_tostring(L, -1);
lua_pop(L, 2);
std::vector<std::string> paths = hg::split(package_cpath, ";"), out;
for (size_t i = 0; i < paths.size(); ++i) {
std::string path = paths[i];
std::replace(path.begin(), path.end(), '\\\\', '/');
std::vector<std::string> elms = hg::split(path, "/");
path = "";
for (auto &elm : elms)
if (elm.find('?') == std::string::npos)
path += elm + "/";
if (path == "./")
continue;
if (hg::ends_with(path, "loadall.dll/"))
continue;
out.push_back(path);
}
// for (auto &path : out)
// hg::g_plugin_system.get().default_search_paths.push_back(path);
}
\n''')
''')
gen.insert_binding_code('''
#include "foundation/build_info.h"
@ -4384,14 +4364,17 @@ static void OutputLicensingTerms(const char *lang) {
.arg(hg::get_version_string()).arg(lang).arg(hg::get_target_string())
.arg(hg::get_build_sha()).arg(__DATE__).arg(__TIME__)
);
hg::log("See http://harfang3d.com/license for licensing terms");
hg::log("See https://www.harfang3d.com/license for licensing terms");
}
''')
if gen.get_language() == 'Lua':
gen.add_custom_init_code('OutputLicensingTerms("Lua 5.3");\n')
elif gen.get_language() == 'CPython':
gen.add_custom_init_code('OutputLicensingTerms("CPython 3.2+");\n')
gen.add_custom_init_code('''
InstallLogHook();
OutputLicensingTerms("CPython 3.2+");
''')
gen.add_custom_free_code('\n')

View File

@ -1,3 +1,3 @@
Convert a value in centimeters to the Harfang internal unit system.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert an angle in degrees to the engine unit system.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert a triplet of angles in degrees to the engine unit system.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Duplicate a node and its child hierarchy. Resources will be load from the assets system.
Duplicate a node and its child hierarchy. Resources will be loaded from the assets system.
See [man.Assets].

View File

@ -1,3 +1,3 @@
Duplicate a node and its child hierarchy. Resources will be load from the local filesystem.
Duplicate a node and its child hierarchy. Resources will be loaded from the local filesystem.
See [man.Assets].

View File

@ -1,3 +1,3 @@
Duplicate a node. Resources will be load from the assets system.
Duplicate a node. Resources will be loaded from the assets system.
See [man.Assets].

View File

@ -1,3 +1,3 @@
Duplicate a node. Resources will be load from the local filesystem.
Duplicate a node. Resources will be loaded from the local filesystem.
See [man.Assets].

View File

@ -1 +1,3 @@
Call [DuplicateNodeAndChildrenFromAssets] for each node in a list.
Duplicate each node and children hierarchy of a list. Resources will be loaded from the assets system.
See [man.Assets].

View File

@ -1 +1,3 @@
Call [DuplicateNodeAndChildrenFromFile] for each node in a list.
Duplicate each node and children hierarchy of a list. Resources will be loaded from the local filesystem.
See [man.Assets].

View File

@ -1 +1 @@
Call [DuplicateNodesFromAssets] for each node in a list.
Duplicate each node of a list. Resources will be loaded from the assets system.

View File

@ -1 +1,3 @@
Call [DuplicateNodeFromFile] for each node in a list.
Duplicate each node of a list. Resources will be loaded from the local filesystem.
See [man.Assets].

View File

@ -1,3 +1,3 @@
Convert a value in inches to the Harfang internal unit system.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert a value in kilometers to the Harfang internal unit system.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert a value in millimeters to the Harfang internal unit system.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert a value in milliseconds to the Harfang internal unit system.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert a value in meters to the Harfang internal unit system.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert an angle in radians to the engine unit system.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert a triplet of angles in radians to the engine unit system.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert a value in seconds to the Harfang internal unit system.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -13,7 +13,7 @@ Start the python interpreter and type `import harfang`, you should get an output
```text
Harfang 2.0.0 for CPython 3.2+ on windows-x64 (build ba08463ee9e6c0c93960230fb880c1d9b230610d Sep 30 2020 16:08:22)
See http://harfang3d.com/license for licensing terms
See https://www.harfang3d.com/license for licensing terms
```
### Troubleshooting

View File

@ -1,6 +1,7 @@
.title System of Units
.title Coordinates and Units System
Harfang internally uses the Internal System of Units (SI) or metric system:
Harfang uses a left-handed coordinate system with the X axis pointing right, the Y axis pointing up and the Z axis pointing away from the viewer.
For units, it uses the International System of Units (SI) or metric system:
- Distances are expressed in meters, see [Mtr], [Cm] and [Mm].
- Angles are expressed in radians, see [Rad] and [Deg].
@ -14,4 +15,4 @@ Time is internally stored in nanoseconds as a 64 bit integer, many functions are
*Note:* Storing time as integer is extremely important! As values in nanoseconds get very large, very fast, floating point representations will quickly loose precision due to quantization. If you really need time as a floating point value, you should perform fixed-point arithmetics on integer representation then convert the result to floating point as the very last step.
The current system time can be queried using [time_now].
The current system time can be queried using [time_now].

View File

@ -15,7 +15,7 @@ Start the Lua interpreter and type `hg = require("harfang")`, you should get an
```text
Harfang 2.0.0 for Lua 5.3 on windows-x64 (build ba08463ee9e6c0c93960230fb880c1d9b230610d Sep 30 2020 16:08:22)
See http://harfang3d.com/license for licensing terms
See https://www.harfang3d.com/license for licensing terms
```
## First Program

View File

@ -8,7 +8,7 @@ Once you have a functioning installation of Harfang for your language of choice:
Follow the following steps:
1. Download the tutorials from Github [here](https://github.com/harfang3d/tutorials-hg2.git) and unzip them to your computer _(eg. in `d:/tutorials-hg2`)_.
1. Download _assetc_ for your platform from [here](https://harfang3d.com/releases) to compile the tutorial resources.
1. Download _assetc_ for your platform from [here](https://www.harfang3d.com/releases) to compile the tutorial resources.
1. Drag and drop the tutorial resources folder on the assetc executable **-OR-** execute assetc passing it the path to the tutorial resources folder _(eg. `assetc d:/tutorials-hg2/resources`)_.
![assetc drag & drop](/images/docs/${HG_VERSION}/assetc.gif)

View File

@ -1,3 +1,3 @@
Convert days to time.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert hours to time.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert minutes to time.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert milliseconds to time.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert milliseconds to time.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert nanoseconds to time.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert seconds to time.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert fractional seconds to time.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert microseconds to time.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -1,3 +1,3 @@
Convert fractional microseconds to time.
See [man.UnitSystem].
See [man.CoordinateAndUnitSystem].

View File

@ -21,7 +21,7 @@ man.Views
man.ForwardPipeline
man.Ownership
man.UnitSystem
man.CoordinateAndUnitSystem
man.Scene
man.DrawingScene

2
extern/glfw vendored

@ -1 +1 @@
Subproject commit 7d5a16ce714f0b5f4efa3262de22e4d948851525
Subproject commit 45ce5ddd197d5c58f50fdd3296a5131c894e5527

2
extern/openal-soft vendored

@ -1 +1 @@
Subproject commit ae4eacf147e2c2340cc4e02a790df04c793ed0a9
Subproject commit c1c63a27de66cd44ef756b190a73bfa8bc6dbbab

5
extern/stb_image/stb_image.c vendored Normal file
View File

@ -0,0 +1,5 @@
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

2
extern/stb_image/stb_image_write.c vendored Normal file
View File

@ -0,0 +1,2 @@
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

View File

@ -28,6 +28,7 @@ LOOKUP_CACHE_SIZE = 0
# Build related configuration options
JAVADOC_AUTOBRIEF = YES
EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
EXTRACT_PACKAGE = NO

View File

@ -20,7 +20,6 @@ set(HDRS
lua_object.h
load_save_scene_flags.h
meta.h
mikktspace.h
model_builder.h
motion_blur.h
node.h
@ -39,8 +38,6 @@ set(HDRS
script_param.h
sranipal_api.h
ssgi.h
stb_image.h
stb_image_write.h
hiz.h
ssr.h
taa.h
@ -78,7 +75,6 @@ set(SRCS
meta.cpp
model_builder.cpp
motion_blur.cpp
mikktspace.c
node.cpp
openvr_api.cpp
physics.cpp
@ -103,6 +99,7 @@ set(SRCS
temporal_accumulation.cpp
wav_audio_stream.cpp
ogg_audio_stream.cpp
vertex.cpp
video_stream.cpp)
if(HG_ENABLE_RECAST_DETOUR_API)
@ -117,13 +114,13 @@ endif()
add_library(engine STATIC ${SRCS} ${HDRS})
target_link_libraries(engine PUBLIC bind_hg_lua script foundation platform bgfx imgui jsonhpp meshoptimizer PRIVATE stb_truetype stb_vorbis)
target_link_libraries(engine PUBLIC bind_hg_lua script foundation platform bgfx imgui jsonhpp meshoptimizer PRIVATE stb_truetype stb_vorbis stb_image_write)
if(NOT (CMAKE_SYSTEM_NAME STREQUAL "Emscripten"))
target_link_libraries(engine PRIVATE OpenAL)
endif()
target_link_libraries(engine PRIVATE miniz)
target_link_libraries(engine PRIVATE miniz mikktspace)
if(HG_ENABLE_OPENVR_API)
target_link_libraries(engine PRIVATE OpenVR)

View File

@ -204,7 +204,7 @@ void LoadAnimFromBinary(const Reader &ir, const Handle &h, Anim &anim) {
if (version >= 2)
LoadInstanceAnimTrack(ir, h, anim.instance_anim_track);
MigrateLegacyAnimationTracks(anim);
MigrateLegacyAnimTracks(anim);
}
} // namespace hg

View File

@ -151,7 +151,7 @@ void LoadAnimFromJson(const json &js, Anim &anim) {
anim.flags |= AF_UseQuaternionForRotation;
}
MigrateLegacyAnimationTracks(anim);
MigrateLegacyAnimTracks(anim);
}
} // namespace hg

View File

@ -76,6 +76,35 @@ void ReverseAnim(Anim &anim, time_ns t_start, time_ns t_end) {
ReverseAnimTrack(anim.instance_anim_track, t_start, t_end);
}
template <typename Track> void QuantizeAnimTrack(Track &track, time_ns t_step) {
for (auto &key : track.keys)
key.t = (key.t / t_step) * t_step;
SortAnimTrackKeys(track);
}
void QuantizeAnim(Anim &anim, time_ns t_step) {
for (auto &track : anim.bool_tracks)
QuantizeAnimTrack(track, t_step);
for (auto &track : anim.int_tracks)
QuantizeAnimTrack(track, t_step);
for (auto &track : anim.float_tracks)
QuantizeAnimTrack(track, t_step);
for (auto &track : anim.vec2_tracks)
QuantizeAnimTrack(track, t_step);
for (auto &track : anim.vec3_tracks)
QuantizeAnimTrack(track, t_step);
for (auto &track : anim.vec4_tracks)
QuantizeAnimTrack(track, t_step);
for (auto &track : anim.quat_tracks)
QuantizeAnimTrack(track, t_step);
for (auto &track : anim.color_tracks)
QuantizeAnimTrack(track, t_step);
for (auto &track : anim.string_tracks)
QuantizeAnimTrack(track, t_step);
QuantizeAnimTrack(anim.instance_anim_track, t_step);
}
void ConformAnimTrackKeys(AnimTrackT<Quaternion> &track) {
// make sure adjacent quaternions use the shortest path when interpolated
for (size_t i = 1; i < track.keys.size(); i++) {
@ -90,7 +119,7 @@ void ConformAnimTrackKeys(AnimTrackT<Quaternion> &track) {
}
//
void MigrateLegacyAnimationTracks(Anim &anim) {
void MigrateLegacyAnimTracks(Anim &anim) {
for (auto i = std::begin(anim.string_tracks); i != std::end(anim.string_tracks);) {
const auto &track = *i;
@ -109,4 +138,53 @@ void MigrateLegacyAnimationTracks(Anim &anim) {
}
}
//
template <typename AnimTrack> bool AnimTracksHaveKeys(const std::vector<AnimTrack> &tracks) {
for (auto &track : tracks)
if (!track.keys.empty())
return true;
return false;
}
bool AnimHasKeys(const Anim &anim) {
if (AnimTracksHaveKeys(anim.vec3_tracks))
return true;
if (AnimTracksHaveKeys(anim.vec4_tracks))
return true;
if (AnimTracksHaveKeys(anim.quat_tracks))
return true;
if (AnimTracksHaveKeys(anim.color_tracks))
return true;
if (AnimTracksHaveKeys(anim.float_tracks))
return true;
if (AnimTracksHaveKeys(anim.bool_tracks))
return true;
if (AnimTracksHaveKeys(anim.int_tracks))
return true;
if (AnimTracksHaveKeys(anim.vec2_tracks))
return true;
if (AnimTracksHaveKeys(anim.string_tracks))
return true;
if (!anim.instance_anim_track.keys.empty())
return true;
return false;
}
//
template <typename AnimTrack> void DeleteEmptyAnimTracks_(std::vector<AnimTrack> &tracks) {
tracks.erase(std::remove_if(std::begin(tracks), std::end(tracks), [](const AnimTrack &track) { return track.keys.empty(); }), std::end(tracks));
}
void DeleteEmptyAnimTracks(Anim &anim) {
DeleteEmptyAnimTracks_(anim.vec3_tracks);
DeleteEmptyAnimTracks_(anim.vec4_tracks);
DeleteEmptyAnimTracks_(anim.quat_tracks);
DeleteEmptyAnimTracks_(anim.color_tracks);
DeleteEmptyAnimTracks_(anim.float_tracks);
DeleteEmptyAnimTracks_(anim.bool_tracks);
DeleteEmptyAnimTracks_(anim.int_tracks);
DeleteEmptyAnimTracks_(anim.vec2_tracks);
DeleteEmptyAnimTracks_(anim.string_tracks);
}
} // namespace hg

View File

@ -15,6 +15,7 @@
#include <algorithm>
#include <deque>
#include <map>
#include <set>
#include <string>
#include <vector>
@ -30,8 +31,9 @@ template <typename T> struct AnimKeyT {
};
template <typename T> struct AnimTrackT {
using Key = AnimKeyT<T>;
std::string target;
std::deque<AnimKeyT<T>> keys;
std::deque<Key> keys;
};
//
@ -42,15 +44,20 @@ template <typename T> struct AnimKeyHermiteT {
};
template <typename T> struct AnimTrackHermiteT {
using Key = AnimKeyHermiteT<T>;
std::string target;
std::deque<AnimKeyHermiteT<T>> keys;
std::deque<Key> keys;
};
static const int InvalidKeyIdx = -1;
// AnimTrackT is expected to be sorted
template <typename AnimTrack> int GetKey(const AnimTrack &track, time_ns t) {
const auto key_count = track.keys.size();
if (key_count == 0)
return -1;
return InvalidKeyIdx;
// FIXME below a certain number of keys a linear search might be faster
int lo = 0, hi = numeric_cast<int>(key_count) - 1;
@ -71,13 +78,13 @@ template <typename AnimTrack> int GetKey(const AnimTrack &track, time_ns t) {
else
lo = mid;
}
return -1;
return InvalidKeyIdx;
}
template <typename AnimTrack, typename T> void SetKey(AnimTrack &track, time_ns t, T v) {
const int idx = GetKey(track, t);
if (idx != -1) {
if (idx != InvalidKeyIdx) {
track.keys[idx].v = std::move(v);
} else {
auto i = std::begin(track.keys), e = std::end(track.keys);
@ -89,6 +96,13 @@ template <typename AnimTrack, typename T> void SetKey(AnimTrack &track, time_ns
}
}
template <typename AnimTrack> void DeleteKey(AnimTrack &track, time_ns t) {
const int idx = GetKey(track, t);
if (idx != InvalidKeyIdx)
track.keys.erase(std::begin(track.keys) + idx);
}
//
template <typename AnimTrack, typename T> bool GetIntervalKeys(const AnimTrack &track, time_ns t, int &kf0, int &kf1) {
const auto key_count = numeric_cast<int>(track.keys.size());
@ -155,6 +169,9 @@ template <typename T> bool EvaluateHermite(const AnimTrackHermiteT<T> &track, ti
//
template <typename Track> void SortAnimTrackKeys(Track &track) {
std::sort(std::begin(track.keys), std::end(track.keys), [](decltype(*std::cbegin(track.keys)) &a, decltype(a) &b) { return a.t < b.t; });
track.keys.erase(
std::unique(std::begin(track.keys), std::end(track.keys), [](const typename Track::Key &a, const typename Track::Key &b) { return a.t == b.t; }),
std::end(track.keys));
}
template <typename Track> void ConformAnimTrackKeys(Track &track) {}
@ -223,6 +240,7 @@ template <typename Track> void ResampleAnimTrack(Track &track, time_ns old_start
void ResampleAnim(Anim &anim, time_ns old_start, time_ns old_end, time_ns new_start, time_ns new_end, time_ns frame_duration);
void ReverseAnim(Anim &anim, time_ns t_start, time_ns t_end);
void QuantizeAnim(Anim &anim, time_ns t_step);
static bool CompareKeyValue(const Vec3 &v_a, const Vec3 &v_b, float epsilon) { return Len(v_a - v_b) <= epsilon; }
@ -268,6 +286,10 @@ template <typename AnimTrack, typename T> size_t SimplifyAnimTrackT(AnimTrack &t
}
//
void MigrateLegacyAnimationTracks(Anim &anim);
void MigrateLegacyAnimTracks(Anim &anim);
//
bool AnimHasKeys(const Anim &anim);
void DeleteEmptyAnimTracks(Anim &anim);
} // namespace hg

View File

@ -21,6 +21,14 @@
namespace hg {
enum AssetsSourceType { AssetsFolder, AssetsPackage };
struct AssetsSource {
AssetsSourceType type;
std::string name;
};
//
static std::mutex assets_mutex;
static std::deque<std::string> assets_folders;
@ -151,7 +159,7 @@ Asset OpenAsset(const char *name, bool silent) {
} else {
const mz_zip_error err = mz_zip_get_last_error(&p.archive);
if (!silent)
error(format("Failed to open asset '%1' from file '%2' (asset was found but failed to open) : %3")
warn(format("Failed to open asset '%1' from file '%2' (asset was found but failed to open) : %3")
.arg(name)
.arg(p.filename)
.arg(mz_zip_get_error_string(err)));
@ -160,7 +168,7 @@ Asset OpenAsset(const char *name, bool silent) {
}
if (!silent)
error(format("Failed to open asset '%1' (file not found)").arg(name));
warn(format("Failed to open asset '%1' (file not found)").arg(name));
return {};
}

View File

@ -12,20 +12,14 @@
namespace hg {
/// Mount a local filesystem folder as an assets source.
bool AddAssetsFolder(const char *path);
void RemoveAssetsFolder(const char *path);
/// Mount an archive stored on the local filesystem as an assets source.
bool AddAssetsPackage(const char *path);
void RemoveAssetsPackage(const char *path);
//
enum AssetsSourceType { AssetsFolder, AssetsPackage };
struct AssetsSource {
AssetsSourceType type;
std::string name;
};
//
struct Asset {
gen_ref ref;

View File

@ -59,22 +59,22 @@ static bool CheckALSuccess(const char *file = "unknown", ALuint line = 0) {
case AL_NO_ERROR:
return true;
case AL_INVALID_NAME:
error(format("AL invalid name (%1:%2)").arg(file).arg(line));
warn(format("AL invalid name (%1:%2)").arg(file).arg(line));
break;
case AL_INVALID_ENUM:
error(format("AL invalid enum (%1:%2)").arg(file).arg(line));
warn(format("AL invalid enum (%1:%2)").arg(file).arg(line));
break;
case AL_INVALID_VALUE:
error(format("AL invalid value (%1:%2)").arg(file).arg(line));
warn(format("AL invalid value (%1:%2)").arg(file).arg(line));
break;
case AL_INVALID_OPERATION:
error(format("AL invalid operation (%1:%2)").arg(file).arg(line));
warn(format("AL invalid operation (%1:%2)").arg(file).arg(line));
break;
case AL_OUT_OF_MEMORY:
error(format("AL out of memory (%1:%2)").arg(file).arg(line));
warn(format("AL out of memory (%1:%2)").arg(file).arg(line));
break;
default:
error(format("AL error (%1:%2)").arg(file).arg(line));
warn(format("AL error (%1:%2)").arg(file).arg(line));
break;
}
return false;
@ -163,7 +163,7 @@ static bool UpdateSourceStream(SourceRef src_ref) {
ALint processed;
__AL_CALL_RET(alGetSourcei(src, AL_BUFFERS_PROCESSED, &processed));
if (processed < 0 || processed > numeric_cast<ALint>(stream.buffers.size())) {
error("Incoherent processed buffer count returned from the OpenAL back-end");
warn("Incoherent processed buffer count returned from the OpenAL back-end");
return false;
}
@ -255,14 +255,14 @@ bool AudioInit() {
al_mixer.device = alcOpenDevice("Generic Software");
if (!al_mixer.device) {
error("OpenAL initialization failed");
warn("OpenAL initialization failed");
return false;
}
}
ALCint freq;
alcGetIntegerv(al_mixer.device, ALC_FREQUENCY, 1, &freq);
debug(format("OpenAL ready on device '%1' - @%2hz").arg(alcGetString(al_mixer.device, ALC_DEVICE_SPECIFIER)).arg(freq));
log(format("OpenAL ready on device '%1' - @%2hz").arg(alcGetString(al_mixer.device, ALC_DEVICE_SPECIFIER)).arg(freq));
al_mixer.context = alcCreateContext(al_mixer.device, nullptr);
alcMakeContextCurrent(al_mixer.context);

View File

@ -10,7 +10,9 @@
namespace hg {
/// Initialize the audio system.
bool AudioInit();
/// Shutdown the audio system.
void AudioShutdown();
bool IsAudioUp();

View File

@ -23,7 +23,7 @@ static bool LoadShaders(Bloom &bloom, const Reader &ir, const ReadProvider &ip,
bloom.u_params = bgfx::createUniform("u_params", bgfx::UniformType::Vec4);
bloom.u_source_rect = bgfx::createUniform("u_source_rect", bgfx::UniformType::Vec4);
if (!(bgfx::isValid(bloom.u_source) && bgfx::isValid(bloom.u_input) && bgfx::isValid(bloom.u_params) && bgfx::isValid(bloom.u_source_rect))) {
error("failed to create bloom uniforms.");
warn("failed to create bloom uniforms.");
return false;
}
@ -34,7 +34,7 @@ static bool LoadShaders(Bloom &bloom, const Reader &ir, const ReadProvider &ip,
bloom.prg_combine = LoadProgram(ir, ip, format("%1/bloom_combine").arg(path));
if (!(bgfx::isValid(bloom.prg_threshold) && bgfx::isValid(bloom.prg_downsample) && bgfx::isValid(bloom.prg_upsample) && bgfx::isValid(bloom.prg_combine))) {
error("failed to load bloom programs.");
warn("failed to load bloom programs.");
return false;
}
@ -91,8 +91,8 @@ void DestroyBloom(Bloom &bloom) {
bgfx_Destroy(bloom.prg_combine);
}
void ApplyBloom(bgfx::ViewId &view_id, const iRect &rect, const hg::Texture &input, bgfx::FrameBufferHandle output,
const Bloom &bloom, float threshold, float smoothness, float intensity) {
void ApplyBloom(bgfx::ViewId &view_id, const iRect &rect, const hg::Texture &input, bgfx::FrameBufferHandle output, const Bloom &bloom, float threshold,
float smoothness, float intensity) {
auto stats = bgfx::getStats();
const int width = stats->width;
@ -101,8 +101,7 @@ void ApplyBloom(bgfx::ViewId &view_id, const iRect &rect, const hg::Texture &inp
}
void ApplyBloom(bgfx::ViewId &view_id, const iRect &rect, const hg::Texture &input, const hg::iVec2 &fb_size, bgfx::FrameBufferHandle output,
const Bloom &bloom, float threshold,
float smoothness, float intensity) {
const Bloom &bloom, float threshold, float smoothness, float intensity) {
__ASSERT__(IsValid(bloom));
const bgfx::Caps *caps = bgfx::getCaps();

View File

@ -11,6 +11,8 @@
namespace hg {
/// Bloom post-process object holding internal states and resources.
/// Create with CreateBloomFromAssets() or CreateBloomFromFile(), use with ApplyBloom(), finally call DestroyBloom() to dispose of resources when done.
struct Bloom {
mutable bgfx::FrameBufferHandle in_fb = BGFX_INVALID_HANDLE;
mutable bgfx::FrameBufferHandle out_fb = BGFX_INVALID_HANDLE;
@ -37,8 +39,11 @@ static Bloom CreateBloomFromAssets(const char *path, bgfx::BackbufferRatio::Enum
return CreateBloomFromAssets(path, RenderBufferResourceFactory::Backbuffer(), ratio);
}
/// Destroy a bloom post process object and all associated resources.
void DestroyBloom(Bloom &bloom);
/// Process `input` texture and generate a bloom overlay on top of `output`, input and output must be of the same size.
/// Use CreateBloomFromFile() or CreateBloomFromAssets() to create a Bloom object and DestroyBloom() to destroy its internal resources after usage.
void ApplyBloom(bgfx::ViewId &view_id, const iRect &rect, const hg::Texture &input, const hg::iVec2 &fb_size, bgfx::FrameBufferHandle output,
const Bloom &bloom, float threshold, float smoothness, float intensity);

File diff suppressed because it is too large Load Diff

View File

@ -5,11 +5,23 @@
namespace hg {
/// Create a cube render model.
/// @see CreateCapsuleModel, CreateConeModel, CreateCylinderModel, CreatePlaneModel, CreateSphereModel and DrawModel.
Model CreateCubeModel(const bgfx::VertexLayout &decl, float x, float y, float z);
/// Create a sphere render model.
/// @see CreateCubeModel, CreateConeModel, CreateCylinderModel, CreatePlaneModel, CreateCapsuleModel and DrawModel.
Model CreateSphereModel(const bgfx::VertexLayout &decl, float radius, int subdiv_x, int subdiv_y);
/// Create a plane render model.
/// @see CreateCubeModel, CreateConeModel, CreateCylinderModel, CreatePlaneModel, CreateCapsuleModel and DrawModel.
Model CreatePlaneModel(const bgfx::VertexLayout &decl, float width, float length, int subdiv_x, int subdiv_z);
/// Create a cylinder render model.
/// @see CreateCubeModel, CreateConeModel, CreateCylinderModel, CreatePlaneModel, CreateCapsuleModel and DrawModel.
Model CreateCylinderModel(const bgfx::VertexLayout &decl, float radius, float height, int subdiv_x);
/// Create a cone render model.
/// @see CreateCubeModel, CreateConeModel, CreateCylinderModel, CreatePlaneModel, CreateCapsuleModel and DrawModel.
Model CreateConeModel(const bgfx::VertexLayout &decl, float radius, float height, int subdiv_x);
/// Create a capsule render model.
/// @see CreateCubeModel, CreateConeModel, CreateCylinderModel, CreatePlaneModel, CreateCapsuleModel and DrawModel.
Model CreateCapsuleModel(const bgfx::VertexLayout &decl, float radius, float height, int subdiv_x, int subdiv_y);
} // namespace hg

View File

@ -1,183 +1,50 @@
// HARFANG(R) Copyright (C) 2021 Emmanuel Julien, NWNC HARFANG. Released under GPL/LGPL/Commercial Licence, see licence.txt for details.
#include "engine/cubemap.h"
#include "engine/forward_pipeline.h"
#include "engine/scene.h"
#include "engine/scene_forward_pipeline.h"
#include "foundation/matrix3.h"
namespace hg {
Picture RenderCubemap(bgfx::ViewId &view_id, const PipelineResources &resources, Scene &scene, const Mat4 &transform, CubemapLayout cube_layout,
uint16_t tex_size, bgfx::TextureFormat::Enum format, PictureFormat pic_format, const ForwardPipelineAAAConfig *aaa_config, float znear, float zfar) {
bgfx::TextureHandle cubemap[hg::CF_Max];
for (int i = 0; i < hg::CF_Max; i++) {
cubemap[i] = bgfx::createTexture2D(tex_size, tex_size, false, 1, format, BGFX_TEXTURE_BLIT_DST | BGFX_TEXTURE_READ_BACK);
}
static Mat4 GetFaceMatrix(CubemapFaces face) {
if (face == CF_XP)
return (Mat4)RotationMatY(Deg(90.f));
if (face == CF_XN)
return (Mat4)RotationMatY(Deg(-90.f));
if (face == CF_YP)
return (Mat4)RotationMatX(-Deg(90.f));
if (face == CF_YN)
return (Mat4)RotationMatX(Deg(90.f));
if (face == CF_ZP)
return (Mat4)RotationMatY(Deg(180.f));
if (face == CF_ZN)
return (Mat4)RotationMatY(Deg(0.f));
RenderCubemap(view_id, resources, scene, transform, cubemap, tex_size, format, aaa_config, znear, zfar);
auto pic = hg::CreatePictureFromCubemap(cubemap, hg::CL_CubeCross, tex_size, pic_format);
for (int i = 0; i < hg::CF_Max; i++) {
bgfx::destroy(cubemap[i]);
}
return pic;
__ASSERT__("Invalid cubemap face index");
return Mat4::Identity;
}
void RenderCubemap(bgfx::ViewId &view_id, const PipelineResources &resources, Scene &scene, const Mat4 &transform, bgfx::TextureHandle tgt_textures[CF_Max],
uint16_t tex_size, bgfx::TextureFormat::Enum format, const ForwardPipelineAAAConfig *aaa_config, float znear, float zfar) {
ForwardPipeline pipeline = CreateForwardPipeline();
bgfx::TextureHandle texs[2] = {
bgfx::createTexture2D(tex_size, tex_size, false, 1, format, BGFX_TEXTURE_BLIT_DST | BGFX_TEXTURE_RT_MSAA_X8),
bgfx::createTexture2D(tex_size, tex_size, false, 1, bgfx::TextureFormat::D32, BGFX_TEXTURE_RT_WRITE_ONLY | BGFX_TEXTURE_RT_MSAA_X8)};
const auto frame_buffer = bgfx::createFrameBuffer(2, texs, true);
hg::ForwardPipelineAAA aaa[CF_Max];
if (aaa_config != nullptr) {
for (size_t i = 0; i < CF_Max; i++) {
aaa[i] = hg::CreateForwardPipelineAAAFromAssets("core", *aaa_config, tex_size, tex_size);
}
}
RenderCubemap(
view_id, frame_buffer, pipeline, resources, scene, transform, tgt_textures, tex_size, aaa_config != nullptr ? aaa : nullptr, aaa_config, znear, zfar);
if (aaa_config != nullptr) {
for (size_t i = 0; i < CF_Max; i++) {
hg::DestroyForwardPipelineAAA(aaa[i]);
}
}
hg::DestroyForwardPipeline(pipeline);
bgfx::destroy(frame_buffer);
bgfx::destroy(texs[0]);
bgfx::destroy(texs[1]);
}
void RenderCubemap(bgfx::ViewId &view_id, const bgfx::FrameBufferHandle frame_buffer, ForwardPipeline &pipeline, const PipelineResources &resources,
Scene &scene, const Mat4 &transform, bgfx::TextureHandle tgt_textures[CF_Max], uint16_t tex_size, ForwardPipelineAAA *aaa,
const ForwardPipelineAAAConfig *aaa_config,
void RenderCubemapFace(bgfx::ViewId &view_id, const bgfx::FrameBufferHandle frame_buffer, const Scene &scene, const Mat4 &world, CubemapFaces face,
uint16_t res, int frame, ForwardPipeline &pipeline, const PipelineResources &resources, ForwardPipelineAAA &aaa, const ForwardPipelineAAAConfig &aaa_config,
float znear, float zfar) {
const auto view_state = ComputePerspectiveViewState(world * GetFaceMatrix(face), Deg(90.f), znear, zfar, Vec2::One);
const auto debug_name = std::string("cubemap");
bgfx::setViewFrameBuffer(view_id, frame_buffer);
auto color_texture = bgfx::getTexture(frame_buffer, 0);
SceneForwardPipelinePassViewId views;
SceneForwardPipelineRenderData render_data;
PrepareSceneForwardPipelineCommonRenderData(view_id, scene, render_data, pipeline, resources, views, "RenderCubemapFace AAA");
PrepareSceneForwardPipelineViewDependentRenderData(view_id, view_state, scene, render_data, pipeline, resources, views, "RenderCubemapFace AAA");
SubmitSceneToForwardPipeline(view_id, scene, iRect(0, 0, res, res), view_state, pipeline, render_data, resources, views, aaa, aaa_config, frame, res, res,
frame_buffer, "RenderCubemapFace AAA");
++view_id;
Mat4 rotations[CF_Max];
rotations[CF_XP] = (Mat4)RotationMatY(-Deg(90.f));
rotations[CF_XN] = (Mat4)RotationMatY(Deg(90.f));
rotations[CF_YP] = (Mat4)RotationMatX(-Deg(90.f)) * (Mat4)RotationMatZ(Deg(180.f));
rotations[CF_YN] = (Mat4)RotationMatX(Deg(90.f)) * (Mat4)RotationMatZ(Deg(180.f));
rotations[CF_ZP] = (Mat4)RotationMatY(Deg(0.f));
rotations[CF_ZN] = (Mat4)RotationMatY(Deg(180.f));
if (aaa != nullptr) {
// use more frames if you use TAA, needs more reflection samples, etc
// Seems like we need at least 2 frames for the rendering to work properly
const int frames_to_render_per_view = 2;
scene.Update(hg::time_from_ms_f(0.0f)); // needs prev_mtx for aaa pass
for (int i = 0; i < CF_Max; i++) {
const auto view_state = ComputePerspectiveViewState(transform * rotations[i], Deg(90.f), znear, zfar, Vec2::One);
for (int frame = 0; frame < frames_to_render_per_view; frame++) {
bgfx::touch(view_id);
bgfx::setViewFrameBuffer(view_id, frame_buffer);
SceneForwardPipelinePassViewId views;
SceneForwardPipelineRenderData render_data;
PrepareSceneForwardPipelineCommonRenderData(view_id, scene, render_data, pipeline, resources, views, debug_name.c_str());
PrepareSceneForwardPipelineViewDependentRenderData(view_id, view_state, scene, render_data, pipeline, resources, views, debug_name.c_str());
SubmitSceneToForwardPipeline(view_id, scene, Rect<int>(0, 0, tex_size, tex_size), view_state, pipeline, render_data, resources, views, aaa[i],
*aaa_config, frame, tex_size, tex_size, frame_buffer, debug_name.c_str());
aaa[i].Flip(view_state);
// TODO: how to get rid of this call?
bgfx::frame();
view_id = 0;
//
view_id++;
}
view_id++;
bgfx::touch(view_id);
bgfx::blit(view_id, tgt_textures[i], 0, 0, color_texture, 0, 0, tex_size, tex_size);
view_id++;
}
} else {
for (int i = 0; i < CF_Max; i++) {
bgfx::touch(view_id);
const auto view_state = ComputePerspectiveViewState(transform * rotations[i], Deg(90.f), znear, zfar, Vec2::One);
SceneForwardPipelinePassViewId views;
SceneForwardPipelineRenderData render_data;
PrepareSceneForwardPipelineCommonRenderData(view_id, scene, render_data, pipeline, resources, views, debug_name.c_str());
PrepareSceneForwardPipelineViewDependentRenderData(view_id, view_state, scene, render_data, pipeline, resources, views, debug_name.c_str());
SubmitSceneToForwardPipeline(
view_id, scene, Rect<int>(0, 0, tex_size, tex_size), view_state, pipeline, render_data, resources, views, frame_buffer, debug_name.c_str());
view_id++;
bgfx::touch(view_id);
bgfx::blit(view_id, tgt_textures[i], 0, 0, color_texture, 0, 0, tex_size, tex_size);
view_id++;
}
}
aaa.Flip(view_state);
}
Picture CreatePictureFromCubemap(bgfx::TextureHandle cubemap[CF_Max], CubemapLayout cube_layout, uint16_t tex_size, PictureFormat pic_format) {
const size_t size_pixel = hg::size_of(pic_format);
const uint16_t pic_width = 4 * tex_size;
const uint16_t pic_height = 3 * tex_size;
const size_t stride = pic_width * size_pixel;
const size_t pic_buffer_size = pic_width * pic_height * size_pixel;
Picture pic(pic_width, pic_height, pic_format);
// init image to black, seems important for cmft to detect this is a cubemap
memset(pic.GetData(), 0, pic_buffer_size);
Picture tmp_pic(tex_size, tex_size, pic_format);
for (int i = 0; i < CF_Max; i++) {
auto frame_id = bgfx::readTexture(cubemap[i], tmp_pic.GetData());
auto current_frame = bgfx::frame();
while (current_frame < frame_id) {
current_frame = bgfx::frame();
}
size_t offset = 0;
if (i == CF_XP)
offset = (pic_width * tex_size + 2 * tex_size) * size_pixel;
if (i == CF_XN)
offset = pic_width * tex_size * size_pixel;
if (i == CF_YP)
offset = 1 * tex_size * size_pixel;
if (i == CF_YN)
offset = (2 * pic_width * tex_size + 1 * tex_size) * size_pixel;
if (i == CF_ZP)
offset = (pic_width * tex_size + 3 * tex_size) * size_pixel;
if (i == CF_ZN)
offset = (pic_width * tex_size + 1 * tex_size) * size_pixel;
for (size_t y = 0; y < tex_size; y++) {
memcpy(pic.GetData() + offset + y * stride, tmp_pic.GetData() + y * tex_size * size_pixel, tex_size * size_pixel);
}
}
return pic;
void CaptureCubemapFace(bgfx::ViewId &view_id, const bgfx::FrameBufferHandle frame_buffer, bgfx::TextureHandle tgt, uint16_t res) {
bgfx::touch(view_id);
bgfx::blit(view_id, tgt, 0, 0, bgfx::getTexture(frame_buffer, 0), 0, 0, res, res);
++view_id;
}
} // namespace hg

View File

@ -2,45 +2,17 @@
#pragma once
#include <stdint.h>
#include <bgfx/bgfx.h>
#include "engine/picture.h"
#include "engine/scene_forward_pipeline.h"
namespace bgfx {
struct TextureHandle;
struct FrameBufferHandle;
typedef uint16_t ViewId;
}
namespace hg {
struct PipelineResources;
struct ForwardPipeline;
struct ForwardPipelineAAAConfig;
struct ForwardPipelineAAA;
class Scene;
struct Mat4;
enum CubemapFaces { CF_XP, CF_XN, CF_YP, CF_YN, CF_ZP, CF_ZN, CF_Max };
static_assert(CF_XP == 0, "");
enum CubemapLayout {
CL_CubeCross,
};
// pass nullptr for aaa_config for forward rendering, and a config for AAA rendering
Picture RenderCubemap(bgfx::ViewId &view_id, const PipelineResources &resources, Scene &scene, const Mat4 &transform, CubemapLayout cube_layout,
uint16_t tex_size, bgfx::TextureFormat::Enum format, PictureFormat pic_format, const ForwardPipelineAAAConfig *aaa_config = nullptr, float znear = 0.01f,
float zfar = 1000.f);
void RenderCubemap(bgfx::ViewId &view_id, const PipelineResources &resources, Scene &scene, const Mat4 &transform, bgfx::TextureHandle tgt_textures[CF_Max],
uint16_t tex_size, bgfx::TextureFormat::Enum format, const ForwardPipelineAAAConfig *aaa_config, float znear, float zfar);
// aaa currently needs 1 pipeline per cubemap as ForwardPipelineAAA stores the previous frame
void RenderCubemap(bgfx::ViewId &view_id, const bgfx::FrameBufferHandle frame_buffer, ForwardPipeline &pipeline, const PipelineResources &resources,
Scene &scene, const Mat4 &transform, bgfx::TextureHandle tgt_textures[CF_Max], uint16_t tex_size, ForwardPipelineAAA aaa[CF_Max],
const ForwardPipelineAAAConfig *aaa_config,
void RenderCubemapFace(bgfx::ViewId &view_id, const bgfx::FrameBufferHandle frame_buffer, const Scene &scene, const Mat4 &world, CubemapFaces face,
uint16_t res, int frame, ForwardPipeline &pipeline, const PipelineResources &resources, ForwardPipelineAAA &aaa, const ForwardPipelineAAAConfig &aaa_config,
float znear, float zfar);
Picture CreatePictureFromCubemap(bgfx::TextureHandle cubemap[CF_Max], CubemapLayout cube_layout, uint16_t tex_size, PictureFormat pic_format);
void CaptureCubemapFace(bgfx::ViewId &view_id, const bgfx::FrameBufferHandle frame_buffer, bgfx::TextureHandle tgt, uint16_t res);
} // namespace hg

View File

@ -19,7 +19,8 @@ namespace hg {
#define IMGUI_FLAGS_NONE UINT8_C(0x00)
#define IMGUI_FLAGS_ALPHA_BLEND UINT8_C(0x01)
std::string input_buffer;
static std::string input_buffer;
static Signal<void(const char *)>::Connection on_text_input_connection;
typedef union {
ImTextureID ptr;
@ -482,8 +483,7 @@ DearImguiContext *ImGuiInitContext(float font_size, bgfx::ProgramHandle imgui_pr
*/
//
if (!on_text_input)
on_text_input = [=](const char *utf8) { input_buffer += utf8; };
on_text_input_connection = on_text_input.Connect([=](const char *utf8) { input_buffer += utf8; });
return ctx;
}
@ -494,6 +494,8 @@ void ImGuiInit(float font_size, bgfx::ProgramHandle imgui_program, bgfx::Program
}
void ImGuiShutdown(DearImguiContext &ctx) {
on_text_input.Disconnect(on_text_input_connection);
ImGui::DestroyContext(ctx.m_imgui);
if (bgfx::isValid(ctx.s_tex))

View File

@ -17,6 +17,7 @@ struct AllocatorI;
namespace hg {
/// Context to render immediate GUI.
struct DearImguiContext {
ImGuiContext *m_imgui = nullptr;
bx::AllocatorI *m_allocator = nullptr;

View File

@ -21,14 +21,14 @@ static Font LoadFont(const ReadProvider &ip, const Reader &ir, const char *name,
ScopedReadHandle h(ip, name);
if (!ir.is_valid(h)) {
error(format("Failed to open '%1'").arg(name));
warn(format("Failed to open '%1'").arg(name));
return {};
}
const auto data = LoadData(ir, h);
if (data.Empty()) {
error(format("Failed to read data from '%1'").arg(name));
warn(format("Failed to read data from '%1'").arg(name));
return {};
}

View File

@ -13,6 +13,7 @@
namespace hg {
/// Font object for realtime rendering.
struct Font {
struct PackedChar {
iRect box;
@ -39,7 +40,7 @@ Font LoadFontFromAssets(const char *name, float size = 16.f, uint16_t resolution
enum DrawTextHAlign { DTHA_Left, DTHA_Center, DTHA_Right };
enum DrawTextVAlign { DTVA_Top, DTVA_Center, DTVA_Bottom };
//
/// Write text to the specified view using the provided shader program and uniform values.
void DrawText(bgfx::ViewId view_id, const Font &font, const char *text, bgfx::ProgramHandle program, const char *page_uniform, uint8_t page_stage,
const Mat4 *mtxs, size_t mtx_count, Vec3 pos = {}, DrawTextHAlign halign = DTHA_Left, DrawTextVAlign valign = DTVA_Top,
const std::vector<UniformSetValue> &values = {}, const std::vector<UniformSetTexture> &textures = {}, RenderState state = {}, uint32_t depth = 0);
@ -51,7 +52,9 @@ void DrawText(bgfx::ViewId view_id, const Font &font, const char *text, bgfx::Pr
//
float GetKerning(const Font &font, uint32_t cp0, uint32_t cp1);
/// Compute the width and height of a text string.
fRect ComputeTextRect(const Font &font, const char *text, float xpos = 0.f, float ypos = 0.f);
/// Compute the height of a text string.
float ComputeTextHeight(const Font &font, const char *text);
} // namespace hg

View File

@ -45,9 +45,13 @@ enum ForwardPipelineUniformValue {
UV_PreviousViewProjection,
UV_ViewProjUnjittered, // for TAA
UV_AAAParams, // [0].x: ssgi ratio, [0].y: ssr ratio, [0].z: temporal AA weight, [0].w: motion blur strength
// [1].x: exposure, [1].y: 1/gamma, [1].z: sample count, [1].w max distance
// [1].x: exposure, [1].y: 1/gamma, [1].z: sample count, [1].w: screen-space ray max len
// [2].x: spec weight [2].y: sharpen
UV_MainInvView,
UV_ProbeMatrix,
UV_InvProbeMatrix,
UV_ProbeData, // x: type (0: sphere 1: cube) y: parallax
UV_Count
};
@ -55,6 +59,8 @@ enum ForwardPipelineUniformValue {
enum ForwardPipelineUniformTexture {
UT_IrradianceMap,
UT_RadianceMap,
UT_SSIrradianceMap,
UT_SSRadianceMap,
UT_BrdfMap,
UT_NoiseMap,
@ -158,10 +164,15 @@ void UpdateForwardPipeline(ForwardPipeline &pipeline, const ForwardPipelineShado
pipeline.uniform_values[UV_Resolution].value = {float(fb_size.x), float(fb_size.y), -1, -1};
}
void UpdateForwardPipelinePBRProbe(ForwardPipeline &pipeline, Texture irradiance, Texture radiance, Texture brdf) {
void UpdateForwardPipelineProbe(
ForwardPipeline &pipeline, Texture irradiance, Texture radiance, Texture brdf, ProbeType type, const Mat4 &world, float parallax) {
pipeline.uniform_textures[UT_IrradianceMap].texture = irradiance;
pipeline.uniform_textures[UT_RadianceMap].texture = radiance;
pipeline.uniform_textures[UT_BrdfMap].texture = brdf;
memcpy(pipeline.uniform_values[UV_ProbeMatrix].value.data(), to_bgfx(world).data(), sizeof(float) * 16);
memcpy(pipeline.uniform_values[UV_InvProbeMatrix].value.data(), to_bgfx(InverseFast(world)).data(), sizeof(float) * 16);
pipeline.uniform_values[UV_ProbeData].value = {float(type), parallax, -1, -1};
}
void UpdateForwardPipelineNoise(ForwardPipeline &pipeline, Texture noise) { pipeline.uniform_textures[UT_NoiseMap].texture = noise; }
@ -171,9 +182,8 @@ void UpdateForwardPipelineAO(ForwardPipeline &pipeline, Texture ao) { pipeline.u
static float backbuffer_ratio[bgfx::BackbufferRatio::Count] = {1.f, 2.f, 4.f, 8.f, 16.f, 0.5f};
void UpdateForwardPipelineAAA(ForwardPipeline &pipeline, const iRect &rect, const Mat4 &view, const Mat44 &proj, const Mat4 &prv_view, const Mat44 &prv_proj,
const Vec2 &jitter, bgfx::BackbufferRatio::Enum ssgi_ratio, bgfx::BackbufferRatio::Enum ssr_ratio, float temporal_aa_weight,
float motion_blur_strength,
float exposure, float gamma, int sample_count, float max_distance) {
const Vec2 &jitter, bgfx::BackbufferRatio::Enum ssgi_ratio, bgfx::BackbufferRatio::Enum ssr_ratio, float temporal_aa_weight, float motion_blur_strength,
float exposure, float gamma, int sample_count, float max_distance, float specular_weight, float sharpen) {
pipeline.uniform_values[UV_Projection].value = {1.f / proj.m[0][0], 1.f / proj.m[1][1], proj.m[2][2], proj.m[2][3]};
memcpy(pipeline.uniform_values[UV_ViewProjUnjittered].value.data(), to_bgfx(proj * view).data(), sizeof(float) * 16);
@ -183,11 +193,16 @@ void UpdateForwardPipelineAAA(ForwardPipeline &pipeline, const iRect &rect, cons
memcpy(pipeline.uniform_values[UV_MainInvProjection].value.data(), to_bgfx(Inverse(proj)).data(), sizeof(float) * 16);
pipeline.uniform_values[UV_AAAParams].value = {backbuffer_ratio[ssgi_ratio], backbuffer_ratio[ssr_ratio], temporal_aa_weight, motion_blur_strength,
exposure, 1.f / gamma, float(sample_count), max_distance};
exposure, 1.f / gamma, float(sample_count), max_distance, specular_weight, sharpen};
memcpy(pipeline.uniform_values[UV_MainInvView].value.data(), to_bgfx(InverseFast(view)).data(), sizeof(float) * 16);
}
void UpdateForwardPipelineAAA(ForwardPipeline &pipeline, Texture ssgi, Texture ssr) {
pipeline.uniform_textures[UT_SSIrradianceMap].texture = ssgi;
pipeline.uniform_textures[UT_SSRadianceMap].texture = ssr;
}
//
void SubmitModelToForwardPipeline(bgfx::ViewId view_id, const Model &mdl, const ForwardPipeline &pipeline, const PipelineProgram &prg, uint32_t prg_variant,
uint8_t pipeline_stage, const Color &ambient, const ForwardPipelineLights &lights, const ForwardPipelineFog &fog, const Mat4 &mtx) {
@ -330,10 +345,12 @@ void GenerateLinearShadowMapForForwardPipeline(bgfx::ViewId &view_id, const View
std::vector<ModelDisplayList> culled_display_lists = display_lists;
CullModelDisplayLists(frustum, culled_display_lists, mtxs, res);
DrawModelDisplayLists(view_id, culled_display_lists, 9, pipeline.uniform_values, pipeline.uniform_textures, mtxs, res); // config idx is 9 for FPS_DepthOnly
DrawModelDisplayLists(
view_id, culled_display_lists, 9, pipeline.uniform_values, pipeline.uniform_textures, mtxs, res); // config idx is 9 for FPS_DepthOnly
// FIXME cull skinned models!
DrawSkinnedModelDisplayLists(view_id, skinned_display_lists, 9, pipeline.uniform_values, pipeline.uniform_textures, mtxs, res); // config idx is 9 for FPS_DepthOnly
DrawSkinnedModelDisplayLists(
view_id, skinned_display_lists, 9, pipeline.uniform_values, pipeline.uniform_textures, mtxs, res); // config idx is 9 for FPS_DepthOnly
views[FPSP_Slot0LinearSplit0 + i] = view_id++;
}
@ -377,10 +394,12 @@ void GenerateSpotShadowMapForForwardPipeline(bgfx::ViewId &view_id, const std::v
std::vector<ModelDisplayList> culled_display_lists = display_lists;
CullModelDisplayLists(frustum, culled_display_lists, mtxs, res);
DrawModelDisplayLists(view_id, culled_display_lists, 9, pipeline.uniform_values, pipeline.uniform_textures, mtxs, res); // config idx is 9 for FPS_DepthOnly
DrawModelDisplayLists(
view_id, culled_display_lists, 9, pipeline.uniform_values, pipeline.uniform_textures, mtxs, res); // config idx is 9 for FPS_DepthOnly
// FIXME cull skinned models!
DrawSkinnedModelDisplayLists(view_id, skinned_display_lists, 9, pipeline.uniform_values, pipeline.uniform_textures, mtxs, res); // config idx is 9 for FPS_DepthOnly
DrawSkinnedModelDisplayLists(
view_id, skinned_display_lists, 9, pipeline.uniform_values, pipeline.uniform_textures, mtxs, res); // config idx is 9 for FPS_DepthOnly
views[FPSP_Slot1Spot] = view_id++;
}
@ -464,19 +483,23 @@ ForwardPipeline CreateForwardPipeline(int shadow_map_resolution, bool spot_16bit
MakeUniformSetValue("uPreviousViewProjection", Mat4{}),
MakeUniformSetValue("uViewProjUnjittered", Mat4{}),
MakeUniformSetValue("uAAAParams", Vec4{}, 2), // [2]
MakeUniformSetValue("uAAAParams", Vec4{}, 3), // [3]
MakeUniformSetValue("uMainInvView", Mat4{}),
MakeUniformSetValue("uProbeMatrix", Mat4{}),
MakeUniformSetValue("uInvProbeMatrix", Mat4{}),
MakeUniformSetValue("uProbeData", Vec4{}),
};
__ASSERT__(pipeline.uniform_values.size() == UV_Count);
pipeline.uniform_textures = {
MakeUniformSetTexture("uIrradianceMap", {}, 8),
MakeUniformSetTexture("uRadianceMap", {}, 9),
MakeUniformSetTexture("uBrdfMap", {}, 10),
MakeUniformSetTexture("uNoiseMap", {}, 11),
MakeUniformSetTexture("uIrradianceMap", {}, 7),
MakeUniformSetTexture("uRadianceMap", {}, 8),
MakeUniformSetTexture("uSSIrradianceMap", {}, 9),
MakeUniformSetTexture("uSSRadianceMap", {}, 10),
MakeUniformSetTexture("uBrdfMap", {}, 11),
MakeUniformSetTexture("uNoiseMap", {}, 12),
MakeUniformSetTexture("uAmbientOcclusion", {}, 13),
MakeUniformSetTexture("uLinearShadowMap", {BGFX_TEXTURE_RT | BGFX_SAMPLER_COMPARE_LEQUAL, pipeline.textures["linear_shadow_map"]}, 14),
MakeUniformSetTexture("uSpotShadowMap", {BGFX_TEXTURE_RT | BGFX_SAMPLER_COMPARE_LEQUAL, pipeline.textures["spot_shadow_map"]}, 15),

View File

@ -24,6 +24,11 @@ static const int forward_light_count = 8;
enum ForwardPipelineLightType { FPLT_None, FPLT_Point, FPLT_Spot, FPLT_Linear };
enum ForwardPipelineShadowType { FPST_None, FPST_Map };
/*!
Single light for the forward pipeline.
@note The complete lighting rig is passed as a ForwardPipelineLights.
@see PrepareForwardPipelineLights.
*/
struct ForwardPipelineLight { // 112B
ForwardPipelineLightType type;
ForwardPipelineShadowType shadow_type;
@ -62,18 +67,27 @@ struct ForwardPipelineShadowData {
Mat44 spot_shadow_mtx; // slot 1: spot light
};
//
/// Fog properties for the forward pipeline.
struct ForwardPipelineFog {
float near{}, far{};
Color color{};
};
//
/*!
Rendering pipeline implementing a forward rendering strategy.
The main characteristics of this pipeline are:
- Render in two passes: opaque display lists then transparent ones.
- Fixed 8 light slots supporting 1 linear light with PSSM shadow mapping, 1 spot with shadow mapping and up to 6 point lights with no shadow mapping.
*/
struct ForwardPipeline : Pipeline {
int shadow_map_resolution{1024};
};
/// Create a forward pipeline and its resources.
/// @see DestroyForwardPipeline.
ForwardPipeline CreateForwardPipeline(int shadow_map_resolution = 1024, bool spot_16bit_shadow_map = true);
/// Destroy a forward pipeline object.
inline void DestroyForwardPipeline(ForwardPipeline &pipeline) { DestroyPipeline(pipeline); }
//

View File

@ -6,6 +6,11 @@ namespace hg {
struct Vec3;
/*!
Implement a first-person-shooter like controller.
The input position and rotation parameters are returned modified according to the state of the control keys.
This function is usually used by passing the current camera position and rotation then updating the camera transformation with the returned values.
*/
void FpsController(
bool key_up, bool key_down, bool key_left, bool key_right, bool btn, float dx, float dy, Vec3 &pos, Vec3 &rot, float speed, time_ns dt_t);

View File

@ -2,7 +2,6 @@
#include "engine/geometry.h"
#include "engine/file_format.h"
#include "engine/mikktspace.h"
#include "foundation/file.h"
#include "foundation/file_rw_interface.h"
@ -12,6 +11,8 @@
#include "foundation/pack_float.h"
#include "foundation/time.h"
#include "mikktspace.h"
#include <bgfx/bgfx.h>
#include <numeric>
@ -109,7 +110,7 @@ std::vector<VertexToVertex> ComputeVertexToVertex(const Geometry &geo, const std
if (insert) {
if (vtx_vtx_count == __VertexToVertexTempListSize) {
error("Temporary list exceeded, vertex to vertex LUT corrupted");
warn("Temporary list exceeded, vertex to vertex LUT corrupted");
vtx_vtx_count = __VertexToVertexTempListSize - 1;
}
@ -313,10 +314,10 @@ bool Validate(const Geometry &geo) {
int error_count = 0;
const auto validation_error = [&](const char *msg) {
error(msg);
warn(msg);
if (++error_count == 32) {
error("Too many errors in geometry, aborting validation");
warn("Too many errors in geometry, aborting validation");
return false;
}
@ -371,17 +372,17 @@ Geometry LoadGeometry(const Reader &ir, const Handle &h, const char *name) {
Geometry geo;
if (!ir.is_valid(h)) {
error(format("Cannot load model '%1', invalid file handle").arg(name));
warn(format("Cannot load model '%1', invalid file handle").arg(name));
return geo;
}
if (Read<uint32_t>(ir, h) != HarfangMagic) {
error(format("Cannot load model '%1', invalid magic marker").arg(name));
warn(format("Cannot load model '%1', invalid magic marker").arg(name));
return geo;
}
if (Read<uint8_t>(ir, h) != ModelMarker) {
error(format("Cannot load model '%1', invalid model marker").arg(name));
warn(format("Cannot load model '%1', invalid model marker").arg(name));
return geo;
}
@ -392,7 +393,7 @@ Geometry LoadGeometry(const Reader &ir, const Handle &h, const char *name) {
*/
const auto version = Read<uint32_t>(ir, h);
if (version > 2) {
error(format("Cannot load model '%1', unsupported version").arg(name));
warn(format("Cannot load model '%1', unsupported version").arg(name));
return geo;
}

View File

@ -79,7 +79,7 @@ json LoadJson(const Reader &ir, const Handle &h, bool *result) {
js = json::parse(LoadString(ir, h));
if (result)
*result = true;
} catch (const json::parse_error &e) { error(format("JSON error: %1").arg(e.what())); }
} catch (const json::parse_error &e) { warn(format("JSON error: %1").arg(e.what())); }
return js;
}

View File

@ -97,14 +97,7 @@ void ModelBuilder::AddTriangle(VtxIdxType a, VtxIdxType b, VtxIdxType c) {
list.idx.push_back(c);
}
void ModelBuilder::AddQuad(VtxIdxType a, VtxIdxType b, VtxIdxType c, VtxIdxType d) {
auto &list = lists.back();
list.idx.push_back(a);
list.idx.push_back(b);
list.idx.push_back(c);
list.idx.push_back(d);
}
void ModelBuilder::AddQuad(VtxIdxType a, VtxIdxType b, VtxIdxType c, VtxIdxType d) { AddPolygon({a, b, c, d}); }
void ModelBuilder::AddPolygon(const std::vector<VtxIdxType> &idxs) {
for (int i = 1; i < idxs.size() - 1; ++i)

View File

@ -72,6 +72,8 @@ struct CameraZRange {
float znear{0.01f}, zfar{1000.f};
};
/// Add this component to a Node to implement the camera aspect.
/// Create a camera component with Scene_CreateCamera, use CreateCamera to create a complete camera node.
struct Camera { // 16B on 64 bit
bool IsValid() const;
explicit operator bool() const { return IsValid(); }
@ -207,7 +209,12 @@ struct Collision { // 16B on 64 bit
void SetType(CollisionType type);
Mat4 GetLocalTransform() const;
void SetLocalTransform(Mat4 m);
void SetLocalTransform(const Mat4 &local);
Vec3 GetPosition() const;
void SetPosition(const Vec3 &pos);
Vec3 GetRotation() const;
void SetRotation(const Vec3 &rot);
float GetMass() const;
void SetMass(float mass);
float GetRadius() const;

View File

@ -130,17 +130,17 @@ bool OpenVRInit() {
vr_system = vr::VR_Init(&eError, vr::VRApplication_Scene);
if (!vr_system) {
error(format("OpenVR initialization failed: %1").arg(vr::VR_GetVRInitErrorAsEnglishDescription(eError)));
warn(format("OpenVR initialization failed: %1").arg(vr::VR_GetVRInitErrorAsEnglishDescription(eError)));
return false; // initialization failure
}
const auto driver = GetStringTrackedDeviceProperty_(vr_system, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_TrackingSystemName_String);
const auto display = GetStringTrackedDeviceProperty_(vr_system, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SerialNumber_String);
debug(format("OpenVR driver %2 initialized on display %1").arg(display).arg(driver));
log(format("OpenVR driver %2 initialized on display %1").arg(display).arg(driver));
vr_system->GetRecommendedRenderTargetSize(&rt_width, &rt_height);
debug(format("OpenVR recommended render target size %1x%2").arg(rt_width).arg(rt_height));
log(format("OpenVR recommended render target size %1x%2").arg(rt_width).arg(rt_height));
AddVRControllerReader("openvr_controller_0", OpenVRControllerReader<0>, OpenVRControllerSendHapticPulse<0>);
AddVRControllerReader("openvr_controller_1", OpenVRControllerReader<1>, OpenVRControllerSendHapticPulse<1>);
@ -179,7 +179,7 @@ bool OpenVRInit() {
}
void OpenVRShutdown() {
debug("OpenVR shutting down");
log("OpenVR shutting down");
RemoveVRControllerReader("openvr_controller_0");
RemoveVRControllerReader("openvr_controller_1");
@ -340,7 +340,7 @@ Texture OpenVRGetDepthTexture(const OpenVREyeFrameBuffer &eye) { return {BGFX_TE
namespace hg {
bool OpenVRInit() {
error("OpenVR support DISABLED when building Harfang");
warn("OpenVR support DISABLED when building Harfang");
return false;
}

View File

@ -12,6 +12,7 @@ namespace hg {
enum CollisionEventTrackingMode : uint8_t { CETM_EventOnly, CETM_EventAndContacts };
/// Object containing the world space position, normal and depth of a contact as reported by the collision system.
struct Contact {
Vec3 P, N;
float d;

View File

@ -1,18 +1,20 @@
// HARFANG(R) Copyright (C) 2021 Emmanuel Julien, NWNC HARFANG. Released under GPL/LGPL/Commercial Licence, see licence.txt for details.
#include "engine/picture.h"
#include <assert.h>
#include "foundation/cext.h"
#include "foundation/file.h"
#include "foundation/math.h"
#include "foundation/profiler.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "engine/stb_image.h"
#include "engine/stb_image_write.h"
#include "bimg/encode.h"
#include "bx/allocator.h"
#include <bx/file.h>
#include "bx/file.h"
#include "stb_image.h"
#include "stb_image_write.h"
namespace hg {
@ -33,7 +35,10 @@ Picture::Picture(uint16_t width, uint16_t height, PictureFormat format)
Picture::Picture(void *data, uint16_t width, uint16_t height, PictureFormat format) : w(width), h(height), f(format), d(reinterpret_cast<uint8_t *>(data)) {}
Picture::Picture(const Picture &pic)
: w(pic.w), h(pic.h), f(pic.f), has_ownership(pic.has_ownership), d(pic.has_ownership ? new uint8_t[w * h * size_of(f)] : pic.d) {}
: w(pic.w), h(pic.h), f(pic.f), has_ownership(pic.has_ownership), d(pic.has_ownership ? new uint8_t[w * h * size_of(f)] : pic.d) {
if (pic.has_ownership)
std::copy(pic.d, pic.d + w * h * size_of(f), d);
}
Picture::Picture(Picture &&pic) noexcept : w(pic.w), h(pic.h), f(pic.f), has_ownership(pic.has_ownership), d(pic.d) {
pic.w = 0;
@ -256,7 +261,9 @@ public:
size_t m_minAlignment;
};
static bool SaveBimg(const Picture& pic, const char* path, bool fast, bimg::TextureFormat::Enum format) {
static bool SaveBimg(const Picture &pic, const char *path, bool fast, bimg::TextureFormat::Enum format) {
ProfilerPerfSection section("SaveBimg", path);
if (!pic.GetHeight() || !pic.GetWidth())
return false;
@ -300,7 +307,7 @@ static bool SaveBimg(const Picture& pic, const char* path, bool fast, bimg::Text
bool SaveBC6H(const Picture &pic, const char *path, bool fast) { return SaveBimg(pic, path, fast, bimg::TextureFormat::BC6H); }
bool SaveBC7(const Picture& pic, const char* path, bool fast) { return SaveBimg(pic, path, fast, bimg::TextureFormat::BC7); }
bool SaveBC7(const Picture &pic, const char *path, bool fast) { return SaveBimg(pic, path, fast, bimg::TextureFormat::BC7); }
bool SaveTGA(const Picture &pic, const char *path) {
ProfilerPerfSection section("SaveTGA", path);
@ -315,4 +322,22 @@ bool SaveTGA(const Picture &pic, const char *path) {
return stbi_write_tga_to_func(STB_write, &file, pic.GetWidth(), pic.GetHeight(), size_of(pic.GetFormat()), pic.GetData()) != 0;
}
bool SaveHDR(const Picture &pic, const char *path) {
ProfilerPerfSection section("SaveHDR", path);
if (!pic.GetHeight() || !pic.GetWidth())
return false;
ScopedFile file(OpenWrite(path));
if (!file)
return false;
int comp = 4;
assert(pic.GetFormat() == PF_RGBA32F);
if (pic.GetFormat() != PF_RGBA32F)
return false;
return stbi_write_hdr_to_func(STB_write, &file, pic.GetWidth(), pic.GetHeight(), comp, (const float *)pic.GetData()) != 0;
}
} // namespace hg

View File

@ -77,5 +77,6 @@ bool SaveTGA(const Picture &pic, const char *path);
bool SaveBMP(const Picture &pic, const char *path);
bool SaveBC6H(const Picture &pic, const char *path, bool fast);
bool SaveBC7(const Picture &pic, const char *path, bool fast);
bool SaveHDR(const Picture &pic, const char *path);
} // namespace hg

View File

@ -208,7 +208,7 @@ void AddGeometryToNavMeshInput(NavMeshInput &input, const Geometry &geo, const M
input.vtx.reserve(input.vtx.size() + geo.vtx.size());
for (auto &vtx : geo.vtx)
input.vtx.push_back(vtx * world);
input.vtx.push_back(world * vtx);
//
const auto tri_count = ComputeTriangleCount(geo);
@ -306,7 +306,7 @@ dtNavMesh *CreateNavMesh(const NavMeshInput &input, float radius, float height,
std::unique_ptr<rcHeightfield> solid(rcAllocHeightfield());
if (!rcCreateHeightfield(&ctx, *solid, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch)) {
error("Navigation: Could not create solid height field.");
warn("Navigation: Could not create solid height field.");
return nullptr;
}
@ -345,7 +345,7 @@ dtNavMesh *CreateNavMesh(const NavMeshInput &input, float radius, float height,
std::unique_ptr<rcCompactHeightfield> chf(rcAllocCompactHeightfield());
if (!rcBuildCompactHeightfield(&ctx, cfg.walkableHeight, cfg.walkableClimb, *solid, *chf)) {
error("buildNavigation: Could not build compact data.");
warn("buildNavigation: Could not build compact data.");
return nullptr;
}
@ -353,7 +353,7 @@ dtNavMesh *CreateNavMesh(const NavMeshInput &input, float radius, float height,
// Erode the walkable area by agent radius.
if (!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf)) {
error("buildNavigation: Could not erode.");
warn("buildNavigation: Could not erode.");
return nullptr;
}
@ -386,26 +386,26 @@ dtNavMesh *CreateNavMesh(const NavMeshInput &input, float radius, float height,
if (partitionType == SAMPLE_PARTITION_WATERSHED) {
// Prepare for region partitioning, by calculating distance field along the walkable surface.
if (!rcBuildDistanceField(&ctx, *chf)) {
error("buildNavigation: Could not build distance field.");
warn("buildNavigation: Could not build distance field.");
return nullptr;
}
// Partition the walkable surface into simple regions without holes.
if (!rcBuildRegions(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea)) {
error("buildNavigation: Could not build watershed regions.");
warn("buildNavigation: Could not build watershed regions.");
return nullptr;
}
} else if (partitionType == SAMPLE_PARTITION_MONOTONE) {
// Partition the walkable surface into simple regions without holes.
// Monotone partitioning does not need distance field.
if (!rcBuildRegionsMonotone(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea)) {
error("buildNavigation: Could not build monotone regions.");
warn("buildNavigation: Could not build monotone regions.");
return nullptr;
}
} else { // SAMPLE_PARTITION_LAYERS
// Partition the walkable surface into simple regions without holes.
if (!rcBuildLayerRegions(&ctx, *chf, 0, cfg.minRegionArea)) {
error("buildNavigation: Could not build layer regions.");
warn("buildNavigation: Could not build layer regions.");
return nullptr;
}
}
@ -418,7 +418,7 @@ dtNavMesh *CreateNavMesh(const NavMeshInput &input, float radius, float height,
std::unique_ptr<rcContourSet> cset(rcAllocContourSet());
if (!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset)) {
error("buildNavigation: Could not create contours.");
warn("buildNavigation: Could not create contours.");
return nullptr;
}
@ -430,7 +430,7 @@ dtNavMesh *CreateNavMesh(const NavMeshInput &input, float radius, float height,
std::unique_ptr<rcPolyMesh> pmesh(rcAllocPolyMesh());
if (!rcBuildPolyMesh(&ctx, *cset, cfg.maxVertsPerPoly, *pmesh)) {
error("buildNavigation: Could not triangulate contours.");
warn("buildNavigation: Could not triangulate contours.");
return nullptr;
}
@ -441,7 +441,7 @@ dtNavMesh *CreateNavMesh(const NavMeshInput &input, float radius, float height,
std::unique_ptr<rcPolyMeshDetail> dmesh(rcAllocPolyMeshDetail());
if (!rcBuildPolyMeshDetail(&ctx, *pmesh, *chf, cfg.detailSampleDist, cfg.detailSampleMaxError, *dmesh)) {
error("buildNavigation: Could not build detail mesh.");
warn("buildNavigation: Could not build detail mesh.");
return nullptr;
}
@ -514,14 +514,14 @@ dtNavMesh *CreateNavMesh(const NavMeshInput &input, float radius, float height,
params.buildBvTree = true;
if (!dtCreateNavMeshData(&params, &navData, &navDataSize)) {
error("Could not build Detour navmesh.");
warn("Could not build Detour navmesh.");
return nullptr;
}
navMesh = dtAllocNavMesh();
if (!navMesh) {
dtFree(navData);
error("Could not create Detour navmesh");
warn("Could not create Detour navmesh");
return nullptr;
}
@ -530,7 +530,7 @@ dtNavMesh *CreateNavMesh(const NavMeshInput &input, float radius, float height,
status = navMesh->init(navData, navDataSize, DT_TILE_FREE_DATA);
if (dtStatusFailed(status)) {
dtFree(navData);
error("Could not init Detour navmesh");
warn("Could not init Detour navmesh");
return nullptr;
}

View File

@ -28,9 +28,11 @@ dtNavMesh *LoadNavMesh(const char *path, const Reader &ir, const ReadProvider &i
dtNavMesh *LoadNavMeshFromFile(const char *path);
dtNavMesh *LoadNavMeshFromAssets(const char *path);
/// Destroy a navigation mesh object.
void DestroyNavMesh(dtNavMesh *mesh);
//
/// Draw a navigation mesh to the specified view. This is function is for debugging purpose.
/// @see UniformSetValueList and UniformSetTextureList to pass uniform values to the shader program.
void DrawNavMesh(const dtNavMesh *mesh, bgfx::ViewId view_id, const bgfx::VertexLayout &vtx_layout, bgfx::ProgramHandle program,
const std::vector<UniformSetValue> &values, const std::vector<UniformSetTexture> &textures, RenderState state);
@ -45,9 +47,11 @@ dtNavMesh *CreateNavMesh(const NavMeshInput &input, float radius, float height,
//
dtNavMeshQuery *CreateNavMeshQuery(const dtNavMesh *mesh);
/// Destroy a navigation mesh query object.
void DestroyNavMeshQuery(dtNavMeshQuery *query);
/// Perform a path query.
/// Return the navigation path between `from` and `to` as a list of [Vec3] world positions.
/// @see CreateNavMeshQuery.
std::vector<Vec3> FindNavigationPathTo(const dtNavMeshQuery *query, const Vec3 &from, const Vec3 &to);
} // namespace hg

View File

@ -93,8 +93,8 @@ uint32_t ComputeSortKey(float view_depth) {
return uint32_t(view_depth * 1000.f); // 1mm precision
}
uint32_t ComputeSortKeyFromWorld(const Vec3 &T, const Mat4 &view) { return ComputeSortKey((T * view).z); }
uint32_t ComputeSortKeyFromWorld(const Vec3 &T, const Mat4 &view, const Mat4 &model) { return ComputeSortKey((T * (view * model)).z); }
uint32_t ComputeSortKeyFromWorld(const Vec3 &T, const Mat4 &view) { return ComputeSortKey((view * T).z); }
uint32_t ComputeSortKeyFromWorld(const Vec3 &T, const Mat4 &view, const Mat4 &model) { return ComputeSortKey(((view * model) * T).z); }
//
UniformSetValue::UniformSetValue(const UniformSetValue &v) { *this = v; }
@ -574,13 +574,13 @@ bgfx::ProgramHandle LoadProgram(const Reader &ir, const ReadProvider &ip, const
if (!ir.is_valid(vs_h)) {
if (!silent)
error(format("Vertex shader '%1' not found").arg(vs_name));
warn(format("Vertex shader '%1' not found").arg(vs_name));
return BGFX_INVALID_HANDLE;
}
if (!ir.is_valid(fs_h)) {
if (!silent)
error(format("Fragment shader '%1' not found").arg(fs_name));
warn(format("Fragment shader '%1' not found").arg(fs_name));
return BGFX_INVALID_HANDLE;
}
@ -588,7 +588,7 @@ bgfx::ProgramHandle LoadProgram(const Reader &ir, const ReadProvider &ip, const
if (!bgfx::isValid(vs)) {
if (!silent)
error(format("Failed to load vertex shader '%1'").arg(vs_name));
warn(format("Failed to load vertex shader '%1'").arg(vs_name));
return BGFX_INVALID_HANDLE;
}
@ -596,7 +596,7 @@ bgfx::ProgramHandle LoadProgram(const Reader &ir, const ReadProvider &ip, const
if (!bgfx::isValid(fs)) {
if (!silent)
error(format("Failed to load fragment shader '%1'").arg(vs_name));
warn(format("Failed to load fragment shader '%1'").arg(vs_name));
return BGFX_INVALID_HANDLE;
}
@ -604,7 +604,7 @@ bgfx::ProgramHandle LoadProgram(const Reader &ir, const ReadProvider &ip, const
if (!bgfx::isValid(prg_h)) {
if (!silent)
error(format("Failed to create program from shader '%1' and '%2'").arg(vs_name).arg(fs_name));
warn(format("Failed to create program from shader '%1' and '%2'").arg(vs_name).arg(fs_name));
return BGFX_INVALID_HANDLE;
}
@ -625,7 +625,7 @@ bgfx::ProgramHandle LoadComputeProgram(const Reader &ir, const ReadProvider &ip,
if (!ir.is_valid(cs_h)) {
if (!silent)
error(format("Compute shader '%1' not found").arg(cs_name));
warn(format("Compute shader '%1' not found").arg(cs_name));
return BGFX_INVALID_HANDLE;
}
@ -633,7 +633,7 @@ bgfx::ProgramHandle LoadComputeProgram(const Reader &ir, const ReadProvider &ip,
if (!bgfx::isValid(cs)) {
if (!silent)
error(format("Failed to load compute shader '%1'").arg(cs_name));
warn(format("Failed to load compute shader '%1'").arg(cs_name));
return BGFX_INVALID_HANDLE;
}
@ -641,7 +641,7 @@ bgfx::ProgramHandle LoadComputeProgram(const Reader &ir, const ReadProvider &ip,
if (!bgfx::isValid(prg_h)) {
if (!silent)
error(format("Failed to create program from shader '%1'").arg(cs_name));
warn(format("Failed to create program from shader '%1'").arg(cs_name));
return BGFX_INVALID_HANDLE;
}
@ -798,7 +798,7 @@ bool LoadPipelineProgramUniforms(const Reader &ir, const ReadProvider &ip, const
texs.push_back(u);
} else {
error(format("Ignoring invalid uniform '%1' sampler definition in '%2': no channel specified").arg(uname).arg(name));
warn(format("Ignoring invalid uniform '%1' sampler definition in '%2': no channel specified").arg(uname).arg(name));
}
}
}
@ -825,12 +825,12 @@ PipelineProgram LoadPipelineProgram(
prg.features = LoadPipelineProgramFeatures(ir, ip, name, success, silent);
if (!success)
error(format("Failed to load pipeline program features '%1'").arg(name));
warn(format("Failed to load pipeline program features '%1'").arg(name));
else
prg.programs.resize(GetPipelineProgramVariantCount(prg.features) * pipeline.configs.size());
if (!LoadPipelineProgramUniforms(ir, ip, name, prg.texture_uniforms, prg.vec4_uniforms, resources, silent))
error(format("Failed to load pipeline program uniforms '%1'").arg(name));
warn(format("Failed to load pipeline program uniforms '%1'").arg(name));
prg.name = CutFileExtension(name);
prg.pipeline = pipeline;
@ -1021,13 +1021,13 @@ void UpdateMaterialPipelineProgramVariant(Material &mat, const PipelineResources
static bx::DefaultAllocator g_allocator;
//
json LoadResourceMeta(const Reader &ir, const ReadProvider &ip, const std::string &name, bool silent) {
json LoadResourceMeta(const Reader &ir, const ReadProvider &ip, const std::string &name) {
const auto meta_path = name + ".meta";
return LoadJson(ir, ScopedReadHandle(ip, meta_path.c_str(), silent));
return LoadJson(ir, ScopedReadHandle(ip, meta_path.c_str(), true));
}
json LoadResourceMetaFromFile(const std::string &path, bool silent) { return LoadResourceMeta(g_file_reader, g_file_read_provider, path, silent); }
json LoadResourceMetaFromAssets(const std::string &name, bool silent) { return LoadResourceMeta(g_assets_reader, g_assets_read_provider, name, silent); }
json LoadResourceMetaFromFile(const std::string &path) { return LoadResourceMeta(g_file_reader, g_file_read_provider, path); }
json LoadResourceMetaFromAssets(const std::string &name) { return LoadResourceMeta(g_assets_reader, g_assets_read_provider, name); }
bool SaveResourceMetaToFile(const std::string &path, const json &meta) { return SaveJsonToFile(meta, (path + ".meta").c_str()); }
@ -1035,7 +1035,7 @@ bool SaveResourceMetaToFile(const std::string &path, const json &meta) { return
TextureMeta LoadTextureMeta(const Reader &ir, const ReadProvider &ip, const std::string &name, bool silent) {
ProfilerPerfSection section("LoadTextureMeta", name);
const auto js = LoadResourceMeta(ir, ip, name, silent);
const auto js = LoadResourceMeta(ir, ip, name);
TextureMeta meta;
@ -1117,7 +1117,7 @@ Texture CreateTexture(int width, int height, const char *name, uint64_t flags, b
if (bgfx::isValid(handle))
bgfx::setName(handle, name);
else
error(format("Failed to create texture '%1', format:%2 flags:%3").arg(name).arg(texture_format).arg(flags).c_str());
warn(format("Failed to create texture '%1', format:%2 flags:%3").arg(name).arg(texture_format).arg(flags).c_str());
return MakeTexture(handle, flags);
}
@ -1137,7 +1137,7 @@ Texture CreateTextureFromPicture(const Picture &pic, const char *name, uint64_t
if (bgfx::isValid(handle))
bgfx::setName(handle, name);
else
error(format("Failed to create texture '%1', format:%2 flags:%3").arg(name).arg(texture_format).arg(flags).c_str());
warn(format("Failed to create texture '%1', format:%2 flags:%3").arg(name).arg(texture_format).arg(flags).c_str());
return MakeTexture(handle, flags);
}
@ -1183,7 +1183,7 @@ Texture LoadTexture(
if (!bgfx::isValid(handle)) {
if (!silent)
error(format("Failed to load texture '%1', unsupported format").arg(name).c_str());
warn(format("Failed to load texture '%1', unsupported format").arg(name).c_str());
static const uint32_t dummy = 0xff00ffff;
handle = bgfx::createTexture2D(1, 1, false, 1, bgfx::TextureFormat::RGBA8, BGFX_SAMPLER_NONE, bgfx::copy(&dummy, 4));
@ -1193,7 +1193,7 @@ Texture LoadTexture(
bgfx::setName(handle, name);
} else {
if (!silent)
error(format("Failed to load texture '%1', could not load data").arg(name).c_str());
warn(format("Failed to load texture '%1', could not load data").arg(name).c_str());
}
return MakeTexture(handle, flags);
@ -1687,19 +1687,19 @@ Model LoadModel(const Reader &ir, const Handle &h, const char *name, ModelInfo *
if (!ir.is_valid(h)) {
if (!silent)
error(format("Cannot load model '%1', invalid file handle").arg(name));
warn(format("Cannot load model '%1', invalid file handle").arg(name));
return {};
}
if (Read<uint32_t>(ir, h) != HarfangMagic) {
if (!silent)
error(format("Cannot load model '%1', invalid magic marker").arg(name));
warn(format("Cannot load model '%1', invalid magic marker").arg(name));
return {};
}
if (Read<uint8_t>(ir, h) != ModelMarker) {
if (!silent)
error(format("Cannot load model '%1', invalid file marker").arg(name));
warn(format("Cannot load model '%1', invalid file marker").arg(name));
return {};
}
@ -1707,7 +1707,7 @@ Model LoadModel(const Reader &ir, const Handle &h, const char *name, ModelInfo *
if (version > 2) {
if (!silent)
error(format("Cannot load model '%1', unsupported version %2").arg(name).arg(version));
warn(format("Cannot load model '%1', unsupported version %2").arg(name).arg(version));
return {};
}
@ -1742,7 +1742,7 @@ Model LoadModel(const Reader &ir, const Handle &h, const char *name, ModelInfo *
const auto idx_hnd = bgfx::createIndexBuffer(idx_mem, idx_type_size == 4 ? BGFX_BUFFER_INDEX32 : BGFX_BUFFER_NONE);
if (!bgfx::isValid(idx_hnd)) {
error(format("%1: failed to create index buffer").arg(name));
warn(format("%1: failed to create index buffer").arg(name));
break;
}
@ -1755,7 +1755,7 @@ Model LoadModel(const Reader &ir, const Handle &h, const char *name, ModelInfo *
const auto vtx_hnd = bgfx::createVertexBuffer(vtx_mem, vs_decl);
if (!bgfx::isValid(vtx_hnd)) {
error(format("%1: failed to create vertex buffer").arg(name));
warn(format("%1: failed to create vertex buffer").arg(name));
bgfx::destroy(idx_hnd);
break;
}
@ -2345,7 +2345,7 @@ Vertices::Vertices(const bgfx::VertexLayout &decl_, size_t count) : decl(decl_)
Vertices &Vertices::Begin(size_t i) {
if (i >= GetCount()) {
if (i >= GetCapacity()) {
Reserve(i + 64);
Reserve(i + 1024);
debug(format("Vertices Begin() called with index %1, resizing buffer to accommodate request").arg(i));
}
Resize(i + 1);

View File

@ -111,9 +111,9 @@ bgfx::ProgramHandle LoadComputeProgramFromAssets(const char *cs_name, bool silen
std::vector<bgfx::ShaderHandle> GetProgramShaders(bgfx::ProgramHandle prg_h);
//
json LoadResourceMeta(const Reader &ir, const ReadProvider &ip, const std::string &name, bool silent = false);
json LoadResourceMetaFromFile(const std::string &path, bool silent = false);
json LoadResourceMetaFromAssets(const std::string &name, bool silent = false);
json LoadResourceMeta(const Reader &ir, const ReadProvider &ip, const std::string &name);
json LoadResourceMetaFromFile(const std::string &path);
json LoadResourceMetaFromAssets(const std::string &name);
bool SaveResourceMetaToFile(const std::string &path, const json &meta);
@ -223,8 +223,11 @@ struct DisplayList { // 4B
std::vector<uint16_t> bones_table;
};
//
/// Create an empty texture.
/// @see CreateTextureFromPicture and UpdateTextureFromPicture.
Texture CreateTexture(int width, int height, const char *name, uint64_t flags, bgfx::TextureFormat::Enum format = bgfx::TextureFormat::RGBA8);
/// Create a texture from a picture.
/// @see Picture, CreateTexture and UpdateTextureFromPicture.
Texture CreateTextureFromPicture(const Picture &pic, const char *name, uint64_t flags, bgfx::TextureFormat::Enum format = bgfx::TextureFormat::RGBA8);
void UpdateTextureFromPicture(Texture &tex, const Picture &pic);
@ -370,6 +373,7 @@ enum DepthTest { DT_Less, DT_LessEqual, DT_Equal, DT_GreaterEqual, DT_Greater, D
DepthTest GetMaterialDepthTest(const Material &mat);
void SetMaterialDepthTest(Material &mat, DepthTest test);
/// Control the compositing mode used to draw primitives.
enum BlendMode { BM_Additive, BM_Alpha, BM_Darken, BM_Lighten, BM_Multiply, BM_Opaque, BM_Screen, BM_LinearBurn, BM_Undefined };
BlendMode GetMaterialBlendMode(const Material &mat);
@ -396,7 +400,8 @@ void SetMaterialSkinning(Material &m, bool enable);
bool GetMaterialAlphaCut(const Material &m);
void SetMaterialAlphaCut(Material &m, bool enable);
//
/// Compute a render state to control subsequent render calls culling mode, blending mode, Z mask, etc... The same render state can be used by different render calls.
/// @see DrawLines, DrawTriangles and DrawModel.
RenderState ComputeRenderState(BlendMode blend, bool write_z, bool write_r = true, bool write_g = true, bool write_b = true, bool write_a = true);
RenderState ComputeRenderState(BlendMode blend, DepthTest test = DT_Less, FaceCulling culling = FC_Clockwise, bool write_z = true, bool write_r = true,
bool write_g = true, bool write_b = true, bool write_a = true);
@ -519,6 +524,9 @@ TextureRef LoadTexture(const Reader &ir, const ReadProvider &ip, const char *pat
TextureRef LoadTextureFromFile(const char *path, uint64_t flags, PipelineResources &resources, bool silent = false);
TextureRef LoadTextureFromAssets(const char *path, uint64_t flags, PipelineResources &resources, bool silent = false);
/// Capture a texture content to a Picture. Return the frame counter at which the capture will be complete.
/// A Picture object can be accessed by the CPU.
/// This function is asynchronous and its result will not be available until the returned frame counter is equal or greater to the frame counter returned by Frame.
uint32_t CaptureTexture(const PipelineResources &resources, const TextureRef &t, Picture &pic);
MaterialRef LoadMaterialRef(const Reader &ir, const Handle &h, const char *path, const Reader &deps_ir, const ReadProvider &deps_ip,
@ -547,9 +555,10 @@ void CreateMissingMaterialProgramValues(Material &mat, PipelineResources &resour
void CreateMissingMaterialProgramValuesFromFile(Material &mat, PipelineResources &resources);
void CreateMissingMaterialProgramValuesFromAssets(Material &mat, PipelineResources &resources);
//
/// Compute a sorting key to control the rendering order of a display list, `view_depth` is expected in view space.
uint32_t ComputeSortKey(float view_depth);
/// Compute a sorting key to control the rendering order of a display list.
uint32_t ComputeSortKeyFromWorld(const Vec3 &T, const Mat4 &view);
uint32_t ComputeSortKeyFromWorld(const Vec3 &T, const Mat4 &view, const Mat4 &model);
@ -557,7 +566,8 @@ uint32_t ComputeSortKeyFromWorld(const Vec3 &T, const Mat4 &view, const Mat4 &mo
void DrawDisplayList(bgfx::ViewId view_id, bgfx::IndexBufferHandle idx, bgfx::VertexBufferHandle vtx, bgfx::ProgramHandle prg,
const std::vector<UniformSetValue> &values = {}, const std::vector<UniformSetTexture> &textures = {}, RenderState state = {}, uint32_t depth = 0);
//
/// Draw a model to the specified view.
/// @see UniformSetValueList and UniformSetTextureList to pass uniform values to the shader program.
void DrawModel(bgfx::ViewId view_id, const Model &mdl, bgfx::ProgramHandle prg, const std::vector<UniformSetValue> &values,
const std::vector<UniformSetTexture> &textures, const Mat4 *mtxs, size_t mtx_count = 1, RenderState state = {}, uint32_t depth = 0);
@ -649,14 +659,23 @@ private:
void SetTransform(const Mat4 &world);
void SetUniforms(const std::vector<UniformSetValue> &values, const std::vector<UniformSetTexture> &textures);
/// Draw a list of lines to the specified view.
/// @see UniformSetValueList and UniformSetTextureList to pass uniform values to the shader program.
void DrawLines(bgfx::ViewId view_id, const Vertices &vtx, bgfx::ProgramHandle prg, RenderState = {}, uint32_t depth = 0);
void DrawLines(bgfx::ViewId view_id, const Vertices &vtx, bgfx::ProgramHandle prg, const std::vector<UniformSetValue> &values,
const std::vector<UniformSetTexture> &textures, RenderState = {}, uint32_t depth = 0);
void DrawLines(bgfx::ViewId view_id, const Indices &idx, const Vertices &vtx, bgfx::ProgramHandle prg, const std::vector<UniformSetValue> &values,
const std::vector<UniformSetTexture> &textures, RenderState = {}, uint32_t depth = 0);
/*!
Draw a list of sprites to the specified view.
@see UniformSetValueList and UniformSetTextureList to pass uniform values to the shader program.
@note This function prepares the sprite on the CPU before submitting them all to the GPU as a single draw call.
*/
void DrawSprites(bgfx::ViewId view_id, const Mat3 &inv_view_R, bgfx::VertexLayout &decl, const std::vector<Vec3> &pos, const Vec2 &size,
bgfx::ProgramHandle prg, RenderState state = {}, uint32_t depth = 0);
/// Draw a list of triangles to the specified view.
/// @see UniformSetValueList and UniformSetTextureList to pass uniform values to the shader program.
void DrawTriangles(bgfx::ViewId view_id, const Vertices &vtx, bgfx::ProgramHandle prg, RenderState = {}, uint32_t depth = 0);
void DrawTriangles(bgfx::ViewId view_id, const Vertices &vtx, bgfx::ProgramHandle prg, const std::vector<UniformSetValue> &values,
const std::vector<UniformSetTexture> &textures, RenderState = {}, uint32_t depth = 0);
@ -693,6 +712,7 @@ FrameBuffer CreateFrameBuffer(int width, int height, bgfx::TextureFormat::Enum c
Texture GetColorTexture(FrameBuffer &frameBuffer);
Texture GetDepthTexture(FrameBuffer &frameBuffer);
/// Destroy a frame buffer and its resources.
void DestroyFrameBuffer(FrameBuffer &frameBuffer);
bool CreateFullscreenQuad(bgfx::TransientIndexBuffer &idx, bgfx::TransientVertexBuffer &vtx);

View File

@ -30,6 +30,7 @@ SAO CreateSAOFromAssets(const char *path, bgfx::BackbufferRatio::Enum ratio);
SAO CreateSAOFromFile(const char *path, bgfx::BackbufferRatio::Enum ratio);
SAO CreateSAOFromAssets(const char *path, bgfx::BackbufferRatio::Enum ratio);
/// Destroy an ambient occlusion post process object and its resources.
void DestroySAO(SAO &sao);
/// @note input depth buffer must be in linear depth

View File

@ -225,12 +225,6 @@ size_t Scene::GarbageCollect() {
// stats
const auto t_total = time_now() - t_start;
if (total_removed > 0)
debug(format("Scene garbage collection report: %1 components destroyed in %2 passes, took %3 ms")
.arg(total_removed)
.arg(pass_count)
.arg(time_to_ms_f(t_total)));
return total_removed;
}
@ -239,12 +233,19 @@ ViewState Scene::ComputeCameraViewState(NodeRef ref, const Vec2 &aspect_ratio) c
if (const auto node_ = GetNode_(ref)) {
const auto trs_ref = node_->components[NCI_Transform];
if (IsValidTransformRef(trs_ref))
if (IsValidTransformRef(trs_ref)) {
if (auto cam_ = GetComponent_(cameras, node_->components[NCI_Camera])) {
const auto &world = transform_worlds[trs_ref.idx];
return cam_->ortho ? ComputeOrthographicViewState(world, cam_->size, cam_->zrange.znear, cam_->zrange.zfar, aspect_ratio)
: ComputePerspectiveViewState(world, cam_->fov, cam_->zrange.znear, cam_->zrange.zfar, aspect_ratio);
} else {
warn("Invalid node camera");
}
} else {
warn("Invalid node transform");
}
} else {
warn("Invalid node");
}
return {};
}
@ -402,7 +403,7 @@ std::vector<Node> Scene::GetLights() const {
for (auto i = nodes.first(); i != generational_vector_list<Node_>::invalid_idx; i = nodes.next(i)) {
const auto &node = nodes[i];
if (IsValidLightRef(node.components[NCI_Light]) && IsValidTransformRef(node.components[NCI_Transform]))
lights.push_back(GetNode(i));
lights.push_back(GetNode(GetNodeRef(i)));
}
return lights;
@ -461,8 +462,10 @@ void Scene::DestroyNode(NodeRef ref) { nodes.remove_ref(ref); }
//
void Scene::EnableNode_(NodeRef ref, bool through_instance) {
if (!nodes.is_valid(ref))
if (!nodes.is_valid(ref)) {
warn("Invalid node reference");
return;
}
nodes[ref.idx].flags &= through_instance ? ~NF_InstanceDisabled : ~NF_Disabled;
@ -477,8 +480,10 @@ void Scene::EnableNode_(NodeRef ref, bool through_instance) {
}
void Scene::DisableNode_(NodeRef ref, bool through_instance) {
if (!nodes.is_valid(ref))
if (!nodes.is_valid(ref)) {
warn("Invalid node reference");
return;
}
nodes[ref.idx].flags |= through_instance ? NF_InstanceDisabled : NF_Disabled;
@ -695,24 +700,32 @@ std::vector<Node> Scene::GetNodeChildren(NodeRef ref) const {
std::string Scene::GetNodeName(NodeRef ref) const {
if (auto node_ = GetNode_(ref))
return node_->name;
warn("Invalid node");
return {};
}
void Scene::SetNodeName(NodeRef ref, const std::string &v) {
if (auto node_ = GetNode_(ref))
node_->name = v;
else
warn("Invalid node");
}
//
uint32_t Scene::GetNodeFlags(NodeRef ref) const {
if (auto node_ = GetNode_(ref))
return node_->flags;
warn("Invalid node");
return 0;
}
void Scene::SetNodeFlags(NodeRef ref, uint32_t flags) {
if (auto node_ = GetNode_(ref))
node_->flags = flags;
else
warn("Invalid node");
}
//
@ -721,6 +734,8 @@ ComponentRef Scene::GetNodeTransformRef(NodeRef ref) const { return GetNodeCompo
void Scene::SetNodeTransform(NodeRef ref, ComponentRef cref) {
if (auto node_ = this->GetNode_(ref))
node_->components[NCI_Transform] = cref;
else
warn("Invalid node");
}
//
@ -729,9 +744,15 @@ Mat4 Scene::GetNodeWorldMatrix(NodeRef ref) const {
const auto trs_ref = node_->components[NCI_Transform];
if (transforms.is_valid(trs_ref)) {
__ASSERT__(transform_worlds.size() > trs_ref.idx);
return transform_worlds[trs_ref.idx];
if (trs_ref.idx < transform_worlds.size())
return transform_worlds[trs_ref.idx];
warn("Invalid node transform index");
} else {
warn("Invalid node transform");
}
} else {
warn("Invalid node");
}
return Mat4::Identity;
}
@ -741,10 +762,17 @@ void Scene::SetNodeWorldMatrix(NodeRef ref, const Mat4 &world) {
const auto trs_ref = node_->components[NCI_Transform];
if (transforms.is_valid(trs_ref)) {
__ASSERT__(transform_worlds.size() > trs_ref.idx);
transform_worlds[trs_ref.idx] = world;
transform_worlds_updated[trs_ref.idx] = true;
if (trs_ref.idx < transform_worlds.size()) {
transform_worlds[trs_ref.idx] = world;
transform_worlds_updated[trs_ref.idx] = true;
} else {
warn("Invalid node transform index");
}
} else {
warn("Invalid node transform");
}
} else {
warn("Invalid node");
}
}
@ -761,7 +789,11 @@ Mat4 Scene::ComputeNodeWorldMatrix(NodeRef ref) const {
mtx = ComputeNodeWorldMatrix(trs.parent) * mtx;
return mtx;
} else {
warn("Invalid node transform");
}
} else {
warn("Invalid node");
}
return Mat4::Identity;
}
@ -772,6 +804,8 @@ ComponentRef Scene::GetNodeCameraRef(NodeRef ref) const { return GetNodeComponen
void Scene::SetNodeCamera(NodeRef ref, ComponentRef cref) {
if (auto node_ = this->GetNode_(ref))
node_->components[NCI_Camera] = cref;
else
warn("Invalid node");
}
//
@ -780,6 +814,8 @@ ComponentRef Scene::GetNodeObjectRef(NodeRef ref) const { return GetNodeComponen
void Scene::SetNodeObject(NodeRef ref, ComponentRef cref) {
if (auto node_ = this->GetNode_(ref))
node_->components[NCI_Object] = cref;
else
warn("Invalid node");
}
//
@ -788,6 +824,8 @@ ComponentRef Scene::GetNodeLightRef(NodeRef ref) const { return GetNodeComponent
void Scene::SetNodeLight(NodeRef ref, ComponentRef cref) {
if (auto node_ = this->GetNode_(ref))
node_->components[NCI_Light] = cref;
else
warn("Invalid node");
}
//
@ -796,6 +834,8 @@ ComponentRef Scene::GetNodeRigidBodyRef(NodeRef ref) const { return GetNodeCompo
void Scene::SetNodeRigidBody(NodeRef ref, ComponentRef cref) {
if (auto node_ = this->GetNode_(ref))
node_->components[NCI_RigidBody] = cref;
else
warn("Invalid node");
}
ComponentRef Scene::GetNodeCollisionRef(NodeRef ref, size_t idx) const {
@ -812,6 +852,8 @@ void Scene::SetNodeCollision(NodeRef ref, size_t idx, const Collision &collision
if (nodes.is_valid(ref)) {
node_collisions[ref].resize(idx + 1);
node_collisions[ref][idx] = collision.ref;
} else {
warn("Invalid node");
}
}
@ -824,6 +866,8 @@ void Scene::RemoveNodeCollision(NodeRef ref, ComponentRef cref) {
collisions[slot_idx] = invalid_gen_ref;
_ResizeComponents(collisions);
} else {
warn("Invalid node");
}
}
@ -836,6 +880,8 @@ void Scene::RemoveNodeCollision(NodeRef ref, size_t slot_idx) {
collisions[slot_idx] = invalid_gen_ref;
_ResizeComponents(collisions);
} else {
warn("Invalid node");
}
}
@ -847,67 +893,91 @@ void Scene::DestroyRigidBody(ComponentRef ref) { rigid_bodies.remove_ref(ref); }
void Scene::SetRigidBodyType(ComponentRef ref, RigidBodyType type) {
if (auto rb = GetComponent_(rigid_bodies, ref))
rb->type = type;
else
warn("Invalid rigid body");
}
RigidBodyType Scene::GetRigidBodyType(ComponentRef ref) const {
if (auto rb = GetComponent_(rigid_bodies, ref))
return rb->type;
warn("Invalid rigid body");
return RBT_Dynamic;
}
float Scene::GetRigidBodyLinearDamping(ComponentRef ref) const {
if (auto rb = GetComponent_(rigid_bodies, ref))
return unpack_float<uint8_t>(rb->linear_damping);
warn("Invalid rigid body");
return 0.f;
}
void Scene::SetRigidBodyLinearDamping(ComponentRef ref, float damping) {
if (auto rb = GetComponent_(rigid_bodies, ref))
rb->linear_damping = pack_float<uint8_t>(damping);
else
warn("Invalid rigid body");
}
float Scene::GetRigidBodyAngularDamping(ComponentRef ref) const {
if (auto rb = GetComponent_(rigid_bodies, ref))
return unpack_float<uint8_t>(rb->angular_damping);
warn("Invalid rigid body");
return {};
}
void Scene::SetRigidBodyAngularDamping(ComponentRef ref, float damping) {
if (auto rb = GetComponent_(rigid_bodies, ref))
rb->angular_damping = pack_float<uint8_t>(damping);
else
warn("Invalid rigid body");
}
float Scene::GetRigidBodyRestitution(ComponentRef ref) const {
if (const auto rb = GetComponent_(rigid_bodies, ref))
return unpack_float<uint8_t>(rb->restitution);
warn("Invalid rigid body");
return 0.f;
}
void Scene::SetRigidBodyRestitution(ComponentRef ref, float restitution) {
if (auto rb = GetComponent_(rigid_bodies, ref))
rb->restitution = pack_float<uint8_t>(restitution);
else
warn("Invalid rigid body");
}
float Scene::GetRigidBodyFriction(ComponentRef ref) const {
if (const auto rb = GetComponent_(rigid_bodies, ref))
return unpack_float<uint8_t>(rb->friction);
warn("Invalid rigid body");
return 0.5f;
}
void Scene::SetRigidBodyFriction(ComponentRef ref, float friction) {
if (auto rb = GetComponent_(rigid_bodies, ref))
rb->friction = pack_float<uint8_t>(friction);
else
warn("Invalid rigid body");
}
float Scene::GetRigidBodyRollingFriction(ComponentRef ref) const {
if (const auto rb = GetComponent_(rigid_bodies, ref))
return unpack_float<uint8_t>(rb->rolling_friction);
warn("Invalid rigid body");
return 0.f;
}
void Scene::SetRigidBodyRollingFriction(ComponentRef ref, float rolling_friction) {
if (auto rb = GetComponent_(rigid_bodies, ref))
rb->rolling_friction = pack_float<uint8_t>(rolling_friction);
else
warn("Invalid rigid body");
}
//
@ -917,77 +987,135 @@ void Scene::DestroyCollision(ComponentRef ref) { collisions.remove_ref(ref); }
void Scene::SetCollisionType(ComponentRef ref, CollisionType type) {
if (auto col = GetComponent_(collisions, ref))
col->type = type;
else
warn("Invalid collision");
}
CollisionType Scene::GetCollisionType(ComponentRef ref) const {
if (const auto col = GetComponent_(collisions, ref))
return col->type;
warn("Invalid collision");
return CT_Sphere;
}
void Scene::SetCollisionLocalTransform(ComponentRef ref, Mat4 m) {
void Scene::SetCollisionLocalTransform(ComponentRef ref, const Mat4 &m) {
if (auto col = GetComponent_(collisions, ref))
col->m = m;
Decompose(m, &col->trs.pos, &col->trs.rot, nullptr); // scale is the shape size, scaling collision shapes is NOT supported
else
warn("Invalid collision");
}
Mat4 Scene::GetCollisionLocalTransform(ComponentRef ref) const {
if (const auto col = GetComponent_(collisions, ref))
return col->m;
return TransformationMat4(col->trs.pos, col->trs.rot, Vec3::One); // scale is shape size, scaling collision shapes is NOT supported
warn("Invalid collision");
return Mat4::Identity;
}
Vec3 Scene::GetCollisionPosition(ComponentRef ref) const {
if (const auto col = GetComponent_(collisions, ref))
return col->trs.pos;
warn("Invalid collision");
return {};
}
void Scene::SetCollisionPosition(ComponentRef ref, const Vec3 &pos) {
if (auto col = GetComponent_(collisions, ref))
col->trs.pos = pos;
else
warn("Invalid collision");
}
Vec3 Scene::GetCollisionRotation(ComponentRef ref) const {
if (const auto col = GetComponent_(collisions, ref))
return col->trs.rot;
warn("Invalid collision");
return {};
}
void Scene::SetCollisionRotation(ComponentRef ref, const Vec3 &rot) {
if (auto col = GetComponent_(collisions, ref))
col->trs.rot = rot;
else
warn("Invalid collision");
}
void Scene::SetCollisionMass(ComponentRef ref, float mass) {
if (auto col = GetComponent_(collisions, ref))
col->mass = mass;
else
warn("Invalid collision");
}
float Scene::GetCollisionMass(ComponentRef ref) const {
if (const auto col = GetComponent_(collisions, ref))
return col->mass;
warn("Invalid collision");
return 0.f;
}
void Scene::SetCollisionSize(ComponentRef ref, const Vec3 &size) {
if (auto col = GetComponent_(collisions, ref))
col->size = size;
col->trs.scl = size;
else
warn("Invalid collision");
}
Vec3 Scene::GetCollisionSize(ComponentRef ref) const {
if (const auto col = GetComponent_(collisions, ref))
return col->size;
return col->trs.scl;
warn("Invalid collision");
return {-1.f, -1.f, -1.f};
}
void Scene::SetCollisionRadius(ComponentRef ref, float radius) {
if (auto col = GetComponent_(collisions, ref))
col->size.x = radius;
col->trs.scl.x = radius;
else
warn("Invalid collision");
}
float Scene::GetCollisionRadius(ComponentRef ref) const {
if (const auto col = GetComponent_(collisions, ref))
return col->size.x;
return col->trs.scl.x;
warn("Invalid collision");
return -1.f;
}
void Scene::SetCollisionHeight(ComponentRef ref, float height) {
if (auto col = GetComponent_(collisions, ref))
col->size.y = height;
col->trs.scl.y = height;
else
warn("Invalid collision");
}
float Scene::GetCollisionHeight(ComponentRef ref) const {
if (const auto col = GetComponent_(collisions, ref))
return col->size.y;
return col->trs.scl.y;
warn("Invalid collision");
return -1.f;
}
void Scene::SetCollisionResource(ComponentRef ref, const std::string &path) {
if (auto col = GetComponent_(collisions, ref))
col->resource_path = path;
else
warn("Invalid collision");
}
std::string Scene::GetCollisionResource(ComponentRef ref) {
if (auto col = GetComponent_(collisions, ref))
return col->resource_path;
warn("Invalid collision");
return {};
}
@ -1032,6 +1160,15 @@ Collision Scene::CreateCylinderCollision(float radius, float height, float mass)
return col;
}
Collision Scene::CreateConeCollision(float radius, float height, float mass) {
auto col = CreateCollision();
SetCollisionType(col.ref, CT_Cone);
SetCollisionRadius(col.ref, radius);
SetCollisionHeight(col.ref, height);
SetCollisionMass(col.ref, mass);
return col;
}
Collision Scene::CreateMeshCollision(const std::string &path, float mass) {
auto col = CreateCollision();
SetCollisionType(col.ref, CT_Mesh);
@ -1055,40 +1192,77 @@ void Scene::DestroyInstance(ComponentRef ref) { instances.remove_ref(ref); }
void Scene::SetInstancePath(ComponentRef ref, const std::string &path) {
if (instances.is_valid(ref))
instances[ref.idx].name = path;
else
warn("Invalid instance");
}
std::string Scene::GetInstancePath(ComponentRef ref) const { return instances.is_valid(ref) ? instances[ref.idx].name : std::string{}; }
std::string Scene::GetInstancePath(ComponentRef ref) const {
if (instances.is_valid(ref))
return instances[ref.idx].name;
warn("Invalid instance");
return {};
}
void Scene::SetOnInstantiateAnim(ComponentRef ref, const std::string &anim) {
if (instances.is_valid(ref))
instances[ref.idx].anim = anim;
else
warn("Invalid instance");
}
void Scene::SetOnInstantiateAnimLoopMode(ComponentRef ref, AnimLoopMode loop_mode) {
if (instances.is_valid(ref))
instances[ref.idx].loop_mode = loop_mode;
else
warn("Invalid instance");
}
void Scene::ClearOnInstantiateAnim(ComponentRef ref) {
if (instances.is_valid(ref))
instances[ref.idx].anim.clear();
else
warn("Invalid instance");
}
std::string Scene::GetOnInstantiateAnim(ComponentRef ref) { return instances.is_valid(ref) ? instances[ref.idx].anim : std::string(); }
AnimLoopMode Scene::GetOnInstantiateAnimLoopMode(ComponentRef ref) { return instances.is_valid(ref) ? instances[ref.idx].loop_mode : ALM_Once; }
std::string Scene::GetOnInstantiateAnim(ComponentRef ref) {
if (instances.is_valid(ref))
return instances[ref.idx].anim;
warn("Invalid instance");
return {};
}
AnimLoopMode Scene::GetOnInstantiateAnimLoopMode(ComponentRef ref) {
if (instances.is_valid(ref))
return instances[ref.idx].loop_mode;
warn("Invalid instance");
return {};
}
ScenePlayAnimRef Scene::GetOnInstantiatePlayAnimRef(ComponentRef ref) {
return instances.is_valid(ref) ? instances[ref.idx].play_anim_ref : InvalidScenePlayAnimRef;
if (instances.is_valid(ref))
return instances[ref.idx].play_anim_ref;
warn("Invalid instance");
return InvalidScenePlayAnimRef;
}
Instance Scene::GetNodeInstance(NodeRef ref) const {
const auto cref = GetNodeInstanceRef(ref);
return cref == InvalidComponentRef ? Instance{} : Instance{scene_ref, cref};
if (cref != InvalidComponentRef)
return {scene_ref, cref};
warn("Invalid node instance");
return {};
}
ComponentRef Scene::GetNodeInstanceRef(NodeRef ref) const {
const auto i = node_instance.find(ref);
return i == std::end(node_instance) ? InvalidComponentRef : i->second;
if (i != std::end(node_instance))
return i->second;
return InvalidComponentRef;
}
void Scene::SetNodeInstance(NodeRef ref, ComponentRef cref) {
@ -1115,6 +1289,8 @@ void Scene::NodeDestroyInstance(NodeRef ref) {
if (i != std::end(node_instance_view)) {
DestroyViewContent(i->second);
node_instance_view.erase(i);
} else {
warn("Invalid node instance view");
}
}
@ -1219,7 +1395,7 @@ const SceneView &Scene::GetNodeInstanceSceneView(NodeRef ref) const {
static SceneView dummy_view;
const auto i = node_instance_view.find(ref);
if (i == std::end(node_instance_view)) {
debug(format("No instance scene view on node (%1:%2)").arg(ref.idx).arg(ref.gen));
warn(format("No instance scene view on node (%1:%2)").arg(ref.idx).arg(ref.gen).c_str());
return dummy_view;
}
return i->second;
@ -1337,11 +1513,15 @@ void Scene::DestroyScript(ComponentRef ref) { scripts.remove_ref(ref); }
void Scene::SetScriptPath(ComponentRef ref, const std::string &path) {
if (auto s = GetComponent_(scripts, ref))
s->path = path;
else
warn("Invalid script");
}
std::string Scene::GetScriptPath(ComponentRef ref) const {
if (const auto s = GetComponent_(scripts, ref))
return s->path;
warn("Invalid script");
return {};
}
@ -1349,6 +1529,8 @@ std::string Scene::GetScriptPath(ComponentRef ref) const {
bool Scene::ScriptHasParam(ComponentRef ref, const std::string &name) const {
if (const auto s = GetComponent_(scripts, ref))
return s->params.find(name) != std::end(s->params);
warn("Invalid script");
return false;
}
@ -1357,14 +1539,20 @@ bool Scene::SetScriptParam(ComponentRef ref, const std::string &name, ScriptPara
s->params[name] = std::move(param);
return true;
}
warn("Invalid script");
return false;
}
ScriptParam Scene::GetScriptParam(ComponentRef ref, const std::string &name) const {
if (const auto s = GetComponent_(scripts, ref)) {
auto i = s->params.find(name);
const auto i = s->params.find(name);
if (i != std::end(s->params))
return i->second;
else
warn(format("Invalid script parameter '%1'").arg(name).c_str());
} else {
warn("Invalid script");
}
return {SPT_Null};
}
@ -1374,12 +1562,19 @@ static std::map<std::string, ScriptParam> _empty_script_params;
const std::map<std::string, ScriptParam> &Scene::GetScriptParams(ComponentRef ref) const {
if (const auto s = GetComponent_(scripts, ref))
return s->params;
warn("Invalid script");
return _empty_script_params;
}
//
size_t Scene::GetNodeScriptCount(NodeRef ref) const {
auto i = nodes.is_valid(ref) ? node_scripts.find(ref) : std::end(node_scripts);
if (!nodes.is_valid(ref)) {
warn("Invalid node");
return 0;
}
const auto i = node_scripts.find(ref);
return i != std::end(node_scripts) ? i->second.size() : 0;
}
@ -1397,6 +1592,8 @@ void Scene::SetNodeScript(NodeRef ref, size_t idx, const Script &script) {
if (nodes.is_valid(ref)) {
node_scripts[ref].resize(idx + 1);
node_scripts[ref][idx] = script.ref;
} else {
warn("Invalid node");
}
}
@ -1409,6 +1606,8 @@ void Scene::RemoveNodeScript(NodeRef ref, ComponentRef cref) {
scripts[slot_idx] = invalid_gen_ref;
_ResizeComponents(scripts);
} else {
warn("Invalid node");
}
}
@ -1421,6 +1620,8 @@ void Scene::RemoveNodeScript(NodeRef ref, size_t slot_idx) {
scripts[slot_idx] = invalid_gen_ref;
_ResizeComponents(scripts);
} else {
warn("Invalid node");
}
}
@ -1667,16 +1868,15 @@ std::vector<NodeRef> DuplicateNodes(Scene &scene, const std::vector<NodeRef> &no
Data data;
if (!scene.SaveNodes_binary(g_data_writer, DataWriteHandle(data), nodes, resources)) {
error("Failed to duplicate nodes, an error occurred while saving the node selection");
warn("Failed to duplicate nodes, an error occurred while saving the node selection");
return {};
}
debug(format("DuplicateNodes, %1 nodes serialized to %2 bytes").arg(nodes.size()).arg(data.GetSize()));
data.Rewind();
LoadSceneContext ctx;
if (!scene.LoadNodes_binary(g_data_reader, DataReadHandle(data), "DuplicateNodes", deps_ir, deps_ip, resources, pipeline, ctx)) {
error("Failed to duplicate nodes, an error occurred while loading the node selection");
warn("Failed to duplicate nodes, an error occurred while loading the node selection");
return {};
}
@ -1692,6 +1892,7 @@ static std::vector<NodeRef> GetNodeAndchildren(const Scene &scene, const std::ve
for (auto child_ref : child_refs)
out.insert(child_ref);
}
return {std::begin(out), std::end(out)};
}
@ -1746,8 +1947,6 @@ Node SceneView::GetNode(const Scene &scene, const std::string &name) const {
for (const auto &ref : nodes)
if (scene.GetNodeName(ref) == name)
return scene.GetNode(ref);
debug(format("Node '%1' not found in scene").arg(name));
return {};
}
@ -1756,8 +1955,6 @@ SceneAnimRef SceneView::GetSceneAnim(const Scene &scene, const std::string &name
if (const auto anim = scene.GetSceneAnim(ref))
if (anim->name == name)
return ref;
debug(format("Animation '%1' not found in scene").arg(name));
return InvalidSceneAnimRef;
}
@ -1800,8 +1997,10 @@ const Anim *Scene::GetAnim(AnimRef ref) const { return anims.is_valid(ref) ? &an
void Scene::DestroyAnim(AnimRef anim) { anims.remove_ref(anim); }
BoundToSceneAnim Scene::BindSceneAnim(AnimRef anim_ref) const {
if (!anims.is_valid(anim_ref))
if (!anims.is_valid(anim_ref)) {
warn("Invalid animation");
return {};
}
const auto &anim = anims[anim_ref.idx];
@ -1865,8 +2064,10 @@ static bool SplitMaterialPropertyName(const std::string &name, size_t &slot_idx,
}
BoundToNodeAnim Scene::BindNodeAnim(NodeRef ref, AnimRef anim_ref) const {
if (!anims.is_valid(anim_ref))
if (!anims.is_valid(anim_ref)) {
warn("Invalid animation");
return {};
}
const auto &anim = anims[anim_ref.idx];
@ -1891,6 +2092,8 @@ BoundToNodeAnim Scene::BindNodeAnim(NodeRef ref, AnimRef anim_ref) const {
bound_anim.float_track[NFAT_LightDiffuseIntensity] = int8_t(i);
else if (t.target == "Light.SpecularIntensity")
bound_anim.float_track[NFAT_LightSpecularIntensity] = int8_t(i);
else if (t.target == "Camera.Fov")
bound_anim.float_track[NFAT_CameraFov] = int8_t(i);
}
std::fill(std::begin(bound_anim.vec3_track), std::end(bound_anim.vec3_track), -1);
@ -1976,17 +2179,19 @@ void Scene::EvaluateBoundAnim(const BoundToNodeAnim &bound_anim, time_ns t) {
if (auto lgt = GetComponent_(lights, GetNodeComponentRef_<NCI_Light>(bound_anim.node))) {
if (bound_anim.color_track[NCAT_LightDiffuse] != -1)
Evaluate(anim.color_tracks[bound_anim.color_track[NCAT_LightDiffuse]], t, lgt->diffuse);
if (bound_anim.color_track[NCAT_LightSpecular] != -1)
Evaluate(anim.color_tracks[bound_anim.color_track[NCAT_LightSpecular]], t, lgt->specular);
if (bound_anim.float_track[NFAT_LightDiffuseIntensity] != -1)
Evaluate(anim.float_tracks[bound_anim.float_track[NFAT_LightDiffuseIntensity]], t, lgt->diffuse_intensity);
if (bound_anim.float_track[NFAT_LightSpecularIntensity] != -1)
Evaluate(anim.float_tracks[bound_anim.float_track[NFAT_LightSpecularIntensity]], t, lgt->specular_intensity);
}
if (auto cam = GetComponent_(cameras, GetNodeComponentRef_<NCI_Camera>(bound_anim.node))) {
if (bound_anim.float_track[NFAT_CameraFov] != -1)
Evaluate(anim.float_tracks[bound_anim.float_track[NFAT_CameraFov]], t, cam->fov);
}
if (auto obj = GetComponent_(objects, GetNodeComponentRef_<NCI_Object>(bound_anim.node))) {
// material value tracks
for (auto &mt : bound_anim.vec4_mat_track) {
@ -2114,6 +2319,8 @@ SceneBoundAnim Scene::BindAnim(const SceneAnim &scene_anim) const {
if (anims.is_valid(scene_anim.scene_anim))
bound_anim.bound_scene_anim = BindSceneAnim(scene_anim.scene_anim);
else
warn("Invalid scene animation");
for (const auto &i : scene_anim.node_anims)
bound_anim.bound_node_anims.push_back(BindNodeAnim(i.node, i.anim));
@ -2122,8 +2329,10 @@ SceneBoundAnim Scene::BindAnim(const SceneAnim &scene_anim) const {
}
SceneBoundAnim Scene::BindAnim(SceneAnimRef ref) const {
if (!scene_anims.is_valid(ref))
if (!scene_anims.is_valid(ref)) {
warn("Invalid scene animation reference");
return {};
}
return BindAnim(scene_anims[ref.idx]);
}
@ -2139,7 +2348,7 @@ const ScenePlayAnimRef InvalidScenePlayAnimRef;
ScenePlayAnimRef Scene::PlayAnim(SceneAnimRef ref, AnimLoopMode loop_mode, Easing easing, time_ns t_start, time_ns t_end, bool paused, float t_scale) {
if (!scene_anims.is_valid(ref)) {
debug("Invalid scene animation reference passed to scene PlayAnim");
warn("Invalid scene animation reference");
return InvalidScenePlayAnimRef;
}
@ -2220,8 +2429,11 @@ void Scene::UpdatePlayingAnims(time_ns dt) {
//
SceneAnimRef Scene::DuplicateSceneAnim(SceneAnimRef ref) {
const auto anim = GetSceneAnim(ref);
if (!anim)
if (!anim) {
warn("Invalid scene animation reference");
return InvalidSceneAnimRef;
}
SceneAnim out;
@ -2273,10 +2485,10 @@ void Scene::SetValue(const std::string &key, const std::string &value) { key_val
//
bool GetAnimableNodePropertyBool(const Scene &scene, NodeRef ref, const std::string &name) {
if (const auto node = scene.GetNode(ref)) {
if (const auto node = scene.GetNode(ref))
if (name == "Enable")
return node.IsEnabled();
}
return false;
}
@ -2452,6 +2664,34 @@ void ReverseSceneAnim(Scene &scene, SceneAnim &scene_anim) {
ReverseAnim(*anim, scene_anim.t_start, scene_anim.t_end);
}
void QuantizeSceneAnim(Scene &scene, SceneAnim &scene_anim, time_ns t_step) {
scene_anim.t_start = (scene_anim.t_start / t_step) * t_step;
scene_anim.t_end = (scene_anim.t_end / t_step) * t_step;
if (auto anim = scene.GetAnim(scene_anim.scene_anim))
QuantizeAnim(*anim, t_step);
for (auto node_anim : scene_anim.node_anims)
if (auto anim = scene.GetAnim(node_anim.anim))
QuantizeAnim(*anim, t_step);
}
void DeleteEmptySceneAnims(Scene &scene, SceneAnim &scene_anim) {
if (auto anim = scene.GetAnim(scene_anim.scene_anim))
if (!AnimHasKeys(*anim))
scene_anim.scene_anim = InvalidAnimRef;
for (const auto &node_anim : scene_anim.node_anims)
scene_anim.node_anims.erase(std::remove_if(std::begin(scene_anim.node_anims), std::end(scene_anim.node_anims),
[&](const NodeAnim &node_anim) {
auto anim = scene.GetAnim(node_anim.anim);
if (!anim)
return true; // remove invalid anim ref
return AnimHasKeys(*anim) == false; // remove anim with no keys
}),
std::end(scene_anim.node_anims));
}
//
Node CreateSceneRootNode(Scene &scene, std::string name, const Mat4 &mtx) {
Node root = scene.CreateNode(name);

View File

@ -51,7 +51,7 @@ using AnimRef = gen_ref;
extern const AnimRef InvalidAnimRef;
enum NodeBoolAnimTarget { NBAT_Enable, NBAT_Count };
enum NodeFloatAnimTarget { NFAT_LightDiffuseIntensity, NFAT_LightSpecularIntensity, NFAT_FogNear, NFAT_FogFar, NFAT_Count };
enum NodeFloatAnimTarget { NFAT_LightDiffuseIntensity, NFAT_LightSpecularIntensity, NFAT_CameraFov, NFAT_Count };
enum NodeVec3AnimTarget { NV3AT_TransformPosition, NV3AT_TransformRotation, NV3AT_TransformScale, NV3AT_Count };
enum NodeVec4AnimTarget { NV4AT_Count };
enum NodeQuatAnimTarget { NQAT_TransformRotation, NQAT_Count };
@ -129,6 +129,18 @@ struct SceneBoundAnim {
using ScenePlayAnimRef = gen_ref;
extern const ScenePlayAnimRef InvalidScenePlayAnimRef;
//
enum ProbeType : uint8_t { PT_Sphere, PT_Cube, PT_Count };
struct Probe {
TextureRef irradiance_map;
TextureRef radiance_map;
ProbeType type{PT_Sphere};
uint8_t parallax{0};
TransformTRS trs;
};
//
struct SceneView {
std::vector<NodeRef> nodes;
@ -189,11 +201,6 @@ public:
std::vector<Node> GetNodeChildren(NodeRef ref) const;
std::vector<Node> GetNodeChildren(const Node &node) const { return GetNodeChildren(node.ref); }
Node GetNode(uint32_t idx) const {
auto ref = nodes.get_ref(idx);
return ref != InvalidNodeRef ? Node{scene_ref, ref} : Node{nullptr, {}};
}
NodeRef GetNodeRef(uint32_t idx) const { return nodes.get_ref(idx); }
size_t GetNodeCount() const;
@ -248,7 +255,7 @@ public:
void SetTransformLocalMatrix(ComponentRef ref, const Mat4 &local);
/*!
@short Set and decompose transform world matrix.
Set and decompose transform world matrix.
The provided matrix is decomposed over the position, rotation and scale members of the Transform.
@see SetNodeWorldMatrix to set a node world matrix in the scene graph without affecting the Transform component.
*/
@ -267,17 +274,13 @@ public:
void SetNodeTransform(NodeRef ref, const Transform &v) { SetNodeTransform(ref, v.ref); }
Mat4 GetNodeWorldMatrix(NodeRef ref) const;
/*!
@short Set node world matrix.
Set a node world matrix and flag it as updated so that it won't be computed by the next call to ComputeWorldMatrices().
@note This function INTENTIONALLY does not decompose the provided matrix to the transfrom position/rotation/scale fields.
*/
/// Set node world matrix.
/// Set a node world matrix and flag it as updated so that it won't be computed by the next call to ComputeWorldMatrices().
/// @note This function INTENTIONALLY does not decompose the provided matrix to the transfrom position/rotation/scale fields.
void SetNodeWorldMatrix(NodeRef ref, const Mat4 &world);
/*!
@short Compute node world matrix from scratch on-the-fly.
This function is slow but useful when scene matrices are not yet up-to-date.
*/
/// Compute node world matrix from scratch on-the-fly.
/// This function is slow but useful when scene matrices are not yet up-to-date.
Mat4 ComputeNodeWorldMatrix(NodeRef ref) const;
//
@ -289,6 +292,7 @@ public:
void Update(time_ns dt);
// camera component
/// Create a new Node with a Transform and Camera components.
Camera CreateCamera();
void DestroyCamera(ComponentRef ref);
void DestroyCamera(const Camera &c) { DestroyCamera(c.ref); }
@ -380,11 +384,14 @@ public:
float GetLightShadowBias(ComponentRef ref) const;
void SetLightShadowBias(ComponentRef ref, float v);
/// Create a Node with a Transform and a point Light component.
Light CreatePointLight(float radius, const Color &diffuse = {1, 1, 1}, float diffuse_intensity = 1, const Color &specular = {1, 1, 1},
float specular_intensity = 1, float priority = 0, LightShadowType shadow_type = LST_None, float shadow_bias = default_shadow_bias);
/// Create a Node with a Transform and a spot Light component.
Light CreateSpotLight(float radius, float inner_angle, float outer_angle, const Color &diffuse = {1, 1, 1}, float diffuse_intensity = 1,
const Color &specular = {1, 1, 1}, float specular_intensity = 1, float priority = 0, LightShadowType shadow_type = LST_None,
float shadow_bias = default_shadow_bias);
/// Create a Node with a Transform and a linear Light component.
Light CreateLinearLight(const Color &diffuse = {1, 1, 1}, float diffuse_intensity = 1, const Color &specular = {1, 1, 1}, float specular_intensity = 1,
float priority = 0, LightShadowType shadow_type = LST_None, float shadow_bias = default_shadow_bias, const Vec4 &pssm_split = default_pssm_split);
@ -443,25 +450,31 @@ public:
void DestroyCollision(ComponentRef ref);
void DestroyCollision(const Collision &s) { DestroyCollision(s.ref); }
void SetCollisionType(ComponentRef ref, CollisionType type);
CollisionType GetCollisionType(ComponentRef ref) const;
void SetCollisionLocalTransform(ComponentRef ref, Mat4 m);
Mat4 GetCollisionLocalTransform(ComponentRef ref) const;
void SetCollisionMass(ComponentRef ref, float mass);
void SetCollisionLocalTransform(ComponentRef ref, const Mat4 &local);
CollisionType GetCollisionType(ComponentRef ref) const;
void SetCollisionType(ComponentRef ref, CollisionType type);
Vec3 GetCollisionPosition(ComponentRef ref) const;
void SetCollisionPosition(ComponentRef ref, const Vec3 &pos);
Vec3 GetCollisionRotation(ComponentRef ref) const;
void SetCollisionRotation(ComponentRef ref, const Vec3 &rot);
float GetCollisionMass(ComponentRef ref) const;
void SetCollisionSize(ComponentRef ref, const Vec3 &size);
void SetCollisionMass(ComponentRef ref, float mass);
Vec3 GetCollisionSize(ComponentRef ref) const;
void SetCollisionRadius(ComponentRef ref, float radius);
void SetCollisionSize(ComponentRef ref, const Vec3 &size);
float GetCollisionRadius(ComponentRef ref) const;
void SetCollisionHeight(ComponentRef ref, float radius);
void SetCollisionRadius(ComponentRef ref, float radius);
float GetCollisionHeight(ComponentRef ref) const;
void SetCollisionResource(ComponentRef ref, const std::string &path);
void SetCollisionHeight(ComponentRef ref, float radius);
std::string GetCollisionResource(ComponentRef ref);
void SetCollisionResource(ComponentRef ref, const std::string &path);
Collision CreateSphereCollision(float radius, float mass = Kg(1.f));
Collision CreateCubeCollision(float x, float y, float z, float mass = Kg(1.f));
Collision CreateCapsuleCollision(float radius, float height, float mass = Kg(1.f));
Collision CreateCylinderCollision(float radius, float height, float mass = Kg(1.f));
Collision CreateConeCollision(float radius, float height, float mass = Kg(1.f));
Collision CreateMeshCollision(const std::string &collision_path, float mass = Kg(1.f));
Collision CreateMeshConvexCollision(const std::string &collision_path, float mass = Kg(1.f));
@ -514,6 +527,7 @@ public:
Instance CreateInstance(const std::string &path);
// scripts
/// Helper function to create a Node with a Script component.
Script CreateScript();
Script CreateScript(const std::string &path);
void DestroyScript(ComponentRef ref);
@ -549,7 +563,7 @@ public:
const std::vector<ComponentRef> &GetSceneScripts() const { return scene_scripts; }
const std::map<NodeRef, std::vector<ComponentRef>> &GetNodeScripts() const { return node_scripts; }
// canvas
/// Holds the canvas properties of a scene, see the `canvas` member of class Scene.
struct Canvas {
bool clear_z{true}, clear_color{true};
Color color{0.f, 0.f, 0.f, 1.f};
@ -557,14 +571,14 @@ public:
Canvas canvas;
// environment
/// Environment properties of a scene.
/// @see Scene::environment member of the Scene class.
struct Environment {
Color ambient;
Color fog_color;
float fog_near, fog_far;
TextureRef irradiance_map;
TextureRef radiance_map;
Probe probe;
TextureRef brdf_map;
};
@ -605,6 +619,8 @@ public:
Anim *GetAnim(AnimRef ref);
const Anim *GetAnim(AnimRef ref) const;
AnimRef GetAnimRef(uint32_t idx) const { return anims.get_ref(idx); }
BoundToSceneAnim BindSceneAnim(AnimRef ref) const;
void EvaluateBoundAnim(const BoundToSceneAnim &bound_anim, time_ns t);
BoundToNodeAnim BindNodeAnim(NodeRef node_ref, AnimRef anim_ref) const;
@ -738,9 +754,9 @@ private:
LightShadowType shadow_type{LST_None};
Color diffuse{1.f, 1.f, 1.f, 1.f};
float diffuse_intensity{1.0f};
float diffuse_intensity{1.f};
Color specular{1.f, 1.f, 1.f, 1.f};
float specular_intensity{1.0f};
float specular_intensity{1.f};
float radius{0.f};
float inner_angle{Deg(30.f)}, outer_angle{Deg(45.f)};
@ -771,10 +787,8 @@ private:
struct Collision_ {
CollisionType type;
float mass;
Vec3 size;
std::string resource_path;
Mat4 m;
Collision_() : m(Mat4::Identity) {}
TransformTRS trs;
};
generational_vector_list<Collision_> collisions;
@ -889,7 +903,7 @@ struct SceneRef {
uint32_t ref_count{};
};
//
/// Helper function to create a Node with a Transform component then parent all root nodes in the scene to it.
Node CreateSceneRootNode(Scene &scene, std::string name = {}, const Mat4 &mtx = Mat4::Identity);
Node CreateCamera(Scene &scene, const Mat4 &mtx, float znear, float zfar, float fov = Deg(45.f));
@ -961,9 +975,13 @@ bool LoadSceneFromAssets(
std::vector<NodeRef> DuplicateNodes(Scene &scene, const std::vector<NodeRef> &nodes, const Reader &deps_ir, const ReadProvider &deps_ip,
PipelineResources &resources, const PipelineInfo &pipeline);
/// Duplicate each node of a list. Resources will be loaded from the local filesystem.
std::vector<Node> DuplicateNodesFromFile(Scene &scene, const std::vector<Node> &nodes, PipelineResources &resources, const PipelineInfo &pipeline);
/// Duplicate each node of a list. Resources will be loaded from the assets system.
std::vector<Node> DuplicateNodesFromAssets(Scene &scene, const std::vector<Node> &nodes, PipelineResources &resources, const PipelineInfo &pipeline);
/// Duplicate each node and children of a list. Resources will be loaded from the local filesystem.
std::vector<Node> DuplicateNodesAndChildrenFromFile(Scene &scene, const std::vector<Node> &nodes, PipelineResources &resources, const PipelineInfo &pipeline);
/// Duplicate each node and children of a list. Resources will be loaded from the assets system.
std::vector<Node> DuplicateNodesAndChildrenFromAssets(Scene &scene, const std::vector<Node> &nodes, PipelineResources &resources, const PipelineInfo &pipeline);
Node DuplicateNodeFromFile(Scene &scene, Node node, PipelineResources &resources, const PipelineInfo &pipeline);
@ -999,6 +1017,8 @@ Color GetAnimableNodePropertyColor(const Scene &scene, NodeRef ref, const std::s
void SetAnimableNodePropertyColor(Scene &scene, NodeRef ref, const std::string &name, const Color &v);
void ReverseSceneAnim(Scene &scene, SceneAnim &scene_anim);
void QuantizeSceneAnim(Scene &scene, SceneAnim &scene_anim, time_ns t_step);
void DeleteEmptySceneAnims(Scene &scene, SceneAnim &scene_anim);
#ifdef NDEBUG
#define _HG_CHECK_DLL_SCENE_DATA_TYPES

View File

@ -135,7 +135,7 @@ SceneBullet3Physics::~SceneBullet3Physics() { Clear(); }
//
void SceneBullet3Physics::SceneCreatePhysics(const Scene &scene, const Reader &ir, const ReadProvider &ip) {
const auto nodes = scene.GetNodes();
const auto nodes = scene.GetAllNodes();
for (auto &node : nodes)
if (!NodeHasBody(node.ref))
@ -270,20 +270,13 @@ void SceneBullet3Physics::NodeCreatePhysics(const Node &node, const Reader &ir,
auto trs = node.GetTransform();
btTransform bt_trs = btTransform::getIdentity();
if (trs)
bt_trs = to_btTransform(Normalize(node.ComputeWorld())); // require most up-to-date world matrix
bt_trs = to_btTransform(node.ComputeWorld()); // require most up-to-date world matrix
if (!shapes.empty()) {
btCollisionShape *root_shape;
if (shapes.size() == 1) {
root_shape = *shapes.begin();
} else {
auto compound = new btCompoundShape;
for (size_t idx = 0; idx < node.GetCollisionCount(); ++idx) {
const auto col = node.GetCollision(idx);
compound->addChildShape(to_btTransform(col.GetLocalTransform()), shapes[idx]);
}
root_shape = compound;
auto root_shape = new btCompoundShape;
for (size_t idx = 0; idx < node.GetCollisionCount(); ++idx) {
const auto col = node.GetCollision(idx);
root_shape->addChildShape(to_btTransform(col.GetLocalTransform()), shapes[idx]);
}
root_shape->setUserIndex(node.ref.idx); // ref back to node
@ -693,7 +686,7 @@ RaycastOut SceneBullet3Physics::RaycastFirstHit(const Scene &scene, const Vec3 &
RaycastOut out;
out.N = from_btVector3(trace.m_hitNormalWorld);
out.node = scene.GetNode(trace.m_collisionObject->getUserIndex());
out.node = scene.GetNode(scene.GetNodeRef(trace.m_collisionObject->getUserIndex()));
out.P = from_btVector3(trace.m_hitPointWorld);
out.t = Dot(out.P - world_p0, Normalize(world_p1 - world_p0));
@ -743,7 +736,7 @@ std::vector<RaycastOut> SceneBullet3Physics::RaycastAllHits(const Scene &scene,
for (int i = 0; i < trace.m_hitNormalWorld.size(); ++i) {
outs[i].N = from_btVector3(trace.m_hitNormalWorld[i]);
outs[i].node = scene.GetNode(trace.m_collisionObjects[i]->getUserIndex());
outs[i].node = scene.GetNode(scene.GetNodeRef(trace.m_collisionObjects[i]->getUserIndex()));
outs[i].P = from_btVector3(trace.m_hitPointWorld[i]);
outs[i].t = Dot(outs[i].P - world_p0, Normalize(world_p1 - world_p0));
}
@ -784,7 +777,7 @@ btCollisionShape *SceneBullet3Physics::LoadCollisionTree(const Reader &ir, const
collision_trees[name] = collision;
if (!collision)
error(format("Failed to load Bullet3 collision tree '%1'").arg(name));
warn(format("Failed to load Bullet3 collision tree '%1'").arg(name));
return collision;
}

View File

@ -22,11 +22,13 @@ namespace hg {
void UpdateForwardPipeline(ForwardPipeline &pipeline, const ForwardPipelineShadowData &shadow_data, const Color &ambient, const ForwardPipelineLights &lights,
const ForwardPipelineFog &fog, const hg::iVec2 &fb_size);
void UpdateForwardPipelineNoise(ForwardPipeline &pipeline, Texture noise);
void UpdateForwardPipelinePBRProbe(ForwardPipeline &pipeline, Texture irradiance, Texture radiance, Texture brdf);
void UpdateForwardPipelineProbe(
ForwardPipeline &pipeline, Texture irradiance, Texture radiance, Texture brdf, ProbeType type, const Mat4 &world, float parallax);
void UpdateForwardPipelineAO(ForwardPipeline &pipeline, Texture ao);
void UpdateForwardPipelineAAA(ForwardPipeline &pipeline, const iRect &rect, const Mat4 &view, const Mat44 &proj, const Mat4 &prv_view, const Mat44 &prv_proj,
const Vec2 &jitter, bgfx::BackbufferRatio::Enum ssgi_ratio, bgfx::BackbufferRatio::Enum ssr_ratio, float temporal_aa_weight, float motion_blur_strength,
float exposure, float gamma, int sample_count, float max_distance);
float exposure, float gamma, int sample_count, float max_distance, float specular_weight, float sharpen);
void UpdateForwardPipelineAAA(ForwardPipeline &pipeline, Texture ssgi, Texture ssr);
static const uint64_t attribute_texture_flags = 0 | BGFX_TEXTURE_RT | BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP;
@ -246,6 +248,12 @@ static ForwardPipelineAAA _CreateForwardPipelineAAA(const Reader &ir, const Read
aaa.u_depth = bgfx::createUniform("u_depth", bgfx::UniformType::Sampler);
}
{
aaa.copy_prg = LoadProgram(ir, ip, format("%1/shader/copy").arg(path).c_str());
aaa.u_copyColor = bgfx::createUniform("u_copyColor", bgfx::UniformType::Sampler);
aaa.u_copyDepth = bgfx::createUniform("u_copyDepth", bgfx::UniformType::Sampler);
}
aaa.taa = CreateTAAFromAssets(path);
aaa.bloom = CreateBloomFromAssets(hg::format("%1/shader").arg(path), rb_factory, bgfx::BackbufferRatio::Equal);
@ -268,16 +276,17 @@ static ForwardPipelineAAA _CreateForwardPipelineAAA(const Reader &ir, const Read
bgfx::setName(aaa.work_frame_hdr_fb, "Work HDR frame FB");
bgfx::setName(aaa.prv_frame_hdr_fb, "Previous HDR frame FB");
bgfx::setName(aaa.next_frame_hdr_fb, "Next HDR frame FB");
if (ssgi_ratio != bgfx::BackbufferRatio::Equal) {
if (ssgi_ratio != bgfx::BackbufferRatio::Equal)
bgfx::setName(aaa.ssgi_output.handle, "aaa.ssgi_output");
}
if (ssr_ratio != bgfx::BackbufferRatio::Equal) {
if (ssr_ratio != bgfx::BackbufferRatio::Equal)
bgfx::setName(aaa.ssr_output.handle, "aaa.ssr_output");
}
if (ssgi_ratio != ssr_ratio) {
bgfx::setName(aaa.work[1].handle, "aaa.work[1]");
bgfx::setName(aaa.work_fb[1], "Work FB #1");
}
return aaa;
}
@ -352,6 +361,11 @@ void DestroyForwardPipelineAAA(ForwardPipelineAAA &aaa) {
bgfx_Destroy(aaa.u_color);
bgfx_Destroy(aaa.u_depth);
//
bgfx_Destroy(aaa.copy_prg);
bgfx_Destroy(aaa.u_copyColor);
bgfx_Destroy(aaa.u_copyDepth);
//
DestroyTAA(aaa.taa);
DestroyBloom(aaa.bloom);
@ -456,7 +470,7 @@ std::vector<uint32_t> ComputeModelDisplayListSortKeys(
float closest = std::numeric_limits<float>::max();
for (auto &v : vtx) {
const auto &in_view = v * mdl_view_mtx;
const auto &in_view = mdl_view_mtx * v;
if (in_view.z < closest)
closest = in_view.z;
}
@ -572,6 +586,21 @@ static iRect ScreenSpaceRect(const iRect &rect, bgfx::BackbufferRatio::Enum rati
return rect;
}
static void UpdateForwardPipelineProbe(ForwardPipeline &pipeline, const Probe &probe, TextureRef brdf_map_ref, const PipelineResources &resources) {
auto &brdf_map = resources.textures.Get(brdf_map_ref);
auto &irradiance_map = resources.textures.Get(probe.irradiance_map);
auto &radiance_map = resources.textures.Get(probe.radiance_map);
Mat4 world;
if (probe.type == PT_Cube)
world = TransformationMat4(probe.trs.pos, probe.trs.rot, probe.trs.scl);
else if (probe.type == PT_Sphere)
world = TransformationMat4(probe.trs.pos, probe.trs.rot, {probe.trs.scl.x, probe.trs.scl.x, probe.trs.scl.x});
UpdateForwardPipelineProbe(pipeline, irradiance_map, radiance_map, brdf_map, probe.type, world, unpack_float(probe.parallax));
}
void SubmitSceneToForwardPipeline(bgfx::ViewId &view_id, const Scene &scene, const Rect<int> &rect, const ViewState &view_state, ForwardPipeline &pipeline,
const SceneForwardPipelineRenderData &render_data, const PipelineResources &resources, SceneForwardPipelinePassViewId &views, ForwardPipelineAAA &aaa,
const ForwardPipelineAAAConfig &aaa_config, int frame, uint16_t rb_width, uint16_t rb_height, bgfx::FrameBufferHandle fb, const char *debug_name) {
@ -592,7 +621,8 @@ void SubmitSceneToForwardPipeline(bgfx::ViewId &view_id, const Scene &scene, con
UpdateForwardPipeline(pipeline, render_data.shadow_data, scene.environment.ambient, render_data.pipe_lights, render_data.fog, fb_size);
UpdateForwardPipelineAAA(pipeline, rect, view_state.view, view_state.proj, aaa.prv_view_state.view, aaa.prv_view_state.proj, TAAHaltonJitter8(frame),
aaa.ssgi_ratio, aaa.ssr_ratio, aaa_config.temporal_aa_weight, aaa_config.motion_blur, aaa_config.exposure, aaa_config.gamma, aaa_config.sample_count,
aaa_config.max_distance); // [todo] ssgi_ratio/ssr_ratio
aaa_config.max_distance, aaa_config.specular_weight, aaa_config.sharpen); // [todo] ssgi_ratio/ssr_ratio ([EJ] what is wrong with ssgi_ratio/ssr_ratio)
UpdateForwardPipelineProbe(pipeline, scene.environment.probe, scene.environment.brdf_map, resources);
// TAA jittered projection matrix
const auto jitter = TAAHaltonJitter8(frame) / Vec2(float(GetWidth(rect)), float(GetHeight(rect)));
@ -651,7 +681,7 @@ void SubmitSceneToForwardPipeline(bgfx::ViewId &view_id, const Scene &scene, con
// SSGI
{
auto &probe_map = resources.textures.Get(scene.environment.irradiance_map);
auto &probe_map = resources.textures.Get(scene.environment.probe.irradiance_map);
if (aaa.ssgi_ratio != bgfx::BackbufferRatio::Equal) {
ComputeSSGI(view_id, ScreenSpaceRect(rect, aaa.ssgi_ratio), aaa.ssgi_ratio, aaa.downsample.color, aaa.downsample.attr0,
aaa.attr1, // [todo] downsample attr1 ?!
@ -678,7 +708,8 @@ void SubmitSceneToForwardPipeline(bgfx::ViewId &view_id, const Scene &scene, con
// SSR
{
auto &probe_map = resources.textures.Get(scene.environment.radiance_map);
auto &probe_map = resources.textures.Get(scene.environment.probe.radiance_map);
if (aaa.ssr_ratio != bgfx::BackbufferRatio::Equal) {
ComputeSSR(view_id, ScreenSpaceRect(rect, aaa.ssr_ratio), aaa.ssr_ratio, aaa.downsample.color, aaa.downsample.attr0,
aaa.attr1, // [todo] downsample attr1 ?!
@ -708,10 +739,7 @@ void SubmitSceneToForwardPipeline(bgfx::ViewId &view_id, const Scene &scene, con
#endif
// setup environment for AAA pipeline
{
auto &brdf_map = resources.textures.Get(scene.environment.brdf_map);
UpdateForwardPipelinePBRProbe(pipeline, ssgi_output, ssr_output, brdf_map);
}
UpdateForwardPipelineAAA(pipeline, ssgi_output, ssr_output);
// opaque pass
{
@ -742,12 +770,7 @@ void SubmitSceneToForwardPipeline(bgfx::ViewId &view_id, const Scene &scene, con
}
// setup environment for non AAA pipeline
{
auto &irradiance_map = resources.textures.Get(scene.environment.irradiance_map);
auto &radiance_map = resources.textures.Get(scene.environment.radiance_map);
auto &brdf_map = resources.textures.Get(scene.environment.brdf_map);
UpdateForwardPipelinePBRProbe(pipeline, irradiance_map, radiance_map, brdf_map);
}
// UpdateForwardPipelineProbe(pipeline, scene.environment.probe, scene.environment.brdf_map, resources);
// transparent pass
{
@ -787,7 +810,7 @@ void SubmitSceneToForwardPipeline(bgfx::ViewId &view_id, const Scene &scene, con
aaa_config.bloom_threshold, aaa_config.bloom_bias, aaa_config.bloom_intensity);
// final compositing for presentation (exposure/gamma correction)
{
if (aaa_config.use_tonemapping) {
if (bgfx::isValid(fb))
bgfx::setViewName(view_id, "Final compositing to target framebuffer");
else
@ -819,6 +842,30 @@ void SubmitSceneToForwardPipeline(bgfx::ViewId &view_id, const Scene &scene, con
vtx.Begin(3).SetPos({-1, 1, 0}).SetTexCoord0({0, 1.f - k_y}).End();
DrawTriangles(view_id, {0, 1, 2, 0, 2, 3}, vtx, aaa.compositing_prg, {}, {}, ComputeRenderState(BM_Opaque, DT_Always));
++view_id;
} else {
bgfx::setViewName(view_id, "Copying to back buffer");
bgfx::setViewRect(view_id, 0, 0, fb_size.x, fb_size.y);
bgfx::setViewFrameBuffer(view_id, fb);
bgfx::setViewClear(view_id, BGFX_CLEAR_NONE, 0, 1.f);
bgfx::setViewTransform(view_id, nullptr, nullptr);
bgfx::setTexture(0, aaa.u_copyColor, aaa.frame_hdr.handle, BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP);
bgfx::setTexture(1, aaa.u_copyDepth, aaa.depth.handle, uint32_t(aaa.depth.flags));
bgfx::VertexLayout decl;
decl.begin().add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float).add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float).end();
const float k_y = bgfx::getCaps()->originBottomLeft ? 0.f : 1.f;
Vertices vtx(decl, 4);
vtx.Begin(0).SetPos({-1, -1, 0}).SetTexCoord0({0, k_y}).End();
vtx.Begin(1).SetPos({1, -1, 0}).SetTexCoord0({1, k_y}).End();
vtx.Begin(2).SetPos({1, 1, 0}).SetTexCoord0({1, 1.f - k_y}).End();
vtx.Begin(3).SetPos({-1, 1, 0}).SetTexCoord0({0, 1.f - k_y}).End();
DrawTriangles(view_id, {0, 1, 2, 0, 2, 3}, vtx, aaa.copy_prg, {}, {}, ComputeRenderState(BM_Opaque, DT_Always));
++view_id;
}
}
@ -839,13 +886,7 @@ void SubmitSceneToForwardPipeline(bgfx::ViewId &view_id, const Scene &scene, con
UpdateForwardPipeline(
pipeline, render_data.shadow_data, scene.environment.ambient, render_data.pipe_lights, render_data.fog, hg::iVec2(GetWidth(rect), GetHeight(rect)));
// UpdateForwardPipelineAAA(pipeline, rect, view_state.view, view_state.proj, view_state.view, view_state.proj, {});
{
auto &irradiance_map = resources.textures.Get(scene.environment.irradiance_map);
auto &radiance_map = resources.textures.Get(scene.environment.radiance_map);
auto &brdf_map = resources.textures.Get(scene.environment.brdf_map);
UpdateForwardPipelinePBRProbe(pipeline, irradiance_map, radiance_map, brdf_map);
}
UpdateForwardPipelineProbe(pipeline, scene.environment.probe, scene.environment.brdf_map, resources);
const int pipeline_config_idx = ComputeForwardPipelineConfigurationIdx(FPS_Basic, render_data.pipe_lights);

View File

@ -43,6 +43,10 @@ struct ForwardPipelineAAAConfig {
float bloom_threshold = 5.f, bloom_bias = 0.5f, bloom_intensity = 0.1f;
float motion_blur = 1.f;
float exposure = 1.f, gamma = 2.2f;
float sharpen = 0.1f;
bool use_tonemapping = true;
float specular_weight = 1.f;
ForwardPipelineAAADebugBuffer debug_buffer = FPAAADB_None;
};
@ -131,6 +135,11 @@ struct ForwardPipelineAAA {
bgfx::UniformHandle u_depth;
bgfx::ProgramHandle compositing_prg;
// no compositing
bgfx::UniformHandle u_copyColor;
bgfx::UniformHandle u_copyDepth;
bgfx::ProgramHandle copy_prg;
//
Bloom bloom;
};
@ -194,8 +203,8 @@ void PrepareSceneForwardPipelineViewDependentRenderData(bgfx::ViewId &view_id, c
SceneForwardPipelineRenderData &render_data, const ForwardPipeline &pipeline, const PipelineResources &resources, SceneForwardPipelinePassViewId &views,
const char *debug_name = "scene");
/// Submit a scene to the forward pipeline. Scene render data must be prepared beforehand by calling PrepareSceneForwardPipelineCommonRenderData and
/// PrepareSceneForwardPipelineViewDependentRenderData.
/// Submit a scene to the forward pipeline.
/// Scene render data must be prepared beforehand by calling PrepareSceneForwardPipelineCommonRenderData and PrepareSceneForwardPipelineViewDependentRenderData.
void SubmitSceneToForwardPipeline(bgfx::ViewId &view_id, const Scene &scene, const Rect<int> &rect, const ViewState &view_state, ForwardPipeline &pipeline,
const SceneForwardPipelineRenderData &render_data, const PipelineResources &resources, SceneForwardPipelinePassViewId &views,
bgfx::FrameBufferHandle fb = BGFX_INVALID_HANDLE, const char *debug_name = "scene");

View File

@ -12,6 +12,8 @@
namespace hg {
// [EJ] note: SaveComponent/LoadComponent are friend of class Scene and cannot be made static (breaks clang/gcc builds)
void SaveComponent(const Scene::Transform_ *data_, const Writer &iw, const Handle &h) {
Write(iw, h, data_->TRS); // pos, rot, scl
Write(iw, h, data_->parent.idx);
@ -64,6 +66,13 @@ void SaveComponent(const Scene::RigidBody_ *data_, const Writer &iw, const Handl
Write(iw, h, data_->rolling_friction);
}
void SaveComponent(const Scene::Collision_ *data_, const Writer &iw, const Handle &h) {
Write(iw, h, data_->type);
Write(iw, h, data_->mass);
Write(iw, h, data_->resource_path);
Write(iw, h, data_->trs);
}
void SaveComponent(const Scene::Script_ *data_, const Writer &iw, const Handle &h) {
Write(iw, h, data_->path);
@ -91,6 +100,15 @@ void SaveComponent(const Scene::Instance_ *data_, const Writer &iw, const Handle
Write(iw, h, uint8_t(data_->loop_mode));
}
static void SaveProbe(const Probe &probe, const Writer &iw, const Handle &h, const PipelineResources &resources) {
Write(iw, h, resources.textures.GetName(probe.irradiance_map));
Write(iw, h, resources.textures.GetName(probe.radiance_map));
Write(iw, h, probe.type);
Write(iw, h, probe.parallax);
Write(iw, h, probe.trs);
}
//
void LoadComponent(Scene::Transform_ *data_, const Reader &ir, const Handle &h) {
Read(ir, h, data_->TRS);
@ -104,8 +122,8 @@ void LoadComponent(Scene::Camera_ *data_, const Reader &ir, const Handle &h) {
Read(ir, h, data_->size);
}
void LoadComponent(Scene::Object_ *data_, const Reader &ir, const Handle &h, const Reader &deps_ir, const ReadProvider &deps_ip, PipelineResources &resources,
const PipelineInfo &pipeline, bool queue_model_loads, bool queue_texture_loads, bool do_not_load_resources, bool silent) {
void LoadComponent(Scene::Object_ *data_, const Reader &ir, const Handle &h, const Reader &deps_ir, const ReadProvider &deps_ip,
PipelineResources &resources, const PipelineInfo &pipeline, bool queue_model_loads, bool queue_texture_loads, bool do_not_load_resources, bool silent) {
std::string name;
Read(ir, h, name);
@ -151,6 +169,13 @@ void LoadComponent(Scene::RigidBody_ *data_, const Reader &ir, const Handle &h)
Read(ir, h, data_->rolling_friction);
}
void LoadComponent(Scene::Collision_ *data_, const Reader &ir, const Handle &h) {
Read(ir, h, data_->type);
Read(ir, h, data_->mass);
Read(ir, h, data_->resource_path);
Read(ir, h, data_->trs);
}
void LoadComponent(Scene::Script_ *data_, const Reader &ir, const Handle &h) {
data_->path = Read<std::string>(ir, h);
@ -181,8 +206,33 @@ void LoadComponent(Scene::Instance_ *data_, const Reader &ir, const Handle &h) {
data_->loop_mode = AnimLoopMode(Read<uint8_t>(ir, h));
}
static void LoadProbe(Probe &probe, const Reader &ir, const Handle &h, const Reader &deps_ir, const ReadProvider &deps_ip, PipelineResources &resources,
const PipelineInfo &pipeline, bool queue_texture_loads, bool do_not_load_resources, bool silent) {
std::string tex_name;
Read(ir, h, tex_name);
if (!tex_name.empty())
probe.irradiance_map = SkipLoadOrQueueTextureLoad(deps_ir, deps_ip, tex_name.c_str(), resources, queue_texture_loads, do_not_load_resources, silent);
Read(ir, h, tex_name);
if (!tex_name.empty())
probe.radiance_map = SkipLoadOrQueueTextureLoad(deps_ir, deps_ip, tex_name.c_str(), resources, queue_texture_loads, do_not_load_resources, silent);
Read(ir, h, probe.type);
Read(ir, h, probe.parallax);
Read(ir, h, probe.trs);
}
static void SkipProbe(const Reader &ir, const Handle &h) {
SkipString(ir, h);
SkipString(ir, h);
Skip<ProbeType>(ir, h);
Skip<uint8_t>(ir, h);
Skip<TransformTRS>(ir, h);
}
//
uint32_t GetSceneBinaryFormatVersion() { return 7; }
uint32_t GetSceneBinaryFormatVersion() { return 9; }
bool Scene::Save_binary(
const Writer &iw, const Handle &h, const PipelineResources &resources, uint32_t save_flags, const std::vector<NodeRef> *nodes_to_save) const {
@ -201,6 +251,8 @@ bool Scene::Save_binary(
version 5: save rigid body properties
version 6: remove obsolete rigid properties
version 7: store animation chunk size so that it can be jumped over without parsing its content
version 8: save collision properties
version 9: save environment probe
*/
const auto version = GetSceneBinaryFormatVersion();
Write<uint32_t>(iw, h, version);
@ -232,10 +284,10 @@ bool Scene::Save_binary(
//
std::array<std::set<ComponentRef>, 5> used_component_refs;
std::set<ComponentRef> used_script_refs, used_instance_refs;
std::set<ComponentRef> used_script_refs, used_instance_refs, used_collision_refs;
if (save_flags & LSSF_Nodes)
for (const auto ref : node_refs)
for (const auto &ref : node_refs)
if (const auto *node_ = GetNode_(ref)) {
if (transforms.is_valid(node_->components[NCI_Transform]))
used_component_refs[NCI_Transform].insert(node_->components[NCI_Transform]);
@ -246,122 +298,146 @@ bool Scene::Save_binary(
if (lights.is_valid(node_->components[NCI_Light]))
used_component_refs[NCI_Light].insert(node_->components[NCI_Light]);
if (save_flags & LSSF_Physics)
if (save_flags & LSSF_Physics) {
if (rigid_bodies.is_valid(node_->components[NCI_RigidBody]))
used_component_refs[NCI_RigidBody].insert(node_->components[NCI_RigidBody]);
{
const auto &i = node_collisions.find(ref);
if (i != std::end(node_collisions))
for (const auto &ref : i->second)
used_collision_refs.insert(ref);
}
}
if (save_flags & LSSF_Scripts) {
const auto i = node_scripts.find(ref);
const auto &i = node_scripts.find(ref);
if (i != std::end(node_scripts))
for (const auto ref : i->second)
for (const auto &ref : i->second)
used_script_refs.insert(ref);
}
{
const auto i = node_instance.find(ref);
const auto &i = node_instance.find(ref);
if (i != std::end(node_instance))
used_instance_refs.insert(i->second);
}
}
if (save_flags & LSSF_Scene)
for (auto ref : scene_scripts)
for (auto &ref : scene_scripts)
used_script_refs.insert(ref); // flag scene scripts as in-use
//
Write(iw, h, uint32_t(used_component_refs[NCI_Transform].size()));
for (const auto ref : used_component_refs[NCI_Transform])
Write(iw, h, numeric_cast<uint32_t>(used_component_refs[NCI_Transform].size()));
for (const auto &ref : used_component_refs[NCI_Transform])
if (transforms.is_valid(ref))
SaveComponent(&transforms[ref.idx], iw, h);
Write(iw, h, uint32_t(used_component_refs[NCI_Camera].size()));
for (const auto ref : used_component_refs[NCI_Camera])
Write(iw, h, numeric_cast<uint32_t>(used_component_refs[NCI_Camera].size()));
for (const auto &ref : used_component_refs[NCI_Camera])
if (cameras.is_valid(ref))
SaveComponent(&cameras[ref.idx], iw, h);
Write(iw, h, uint32_t(used_component_refs[NCI_Object].size()));
for (const auto ref : used_component_refs[NCI_Object])
Write(iw, h, numeric_cast<uint32_t>(used_component_refs[NCI_Object].size()));
for (const auto &ref : used_component_refs[NCI_Object])
if (objects.is_valid(ref))
SaveComponent(&objects[ref.idx], iw, h, resources);
Write(iw, h, uint32_t(used_component_refs[NCI_Light].size()));
for (const auto ref : used_component_refs[NCI_Light])
Write(iw, h, numeric_cast<uint32_t>(used_component_refs[NCI_Light].size()));
for (const auto &ref : used_component_refs[NCI_Light])
if (lights.is_valid(ref))
SaveComponent(&lights[ref.idx], iw, h);
if (save_flags & LSSF_Physics) {
Write(iw, h, uint32_t(used_component_refs[NCI_RigidBody].size()));
for (const auto ref : used_component_refs[NCI_RigidBody])
Write(iw, h, numeric_cast<uint32_t>(used_component_refs[NCI_RigidBody].size()));
for (const auto &ref : used_component_refs[NCI_RigidBody])
if (rigid_bodies.is_valid(ref))
SaveComponent(&rigid_bodies[ref.idx], iw, h);
Write(iw, h, numeric_cast<uint32_t>(used_collision_refs.size()));
for (const auto &ref : used_collision_refs)
if (collisions.is_valid(ref))
SaveComponent(&collisions[ref.idx], iw, h);
}
if (save_flags & LSSF_Scripts) {
Write(iw, h, uint32_t(used_script_refs.size()));
for (const auto ref : used_script_refs)
Write(iw, h, numeric_cast<uint32_t>(used_script_refs.size()));
for (const auto &ref : used_script_refs)
if (scripts.is_valid(ref))
SaveComponent(&scripts[ref.idx], iw, h);
}
Write(iw, h, uint32_t(used_instance_refs.size()));
for (const auto ref : used_instance_refs)
Write(iw, h, numeric_cast<uint32_t>(used_instance_refs.size()));
for (const auto &ref : used_instance_refs)
if (instances.is_valid(ref))
SaveComponent(&instances[ref.idx], iw, h);
//
if (save_flags & LSSF_Nodes) {
Write(iw, h, uint32_t(node_refs.size()));
Write(iw, h, numeric_cast<uint32_t>(node_refs.size()));
for (const auto ref : node_refs)
for (const auto &ref : node_refs)
if (const auto *node_ = GetNode_(ref)) {
Write(iw, h, ref.idx);
Write(iw, h, node_->name);
Write(iw, h, node_->flags & NF_SerializedMask);
if (transforms.is_valid(node_->components[NCI_Transform])) {
const auto i = used_component_refs[NCI_Transform].find(node_->components[NCI_Transform]);
Write(iw, h, uint32_t(std::distance(std::begin(used_component_refs[NCI_Transform]), i)));
const auto &i = used_component_refs[NCI_Transform].find(node_->components[NCI_Transform]);
Write(iw, h, numeric_cast<uint32_t>(std::distance(std::begin(used_component_refs[NCI_Transform]), i)));
} else {
Write<uint32_t>(iw, h, 0xffffffff);
}
if (cameras.is_valid(node_->components[NCI_Camera])) {
const auto i = used_component_refs[NCI_Camera].find(node_->components[NCI_Camera]);
Write(iw, h, uint32_t(std::distance(std::begin(used_component_refs[NCI_Camera]), i)));
const auto &i = used_component_refs[NCI_Camera].find(node_->components[NCI_Camera]);
Write(iw, h, numeric_cast<uint32_t>(std::distance(std::begin(used_component_refs[NCI_Camera]), i)));
} else {
Write<uint32_t>(iw, h, 0xffffffff);
}
if (objects.is_valid(node_->components[NCI_Object])) {
const auto i = used_component_refs[NCI_Object].find(node_->components[NCI_Object]);
Write(iw, h, uint32_t(std::distance(std::begin(used_component_refs[NCI_Object]), i)));
const auto &i = used_component_refs[NCI_Object].find(node_->components[NCI_Object]);
Write(iw, h, numeric_cast<uint32_t>(std::distance(std::begin(used_component_refs[NCI_Object]), i)));
} else {
Write<uint32_t>(iw, h, 0xffffffff);
}
if (lights.is_valid(node_->components[NCI_Light])) {
const auto i = used_component_refs[NCI_Light].find(node_->components[NCI_Light]);
Write(iw, h, uint32_t(std::distance(std::begin(used_component_refs[NCI_Light]), i)));
const auto &i = used_component_refs[NCI_Light].find(node_->components[NCI_Light]);
Write(iw, h, numeric_cast<uint32_t>(std::distance(std::begin(used_component_refs[NCI_Light]), i)));
} else {
Write<uint32_t>(iw, h, 0xffffffff);
}
if (save_flags & LSSF_Physics) {
if (rigid_bodies.is_valid(node_->components[NCI_RigidBody])) {
const auto i = used_component_refs[NCI_RigidBody].find(node_->components[NCI_RigidBody]);
Write(iw, h, uint32_t(std::distance(std::begin(used_component_refs[NCI_RigidBody]), i)));
const auto &i = used_component_refs[NCI_RigidBody].find(node_->components[NCI_RigidBody]);
Write(iw, h, numeric_cast<uint32_t>(std::distance(std::begin(used_component_refs[NCI_RigidBody]), i)));
} else {
Write<uint32_t>(iw, h, 0xffffffff);
}
const auto &c = node_collisions.find(ref);
if (c != std::end(node_collisions)) {
Write(iw, h, numeric_cast<uint32_t>(c->second.size())); // collision count
for (const auto &col_ref : c->second) {
const auto &i = used_collision_refs.find(col_ref);
Write(iw, h, numeric_cast<uint32_t>(std::distance(std::begin(used_collision_refs), i))); // idx
}
} else {
Write<uint32_t>(iw, h, 0); // collision count
}
}
if (save_flags & LSSF_Scripts) {
const auto c = node_scripts.find(ref);
const auto &c = node_scripts.find(ref);
if (c != std::end(node_scripts)) {
Write(iw, h, uint32_t(c->second.size())); // script count
for (const auto script_ref : c->second) {
const auto i = used_script_refs.find(script_ref);
Write(iw, h, uint32_t(std::distance(std::begin(used_script_refs), i))); // idx
Write(iw, h, numeric_cast<uint32_t>(c->second.size())); // script count
for (const auto &script_ref : c->second) {
const auto &i = used_script_refs.find(script_ref);
Write(iw, h, numeric_cast<uint32_t>(std::distance(std::begin(used_script_refs), i))); // idx
}
} else {
Write<uint32_t>(iw, h, 0); // script count
@ -371,8 +447,8 @@ bool Scene::Save_binary(
{
const auto c = node_instance.find(ref);
if (c != std::end(node_instance)) {
const auto i = used_instance_refs.find(c->second);
Write(iw, h, uint32_t(std::distance(std::begin(used_instance_refs), i)));
const auto &i = used_instance_refs.find(c->second);
Write(iw, h, numeric_cast<uint32_t>(std::distance(std::begin(used_instance_refs), i)));
} else {
Write(iw, h, InvalidComponentRef.idx);
}
@ -389,8 +465,7 @@ bool Scene::Save_binary(
Write(iw, h, environment.fog_far);
Write(iw, h, environment.fog_color);
Write(iw, h, resources.textures.GetName(environment.irradiance_map));
Write(iw, h, resources.textures.GetName(environment.radiance_map));
SaveProbe(environment.probe, iw, h, resources);
Write(iw, h, resources.textures.GetName(environment.brdf_map));
Write(iw, h, canvas.clear_z);
@ -459,7 +534,7 @@ bool Scene::Save_binary(
if (save_flags & LSSF_KeyValues) {
Write(iw, h, uint32_t(key_values.size()));
for (auto i : key_values) {
for (const auto &i : key_values) {
Write(iw, h, i.first);
Write(iw, h, i.second);
}
@ -477,26 +552,26 @@ bool Scene::Load_binary(const Reader &ir, const Handle &h, const char *name, con
if (!ir.is_valid(h)) {
if (!silent)
error(format("Cannot load scene '%1', invalid read handle").arg(name));
warn(format("Cannot load scene '%1', invalid read handle").arg(name));
return false;
}
if (Read<uint32_t>(ir, h) != HarfangMagic) {
if (!silent)
error(format("Cannot load scene '%1', invalid magic marker").arg(name));
warn(format("Cannot load scene '%1', invalid magic marker").arg(name));
return false;
}
if (Read<uint8_t>(ir, h) != SceneMarker) {
if (!silent)
error(format("Cannot load scene '%1', invalid scene marker").arg(name));
warn(format("Cannot load scene '%1', invalid scene marker").arg(name));
return false;
}
const auto version = Read<uint32_t>(ir, h);
if (version != GetSceneBinaryFormatVersion()) {
if (!silent)
error(format("Cannot load scene '%1', unsupported binary version %2").arg(name).arg(version));
warn(format("Cannot load scene '%1', unsupported binary version %2").arg(name).arg(version));
return false;
}
@ -535,7 +610,7 @@ bool Scene::Load_binary(const Reader &ir, const Handle &h, const char *name, con
LoadComponent(&lights[ref.idx], ir, h);
}
std::vector<ComponentRef> rigid_body_refs;
std::vector<ComponentRef> rigid_body_refs, collision_refs;
if (file_flags & LSSF_Physics) {
const auto rigid_body_count = Read<uint32_t>(ir, h);
rigid_body_refs.resize(rigid_body_count);
@ -543,6 +618,13 @@ bool Scene::Load_binary(const Reader &ir, const Handle &h, const char *name, con
const auto ref = rigid_body_refs[i] = CreateRigidBody().ref;
LoadComponent(&rigid_bodies[ref.idx], ir, h);
}
const auto collision_count = Read<uint32_t>(ir, h);
collision_refs.resize(collision_count);
for (size_t i = 0; i < collision_count; ++i) {
const auto ref = collision_refs[i] = CreateCollision().ref;
LoadComponent(&collisions[ref.idx], ir, h);
}
}
std::vector<ComponentRef> script_refs;
@ -606,6 +688,12 @@ bool Scene::Load_binary(const Reader &ir, const Handle &h, const char *name, con
const auto rigid_body_idx = Read<uint32_t>(ir, h);
if (rigid_body_idx != 0xffffffff)
node_.components[NCI_RigidBody] = rigid_body_refs[rigid_body_idx];
const auto collision_count = Read<uint32_t>(ir, h);
for (uint32_t j = 0; j < collision_count; ++j) {
const auto col_idx = Read<uint32_t>(ir, h);
node_collisions[node_ref].push_back(collision_refs[col_idx]);
}
}
if (file_flags & LSSF_Scripts) {
@ -677,19 +765,11 @@ bool Scene::Load_binary(const Reader &ir, const Handle &h, const char *name, con
Read(ir, h, environment.fog_color); // 16B
{
LoadProbe(environment.probe, ir, h, deps_ir, deps_ip, resources, pipeline, load_flags & LSSF_QueueTextureLoads, load_flags & LSSF_DoNotLoadResources, silent);
std::string name;
Read(ir, h, name);
if (!name.empty())
environment.irradiance_map = SkipLoadOrQueueTextureLoad(
deps_ir, deps_ip, name.c_str(), resources, load_flags & LSSF_QueueTextureLoads, load_flags & LSSF_DoNotLoadResources, silent);
Read(ir, h, name);
if (!name.empty())
environment.radiance_map = SkipLoadOrQueueTextureLoad(
deps_ir, deps_ip, name.c_str(), resources, load_flags & LSSF_QueueTextureLoads, load_flags & LSSF_DoNotLoadResources, silent);
Read(ir, h, name);
if (!name.empty())
environment.brdf_map = SkipLoadOrQueueTextureLoad(
deps_ir, deps_ip, name.c_str(), resources, load_flags & LSSF_QueueTextureLoads, load_flags & LSSF_DoNotLoadResources, silent);
@ -704,8 +784,7 @@ bool Scene::Load_binary(const Reader &ir, const Handle &h, const char *name, con
Seek(ir, h, 40, SM_Current); // skip environment chunk
SkipString(ir, h); // irradiance name
SkipString(ir, h); // radiance name
SkipProbe(ir, h); // probe
SkipString(ir, h); // brdf name
Seek(ir, h, sizeof(bool) * 2 + 16, SM_Current);
@ -821,7 +900,7 @@ bool Scene::Load_binary(const Reader &ir, const Handle &h, const char *name, con
//
if (!silent)
debug(format("Load scene '%1' took %2 ms").arg(name).arg(time_to_ms(time_now() - t_start)));
log(format("Load scene '%1' took %2 ms").arg(name).arg(time_to_ms(time_now() - t_start)));
return true;
}

View File

@ -7,13 +7,16 @@
#include "foundation/format.h"
#include "foundation/log.h"
#include <foundation/string.h>
#include "foundation/matrix4.h"
#include "foundation/string.h"
#include <json/json.hpp>
#include <set>
namespace hg {
// [EJ] note: SaveComponent/LoadComponent are friend of class Scene and cannot be made static (breaks clang/gcc builds)
void SaveComponent(const Scene::Transform_ *data_, json &js) {
js["pos"] = data_->TRS.pos;
js["rot"] = RadianToDegree(data_->TRS.rot);
@ -48,7 +51,7 @@ void SaveComponent(const Scene::Object_ *data_, json &js, const PipelineResource
js["material_infos"] = material_infos;
json bones = json::array();
for (auto bone : data_->bones)
for (const auto &bone : data_->bones)
bones.push_back(bone);
js["bones"] = bones;
}
@ -77,10 +80,19 @@ void SaveComponent(const Scene::RigidBody_ *data_, json &js) {
js["rolling_friction"] = unpack_float(data_->rolling_friction);
}
void SaveComponent(const Scene::Collision_ *data_, json &js) {
js["type"] = data_->type;
js["mass"] = data_->mass;
js["path"] = data_->resource_path;
js["pos"] = data_->trs.pos;
js["rot"] = data_->trs.rot;
js["scl"] = data_->trs.scl;
}
void SaveComponent(const Scene::Script_ *data_, json &js) {
js["path"] = data_->path;
for (auto &i : data_->params) {
for (const auto &i : data_->params) {
if (i.second.type == SPT_Bool)
js["parameters"][i.first] = {{"type", "bool"}, {"value", i.second.bv}};
else if (i.second.type == SPT_Int)
@ -101,12 +113,16 @@ void SaveComponent(const Scene::Instance_ *data_, json &js) {
}
}
void SaveComponent(const Scene::Collision_ *data_, json &js) {
js["type"] = data_->type;
js["mass"] = data_->mass;
js["size"] = data_->size;
js["path"] = data_->resource_path;
js["m"] = data_->m;
static void SaveProbe(const Probe &probe, json &js, const PipelineResources &resources) {
js["irradiance_map"] = resources.textures.GetName(probe.irradiance_map);
js["radiance_map"] = resources.textures.GetName(probe.radiance_map);
js["type"] = probe.type;
js["parallax"] = unpack_float(probe.parallax);
js["pos"] = probe.trs.pos;
js["rot"] = probe.trs.rot;
js["scl"] = probe.trs.scl;
}
//
@ -132,7 +148,7 @@ void LoadComponent(Scene::Object_ *data_, const json &js, const Reader &deps_ir,
if (!name.empty())
data_->model = SkipLoadOrQueueModelLoad(deps_ir, deps_ip, name.c_str(), resources, queue_model_loads, do_not_load_resources);
const auto i_mats = js.find("materials");
const auto &i_mats = js.find("materials");
if (i_mats != std::end(js)) {
const auto &mats = *i_mats;
const auto mat_count = mats.size();
@ -141,7 +157,7 @@ void LoadComponent(Scene::Object_ *data_, const json &js, const Reader &deps_ir,
data_->materials[i] = LoadMaterial(mats[i], deps_ir, deps_ip, resources, pipeline, queue_texture_loads, do_not_load_resources);
}
const auto i_mat_infos = js.find("material_infos");
const auto &i_mat_infos = js.find("material_infos");
if (i_mat_infos != std::end(js)) {
const auto &mat_infos = *i_mat_infos;
const auto mat_count = data_->materials.size();
@ -153,7 +169,7 @@ void LoadComponent(Scene::Object_ *data_, const json &js, const Reader &deps_ir,
}
}
const auto i_bones = js.find("bones");
const auto &i_bones = js.find("bones");
if (i_bones != std::end(js)) {
const auto &bones = *i_bones;
const auto bone_count = bones.size();
@ -197,6 +213,15 @@ void LoadComponent(Scene::RigidBody_ *data_, const json &js) {
data_->rolling_friction = pack_float<uint8_t>(js.at("rolling_friction").get<float>());
}
void LoadComponent(Scene::Collision_ *data_, const json &js) {
data_->type = js["type"];
data_->mass = js["mass"];
data_->resource_path = js["path"].get<std::string>();
data_->trs.pos = js["pos"].get<Vec3>();
data_->trs.rot = js["rot"].get<Vec3>();
data_->trs.scl = js["scl"].get<Vec3>();
}
void LoadComponent(Scene::Script_ *data_, const json &js) {
data_->path = js["path"].get<std::string>();
@ -236,12 +261,20 @@ void LoadComponent(Scene::Instance_ *data_, const json &js) {
}
}
void LoadComponent(Scene::Collision_ *data_, const json &js) {
data_->type = js["type"];
data_->mass = js["mass"];
data_->size = js["size"];
data_->resource_path = js["path"].get<std::string>();
data_->m = js["m"];
static void LoadProbe(Probe &probe, const json &js, const Reader &deps_ir, const ReadProvider &deps_ip, PipelineResources &resources, bool queue_texture_loads,
bool do_not_load_resources, bool silent) {
const auto irradiance_map = js["irradiance_map"].get<std::string>();
const auto radiance_map = js["radiance_map"].get<std::string>();
probe.irradiance_map = SkipLoadOrQueueTextureLoad(deps_ir, deps_ip, irradiance_map.c_str(), resources, queue_texture_loads, do_not_load_resources, silent);
probe.radiance_map = SkipLoadOrQueueTextureLoad(deps_ir, deps_ip, radiance_map.c_str(), resources, queue_texture_loads, do_not_load_resources, silent);
probe.type = js["type"];
probe.parallax = pack_float<uint8_t>(js["parallax"].get<float>());
probe.trs.pos = js["pos"].get<Vec3>();
probe.trs.rot = js["rot"].get<Vec3>();
probe.trs.scl = js["scl"].get<Vec3>();
}
//
@ -271,10 +304,10 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
//
std::array<std::set<ComponentRef>, 5> used_component_refs;
std::set<ComponentRef> used_script_refs, used_instance_refs;
std::set<ComponentRef> used_script_refs, used_instance_refs, used_collision_refs;
if (save_flags & LSSF_Nodes)
for (const auto ref : node_refs)
for (const auto &ref : node_refs)
if (const auto *node_ = GetNode_(ref)) {
if (transforms.is_valid(node_->components[NCI_Transform]))
used_component_refs[NCI_Transform].insert(node_->components[NCI_Transform]);
@ -287,10 +320,17 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
if (rigid_bodies.is_valid(node_->components[NCI_RigidBody]))
used_component_refs[NCI_RigidBody].insert(node_->components[NCI_RigidBody]);
{
const auto &i = node_collisions.find(ref);
if (i != std::end(node_collisions))
for (const auto &col_ref : i->second)
used_collision_refs.insert(col_ref);
}
{
const auto &i = node_scripts.find(ref);
if (i != std::end(node_scripts))
for (const auto script_ref : i->second)
for (const auto &script_ref : i->second)
used_script_refs.insert(script_ref);
}
@ -302,13 +342,13 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
}
if (save_flags & LSSF_Scene)
for (auto ref : scene_scripts)
for (auto &ref : scene_scripts)
used_script_refs.insert(ref); // flag scene scripts as in-use
//
if (!used_component_refs[NCI_Transform].empty()) {
auto &js_trsf = js["transforms"];
for (const auto ref : used_component_refs[NCI_Transform]) {
for (const auto &ref : used_component_refs[NCI_Transform]) {
if (transforms.is_valid(ref)) {
json js_comp;
SaveComponent(&transforms[ref.idx], js_comp);
@ -319,7 +359,7 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
if (!used_component_refs[NCI_Camera].empty()) {
auto &js_cams = js["cameras"];
for (const auto ref : used_component_refs[NCI_Camera]) {
for (const auto &ref : used_component_refs[NCI_Camera]) {
if (cameras.is_valid(ref)) {
json js_comp;
SaveComponent(&cameras[ref.idx], js_comp);
@ -330,7 +370,7 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
if (!used_component_refs[NCI_Object].empty()) {
auto &js_objs = js["objects"];
for (const auto ref : used_component_refs[NCI_Object]) {
for (const auto &ref : used_component_refs[NCI_Object]) {
if (objects.is_valid(ref)) {
json js_comp;
SaveComponent(&objects[ref.idx], js_comp, resources);
@ -341,7 +381,7 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
if (!used_component_refs[NCI_Light].empty()) {
auto &js_lgts = js["lights"];
for (const auto ref : used_component_refs[NCI_Light]) {
for (const auto &ref : used_component_refs[NCI_Light]) {
if (lights.is_valid(ref)) {
json js_comp;
SaveComponent(&lights[ref.idx], js_comp);
@ -352,7 +392,7 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
if (!used_component_refs[NCI_RigidBody].empty()) {
auto &js_bodies = js["rigid_bodies"];
for (const auto ref : used_component_refs[NCI_RigidBody]) {
for (const auto &ref : used_component_refs[NCI_RigidBody]) {
if (rigid_bodies.is_valid(ref)) {
json js_comp;
SaveComponent(&rigid_bodies[ref.idx], js_comp);
@ -361,9 +401,20 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
}
}
if (!used_collision_refs.empty()) {
auto &js_cols = js["collisions"];
for (const auto &ref : used_collision_refs) {
if (collisions.is_valid(ref)) {
json js_comp;
SaveComponent(&collisions[ref.idx], js_comp);
js_cols.push_back(js_comp);
}
}
}
if (!used_script_refs.empty()) {
auto &js_scripts = js["scripts"];
for (const auto ref : used_script_refs) {
for (const auto &ref : used_script_refs) {
if (scripts.is_valid(ref)) {
json js_comp;
SaveComponent(&scripts[ref.idx], js_comp);
@ -374,7 +425,7 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
if (!used_instance_refs.empty()) {
auto &js_inss = js["instances"];
for (const auto ref : used_instance_refs) {
for (const auto &ref : used_instance_refs) {
if (instances.is_valid(ref)) {
json js_comp;
SaveComponent(&instances[ref.idx], js_comp);
@ -424,14 +475,31 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
js_node["components"] = idxs;
{
const auto &c = node_collisions.find(ref);
if (c != std::end(node_collisions)) {
auto &js_node_cols = js_node["collisions"];
for (const auto col_ref : c->second) {
const auto &i = used_collision_refs.find(col_ref);
json col;
col["idx"] = std::distance(std::begin(used_collision_refs), i);
js_node_cols.push_back(col);
}
}
}
{
const auto &c = node_scripts.find(ref);
if (c != std::end(node_scripts)) {
auto &js_node_scripts = js_node["scripts"];
for (const auto script_ref : c->second) {
const auto i = used_script_refs.find(script_ref);
for (const auto &script_ref : c->second) {
const auto &i = used_script_refs.find(script_ref);
json script;
script["idx"] = std::distance(std::begin(used_script_refs), i);
@ -444,7 +512,7 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
{
const auto &c = node_instance.find(ref);
if (c != std::end(node_instance)) {
const auto i = used_instance_refs.find(c->second);
const auto &i = used_instance_refs.find(c->second);
js_node["instance"] = std::distance(std::begin(used_instance_refs), i);
}
}
@ -464,10 +532,8 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
env["fog_far"] = environment.fog_far;
env["fog_color"] = environment.fog_color;
if (environment.irradiance_map != InvalidTextureRef)
env["irradiance_map"] = resources.textures.GetName(environment.irradiance_map);
if (environment.radiance_map != InvalidTextureRef)
env["radiance_map"] = resources.textures.GetName(environment.radiance_map);
SaveProbe(environment.probe, env["probe"], resources);
if (environment.brdf_map != InvalidTextureRef)
env["brdf_map"] = resources.textures.GetName(environment.brdf_map);
@ -535,7 +601,7 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
if (save_flags & LSSF_KeyValues) {
auto &kv = js["key_values"];
for (auto i : key_values)
for (const auto &i : key_values)
kv[i.first] = i.second;
}
@ -545,7 +611,7 @@ bool Scene::Save_json(json &js, const PipelineResources &resources, uint32_t sav
bool Scene::Load_json(const json &js, const char *name, const Reader &deps_ir, const ReadProvider &deps_ip, PipelineResources &resources,
const PipelineInfo &pipeline, LoadSceneContext &ctx, uint32_t load_flags) {
if (js.empty()) {
error(format("Cannot load scene '%1', empty JSON").arg(name));
warn(format("Cannot load scene '%1', empty JSON").arg(name));
return false;
}
@ -628,6 +694,21 @@ bool Scene::Load_json(const json &js, const char *name, const Reader &deps_ir, c
}
}
std::vector<ComponentRef> col_refs;
{
const auto &i = js.find("collisions");
if (i != std::end(js)) {
const auto col_count = i->size();
col_refs.resize(col_count);
int n = 0;
for (const auto &j : *i) {
const auto ref = col_refs[n++] = CreateCollision().ref;
LoadComponent(&collisions[ref.idx], j);
}
}
}
std::vector<ComponentRef> script_refs;
{
const auto &i = js.find("scripts");
@ -707,13 +788,32 @@ bool Scene::Load_json(const json &js, const char *name, const Reader &deps_ir, c
nodes[node.ref.idx].components[NCI_RigidBody] = rigid_body_refs[rigid_body_idx];
}
{
const auto &js_cols = js_node.find("collisions");
if (js_cols != std::end(js_node)) {
const auto node_col_count = js_cols->size();
node_collisions[node.ref].reserve(node_col_count);
for (const auto &js_col : *js_cols) {
const auto col_idx = js_col["idx"].get<ComponentRef>().idx;
if (col_idx != 0xffffffff) {
const auto col_ref = col_refs[col_idx];
node_collisions[node.ref].push_back(col_ref);
} else {
node_collisions[node.ref].push_back(InvalidComponentRef);
}
}
}
}
{
const auto &js_scripts = js_node.find("scripts");
if (js_scripts != std::end(js_node)) {
const auto node_script_count = js_scripts->size();
node_scripts[node.ref].reserve(node_script_count);
for (auto &js_script : *js_scripts) {
for (const auto &js_script : *js_scripts) {
const auto script_idx = js_script["idx"].get<ComponentRef>().idx;
if (script_idx != 0xffffffff) {
@ -785,20 +885,37 @@ bool Scene::Load_json(const json &js, const char *name, const Reader &deps_ir, c
environment.fog_far = (*js_env)["fog_far"];
environment.fog_color = (*js_env)["fog_color"];
auto i = js_env->find("irradiance_map");
if (i != std::end(*js_env))
environment.irradiance_map = SkipLoadOrQueueTextureLoad(
deps_ir, deps_ip, i->get<std::string>().c_str(), resources, load_flags & LSSF_QueueTextureLoads, load_flags & LSSF_DoNotLoadResources);
{
const auto &i = js_env->find("probe");
i = js_env->find("radiance_map");
if (i != std::end(*js_env))
environment.radiance_map = SkipLoadOrQueueTextureLoad(
deps_ir, deps_ip, i->get<std::string>().c_str(), resources, load_flags & LSSF_QueueTextureLoads, load_flags & LSSF_DoNotLoadResources);
if (i != std::end(*js_env)) {
LoadProbe(environment.probe, *i, deps_ir, deps_ip, resources, load_flags & LSSF_QueueTextureLoads, load_flags & LSSF_DoNotLoadResources,
load_flags & LSSF_Silent);
} else {
environment.probe = {};
i = js_env->find("brdf_map");
if (i != std::end(*js_env))
environment.brdf_map = SkipLoadOrQueueTextureLoad(
deps_ir, deps_ip, i->get<std::string>().c_str(), resources, load_flags & LSSF_QueueTextureLoads, load_flags & LSSF_DoNotLoadResources);
{
const auto &i = js_env->find("irradiance_map");
if (i != std::end(*js_env))
environment.probe.irradiance_map = SkipLoadOrQueueTextureLoad(deps_ir, deps_ip, i->get<std::string>().c_str(), resources,
load_flags & LSSF_QueueTextureLoads, load_flags & LSSF_DoNotLoadResources, load_flags & LSSF_Silent);
}
{
const auto &i = js_env->find("radiance_map");
if (i != std::end(*js_env))
environment.probe.radiance_map = SkipLoadOrQueueTextureLoad(deps_ir, deps_ip, i->get<std::string>().c_str(), resources,
load_flags & LSSF_QueueTextureLoads, load_flags & LSSF_DoNotLoadResources, load_flags & LSSF_Silent);
}
}
}
{
const auto &i = js_env->find("brdf_map");
if (i != std::end(*js_env))
environment.brdf_map = SkipLoadOrQueueTextureLoad(deps_ir, deps_ip, i->get<std::string>().c_str(), resources,
load_flags & LSSF_QueueTextureLoads, load_flags & LSSF_DoNotLoadResources, load_flags & LSSF_Silent);
}
}
}
@ -878,7 +995,7 @@ bool Scene::Load_json(const json &js, const char *name, const Reader &deps_ir, c
const auto &js_key_values = js.find("key_values");
if (js_key_values != std::end(js))
for (auto i : js_key_values->items())
for (const auto &i : js_key_values->items())
key_values[i.key()] = i.value().get<std::string>();
}

View File

@ -84,7 +84,8 @@ bool SceneLuaVM::CreateScriptFromSource(Scene &scene, ComponentRef ref, const st
for (auto i : scene.GetScriptParams(ref))
SetScriptValue(ref, i.first, LuaObjectFromScriptParam(L, i.second));
} else {
error(format("Failed to load Lua file '%1'").arg(path));
warn(format("Failed to load Lua file '%1'").arg(path));
return false;
}
return true;

View File

@ -99,7 +99,7 @@ SRanipalState SRanipalGetState() {
namespace hg {
bool SRanipalInit() {
error("SRanipal eye-tracking support DISABLED when building Harfang");
warn("SRanipal eye-tracking support DISABLED when building Harfang");
return false;
}

View File

@ -9,6 +9,7 @@ namespace hg {
NLOHMANN_JSON_SERIALIZE_ENUM(LightType, {{LT_Point, "point"}, {LT_Spot, "spot"}, {LT_Linear, "linear"}});
NLOHMANN_JSON_SERIALIZE_ENUM(LightShadowType, {{LST_None, "none"}, {LST_Map, "map"}});
NLOHMANN_JSON_SERIALIZE_ENUM(AnimLoopMode, {{ALM_Once, "none"}, {ALM_Infinite, "infinite"}, {ALM_Loop, "loop"}});
NLOHMANN_JSON_SERIALIZE_ENUM(ProbeType, {{PT_Sphere, "sphere"}, {PT_Cube, "cube"}});
inline void to_json(json &j, const Vec2 &v) { j = {v.x, v.y}; }
inline void from_json(const json &j, Vec2 &v) { v = {j.at(0), j.at(1)}; }

16
harfang/engine/vertex.cpp Normal file
View File

@ -0,0 +1,16 @@
// HARFANG(R) Copyright (C) 2021 Emmanuel Julien, NWNC HARFANG. Released under GPL/LGPL/Commercial Licence, see licence.txt for details.
#include "engine/vertex.h"
namespace hg {
Vertex MakeVertex(const Vec3 &pos, const Vec3 &nrm, const Vec2 &uv0, const Color &color0) {
Vertex vtx;
vtx.pos = pos;
vtx.normal = nrm;
vtx.uv0 = uv0;
vtx.color0 = color0;
return vtx;
}
} // namespace hg

View File

@ -22,4 +22,6 @@ struct Vertex {
float weight[4];
};
Vertex MakeVertex(const Vec3 &pos, const Vec3 &nrm = {0.f, 1.f, 0.f}, const Vec2 &uv = {0.f, 0.f}, const Color &color = {1.f, 1.f, 1.f, 1.f});
} // namespace hg

View File

@ -50,7 +50,7 @@ IVideoStreamer MakeVideoStreamer(const SharedLib &h) {
for (int i = 0; i<VS_BF_Count; i++) {
base_function_handles[i] = GetFunctionPointer(h, base_function_names[i]);
if (!base_function_handles[i]) {
error(format("failed to load %1 video stream function").arg(base_function_names[i]));
warn(format("failed to load %1 video stream function").arg(base_function_names[i]));
return streamer;
}
}
@ -145,13 +145,13 @@ bool UpdateTexture(IVideoStreamer &streamer, VideoStreamHandle &handle, hg::Text
int id = streamer.GetFrame(handle, &data, &width, &height, &pitch, &video_fmt);
if (id == 0) {
error("unable to get video frame");
warn("Unable to get video frame");
return false;
}
bgfx::TextureFormat::Enum fmt = VideoFrameTextureFormat(video_fmt);
if (fmt == bgfx::TextureFormat::Unknown) {
error(format("unsupported video frame format (%1)").arg(video_fmt));
warn(format("Unsupported video frame format (%1)").arg(video_fmt));
streamer.FreeFrame(handle, id);
return false;
}

View File

@ -1,14 +1,32 @@
add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/build_info.cpp
COMMAND ${Python3_EXECUTABLE} gen_build_info_cpp.py --host_prefix=${HG_HOST_PREFIX} --target_prefix=${HG_TARGET_PREFIX}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../version.txt
COMMENT "Generating build_info.cpp"
)
if(NOT DEFINED HG_COMMIT_ID)
if(NOT DEFINED GIT_EXECUTABLE)
find_package(Git QUIET REQUIRED)
endif()
add_custom_target(build_info ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/build_info.cpp)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/build_info.cpp PROPERTIES GENERATED TRUE)
set_target_properties(build_info PROPERTIES FOLDER "harfang")
if(NOT DEFINED GIT_EXECUTABLE)
message(FATAL_ERROR "Unable to determine GIT filepath.")
endif()
get_filename_component(GIT_EXECUTABLE ${GIT_EXECUTABLE} ABSOLUTE)
if(DEFINED GIT_EXECUTABLE)
execute_process(COMMAND
"${GIT_EXECUTABLE}" rev-parse HEAD
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE exit_code
OUTPUT_VARIABLE HG_COMMIT_ID
ERROR_VARIABLE stderr
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT exit_code EQUAL 0)
set(HG_COMMIT_ID "unknown")
endif()
else()
set(HG_COMMIT_ID "unknown")
endif()
endif()
configure_file(build_info.cpp.in ${CMAKE_CURRENT_SOURCE_DIR}/build_info.cpp)
set(HDRS
ascii_encoder.h
@ -142,7 +160,6 @@ set(SRCS
xxhash.c)
add_library(foundation STATIC ${SRCS} ${HDRS})
add_dependencies(foundation build_info)
set_property(TARGET foundation PROPERTY PUBLIC_HEADER "${HDRS}")
set_target_properties(foundation PROPERTIES FOLDER "harfang")

View File

@ -167,7 +167,7 @@ size_t UUDecode(const void *in, size_t len, void *out, size_t max) {
size_t yEncode(const void *in, size_t len, void *out, size_t max, int line_len) {
const auto *p_in = reinterpret_cast<const uint8_t *>(in);
if (line_len <= 0)
__ERR__(error("Invalid line-feed size for yEncoding"), 0);
__ERR__(warn("Invalid line-feed size for yEncoding"), 0);
size_t olen = 0;
auto cchr = line_len;

View File

@ -0,0 +1,16 @@
// HARFANG(R) Copyright (C) 2022 Emmanuel Julien, NWNC HARFANG. Released under GPL/LGPL/Commercial Licence, see licence.txt for details.
// This file is generated by the build system
#include "foundation/build_info.h"
namespace hg {
const char *get_version_string() { return "@HG_VERSION@"; }
const char *get_build_sha() { return "@HG_COMMIT_ID@"; }
const char *get_host_string() { return "@HG_HOST_PREFIX@"; }
const char *get_target_string() { return "@HG_TARGET_PREFIX@"; }
} // namespace hg

View File

@ -1,5 +1,4 @@
// build_info.cpp is generated by the build system using gen_build_info_cpp.py
// HARFANG(R) Copyright (C) 2022 Emmanuel Julien, NWNC HARFANG. Released under GPL/LGPL/Commercial Licence, see licence.txt for details.
#pragma once
namespace hg {

View File

@ -115,9 +115,11 @@ float ColorToGrayscale(const Color &c);
/// Return the color object as an RGBA value.
uint32_t ColorToRGBA32(const Color &c);
/// Return a 32 bit ABGR integer from a color.
uint32_t ColorToABGR32(const Color &c);
/// Load the color object from an RGBA value.
/// Create a color from a 32 bit RGBA integer.
Color ColorFromRGBA32(unsigned int rgba32);
/// Create a color from a 32 bit ABGR integer.
Color ColorFromABGR32(unsigned int abgr32);
/// Convert from ARGB to RGBA.
@ -153,10 +155,12 @@ Color ColorFromVector3(const Vec3 &);
struct Vec4;
Color ColorFromVector4(const Vec4 &);
/// Create a color from integer values in the [0;255] range.
inline Color ColorI(int r, int g, int b, int a = 255) { return {float(r) / 255.f, float(g) / 255.f, float(b) / 255.f, float(a) / 255.f}; }
//
/// Convert input RGBA color to hue/luminance/saturation, alpha channel is left unmodified.
Color ToHLS(const Color &);
/// Convert input hue/luminance/saturation color to RGBA, alpha channel is left unmodified.
Color FromHLS(const Color &);
Color SetHue(const Color &c, float h);

View File

@ -23,6 +23,7 @@ Data &Data::operator=(const Data &data) {
return *this;
}
/*
Data &Data::operator=(Data &&data) {
Free();
@ -38,6 +39,7 @@ Data &Data::operator=(Data &&data) {
return *this;
}
*/
void Data::Reserve(size_t size) {
const auto new_capacity = (size / 8192 + 1) * 8192; // grow in 8KB increments

Some files were not shown because too many files have changed in this diff Show More