summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYRIGHT.txt5
-rw-r--r--SConstruct2
-rw-r--r--core/SCsub84
-rw-r--r--core/bind/SCsub2
-rw-r--r--core/io/SCsub3
-rw-r--r--core/io/http_client.cpp4
-rw-r--r--core/io/marshalls.cpp1
-rw-r--r--core/math/SCsub2
-rw-r--r--core/object.cpp2
-rw-r--r--core/object.h5
-rw-r--r--core/os/SCsub2
-rw-r--r--core/os/dir_access.cpp1
-rw-r--r--core/os/file_access.cpp1
-rw-r--r--core/script_debugger_remote.cpp6
-rw-r--r--doc/classes/@GlobalScope.xml4
-rw-r--r--doc/classes/ArrayMesh.xml17
-rw-r--r--doc/classes/InputEventMouseButton.xml2
-rw-r--r--doc/classes/PhysicsServer.xml4
-rw-r--r--doc/classes/float.xml2
-rw-r--r--drivers/SCsub3
-rw-r--r--drivers/alsa/SCsub2
-rw-r--r--drivers/alsamidi/SCsub2
-rw-r--r--drivers/convex_decomp/SCsub5
-rw-r--r--drivers/coreaudio/SCsub2
-rw-r--r--drivers/coremidi/SCsub2
-rw-r--r--drivers/dummy/rasterizer_dummy.h3
-rw-r--r--drivers/gl_context/SCsub7
-rw-r--r--drivers/gles2/SCsub2
-rw-r--r--drivers/gles2/rasterizer_gles2.h2
-rw-r--r--drivers/gles2/rasterizer_scene_gles2.cpp546
-rw-r--r--drivers/gles2/rasterizer_scene_gles2.h42
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.cpp380
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.h50
-rw-r--r--drivers/gles2/shader_compiler_gles2.cpp6
-rw-r--r--drivers/gles2/shader_gles2.h13
-rw-r--r--drivers/gles2/shaders/cubemap_filter.glsl1
-rw-r--r--drivers/gles2/shaders/scene.glsl384
-rw-r--r--drivers/gles3/rasterizer_gles3.h2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp7
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp19
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h1
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp12
-rw-r--r--drivers/gles3/shaders/scene.glsl7
-rw-r--r--drivers/png/SCsub16
-rw-r--r--drivers/pulseaudio/SCsub2
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp5
-rw-r--r--drivers/rtaudio/SCsub5
-rw-r--r--drivers/unix/SCsub2
-rw-r--r--drivers/unix/net_socket_posix.cpp8
-rw-r--r--drivers/unix/net_socket_posix.h3
-rw-r--r--drivers/unix/socket_helpers.h156
-rw-r--r--drivers/wasapi/SCsub2
-rw-r--r--drivers/windows/SCsub2
-rw-r--r--drivers/winmidi/SCsub2
-rw-r--r--drivers/xaudio2/SCsub2
-rw-r--r--drivers/zlib/SCsub26
-rw-r--r--editor/SCsub3
-rw-r--r--editor/collada/SCsub2
-rw-r--r--editor/doc/SCsub2
-rw-r--r--editor/editor_file_dialog.cpp9
-rw-r--r--editor/editor_inspector.cpp3
-rw-r--r--editor/editor_log.cpp36
-rw-r--r--editor/editor_node.cpp14
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_properties.cpp13
-rw-r--r--editor/editor_properties.h2
-rw-r--r--editor/editor_properties_array_dict.cpp4
-rw-r--r--editor/fileserver/SCsub2
-rw-r--r--editor/filesystem_dock.cpp66
-rw-r--r--editor/filesystem_dock.h2
-rw-r--r--editor/icons/SCsub4
-rw-r--r--editor/import/SCsub2
-rw-r--r--editor/plugins/SCsub2
-rw-r--r--editor/plugins/baked_lightmap_editor_plugin.cpp1
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp1
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp8
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp4
-rw-r--r--gles_builders.py5
-rw-r--r--main/SCsub3
-rw-r--r--main/tests/SCsub4
-rw-r--r--main/tests/test_gdscript.cpp3
-rw-r--r--main/tests/test_shader_lang.cpp3
-rw-r--r--methods.py8
-rw-r--r--modules/SCsub2
-rw-r--r--modules/bullet/SCsub6
-rw-r--r--modules/bullet/area_bullet.cpp7
-rw-r--r--modules/bullet/collision_object_bullet.cpp4
-rw-r--r--modules/bullet/godot_ray_world_algorithm.cpp2
-rw-r--r--modules/bullet/godot_result_callbacks.h15
-rw-r--r--modules/bullet/hinge_joint_bullet.cpp5
-rw-r--r--modules/bullet/rigid_body_bullet.cpp24
-rw-r--r--modules/bullet/slider_joint_bullet.cpp1
-rw-r--r--modules/bullet/soft_body_bullet.cpp8
-rw-r--r--modules/bullet/space_bullet.cpp5
-rw-r--r--modules/bullet/space_bullet.h2
-rw-r--r--modules/cvtt/SCsub5
-rw-r--r--modules/enet/SCsub5
-rw-r--r--modules/etc/SCsub16
-rw-r--r--modules/etc/image_etc.cpp14
-rw-r--r--modules/freetype/SCsub2
-rw-r--r--modules/gdnative/SCsub29
-rw-r--r--modules/gdnative/arvr/SCsub11
-rw-r--r--modules/gdnative/nativescript/SCsub8
-rw-r--r--modules/gdnative/nativescript/nativescript.h4
-rw-r--r--modules/gdnative/net/SCsub9
-rw-r--r--modules/gdnative/pluginscript/SCsub7
-rw-r--r--modules/gdscript/SCsub2
-rw-r--r--modules/gdscript/gdscript.cpp1
-rw-r--r--modules/gdscript/gdscript.h4
-rw-r--r--modules/gdscript/gdscript_editor.cpp2
-rw-r--r--modules/gdscript/gdscript_parser.cpp14
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gridmap/SCsub2
-rw-r--r--modules/jpg/SCsub5
-rwxr-xr-xmodules/mbedtls/SCsub6
-rw-r--r--modules/mobile_vr/SCsub3
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp6
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h6
-rw-r--r--modules/mobile_vr/shaders/SCsub1
-rw-r--r--modules/ogg/SCsub5
-rw-r--r--modules/opensimplex/SCsub20
-rw-r--r--modules/opensimplex/doc_classes/NoiseTexture.xml8
-rw-r--r--modules/opensimplex/doc_classes/OpenSimplexNoise.xml (renamed from modules/opensimplex/doc_classes/SimplexNoise.xml)8
-rw-r--r--modules/opensimplex/noise_texture.cpp8
-rw-r--r--modules/opensimplex/noise_texture.h8
-rw-r--r--modules/opensimplex/open_simplex_noise.cpp (renamed from modules/opensimplex/simplex_noise.cpp)68
-rw-r--r--modules/opensimplex/open_simplex_noise.h (renamed from modules/opensimplex/simplex_noise.h)18
-rw-r--r--modules/opensimplex/register_types.cpp4
-rw-r--r--modules/opus/SCsub12
-rw-r--r--modules/pvr/SCsub5
-rw-r--r--modules/recast/SCsub7
-rw-r--r--modules/regex/SCsub17
-rw-r--r--modules/squish/SCsub5
-rw-r--r--modules/svg/SCsub7
-rw-r--r--modules/thekla_unwrap/SCsub9
-rw-r--r--modules/thekla_unwrap/config.py3
-rw-r--r--modules/theora/SCsub5
-rw-r--r--modules/tinyexr/SCsub5
-rw-r--r--modules/upnp/SCsub6
-rw-r--r--modules/visual_script/SCsub2
-rw-r--r--modules/vorbis/SCsub5
-rw-r--r--modules/webm/SCsub15
-rw-r--r--modules/webm/libvpx/SCsub13
-rw-r--r--modules/webp/SCsub5
-rw-r--r--modules/websocket/SCsub35
-rw-r--r--modules/websocket/lws_peer.cpp10
-rw-r--r--modules/xatlas_unwrap/SCsub46
-rw-r--r--modules/xatlas_unwrap/config.py5
-rw-r--r--modules/xatlas_unwrap/register_types.cpp133
-rw-r--r--modules/xatlas_unwrap/register_types.h32
-rw-r--r--platform/SCsub2
-rw-r--r--platform/android/SCsub10
-rw-r--r--platform/android/java/src/org/godotengine/godot/Godot.java10
-rw-r--r--platform/iphone/SCsub3
-rw-r--r--platform/osx/SCsub2
-rw-r--r--platform/server/SCsub2
-rw-r--r--platform/server/detect.py4
-rw-r--r--platform/windows/SCsub4
-rw-r--r--platform/x11/SCsub2
-rw-r--r--scene/2d/SCsub2
-rw-r--r--scene/3d/SCsub4
-rw-r--r--scene/3d/physics_body.cpp8
-rw-r--r--scene/3d/spring_arm.cpp3
-rw-r--r--scene/3d/spring_arm.h2
-rw-r--r--scene/SCsub9
-rw-r--r--scene/animation/SCsub2
-rw-r--r--scene/animation/animation_player.cpp1
-rw-r--r--scene/animation/animation_tree_player.cpp1
-rw-r--r--scene/animation/skeleton_ik.cpp4
-rw-r--r--scene/animation/tween.cpp20
-rw-r--r--scene/audio/SCsub2
-rw-r--r--scene/gui/SCsub2
-rw-r--r--scene/gui/base_button.cpp5
-rw-r--r--scene/gui/base_button.h1
-rw-r--r--scene/gui/button.cpp15
-rw-r--r--scene/gui/file_dialog.cpp4
-rw-r--r--scene/gui/range.cpp22
-rw-r--r--scene/gui/range.h2
-rw-r--r--scene/gui/text_edit.cpp4
-rw-r--r--scene/gui/texture_button.cpp3
-rw-r--r--scene/gui/texture_progress.cpp22
-rw-r--r--scene/gui/tree.h1
-rw-r--r--scene/gui/video_player.cpp4
-rw-r--r--scene/main/SCsub2
-rw-r--r--scene/main/scene_tree.cpp3
-rw-r--r--scene/resources/SCsub2
-rw-r--r--scene/resources/default_theme/SCsub2
-rw-r--r--scene/resources/default_theme/default_theme.cpp4
-rw-r--r--scene/resources/material.cpp20
-rw-r--r--scene/resources/material.h2
-rw-r--r--scene/resources/particles_material.cpp3
-rw-r--r--scene/resources/texture.cpp3
-rw-r--r--scene/resources/tile_set.h9
-rw-r--r--servers/SCsub2
-rw-r--r--servers/arvr/SCsub2
-rw-r--r--servers/audio/SCsub2
-rw-r--r--servers/audio/effects/SCsub2
-rw-r--r--servers/physics/SCsub2
-rw-r--r--servers/physics/body_sw.cpp4
-rw-r--r--servers/physics/joints/SCsub2
-rw-r--r--servers/physics/joints/cone_twist_joint_sw.cpp2
-rw-r--r--servers/physics/joints/generic_6dof_joint_sw.cpp22
-rw-r--r--servers/physics/joints/hinge_joint_sw.cpp4
-rw-r--r--servers/physics/joints/slider_joint_sw.cpp4
-rw-r--r--servers/physics_2d_server.h5
-rw-r--r--servers/physics_server.cpp1
-rw-r--r--servers/physics_server.h1
-rw-r--r--servers/visual/SCsub2
-rw-r--r--servers/visual/rasterizer.h3
-rw-r--r--servers/visual/shader_language.cpp18
-rw-r--r--servers/visual/visual_server_raster.cpp3
-rw-r--r--servers/visual/visual_server_raster.h3
-rw-r--r--servers/visual/visual_server_scene.cpp9
-rw-r--r--servers/visual/visual_server_wrap_mt.h5
-rw-r--r--servers/visual_server.h3
-rw-r--r--thirdparty/README.md4
-rw-r--r--thirdparty/misc/easing_equations.cpp (renamed from scene/animation/tween_interpolaters.cpp)32
-rw-r--r--thirdparty/xatlas/xatlas.cpp7384
-rw-r--r--thirdparty/xatlas/xatlas.h160
-rw-r--r--thirdparty/zstd/SCsub29
220 files changed, 9893 insertions, 910 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index cf08225a40..894cd1a53a 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -278,6 +278,11 @@ Comment: curl
Copyright: 1998-2012, Daniel Stenberg et al.
License: curl
+Files: ./thirdparty/misc/easing_equations.cpp
+Comment: Robert Penner's Easing Functions
+Copyright: 2001, Robert Penner
+License: BSD-3-clause
+
Files: ./thirdparty/misc/fastlz.c
./thirdparty/misc/fastlz.h
Comment: FastLZ
diff --git a/SConstruct b/SConstruct
index 17518706aa..85f76000b7 100644
--- a/SConstruct
+++ b/SConstruct
@@ -123,6 +123,7 @@ env_base.__class__.add_shared_library = methods.add_shared_library
env_base.__class__.add_library = methods.add_library
env_base.__class__.add_program = methods.add_program
env_base.__class__.CommandNoCache = methods.CommandNoCache
+env_base.__class__.disable_warnings = methods.disable_warnings
env_base["x86_libtheora_opt_gcc"] = False
env_base["x86_libtheora_opt_vc"] = False
@@ -190,6 +191,7 @@ opts.Add(BoolVariable('builtin_pcre2', "Use the built-in PCRE2 library)", True))
opts.Add(BoolVariable('builtin_recast', "Use the built-in Recast library", True))
opts.Add(BoolVariable('builtin_squish', "Use the built-in squish library", True))
opts.Add(BoolVariable('builtin_thekla_atlas', "Use the built-in thekla_altas library", True))
+opts.Add(BoolVariable('builtin_xatlas', "Use the built-in xatlas library", True))
opts.Add(BoolVariable('builtin_zlib', "Use the built-in zlib library", True))
opts.Add(BoolVariable('builtin_zstd', "Use the built-in Zstd library", True))
diff --git a/core/SCsub b/core/SCsub
index 17b6e2c7ea..c6d0b7e5b1 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -8,7 +8,6 @@ from platform_methods import run_in_subprocess
env.core_sources = []
-
# Generate global defaults
gd_call = ""
gd_inc = ""
@@ -55,10 +54,14 @@ with open("script_encryption_key.gen.cpp", "w") as f:
f.write("#include \"core/project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n")
-# Add required thirdparty code. Header paths are hardcoded, we don't need to append
+# Add required thirdparty code.
+env_thirdparty = env.Clone()
+env_thirdparty.disable_warnings()
+
+# Misc thirdparty code: header paths are hardcoded, we don't need to append
# to the include path (saves a few chars on the compiler invocation for touchy MSVC...)
-thirdparty_dir = "#thirdparty/misc/"
-thirdparty_sources = [
+thirdparty_misc_dir = "#thirdparty/misc/"
+thirdparty_misc_sources = [
# C sources
"base64.c",
"fastlz.c",
@@ -72,10 +75,34 @@ thirdparty_sources = [
"pcg.cpp",
"triangulator.cpp",
]
-thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env.add_source_files(env.core_sources, thirdparty_sources)
-
-# Minizip library, can be unbundled in theory
+thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources]
+env_thirdparty.add_source_files(env.core_sources, thirdparty_misc_sources)
+
+# Zlib library, can be unbundled
+if env['builtin_zlib']:
+ thirdparty_zlib_dir = "#thirdparty/zlib/"
+ thirdparty_zlib_sources = [
+ "adler32.c",
+ "compress.c",
+ "crc32.c",
+ "deflate.c",
+ "infback.c",
+ "inffast.c",
+ "inflate.c",
+ "inftrees.c",
+ "trees.c",
+ "uncompr.c",
+ "zutil.c",
+ ]
+ thirdparty_zlib_sources = [thirdparty_zlib_dir + file for file in thirdparty_zlib_sources]
+
+ env_thirdparty.Append(CPPPATH=[thirdparty_zlib_dir])
+ # Needs to be available in main env too
+ env.Append(CPPPATH=[thirdparty_zlib_dir])
+
+ env_thirdparty.add_source_files(env.core_sources, thirdparty_zlib_sources)
+
+# Minizip library, could be unbundled in theory
# However, our version has some custom modifications, so it won't compile with the system one
thirdparty_minizip_dir = "#thirdparty/minizip/"
thirdparty_minizip_sources = [
@@ -84,10 +111,42 @@ thirdparty_minizip_sources = [
"zip.c",
]
thirdparty_minizip_sources = [thirdparty_minizip_dir + file for file in thirdparty_minizip_sources]
-env.add_source_files(env.core_sources, thirdparty_minizip_sources)
-
-if 'builtin_zstd' in env and env['builtin_zstd']:
- SConscript("#thirdparty/zstd/SCsub")
+env_thirdparty.add_source_files(env.core_sources, thirdparty_minizip_sources)
+
+# Zstd library, can be unbundled in theory
+# though we currently use some private symbols
+# https://github.com/godotengine/godot/issues/17374
+if env['builtin_zstd']:
+ thirdparty_zstd_dir = "#thirdparty/zstd/"
+ thirdparty_zstd_sources = [
+ "common/entropy_common.c",
+ "common/error_private.c",
+ "common/fse_decompress.c",
+ "common/pool.c",
+ "common/threading.c",
+ "common/xxhash.c",
+ "common/zstd_common.c",
+ "compress/fse_compress.c",
+ "compress/huf_compress.c",
+ "compress/zstd_compress.c",
+ "compress/zstd_double_fast.c",
+ "compress/zstd_fast.c",
+ "compress/zstd_lazy.c",
+ "compress/zstd_ldm.c",
+ "compress/zstdmt_compress.c",
+ "compress/zstd_opt.c",
+ "decompress/huf_decompress.c",
+ "decompress/zstd_decompress.c",
+ ]
+ thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources]
+
+ env_thirdparty.Append(CPPPATH=[thirdparty_zstd_dir, thirdparty_zstd_dir + "common"])
+ env_thirdparty.Append(CCFLAGS="-DZSTD_STATIC_LINKING_ONLY")
+ env.Append(CPPPATH=thirdparty_zstd_dir)
+ # Also needed in main env includes will trigger warnings
+ env.Append(CCFLAGS="-DZSTD_STATIC_LINKING_ONLY")
+
+ env_thirdparty.add_source_files(env.core_sources, thirdparty_zstd_sources)
# Godot's own sources
@@ -122,4 +181,3 @@ SConscript('bind/SCsub')
# Build it all as a library
lib = env.add_library("core", env.core_sources)
env.Prepend(LIBS=[lib])
-Export('env')
diff --git a/core/bind/SCsub b/core/bind/SCsub
index 4efc902717..1c5f954470 100644
--- a/core/bind/SCsub
+++ b/core/bind/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.core_sources, "*.cpp")
-
-Export('env')
diff --git a/core/io/SCsub b/core/io/SCsub
index 79b56cb716..1c5f954470 100644
--- a/core/io/SCsub
+++ b/core/io/SCsub
@@ -3,6 +3,3 @@
Import('env')
env.add_source_files(env.core_sources, "*.cpp")
-
-Export('env')
-
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index ac563df0c3..36dd688e77 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -374,6 +374,7 @@ Error HTTPClient::poll() {
} break;
}
} break;
+ case STATUS_BODY:
case STATUS_CONNECTED: {
// Check if we are still connected
if (ssl) {
@@ -480,7 +481,8 @@ Error HTTPClient::poll() {
case STATUS_DISCONNECTED: {
return ERR_UNCONFIGURED;
} break;
- case STATUS_CONNECTION_ERROR: {
+ case STATUS_CONNECTION_ERROR:
+ case STATUS_SSL_HANDSHAKE_ERROR: {
return ERR_CONNECTION_ERROR;
} break;
case STATUS_CANT_CONNECT: {
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index e15519da47..d33d436b74 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -824,6 +824,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
flags |= ENCODE_FLAG_OBJECT_AS_ID;
}
} break;
+ default: {} // nothing to do at this stage
}
if (buf) {
diff --git a/core/math/SCsub b/core/math/SCsub
index 4efc902717..1c5f954470 100644
--- a/core/math/SCsub
+++ b/core/math/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.core_sources, "*.cpp")
-
-Export('env')
diff --git a/core/object.cpp b/core/object.cpp
index 96914fe141..86d9fbde20 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -277,8 +277,8 @@ MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyIn
MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name) :
name(p_name),
- flags(METHOD_FLAG_NORMAL),
return_val(p_ret),
+ flags(METHOD_FLAG_NORMAL),
id(0) {
}
diff --git a/core/object.h b/core/object.h
index be405a47a6..c883d44f36 100644
--- a/core/object.h
+++ b/core/object.h
@@ -116,6 +116,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 19,
PROPERTY_USAGE_INTERNAL = 1 << 20,
PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 21, // If the object is duplicated also this property will be duplicated
+ PROPERTY_USAGE_HIGH_END_GFX = 1 << 22,
PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK,
PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED,
@@ -187,11 +188,11 @@ Array convert_property_list(const List<PropertyInfo> *p_list);
struct MethodInfo {
String name;
- List<PropertyInfo> arguments;
- Vector<Variant> default_arguments;
PropertyInfo return_val;
uint32_t flags;
int id;
+ List<PropertyInfo> arguments;
+ Vector<Variant> default_arguments;
inline bool operator==(const MethodInfo &p_method) const { return id == p_method.id; }
inline bool operator<(const MethodInfo &p_method) const { return id == p_method.id ? (name < p_method.name) : (id < p_method.id); }
diff --git a/core/os/SCsub b/core/os/SCsub
index 4efc902717..1c5f954470 100644
--- a/core/os/SCsub
+++ b/core/os/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.core_sources, "*.cpp")
-
-Export('env')
diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp
index dbd62cb3bb..daa3eacd5f 100644
--- a/core/os/dir_access.cpp
+++ b/core/os/dir_access.cpp
@@ -227,6 +227,7 @@ String DirAccess::fix_path(String p_path) const {
return p_path;
} break;
+ case ACCESS_MAX: break; // Can't happen, but silences warning
}
return p_path;
diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp
index 7749d45d4a..e09e5e16ad 100644
--- a/core/os/file_access.cpp
+++ b/core/os/file_access.cpp
@@ -165,6 +165,7 @@ String FileAccess::fix_path(const String &p_path) const {
return r_path;
} break;
+ case ACCESS_MAX: break; // Can't happen, but silences warning
}
return r_path;
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
index fe1dc2cdd1..00a86d11bd 100644
--- a/core/script_debugger_remote.cpp
+++ b/core/script_debugger_remote.cpp
@@ -1094,12 +1094,12 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() :
performance(Engine::get_singleton()->get_singleton_object("Performance")),
requested_quit(false),
mutex(Mutex::create()),
- max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")),
max_messages_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_messages_per_frame")),
- max_errors_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_frame")),
- char_count(0),
n_messages_dropped(0),
+ max_errors_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_frame")),
n_errors_dropped(0),
+ max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")),
+ char_count(0),
last_msec(0),
msec_count(0),
allow_focus_steal_pid(0),
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 5a53e7cb05..65d339c0d5 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -886,8 +886,10 @@
Middle Mouse Button
</constant>
<constant name="BUTTON_XBUTTON1" value="8" enum="ButtonList">
+ Extra Mouse Button 1
</constant>
<constant name="BUTTON_XBUTTON2" value="9" enum="ButtonList">
+ Extra Mouse Button 2
</constant>
<constant name="BUTTON_WHEEL_UP" value="4" enum="ButtonList">
Mouse wheel up
@@ -911,8 +913,10 @@
Middle Mouse Button Mask
</constant>
<constant name="BUTTON_MASK_XBUTTON1" value="128" enum="ButtonList">
+ Extra Mouse Button 1 Mask
</constant>
<constant name="BUTTON_MASK_XBUTTON2" value="256" enum="ButtonList">
+ Extra Mouse Button 2 Mask
</constant>
<constant name="JOY_BUTTON_0" value="0" enum="JoystickList">
Joypad Button 0
diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index 453f28fe5a..ed3d2d2205 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -3,6 +3,23 @@
<brief_description>
</brief_description>
<description>
+ The [code]ArrayMesh[/code] is used to construct a [Mesh] by specifying the attributes as arrays. The most basic example is the creation of a single triangle
+ [codeblock]
+ var vertices = PoolVector3Array()
+ vertices.push_back(Vector3(0,1,0))
+ vertices.push_back(Vector3(1,0,0))
+ vertices.push_back(Vector3(0,0,1))
+ # Initialize the ArrayMesh.
+ var arr_mesh = ArrayMesh.new()
+ var arrays = []
+ arrays.resize(ArrayMesh.ARRAY_MAX)
+ arrays[ArrayMesh.ARRAY_VERTEX] = vertices
+ # Create the Mesh.
+ arr_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
+ var m = MeshInstance.new()
+ m.mesh = arr_mesh
+ [/codeblock]
+ The [code]MeshInstance[/code] is ready to be added to the SceneTree to be shown.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/InputEventMouseButton.xml b/doc/classes/InputEventMouseButton.xml
index 50641dceed..a3a9055087 100644
--- a/doc/classes/InputEventMouseButton.xml
+++ b/doc/classes/InputEventMouseButton.xml
@@ -18,7 +18,7 @@
Mouse button identifier, one of the BUTTON_* or BUTTON_WHEEL_* constants in [@GlobalScope].
</member>
<member name="doubleclick" type="bool" setter="set_doubleclick" getter="is_doubleclick">
- If [code]true[/code] the mouse button's state is a double-click. If [code]false[/code] the mouse button's state is released.
+ If [code]true[/code] the mouse button's state is a double-click.
</member>
<member name="factor" type="float" setter="set_factor" getter="get_factor">
Magnitude. Amount (or delta) of the event. Used for scroll events, indicates scroll amount (vertically or horizontally). Only supported on some platforms, sensitivity varies by platform. May be 0 if not supported.
diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml
index 88a104cb11..f79baea0be 100644
--- a/doc/classes/PhysicsServer.xml
+++ b/doc/classes/PhysicsServer.xml
@@ -1518,9 +1518,7 @@
<constant name="BODY_MODE_RIGID" value="2" enum="BodyMode">
Constant for rigid bodies.
</constant>
- <constant name="BODY_MODE_SOFT" value="3" enum="BodyMode">
- </constant>
- <constant name="BODY_MODE_CHARACTER" value="4" enum="BodyMode">
+ <constant name="BODY_MODE_CHARACTER" value="3" enum="BodyMode">
Constant for rigid bodies in character mode. In this mode, a body can not rotate, and only its linear velocity is affected by physics.
</constant>
<constant name="BODY_PARAM_BOUNCE" value="0" enum="BodyParameter">
diff --git a/doc/classes/float.xml b/doc/classes/float.xml
index ef3c3d72eb..0c5536b5fe 100644
--- a/doc/classes/float.xml
+++ b/doc/classes/float.xml
@@ -35,7 +35,7 @@
<argument index="0" name="from" type="String">
</argument>
<description>
- Cast a [String] value to a floating point value. This method accepts float value strings like [code] '1.23' [/code] and exponential notation strings for its parameter so calling [code] float('1e3') [/code] will return 1000.0 and calling [code] float('1e-3') [/code] will return -0.001.
+ Cast a [String] value to a floating point value. This method accepts float value strings like [code] '1.23' [/code] and exponential notation strings for its parameter so calling [code] float('1e3') [/code] will return 1000.0 and calling [code] float('1e-3') [/code] will return 0.001.
</description>
</method>
</methods>
diff --git a/drivers/SCsub b/drivers/SCsub
index f9cfa3fb05..320d4dc4bb 100644
--- a/drivers/SCsub
+++ b/drivers/SCsub
@@ -4,9 +4,6 @@ Import('env')
env.drivers_sources = []
-if 'builtin_zlib' in env and env['builtin_zlib']:
- SConscript("zlib/SCsub")
-
# OS drivers
SConscript('unix/SCsub')
SConscript('windows/SCsub')
diff --git a/drivers/alsa/SCsub b/drivers/alsa/SCsub
index ee39fd2631..28b315ae66 100644
--- a/drivers/alsa/SCsub
+++ b/drivers/alsa/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.drivers_sources, "*.cpp")
-
-Export('env')
diff --git a/drivers/alsamidi/SCsub b/drivers/alsamidi/SCsub
index 233593b0f9..4c24925192 100644
--- a/drivers/alsamidi/SCsub
+++ b/drivers/alsamidi/SCsub
@@ -4,5 +4,3 @@ Import('env')
# Driver source files
env.add_source_files(env.drivers_sources, "*.cpp")
-
-Export('env')
diff --git a/drivers/convex_decomp/SCsub b/drivers/convex_decomp/SCsub
index f017e55120..65ba5332b7 100644
--- a/drivers/convex_decomp/SCsub
+++ b/drivers/convex_decomp/SCsub
@@ -11,6 +11,7 @@ thirdparty_sources = [
"b2Triangle.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env.add_source_files(env.drivers_sources, thirdparty_sources)
-Export('env')
+env_thirdparty = env.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources)
diff --git a/drivers/coreaudio/SCsub b/drivers/coreaudio/SCsub
index 233593b0f9..4c24925192 100644
--- a/drivers/coreaudio/SCsub
+++ b/drivers/coreaudio/SCsub
@@ -4,5 +4,3 @@ Import('env')
# Driver source files
env.add_source_files(env.drivers_sources, "*.cpp")
-
-Export('env')
diff --git a/drivers/coremidi/SCsub b/drivers/coremidi/SCsub
index 233593b0f9..4c24925192 100644
--- a/drivers/coremidi/SCsub
+++ b/drivers/coremidi/SCsub
@@ -4,5 +4,3 @@ Import('env')
# Driver source files
env.add_source_files(env.drivers_sources, "*.cpp")
-
-Export('env')
diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h
index 126f23feeb..9315026623 100644
--- a/drivers/dummy/rasterizer_dummy.h
+++ b/drivers/dummy/rasterizer_dummy.h
@@ -517,6 +517,7 @@ public:
void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) {}
void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) {}
void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) {}
+ void reflection_probe_set_resolution(RID p_probe, int p_resolution) {}
AABB reflection_probe_get_aabb(RID p_probe) const { return AABB(); }
VS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const { return VisualServer::REFLECTION_PROBE_UPDATE_ONCE; }
@@ -802,6 +803,8 @@ public:
_create_func = _create_current;
}
+ virtual bool is_low_end() const { return true; }
+
RasterizerDummy() {}
~RasterizerDummy() {}
};
diff --git a/drivers/gl_context/SCsub b/drivers/gl_context/SCsub
index 4d66a9f9f1..efb26a7908 100644
--- a/drivers/gl_context/SCsub
+++ b/drivers/gl_context/SCsub
@@ -10,13 +10,14 @@ if (env["platform"] in ["haiku", "osx", "windows", "x11"]):
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env.add_source_files(env.drivers_sources, thirdparty_sources)
env.Append(CPPPATH=[thirdparty_dir])
env.Append(CPPFLAGS=['-DGLAD_ENABLED'])
env.Append(CPPFLAGS=['-DGLES_OVER_GL'])
+ env_thirdparty = env.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources)
+
# Godot source files
env.add_source_files(env.drivers_sources, "*.cpp")
-
-Export('env')
diff --git a/drivers/gles2/SCsub b/drivers/gles2/SCsub
index 2471dd3739..9923e52c73 100644
--- a/drivers/gles2/SCsub
+++ b/drivers/gles2/SCsub
@@ -2,6 +2,6 @@
Import('env')
-env.add_source_files(env.drivers_sources,"*.cpp")
+env.add_source_files(env.drivers_sources, "*.cpp")
SConscript("shaders/SCsub")
diff --git a/drivers/gles2/rasterizer_gles2.h b/drivers/gles2/rasterizer_gles2.h
index c76d5f7f20..45a9db73f2 100644
--- a/drivers/gles2/rasterizer_gles2.h
+++ b/drivers/gles2/rasterizer_gles2.h
@@ -66,6 +66,8 @@ public:
static void make_current();
static void register_config();
+ virtual bool is_low_end() const { return true; }
+
RasterizerGLES2();
~RasterizerGLES2();
};
diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp
index 9d78c4c097..feefb74480 100644
--- a/drivers/gles2/rasterizer_scene_gles2.cpp
+++ b/drivers/gles2/rasterizer_scene_gles2.cpp
@@ -437,29 +437,182 @@ void RasterizerSceneGLES2::reflection_atlas_set_subdivision(RID p_ref_atlas, int
////////////////////////////////////////////////////
RID RasterizerSceneGLES2::reflection_probe_instance_create(RID p_probe) {
- return RID();
+
+ RasterizerStorageGLES2::ReflectionProbe *probe = storage->reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND_V(!probe, RID());
+
+ ReflectionProbeInstance *rpi = memnew(ReflectionProbeInstance);
+
+ rpi->probe_ptr = probe;
+ rpi->self = reflection_probe_instance_owner.make_rid(rpi);
+ rpi->probe = p_probe;
+ rpi->reflection_atlas_index = -1;
+ rpi->render_step = -1;
+ rpi->last_pass = 0;
+ rpi->current_resolution = 0;
+ rpi->dirty = true;
+
+ rpi->last_pass = 0;
+ rpi->index = 0;
+
+ for (int i = 0; i < 6; i++) {
+ glGenFramebuffers(1, &rpi->fbo[i]);
+ }
+
+ glGenFramebuffers(1, &rpi->fbo_blur);
+ glGenRenderbuffers(1, &rpi->depth);
+ glGenTextures(1, &rpi->cubemap);
+
+ return rpi->self;
}
void RasterizerSceneGLES2::reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform) {
+
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance);
+ ERR_FAIL_COND(!rpi);
+ rpi->transform = p_transform;
}
void RasterizerSceneGLES2::reflection_probe_release_atlas_index(RID p_instance) {
}
bool RasterizerSceneGLES2::reflection_probe_instance_needs_redraw(RID p_instance) {
- return false;
+ const ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance);
+ ERR_FAIL_COND_V(!rpi, false);
+
+ bool need_redraw = rpi->probe_ptr->resolution != rpi->current_resolution || rpi->dirty || rpi->probe_ptr->update_mode == VS::REFLECTION_PROBE_UPDATE_ALWAYS;
+ rpi->dirty = false;
+ return need_redraw;
}
bool RasterizerSceneGLES2::reflection_probe_instance_has_reflection(RID p_instance) {
- return false;
+ return true;
}
bool RasterizerSceneGLES2::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) {
- return false;
+
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance);
+ ERR_FAIL_COND_V(!rpi, false);
+
+ rpi->render_step = 0;
+
+ if (rpi->probe_ptr->resolution != rpi->current_resolution) {
+
+ //update cubemap if resolution changed
+ int size = rpi->probe_ptr->resolution;
+ rpi->current_resolution = size;
+
+ int lod = 0;
+
+ GLenum internal_format = GL_RGBA;
+ GLenum format = GL_RGBA;
+ GLenum type = GL_UNSIGNED_BYTE;
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, rpi->cubemap);
+
+ // Set the initial (empty) mipmaps, all need to be set for this to work in GLES2, even if later wont be used.
+ while (size >= 1) {
+
+ for (int i = 0; i < 6; i++) {
+ glTexImage2D(_cube_side_enum[i], lod, internal_format, size, size, 0, format, type, NULL);
+ if (size == rpi->current_resolution) {
+ //adjust framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, rpi->fbo[i]);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], rpi->cubemap, 0);
+ glBindRenderbuffer(GL_RENDERBUFFER, rpi->depth);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size, size);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rpi->depth);
+
+#ifdef DEBUG_ENABLED
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ ERR_CONTINUE(status != GL_FRAMEBUFFER_COMPLETE);
+#endif
+ }
+ }
+
+ lod++;
+
+ size >>= 1;
+ }
+
+ glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ return true;
}
bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_instance) {
- return false;
+
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance);
+ ERR_FAIL_COND_V(!rpi, false);
+
+ int size = rpi->probe_ptr->resolution;
+
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_BLEND);
+ glDepthMask(GL_FALSE);
+
+ for (int i = 0; i < VS::ARRAY_MAX - 1; i++) {
+ glDisableVertexAttribArray(i);
+ }
+ }
+
+ //vdc cache
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, storage->resources.radical_inverse_vdc_cache_tex);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, rpi->fbo_blur);
+ // now render to the framebuffer, mipmap level for mipmap level
+ int lod = 1;
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, rpi->cubemap);
+ glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //use linear, no mipmaps so it does not read from what is being written to
+
+ size >>= 1;
+ int mipmaps = 6;
+ int mm_level = mipmaps - 1;
+
+ storage->shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, false);
+ storage->shaders.cubemap_filter.bind();
+
+ //blur
+ while (size >= 1) {
+
+ for (int i = 0; i < 6; i++) {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], rpi->cubemap, lod);
+
+ glViewport(0, 0, size, size);
+ storage->bind_quad_array();
+ storage->shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::FACE_ID, i);
+ float roughness = CLAMP(lod / (float)(mipmaps - 1), 0, 1);
+ storage->shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::ROUGHNESS, roughness);
+ storage->shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::Z_FLIP, false);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+
+ size >>= 1;
+
+ mm_level--;
+
+ lod++;
+ }
+
+ // restore ranges
+
+ glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ return true;
}
/* ENVIRONMENT API */
@@ -760,6 +913,7 @@ void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::G
e->use_accum = false;
e->light_index = RenderList::MAX_LIGHTS;
e->use_accum_ptr = &e->use_accum;
+ e->instancing = (e->instance->base_type == VS::INSTANCE_MULTIMESH) ? 1 : 0;
if (e->geometry->last_pass != render_pass) {
e->geometry->last_pass = render_pass;
@@ -779,17 +933,39 @@ void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::G
e->material_index = e->material->index;
- e->refprobe_0_index = 0xFF; //refprobe disabled by default
- e->refprobe_1_index = 0xFF; //refprobe disabled by default
+ e->refprobe_0_index = RenderList::MAX_REFLECTION_PROBES; //refprobe disabled by default
+ e->refprobe_1_index = RenderList::MAX_REFLECTION_PROBES; //refprobe disabled by default
if (!p_depth_pass) {
e->depth_layer = e->instance->depth_layer;
e->priority = p_material->render_priority;
- //if (e->instance->reflection_probe_instances.size() > 0 ) {
- // RasterizerStorageGLES2::
- //}
+ int rpsize = e->instance->reflection_probe_instances.size();
+ if (rpsize > 0) {
+ bool first = true;
+ rpsize = MIN(rpsize, 2); //more than 2 per object are not supported, this keeps it stable
+
+ for (int i = 0; i < rpsize; i++) {
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(e->instance->reflection_probe_instances[i]);
+ if (rpi->last_pass != render_pass) {
+ continue;
+ }
+ if (first) {
+ e->refprobe_0_index = rpi->index;
+ first = false;
+ } else {
+ e->refprobe_1_index = rpi->index;
+ break;
+ }
+ }
+
+ /* if (e->refprobe_0_index > e->refprobe_1_index) { //if both are valid, swap them to keep order as best as possible
+ uint64_t tmp = e->refprobe_0_index;
+ e->refprobe_0_index = e->refprobe_1_index;
+ e->refprobe_1_index = tmp;
+ }*/
+ }
//add directional lights
@@ -921,9 +1097,7 @@ void RasterizerSceneGLES2::_fill_render_list(InstanceBase **p_cull_result, int p
} break;
- default: {
-
- } break;
+ default: {}
}
}
}
@@ -1048,7 +1222,7 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste
glDisableVertexAttribArray(i);
switch (i) {
case VS::ARRAY_NORMAL: {
- glVertexAttrib4f(VS::ARRAY_COLOR, 0.0, 0.0, 1, 1);
+ glVertexAttrib4f(VS::ARRAY_NORMAL, 0.0, 0.0, 1, 1);
} break;
case VS::ARRAY_COLOR: {
glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);
@@ -1171,27 +1345,23 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste
//enable transform buffer and bind it
glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer);
- glEnableVertexAttribArray(VS::ARRAY_MAX + 0);
- glEnableVertexAttribArray(VS::ARRAY_MAX + 1);
- glEnableVertexAttribArray(VS::ARRAY_MAX + 2);
+ glEnableVertexAttribArray(INSTANCE_BONE_BASE + 0);
+ glEnableVertexAttribArray(INSTANCE_BONE_BASE + 1);
+ glEnableVertexAttribArray(INSTANCE_BONE_BASE + 2);
- glVertexAttribPointer(VS::ARRAY_MAX + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0));
- glVertexAttribPointer(VS::ARRAY_MAX + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1));
- glVertexAttribPointer(VS::ARRAY_MAX + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2));
+ glVertexAttribPointer(INSTANCE_BONE_BASE + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0));
+ glVertexAttribPointer(INSTANCE_BONE_BASE + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1));
+ glVertexAttribPointer(INSTANCE_BONE_BASE + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2));
clear_skeleton_buffer = false;
}
}
if (clear_skeleton_buffer) {
- // just to make sure
- glDisableVertexAttribArray(VS::ARRAY_MAX + 0);
- glDisableVertexAttribArray(VS::ARRAY_MAX + 1);
- glDisableVertexAttribArray(VS::ARRAY_MAX + 2);
- glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0);
- glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0);
- glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0);
+ glDisableVertexAttribArray(INSTANCE_BONE_BASE + 0);
+ glDisableVertexAttribArray(INSTANCE_BONE_BASE + 1);
+ glDisableVertexAttribArray(INSTANCE_BONE_BASE + 2);
}
} break;
@@ -1213,7 +1383,7 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste
glDisableVertexAttribArray(i);
switch (i) {
case VS::ARRAY_NORMAL: {
- glVertexAttrib4f(VS::ARRAY_COLOR, 0.0, 0.0, 1, 1);
+ glVertexAttrib4f(VS::ARRAY_NORMAL, 0.0, 0.0, 1, 1);
} break;
case VS::ARRAY_COLOR: {
glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);
@@ -1224,25 +1394,22 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste
}
}
- if (!storage->config.float_texture_supported) {
- // just to make sure, clear skeleton buffer too
- glDisableVertexAttribArray(VS::ARRAY_MAX + 0);
- glDisableVertexAttribArray(VS::ARRAY_MAX + 1);
- glDisableVertexAttribArray(VS::ARRAY_MAX + 2);
-
- glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0);
- glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0);
- glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0);
- }
+ // prepare multimesh (disable)
+ glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 0);
+ glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 1);
+ glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 2);
+ glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 3);
+ glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 4);
+ glDisableVertexAttribArray(INSTANCE_BONE_BASE + 0);
+ glDisableVertexAttribArray(INSTANCE_BONE_BASE + 1);
+ glDisableVertexAttribArray(INSTANCE_BONE_BASE + 2);
} break;
case VS::INSTANCE_IMMEDIATE: {
} break;
- default: {
-
- } break;
+ default: {}
}
}
@@ -1261,7 +1428,7 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) {
} else {
glDrawArrays(gl_primitive[s->primitive], 0, s->array_len);
}
-
+ /*
if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) {
//clean up after skeleton
glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer);
@@ -1274,7 +1441,7 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) {
glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0);
glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0);
}
-
+*/
} break;
case VS::INSTANCE_MULTIMESH: {
@@ -1295,53 +1462,33 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) {
// drawing
+ const float *base_buffer = multi_mesh->data.ptr();
+
for (int i = 0; i < amount; i++) {
- float *buffer = &multi_mesh->data.write[i * stride];
+ const float *buffer = base_buffer + i * stride;
{
- // inline of multimesh_get_transform since it's such a pain
- // to get a RID from here...
- Transform transform;
-
- transform.basis.elements[0][0] = buffer[0];
- transform.basis.elements[0][1] = buffer[1];
- transform.basis.elements[0][2] = buffer[2];
- transform.origin.x = buffer[3];
- transform.basis.elements[1][0] = buffer[4];
- transform.basis.elements[1][1] = buffer[5];
- transform.basis.elements[1][2] = buffer[6];
- transform.origin.y = buffer[7];
- transform.basis.elements[2][0] = buffer[8];
- transform.basis.elements[2][1] = buffer[9];
- transform.basis.elements[2][2] = buffer[10];
- transform.origin.z = buffer[11];
-
- float row[3][4] = {
- { transform.basis[0][0], transform.basis[0][1], transform.basis[0][2], transform.origin[0] },
- { transform.basis[1][0], transform.basis[1][1], transform.basis[1][2], transform.origin[1] },
- { transform.basis[2][0], transform.basis[2][1], transform.basis[2][2], transform.origin[2] },
- };
-
- glVertexAttrib4fv(VS::ARRAY_MAX + 0, row[0]);
- glVertexAttrib4fv(VS::ARRAY_MAX + 1, row[1]);
- glVertexAttrib4fv(VS::ARRAY_MAX + 2, row[2]);
+
+ glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 0, &buffer[0]);
+ glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 1, &buffer[4]);
+ glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 2, &buffer[8]);
}
if (multi_mesh->color_floats) {
if (multi_mesh->color_format == VS::MULTIMESH_COLOR_8BIT) {
uint8_t *color_data = (uint8_t *)(buffer + color_ofs);
- glVertexAttrib4f(VS::ARRAY_MAX + 3, color_data[0] / 255.0, color_data[1] / 255.0, color_data[2] / 255.0, color_data[3] / 255.0);
+ glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 3, color_data[0] / 255.0, color_data[1] / 255.0, color_data[2] / 255.0, color_data[3] / 255.0);
} else {
- glVertexAttrib4fv(VS::ARRAY_MAX + 3, buffer + color_ofs);
+ glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 3, buffer + color_ofs);
}
}
if (multi_mesh->custom_data_floats) {
if (multi_mesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) {
uint8_t *custom_data = (uint8_t *)(buffer + custom_data_ofs);
- glVertexAttrib4f(VS::ARRAY_MAX + 4, custom_data[0] / 255.0, custom_data[1] / 255.0, custom_data[2] / 255.0, custom_data[3] / 255.0);
+ glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 4, custom_data[0] / 255.0, custom_data[1] / 255.0, custom_data[2] / 255.0, custom_data[3] / 255.0);
} else {
- glVertexAttrib4fv(VS::ARRAY_MAX + 4, buffer + custom_data_ofs);
+ glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 4, buffer + custom_data_ofs);
}
}
@@ -1465,6 +1612,7 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) {
}
} break;
+ default: {}
}
}
@@ -1508,7 +1656,7 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas
}
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, p_light->light_ptr->directional_blend_splits);
- if (p_light->light_ptr->shadow) {
+ if (!state.render_no_shadows && p_light->light_ptr->shadow) {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3);
glBindTexture(GL_TEXTURE_2D, directional_shadow.depth);
@@ -1520,7 +1668,7 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas
case VS::LIGHT_OMNI: {
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_OMNI, true);
- if (shadow_atlas && p_light->light_ptr->shadow) {
+ if (!state.render_no_shadows && shadow_atlas && p_light->light_ptr->shadow) {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3);
glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth);
@@ -1531,7 +1679,7 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas
case VS::LIGHT_SPOT: {
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_SPOT, true);
- if (shadow_atlas && p_light->light_ptr->shadow) {
+ if (!state.render_no_shadows && shadow_atlas && p_light->light_ptr->shadow) {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3);
glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth);
@@ -1565,7 +1713,7 @@ void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shado
CameraMatrix matrices[4];
- if (light_ptr->shadow && directional_shadow.depth) {
+ if (!state.render_no_shadows && light_ptr->shadow && directional_shadow.depth) {
int shadow_count = 0;
Color split_offsets;
@@ -1660,7 +1808,7 @@ void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shado
attenuation.a = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION];
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ATTENUATION, attenuation);
- if (light_ptr->shadow && shadow_atlas->shadow_owners.has(light->self)) {
+ if (!state.render_no_shadows && light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) {
uint32_t key = shadow_atlas->shadow_owners[light->self];
@@ -1722,7 +1870,7 @@ void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shado
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_ANGLE, angle);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range);
- if (light->light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) {
+ if (!state.render_no_shadows && light->light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) {
uint32_t key = shadow_atlas->shadow_owners[light->self];
uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03;
@@ -1767,8 +1915,56 @@ void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shado
}
} break;
+ default: {}
+ }
+}
- default: break;
+void RasterizerSceneGLES2::_setup_refprobes(ReflectionProbeInstance *p_refprobe1, ReflectionProbeInstance *p_refprobe2, const Transform &p_view_transform, Environment *p_env) {
+
+ if (p_refprobe1) {
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_USE_BOX_PROJECT, p_refprobe1->probe_ptr->box_projection);
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_BOX_EXTENTS, p_refprobe1->probe_ptr->extents);
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_BOX_OFFSET, p_refprobe1->probe_ptr->origin_offset);
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_EXTERIOR, !p_refprobe1->probe_ptr->interior);
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_INTENSITY, p_refprobe1->probe_ptr->intensity);
+
+ Color ambient;
+ if (p_refprobe1->probe_ptr->interior) {
+ ambient = p_refprobe1->probe_ptr->interior_ambient * p_refprobe1->probe_ptr->interior_ambient_energy;
+ ambient.a = p_refprobe1->probe_ptr->interior_ambient_probe_contrib;
+ } else if (p_env) {
+ ambient = p_env->ambient_color * p_env->ambient_energy;
+ ambient.a = p_env->ambient_sky_contribution;
+ }
+
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_AMBIENT, ambient);
+
+ Transform proj = (p_view_transform.inverse() * p_refprobe1->transform).affine_inverse();
+
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_LOCAL_MATRIX, proj);
+ }
+
+ if (p_refprobe2) {
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_USE_BOX_PROJECT, p_refprobe2->probe_ptr->box_projection);
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_BOX_EXTENTS, p_refprobe2->probe_ptr->extents);
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_BOX_OFFSET, p_refprobe2->probe_ptr->origin_offset);
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_EXTERIOR, !p_refprobe2->probe_ptr->interior);
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_INTENSITY, p_refprobe2->probe_ptr->intensity);
+
+ Color ambient;
+ if (p_refprobe2->probe_ptr->interior) {
+ ambient = p_refprobe2->probe_ptr->interior_ambient * p_refprobe2->probe_ptr->interior_ambient_energy;
+ ambient.a = p_refprobe2->probe_ptr->interior_ambient_probe_contrib;
+ } else if (p_env) {
+ ambient = p_env->ambient_color * p_env->ambient_energy;
+ ambient.a = p_env->ambient_sky_contribution;
+ }
+
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_AMBIENT, ambient);
+
+ Transform proj = (p_view_transform.inverse() * p_refprobe2->transform).affine_inverse();
+
+ state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_LOCAL_MATRIX, proj);
}
}
@@ -1776,9 +1972,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas);
- Vector2 screen_pixel_size;
- screen_pixel_size.x = 1.0 / storage->frame.current_rt->width;
- screen_pixel_size.y = 1.0 / storage->frame.current_rt->height;
+ Vector2 screen_pixel_size = state.screen_pixel_size;
bool use_radiance_map = false;
if (!p_shadow && p_base_env) {
@@ -1794,6 +1988,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
RasterizerStorageGLES2::Material *prev_material = NULL;
RasterizerStorageGLES2::Geometry *prev_geometry = NULL;
RasterizerStorageGLES2::Skeleton *prev_skeleton = NULL;
+ RasterizerStorageGLES2::GeometryOwner *prev_owner = NULL;
Transform view_transform_inverse = p_view_transform.inverse();
CameraMatrix projection_inverse = p_projection.inverse();
@@ -1801,6 +1996,8 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
bool prev_base_pass = false;
LightInstance *prev_light = NULL;
bool prev_vertex_lit = false;
+ ReflectionProbeInstance *prev_refprobe_1 = NULL;
+ ReflectionProbeInstance *prev_refprobe_2 = NULL;
int prev_blend_mode = -2; //will always catch the first go
@@ -1810,6 +2007,10 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
glDisable(GL_BLEND);
}
+ RasterizerStorageGLES2::Texture *prev_lightmap = NULL;
+ float lightmap_energy = 1.0;
+ bool prev_use_lightmap_capture = false;
+
for (int i = 0; i < p_element_count; i++) {
RenderList::Element *e = p_elements[i];
@@ -1819,6 +2020,10 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
bool accum_pass = *e->use_accum_ptr;
*e->use_accum_ptr = true; //set to accum for next time this is found
LightInstance *light = NULL;
+ ReflectionProbeInstance *refprobe_1 = NULL;
+ ReflectionProbeInstance *refprobe_2 = NULL;
+ RasterizerStorageGLES2::Texture *lightmap = NULL;
+ bool use_lightmap_capture = false;
if (!p_shadow) {
@@ -1915,9 +2120,61 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
state.scene_shader.set_conditional(SceneShaderGLES2::USE_VERTEX_LIGHTING, vertex_lit);
prev_vertex_lit = vertex_lit;
}
+
+ if (!unshaded && !accum_pass && e->refprobe_0_index != RenderList::MAX_REFLECTION_PROBES) {
+ ERR_FAIL_INDEX(e->refprobe_0_index, reflection_probe_count);
+ refprobe_1 = reflection_probe_instances[e->refprobe_0_index];
+ }
+ if (!unshaded && !accum_pass && e->refprobe_1_index != RenderList::MAX_REFLECTION_PROBES) {
+ ERR_FAIL_INDEX(e->refprobe_1_index, reflection_probe_count);
+ refprobe_2 = reflection_probe_instances[e->refprobe_1_index];
+ }
+
+ if (refprobe_1 != prev_refprobe_1 || refprobe_2 != prev_refprobe_2) {
+ state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE1, refprobe_1 != NULL);
+ state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE2, refprobe_2 != NULL);
+ if (refprobe_1 != NULL && refprobe_1 != prev_refprobe_1) {
+ glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 5);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, refprobe_1->cubemap);
+ }
+ if (refprobe_2 != NULL && refprobe_2 != prev_refprobe_2) {
+ glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 6);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, refprobe_2->cubemap);
+ }
+ rebind = true;
+ }
+
+ use_lightmap_capture = !unshaded && !accum_pass && !e->instance->lightmap_capture_data.empty();
+
+ if (use_lightmap_capture != prev_use_lightmap_capture) {
+
+ state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_CAPTURE, use_lightmap_capture);
+ rebind = true;
+ }
+
+ if (!unshaded && !accum_pass && e->instance->lightmap.is_valid()) {
+
+ lightmap = storage->texture_owner.getornull(e->instance->lightmap);
+ lightmap_energy = 1.0;
+ if (lightmap) {
+ RasterizerStorageGLES2::LightmapCapture *capture = storage->lightmap_capture_data_owner.getornull(e->instance->lightmap_capture->base);
+ if (capture) {
+ lightmap_energy = capture->energy;
+ }
+ }
+ }
+
+ if (lightmap != prev_lightmap) {
+ state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP, lightmap != NULL);
+ if (lightmap != NULL) {
+ glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4);
+ glBindTexture(GL_TEXTURE_2D, lightmap->tex_id);
+ }
+ rebind = true;
+ }
}
- bool instancing = e->instancing;
+ bool instancing = e->instance->base_type == VS::INSTANCE_MULTIMESH;
if (instancing != prev_instancing) {
@@ -1930,7 +2187,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
if (skeleton != prev_skeleton) {
if (skeleton) {
- state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, skeleton != NULL);
+ state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, true);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, !storage->config.float_texture_supported);
} else {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, false);
@@ -1940,7 +2197,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
rebind = true;
}
- if (e->geometry != prev_geometry || skeleton != prev_skeleton) {
+ if (e->owner != prev_owner || e->geometry != prev_geometry || skeleton != prev_skeleton) {
_setup_geometry(e, skeleton);
}
@@ -1949,7 +2206,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
shader_rebind = _setup_material(material, p_reverse_cull, p_alpha_pass, Size2i(skeleton ? skeleton->size * 3 : 0, 0));
}
- if (i == 0 || shader_rebind) { //first time must rebindmakin
+ if (i == 0 || shader_rebind) { //first time must rebind
if (p_shadow) {
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_BIAS, p_shadow_bias);
@@ -1979,6 +2236,14 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
if (light) {
_setup_light(light, shadow_atlas, p_view_transform);
}
+
+ if (refprobe_1 || refprobe_2) {
+ _setup_refprobes(refprobe_1, refprobe_2, p_view_transform, p_env);
+ }
+
+ if (lightmap) {
+ state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_ENERGY, lightmap_energy);
+ }
}
state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, view_transform_inverse);
@@ -1994,23 +2259,39 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
+ if (use_lightmap_capture) { //this is per instance, must be set always if present
+ glUniform4fv(state.scene_shader.get_uniform_location(SceneShaderGLES2::LIGHTMAP_CAPTURES), 12, (const GLfloat *)e->instance->lightmap_capture_data.ptr());
+ state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_CAPTURE_SKY, false);
+ }
+
_render_geometry(e);
prev_geometry = e->geometry;
+ prev_owner = e->owner;
prev_material = material;
prev_skeleton = skeleton;
prev_instancing = instancing;
prev_light = light;
+ prev_refprobe_1 = refprobe_1;
+ prev_refprobe_2 = refprobe_2;
+ prev_lightmap = lightmap;
+ prev_use_lightmap_capture = use_lightmap_capture;
}
_setup_light_type(NULL, NULL); //clear light stuff
+ state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, false);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, false);
state.scene_shader.set_conditional(SceneShaderGLES2::BASE_PASS, false);
+ state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_VERTEX_LIGHTING, false);
+ state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE1, false);
+ state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE2, false);
+ state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP, false);
+ state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_CAPTURE, false);
}
void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_custom_fov, float p_energy) {
@@ -2105,6 +2386,36 @@ void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const C
void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
+ GLuint current_fb = 0;
+ Environment *env = NULL;
+
+ int viewport_width, viewport_height;
+
+ if (p_reflection_probe.is_valid()) {
+ ReflectionProbeInstance *probe = reflection_probe_instance_owner.getornull(p_reflection_probe);
+ ERR_FAIL_COND(!probe);
+ state.render_no_shadows = !probe->probe_ptr->enable_shadows;
+
+ if (!probe->probe_ptr->interior) { //use env only if not interior
+ env = environment_owner.getornull(p_environment);
+ }
+
+ current_fb = probe->fbo[p_reflection_probe_pass];
+ state.screen_pixel_size.x = 1.0 / probe->probe_ptr->resolution;
+ state.screen_pixel_size.y = 1.0 / probe->probe_ptr->resolution;
+
+ viewport_width = probe->probe_ptr->resolution;
+ viewport_height = probe->probe_ptr->resolution;
+
+ } else {
+ state.render_no_shadows = false;
+ current_fb = storage->frame.current_rt->fbo;
+ env = environment_owner.getornull(p_environment);
+ state.screen_pixel_size.x = 1.0 / storage->frame.current_rt->width;
+ state.screen_pixel_size.y = 1.0 / storage->frame.current_rt->height;
+ viewport_width = storage->frame.current_rt->width;
+ viewport_height = storage->frame.current_rt->height;
+ }
//push back the directional lights
if (p_light_cull_count) {
@@ -2137,10 +2448,22 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
render_light_instance_count = 0;
}
- glEnable(GL_BLEND);
+ if (p_reflection_probe_cull_count) {
- GLuint current_fb = storage->frame.current_rt->fbo;
- Environment *env = environment_owner.getornull(p_environment);
+ reflection_probe_instances = (ReflectionProbeInstance **)alloca(sizeof(ReflectionProbeInstance *) * p_reflection_probe_cull_count);
+ reflection_probe_count = p_reflection_probe_cull_count;
+ for (int i = 0; i < p_reflection_probe_cull_count; i++) {
+ ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_reflection_probe_cull_result[i]);
+ ERR_CONTINUE(!rpi);
+ rpi->last_pass = render_pass + 1; //will be incremented later
+ rpi->index = i;
+ reflection_probe_instances[i] = rpi;
+ }
+
+ } else {
+ reflection_probe_instances = NULL;
+ reflection_probe_count = 0;
+ }
// render list stuff
@@ -2150,6 +2473,7 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
// other stuff
glBindFramebuffer(GL_FRAMEBUFFER, current_fb);
+ glViewport(0, 0, viewport_width, viewport_height);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
@@ -2251,6 +2575,8 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) {
+ state.render_no_shadows = false;
+
LightInstance *light_instance = light_instance_owner.getornull(p_light);
ERR_FAIL_COND(!light_instance);
@@ -2525,6 +2851,44 @@ void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) {
}
bool RasterizerSceneGLES2::free(RID p_rid) {
+
+ if (light_instance_owner.owns(p_rid)) {
+
+ LightInstance *light_instance = light_instance_owner.getptr(p_rid);
+
+ //remove from shadow atlases..
+ for (Set<RID>::Element *E = light_instance->shadow_atlases.front(); E; E = E->next()) {
+ ShadowAtlas *shadow_atlas = shadow_atlas_owner.get(E->get());
+ ERR_CONTINUE(!shadow_atlas->shadow_owners.has(p_rid));
+ uint32_t key = shadow_atlas->shadow_owners[p_rid];
+ uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3;
+ uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK;
+
+ shadow_atlas->quadrants[q].shadows.write[s].owner = RID();
+ shadow_atlas->shadow_owners.erase(p_rid);
+ }
+
+ light_instance_owner.free(p_rid);
+ memdelete(light_instance);
+
+ } else if (shadow_atlas_owner.owns(p_rid)) {
+
+ ShadowAtlas *shadow_atlas = shadow_atlas_owner.get(p_rid);
+ shadow_atlas_set_size(p_rid, 0);
+ shadow_atlas_owner.free(p_rid);
+ memdelete(shadow_atlas);
+ } else if (reflection_probe_instance_owner.owns(p_rid)) {
+
+ ReflectionProbeInstance *reflection_instance = reflection_probe_instance_owner.get(p_rid);
+
+ reflection_probe_release_atlas_index(p_rid);
+ reflection_probe_instance_owner.free(p_rid);
+ memdelete(reflection_instance);
+
+ } else {
+ return false;
+ }
+
return true;
}
diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h
index 27cbc35299..14b9116952 100644
--- a/drivers/gles2/rasterizer_scene_gles2.h
+++ b/drivers/gles2/rasterizer_scene_gles2.h
@@ -59,6 +59,11 @@ public:
SHADOW_FILTER_PCF13,
};
+ enum {
+ INSTANCE_ATTRIB_BASE = 8,
+ INSTANCE_BONE_BASE = 13,
+ };
+
ShadowFilterMode shadow_filter_mode;
RID default_material;
@@ -204,6 +209,9 @@ public:
float dual_parbolloid_direction;
float dual_parbolloid_zfar;
+ bool render_no_shadows;
+
+ Vector2 screen_pixel_size;
} state;
/* SHADOW ATLAS API */
@@ -287,6 +295,38 @@ public:
/* REFLECTION PROBE INSTANCE */
+ struct ReflectionProbeInstance : public RID_Data {
+
+ RasterizerStorageGLES2::ReflectionProbe *probe_ptr;
+ RID probe;
+ RID self;
+ RID atlas;
+
+ int reflection_atlas_index;
+
+ int render_step;
+ int reflection_index;
+
+ GLuint fbo[6];
+ GLuint cubemap;
+ GLuint depth;
+
+ GLuint fbo_blur;
+
+ int current_resolution;
+ mutable bool dirty;
+
+ uint64_t last_pass;
+ uint32_t index;
+
+ Transform transform;
+ };
+
+ mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner;
+
+ ReflectionProbeInstance **reflection_probe_instances;
+ int reflection_probe_count;
+
virtual RID reflection_probe_instance_create(RID p_probe);
virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform);
virtual void reflection_probe_release_atlas_index(RID p_instance);
@@ -424,6 +464,7 @@ public:
enum {
MAX_LIGHTS = 255,
+ MAX_REFLECTION_PROBES = 255,
DEFAULT_MAX_ELEMENTS = 65536
};
@@ -587,6 +628,7 @@ public:
_FORCE_INLINE_ void _setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton);
_FORCE_INLINE_ void _setup_light_type(LightInstance *p_light, ShadowAtlas *shadow_atlas);
_FORCE_INLINE_ void _setup_light(LightInstance *p_light, ShadowAtlas *shadow_atlas, const Transform &p_view_transform);
+ _FORCE_INLINE_ void _setup_refprobes(ReflectionProbeInstance *p_refprobe1, ReflectionProbeInstance *p_refprobe2, const Transform &p_view_transform, Environment *p_env);
_FORCE_INLINE_ void _render_geometry(RenderList::Element *p_element);
virtual void render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp
index 21dd0faa85..0863e03b76 100644
--- a/drivers/gles2/rasterizer_storage_gles2.cpp
+++ b/drivers/gles2/rasterizer_storage_gles2.cpp
@@ -496,22 +496,6 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p
glTexParameterf(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
-//set swizle for older format compatibility
-#ifdef GLES_OVER_GL
- switch (texture->format) {
-
- case Image::FORMAT_L8: {
-
- } break;
- case Image::FORMAT_LA8: {
-
- } break;
- default: {
-
- } break;
- }
-#endif
-
int mipmaps = ((texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && img->has_mipmaps()) ? img->get_mipmap_count() + 1 : 1;
int w = img->get_width();
@@ -591,7 +575,7 @@ Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, int p_layer)
PoolVector<uint8_t> data;
- int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1 ? -1 : 0);
+ int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1);
data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers
PoolVector<uint8_t>::Write wb = data.write();
@@ -1309,8 +1293,13 @@ void RasterizerStorageGLES2::shader_get_param_list(RID p_shader, List<PropertyIn
pi.hint_string = "CubeMap";
} break;
- default: {
-
+ case ShaderLanguage::TYPE_SAMPLER2DARRAY:
+ case ShaderLanguage::TYPE_ISAMPLER2DARRAY:
+ case ShaderLanguage::TYPE_USAMPLER2DARRAY:
+ case ShaderLanguage::TYPE_SAMPLER3D:
+ case ShaderLanguage::TYPE_ISAMPLER3D:
+ case ShaderLanguage::TYPE_USAMPLER3D: {
+ // Not implemented in GLES2
} break;
}
@@ -2635,10 +2624,10 @@ void RasterizerStorageGLES2::update_dirty_multimeshes() {
if (multimesh->mesh.is_valid()) {
mesh_aabb = mesh_get_aabb(multimesh->mesh, RID());
- } else {
- mesh_aabb.size += Vector3(0.001, 0.001, 0.001);
}
+ mesh_aabb.size += Vector3(0.001, 0.001, 0.001); //in case mesh is empty in one of the sides
+
int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats;
int count = multimesh->data.size();
float *data = multimesh->data.ptrw();
@@ -3110,6 +3099,7 @@ void RasterizerStorageGLES2::light_set_param(RID p_light, VS::LightParam p_param
light->version++;
light->instance_change_notify();
} break;
+ default: {}
}
light->param[p_param] = p_value;
@@ -3301,69 +3291,194 @@ AABB RasterizerStorageGLES2::light_get_aabb(RID p_light) const {
/* PROBE API */
RID RasterizerStorageGLES2::reflection_probe_create() {
- return RID();
+
+ ReflectionProbe *reflection_probe = memnew(ReflectionProbe);
+
+ reflection_probe->intensity = 1.0;
+ reflection_probe->interior_ambient = Color();
+ reflection_probe->interior_ambient_energy = 1.0;
+ reflection_probe->max_distance = 0;
+ reflection_probe->extents = Vector3(1, 1, 1);
+ reflection_probe->origin_offset = Vector3(0, 0, 0);
+ reflection_probe->interior = false;
+ reflection_probe->box_projection = false;
+ reflection_probe->enable_shadows = false;
+ reflection_probe->cull_mask = (1 << 20) - 1;
+ reflection_probe->update_mode = VS::REFLECTION_PROBE_UPDATE_ONCE;
+ reflection_probe->resolution = 128;
+
+ return reflection_probe_owner.make_rid(reflection_probe);
}
void RasterizerStorageGLES2::reflection_probe_set_update_mode(RID p_probe, VS::ReflectionProbeUpdateMode p_mode) {
+
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->update_mode = p_mode;
+ reflection_probe->instance_change_notify();
}
void RasterizerStorageGLES2::reflection_probe_set_intensity(RID p_probe, float p_intensity) {
+
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->intensity = p_intensity;
}
void RasterizerStorageGLES2::reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient) {
+
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->interior_ambient = p_ambient;
}
void RasterizerStorageGLES2::reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) {
+
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->interior_ambient_energy = p_energy;
}
void RasterizerStorageGLES2::reflection_probe_set_interior_ambient_probe_contribution(RID p_probe, float p_contrib) {
+
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->interior_ambient_probe_contrib = p_contrib;
}
void RasterizerStorageGLES2::reflection_probe_set_max_distance(RID p_probe, float p_distance) {
-}
-void RasterizerStorageGLES2::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->max_distance = p_distance;
+ reflection_probe->instance_change_notify();
}
+void RasterizerStorageGLES2::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) {
+
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+ reflection_probe->extents = p_extents;
+ reflection_probe->instance_change_notify();
+}
void RasterizerStorageGLES2::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) {
+
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->origin_offset = p_offset;
+ reflection_probe->instance_change_notify();
}
void RasterizerStorageGLES2::reflection_probe_set_as_interior(RID p_probe, bool p_enable) {
-}
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->interior = p_enable;
+}
void RasterizerStorageGLES2::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) {
+
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->box_projection = p_enable;
}
void RasterizerStorageGLES2::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) {
-}
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->enable_shadows = p_enable;
+ reflection_probe->instance_change_notify();
+}
void RasterizerStorageGLES2::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) {
+
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->cull_mask = p_layers;
+ reflection_probe->instance_change_notify();
+}
+
+void RasterizerStorageGLES2::reflection_probe_set_resolution(RID p_probe, int p_resolution) {
+
+ ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND(!reflection_probe);
+
+ reflection_probe->resolution = p_resolution;
}
AABB RasterizerStorageGLES2::reflection_probe_get_aabb(RID p_probe) const {
- return AABB();
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND_V(!reflection_probe, AABB());
+
+ AABB aabb;
+ aabb.position = -reflection_probe->extents;
+ aabb.size = reflection_probe->extents * 2.0;
+
+ return aabb;
}
VS::ReflectionProbeUpdateMode RasterizerStorageGLES2::reflection_probe_get_update_mode(RID p_probe) const {
- return VS::REFLECTION_PROBE_UPDATE_ALWAYS;
+
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND_V(!reflection_probe, VS::REFLECTION_PROBE_UPDATE_ALWAYS);
+
+ return reflection_probe->update_mode;
}
uint32_t RasterizerStorageGLES2::reflection_probe_get_cull_mask(RID p_probe) const {
- return 0;
+
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND_V(!reflection_probe, 0);
+
+ return reflection_probe->cull_mask;
}
Vector3 RasterizerStorageGLES2::reflection_probe_get_extents(RID p_probe) const {
- return Vector3();
+
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND_V(!reflection_probe, Vector3());
+
+ return reflection_probe->extents;
}
Vector3 RasterizerStorageGLES2::reflection_probe_get_origin_offset(RID p_probe) const {
- return Vector3();
+
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND_V(!reflection_probe, Vector3());
+
+ return reflection_probe->origin_offset;
}
bool RasterizerStorageGLES2::reflection_probe_renders_shadows(RID p_probe) const {
- return false;
+
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND_V(!reflection_probe, false);
+
+ return reflection_probe->enable_shadows;
}
float RasterizerStorageGLES2::reflection_probe_get_origin_max_distance(RID p_probe) const {
- return 0;
+
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND_V(!reflection_probe, 0);
+
+ return reflection_probe->max_distance;
+}
+
+int RasterizerStorageGLES2::reflection_probe_get_resolution(RID p_probe) const {
+
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
+ ERR_FAIL_COND_V(!reflection_probe, 0);
+
+ return reflection_probe->resolution;
}
RID RasterizerStorageGLES2::gi_probe_create() {
@@ -3464,46 +3579,100 @@ void RasterizerStorageGLES2::gi_probe_dynamic_data_update(RID p_gi_probe_data, i
///////
RID RasterizerStorageGLES2::lightmap_capture_create() {
- return RID();
+
+ LightmapCapture *capture = memnew(LightmapCapture);
+ return lightmap_capture_data_owner.make_rid(capture);
}
void RasterizerStorageGLES2::lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) {
-}
-AABB RasterizerStorageGLES2::lightmap_capture_get_bounds(RID p_capture) const {
- return AABB();
+ LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND(!capture);
+ capture->bounds = p_bounds;
+ capture->instance_change_notify();
}
+AABB RasterizerStorageGLES2::lightmap_capture_get_bounds(RID p_capture) const {
-void RasterizerStorageGLES2::lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree) {
+ const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND_V(!capture, AABB());
+ return capture->bounds;
}
+void RasterizerStorageGLES2::lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree) {
+
+ LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND(!capture);
+ ERR_FAIL_COND(p_octree.size() == 0 || (p_octree.size() % sizeof(LightmapCaptureOctree)) != 0);
+
+ capture->octree.resize(p_octree.size() / sizeof(LightmapCaptureOctree));
+ if (p_octree.size()) {
+ PoolVector<LightmapCaptureOctree>::Write w = capture->octree.write();
+ PoolVector<uint8_t>::Read r = p_octree.read();
+ copymem(w.ptr(), r.ptr(), p_octree.size());
+ }
+ capture->instance_change_notify();
+}
PoolVector<uint8_t> RasterizerStorageGLES2::lightmap_capture_get_octree(RID p_capture) const {
- return PoolVector<uint8_t>();
+
+ const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND_V(!capture, PoolVector<uint8_t>());
+
+ if (capture->octree.size() == 0)
+ return PoolVector<uint8_t>();
+
+ PoolVector<uint8_t> ret;
+ ret.resize(capture->octree.size() * sizeof(LightmapCaptureOctree));
+ {
+ PoolVector<LightmapCaptureOctree>::Read r = capture->octree.read();
+ PoolVector<uint8_t>::Write w = ret.write();
+ copymem(w.ptr(), r.ptr(), ret.size());
+ }
+
+ return ret;
}
void RasterizerStorageGLES2::lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) {
+ LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND(!capture);
+ capture->cell_xform = p_xform;
}
Transform RasterizerStorageGLES2::lightmap_capture_get_octree_cell_transform(RID p_capture) const {
- return Transform();
+ const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND_V(!capture, Transform());
+ return capture->cell_xform;
}
void RasterizerStorageGLES2::lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) {
+ LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND(!capture);
+ capture->cell_subdiv = p_subdiv;
}
int RasterizerStorageGLES2::lightmap_capture_get_octree_cell_subdiv(RID p_capture) const {
- return 0;
+ const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND_V(!capture, 0);
+ return capture->cell_subdiv;
}
void RasterizerStorageGLES2::lightmap_capture_set_energy(RID p_capture, float p_energy) {
+
+ LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND(!capture);
+ capture->energy = p_energy;
}
float RasterizerStorageGLES2::lightmap_capture_get_energy(RID p_capture) const {
- return 0.0;
+
+ const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND_V(!capture, 0);
+ return capture->energy;
}
const PoolVector<RasterizerStorage::LightmapCaptureOctree> *RasterizerStorageGLES2::lightmap_capture_get_octree_ptr(RID p_capture) const {
- return NULL;
+ const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+ ERR_FAIL_COND_V(!capture, NULL);
+ return &capture->octree;
}
///////
@@ -3595,15 +3764,115 @@ void RasterizerStorageGLES2::update_particles() {
////////
void RasterizerStorageGLES2::instance_add_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) {
+
+ Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
+ ERR_FAIL_COND(!skeleton);
+
+ skeleton->instances.insert(p_instance);
}
void RasterizerStorageGLES2::instance_remove_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) {
+
+ Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
+ ERR_FAIL_COND(!skeleton);
+
+ skeleton->instances.erase(p_instance);
}
void RasterizerStorageGLES2::instance_add_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) {
+
+ Instantiable *inst = NULL;
+ switch (p_instance->base_type) {
+ case VS::INSTANCE_MESH: {
+ inst = mesh_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ case VS::INSTANCE_MULTIMESH: {
+ inst = multimesh_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ case VS::INSTANCE_IMMEDIATE: {
+ inst = immediate_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ /*case VS::INSTANCE_PARTICLES: {
+ inst = particles_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;*/
+ case VS::INSTANCE_REFLECTION_PROBE: {
+ inst = reflection_probe_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ case VS::INSTANCE_LIGHT: {
+ inst = light_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ /*case VS::INSTANCE_GI_PROBE: {
+ inst = gi_probe_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;*/
+ case VS::INSTANCE_LIGHTMAP_CAPTURE: {
+ inst = lightmap_capture_data_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ default: {
+ if (!inst) {
+ ERR_FAIL();
+ }
+ }
+ }
+
+ inst->instance_list.add(&p_instance->dependency_item);
}
void RasterizerStorageGLES2::instance_remove_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) {
+
+ Instantiable *inst = NULL;
+
+ switch (p_instance->base_type) {
+ case VS::INSTANCE_MESH: {
+ inst = mesh_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ case VS::INSTANCE_MULTIMESH: {
+ inst = multimesh_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ case VS::INSTANCE_IMMEDIATE: {
+ inst = immediate_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ /*case VS::INSTANCE_PARTICLES: {
+ inst = particles_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;*/
+ case VS::INSTANCE_REFLECTION_PROBE: {
+ inst = reflection_probe_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ case VS::INSTANCE_LIGHT: {
+ inst = light_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ /*case VS::INSTANCE_GI_PROBE: {
+ inst = gi_probe_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break; */
+ case VS::INSTANCE_LIGHTMAP_CAPTURE: {
+ inst = lightmap_capture_data_owner.getornull(p_base);
+ ERR_FAIL_COND(!inst);
+ } break;
+ default: {
+
+ if (!inst) {
+ ERR_FAIL();
+ }
+ }
+ }
+
+ ERR_FAIL_COND(!inst);
+
+ inst->instance_list.remove(&p_instance->dependency_item);
}
/* RENDER TARGET */
@@ -3861,6 +4130,10 @@ VS::InstanceType RasterizerStorageGLES2::get_base_type(RID p_rid) const {
return VS::INSTANCE_MULTIMESH;
} else if (immediate_owner.owns(p_rid)) {
return VS::INSTANCE_IMMEDIATE;
+ } else if (reflection_probe_owner.owns(p_rid)) {
+ return VS::INSTANCE_REFLECTION_PROBE;
+ } else if (lightmap_capture_data_owner.owns(p_rid)) {
+ return VS::INSTANCE_LIGHTMAP_CAPTURE;
} else {
return VS::INSTANCE_NONE;
}
@@ -4038,6 +4311,25 @@ bool RasterizerStorageGLES2::free(RID p_rid) {
memdelete(light);
return true;
+ } else if (reflection_probe_owner.owns(p_rid)) {
+
+ // delete the texture
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get(p_rid);
+ reflection_probe->instance_remove_deps();
+
+ reflection_probe_owner.free(p_rid);
+ memdelete(reflection_probe);
+
+ return true;
+ } else if (lightmap_capture_data_owner.owns(p_rid)) {
+
+ // delete the texture
+ LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get(p_rid);
+ lightmap_capture->instance_remove_deps();
+
+ lightmap_capture_data_owner.free(p_rid);
+ memdelete(lightmap_capture);
+ return true;
} else {
return false;
}
@@ -4089,6 +4381,7 @@ void RasterizerStorageGLES2::initialize() {
}
config.shrink_textures_x2 = false;
+
config.float_texture_supported = config.extensions.has("GL_ARB_texture_float") || config.extensions.has("GL_OES_texture_float");
config.s3tc_supported = config.extensions.has("GL_EXT_texture_compression_s3tc");
config.etc1_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture");
@@ -4249,6 +4542,7 @@ void RasterizerStorageGLES2::update_dirty_resources() {
update_dirty_shaders();
update_dirty_materials();
update_dirty_skeletons();
+ update_dirty_multimeshes();
}
RasterizerStorageGLES2::RasterizerStorageGLES2() {
diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h
index e6708914ec..b42e2dfb1f 100644
--- a/drivers/gles2/rasterizer_storage_gles2.h
+++ b/drivers/gles2/rasterizer_storage_gles2.h
@@ -157,7 +157,7 @@ public:
//////////////////////////////////DATA///////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
- struct Instanciable : public RID_Data {
+ struct Instantiable : public RID_Data {
SelfList<RasterizerScene::InstanceBase>::List instance_list;
_FORCE_INLINE_ void instance_change_notify() {
@@ -187,15 +187,15 @@ public:
}
}
- Instanciable() {}
+ Instantiable() {}
- virtual ~Instanciable() {}
+ virtual ~Instantiable() {}
};
- struct GeometryOwner : public Instanciable {
+ struct GeometryOwner : public Instantiable {
};
- struct Geometry : public Instanciable {
+ struct Geometry : public Instantiable {
enum Type {
GEOMETRY_INVALID,
@@ -893,7 +893,7 @@ public:
/* Light API */
- struct Light : Instanciable {
+ struct Light : Instantiable {
VS::LightType type;
float param[VS::LIGHT_PARAM_MAX];
@@ -955,6 +955,26 @@ public:
virtual uint64_t light_get_version(RID p_light) const;
/* PROBE API */
+
+ struct ReflectionProbe : Instantiable {
+
+ VS::ReflectionProbeUpdateMode update_mode;
+ float intensity;
+ Color interior_ambient;
+ float interior_ambient_energy;
+ float interior_ambient_probe_contrib;
+ float max_distance;
+ Vector3 extents;
+ Vector3 origin_offset;
+ bool interior;
+ bool box_projection;
+ bool enable_shadows;
+ uint32_t cull_mask;
+ int resolution;
+ };
+
+ mutable RID_Owner<ReflectionProbe> reflection_probe_owner;
+
virtual RID reflection_probe_create();
virtual void reflection_probe_set_update_mode(RID p_probe, VS::ReflectionProbeUpdateMode p_mode);
@@ -969,11 +989,14 @@ public:
virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable);
virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable);
virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers);
+ virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution);
virtual AABB reflection_probe_get_aabb(RID p_probe) const;
virtual VS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const;
virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const;
+ virtual int reflection_probe_get_resolution(RID p_probe) const;
+
virtual Vector3 reflection_probe_get_extents(RID p_probe) const;
virtual Vector3 reflection_probe_get_origin_offset(RID p_probe) const;
virtual float reflection_probe_get_origin_max_distance(RID p_probe) const;
@@ -1023,6 +1046,21 @@ public:
/* LIGHTMAP */
+ struct LightmapCapture : public Instantiable {
+
+ PoolVector<LightmapCaptureOctree> octree;
+ AABB bounds;
+ Transform cell_xform;
+ int cell_subdiv;
+ float energy;
+ LightmapCapture() {
+ energy = 1.0;
+ cell_subdiv = 1;
+ }
+ };
+
+ mutable RID_Owner<LightmapCapture> lightmap_capture_data_owner;
+
virtual RID lightmap_capture_create();
virtual void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds);
virtual AABB lightmap_capture_get_bounds(RID p_capture) const;
diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp
index bedcfbb798..082c520480 100644
--- a/drivers/gles2/shader_compiler_gles2.cpp
+++ b/drivers/gles2/shader_compiler_gles2.cpp
@@ -643,11 +643,11 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
case SL::OP_MOD: {
- code += "mod(";
+ code += "mod(float(";
code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
- code += ", ";
+ code += "), float(";
code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
- code += ")";
+ code += "))";
} break;
default: {
diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h
index 78e1962f80..9160a7c265 100644
--- a/drivers/gles2/shader_gles2.h
+++ b/drivers/gles2/shader_gles2.h
@@ -335,6 +335,19 @@ public:
case ShaderLanguage::TYPE_SAMPLERCUBE: {
} break;
+
+ case ShaderLanguage::TYPE_SAMPLER2DARRAY:
+ case ShaderLanguage::TYPE_ISAMPLER2DARRAY:
+ case ShaderLanguage::TYPE_USAMPLER2DARRAY:
+ case ShaderLanguage::TYPE_SAMPLER3D:
+ case ShaderLanguage::TYPE_ISAMPLER3D:
+ case ShaderLanguage::TYPE_USAMPLER3D: {
+ // Not implemented in GLES2
+ } break;
+
+ case ShaderLanguage::TYPE_VOID: {
+ // Nothing to do?
+ } break;
}
}
diff --git a/drivers/gles2/shaders/cubemap_filter.glsl b/drivers/gles2/shaders/cubemap_filter.glsl
index 2a1ad8d8f2..67b937984e 100644
--- a/drivers/gles2/shaders/cubemap_filter.glsl
+++ b/drivers/gles2/shaders/cubemap_filter.glsl
@@ -176,7 +176,6 @@ void main() {
#ifdef USE_SOURCE_PANORAMA
sum.rgb += texturePanorama(source_panorama, L).rgb * NdotL;
#else
- L.y = -L.y;
sum.rgb += textureCubeLod(source_cube, L, 0.0).rgb * NdotL;
#endif
diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl
index da4c3a84f1..462aff8828 100644
--- a/drivers/gles2/shaders/scene.glsl
+++ b/drivers/gles2/shaders/scene.glsl
@@ -27,15 +27,15 @@ attribute vec3 normal_attrib; // attrib:1
attribute vec4 tangent_attrib; // attrib:2
#endif
-#ifdef ENABLE_COLOR_INTERP
+#if defined(ENABLE_COLOR_INTERP)
attribute vec4 color_attrib; // attrib:3
#endif
-#ifdef ENABLE_UV_INTERP
+#if defined(ENABLE_UV_INTERP)
attribute vec2 uv_attrib; // attrib:4
#endif
-#ifdef ENABLE_UV2_INTERP
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
attribute vec2 uv2_attrib; // attrib:5
#endif
@@ -43,9 +43,9 @@ attribute vec2 uv2_attrib; // attrib:5
#ifdef USE_SKELETON_SOFTWARE
-attribute highp vec4 bone_transform_row_0; // attrib:8
-attribute highp vec4 bone_transform_row_1; // attrib:9
-attribute highp vec4 bone_transform_row_2; // attrib:10
+attribute highp vec4 bone_transform_row_0; // attrib:13
+attribute highp vec4 bone_transform_row_1; // attrib:14
+attribute highp vec4 bone_transform_row_2; // attrib:15
#else
@@ -102,15 +102,15 @@ varying vec3 tangent_interp;
varying vec3 binormal_interp;
#endif
-#ifdef ENABLE_COLOR_INTERP
+#if defined(ENABLE_COLOR_INTERP)
varying vec4 color_interp;
#endif
-#ifdef ENABLE_UV_INTERP
+#if defined(ENABLE_UV_INTERP)
varying vec2 uv_interp;
#endif
-#ifdef ENABLE_UV2_INTERP
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
varying vec2 uv2_interp;
#endif
@@ -130,11 +130,6 @@ uniform highp float shadow_dual_paraboloid_render_side;
#if defined(USE_SHADOW) && defined(USE_LIGHTING)
-#ifdef LIGHT_MODE_DIRECTIONAL
-uniform highp sampler2D light_directional_shadow; // texunit:-3
-uniform highp vec4 light_split_offsets;
-#endif
-
uniform highp mat4 light_shadow_matrix;
varying highp vec4 shadow_coord;
@@ -262,6 +257,35 @@ void light_compute(
#endif
+#ifdef USE_VERTEX_LIGHTING
+
+#ifdef USE_REFLECTION_PROBE1
+
+uniform mat4 refprobe1_local_matrix;
+varying mediump vec4 refprobe1_reflection_normal_blend;
+uniform vec3 refprobe1_box_extents;
+
+#ifndef USE_LIGHTMAP
+varying mediump vec3 refprobe1_ambient_normal;
+#endif
+
+#endif //reflection probe1
+
+#ifdef USE_REFLECTION_PROBE2
+
+uniform mat4 refprobe2_local_matrix;
+varying mediump vec4 refprobe2_reflection_normal_blend;
+uniform vec3 refprobe2_box_extents;
+
+#ifndef USE_LIGHTMAP
+varying mediump vec3 refprobe2_ambient_normal;
+#endif
+
+#endif //reflection probe2
+
+#endif //vertex lighting for refprobes
+
+
void main() {
highp vec4 vertex = vertex_attrib;
@@ -277,6 +301,7 @@ void main() {
vec4(0.0, 0.0, 0.0, 1.0));
world_matrix = world_matrix * transpose(m);
}
+
#endif
vec3 normal = normal_attrib * normal_mult;
@@ -288,18 +313,18 @@ void main() {
vec3 binormal = normalize(cross(normal, tangent) * binormalf);
#endif
-#ifdef ENABLE_COLOR_INTERP
+#if defined(ENABLE_COLOR_INTERP)
color_interp = color_attrib;
#ifdef USE_INSTANCING
color_interp *= instance_color;
#endif
#endif
-#ifdef ENABLE_UV_INTERP
+#if defined(ENABLE_UV_INTERP)
uv_interp = uv_attrib;
#endif
-#ifdef ENABLE_UV2_INTERP
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
uv2_interp = uv2_attrib;
#endif
@@ -492,12 +517,58 @@ VERTEX_SHADER_CODE
#if defined(LIGHT_USE_PSSM4)
shadow_coord3 = light_shadow_matrix3 * vi4;
- shadow_coord3 = light_shadow_matrix3 * vi4;
+ shadow_coord4 = light_shadow_matrix4 * vi4;
#endif
#endif //use shadow and use lighting
+#ifdef USE_VERTEX_LIGHTING
+
+#ifdef USE_REFLECTION_PROBE1
+ {
+ vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp));
+ vec3 local_pos = (refprobe1_local_matrix * vec4(vertex_interp, 1.0)).xyz;
+ vec3 inner_pos = abs(local_pos / refprobe1_box_extents);
+ float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z));
+
+ {
+ vec3 local_ref_vec = (refprobe1_local_matrix * vec4(ref_normal, 0.0)).xyz;
+ refprobe1_reflection_normal_blend.xyz = local_ref_vec;
+ refprobe1_reflection_normal_blend.a = blend;
+
+ }
+#ifndef USE_LIGHTMAP
+
+ refprobe1_ambient_normal = (refprobe1_local_matrix * vec4(normal_interp, 0.0)).xyz;
+#endif
+ }
+
+#endif //USE_REFLECTION_PROBE1
+
+
+#ifdef USE_REFLECTION_PROBE2
+ {
+ vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp));
+ vec3 local_pos = (refprobe2_local_matrix * vec4(vertex_interp, 1.0)).xyz;
+ vec3 inner_pos = abs(local_pos / refprobe2_box_extents);
+ float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z));
+
+ {
+ vec3 local_ref_vec = (refprobe2_local_matrix * vec4(ref_normal, 0.0)).xyz;
+ refprobe2_reflection_normal_blend.xyz = local_ref_vec;
+ refprobe2_reflection_normal_blend.a = blend;
+
+ }
+#ifndef USE_LIGHTMAP
+
+ refprobe2_ambient_normal = (refprobe2_local_matrix * vec4(normal_interp, 0.0)).xyz;
+#endif
+ }
+
+#endif //USE_REFLECTION_PROBE2
+
+#endif //use vertex lighting
gl_Position = projection_matrix * vec4(vertex_interp, 1.0);
}
@@ -537,7 +608,7 @@ uniform mat4 world_transform;
uniform highp float time;
-#ifdef SCREEN_UV_USED
+#if defined(SCREEN_UV_USED)
uniform vec2 screen_pixel_size;
#endif
@@ -548,10 +619,170 @@ uniform vec2 screen_pixel_size;
uniform highp sampler2D screen_texture; //texunit:-4
#endif
-#ifdef USE_RADIANCE_MAP
+#ifdef USE_REFLECTION_PROBE1
+
+#ifdef USE_VERTEX_LIGHTING
+
+varying mediump vec4 refprobe1_reflection_normal_blend;
+#ifndef USE_LIGHTMAP
+varying mediump vec3 refprobe1_ambient_normal;
+#endif
+
+#else
+
+uniform bool refprobe1_use_box_project;
+uniform vec3 refprobe1_box_extents;
+uniform vec3 refprobe1_box_offset;
+uniform mat4 refprobe1_local_matrix;
+
+#endif //use vertex lighting
+
+uniform bool refprobe1_exterior;
+
+uniform highp samplerCube reflection_probe1; //texunit:-5
+
+uniform float refprobe1_intensity;
+uniform vec4 refprobe1_ambient;
+
+#endif //USE_REFLECTION_PROBE1
+
+#ifdef USE_REFLECTION_PROBE2
+
+#ifdef USE_VERTEX_LIGHTING
+
+varying mediump vec4 refprobe2_reflection_normal_blend;
+#ifndef USE_LIGHTMAP
+varying mediump vec3 refprobe2_ambient_normal;
+#endif
+
+#else
+
+uniform bool refprobe2_use_box_project;
+uniform vec3 refprobe2_box_extents;
+uniform vec3 refprobe2_box_offset;
+uniform mat4 refprobe2_local_matrix;
+
+#endif //use vertex lighting
+
+uniform bool refprobe2_exterior;
+
+uniform highp samplerCube reflection_probe2; //texunit:-6
+
+uniform float refprobe2_intensity;
+uniform vec4 refprobe2_ambient;
+
+#endif //USE_REFLECTION_PROBE2
#define RADIANCE_MAX_LOD 6.0
+#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2)
+
+void reflection_process(samplerCube reflection_map,
+#ifdef USE_VERTEX_LIGHTING
+ vec3 ref_normal,
+#ifndef USE_LIGHTMAP
+ vec3 amb_normal,
+#endif
+ float ref_blend,
+
+#else //no vertex lighting
+ vec3 normal, vec3 vertex,
+ mat4 local_matrix,
+ bool use_box_project, vec3 box_extents, vec3 box_offset,
+#endif //vertex lighting
+ bool exterior,float intensity, vec4 ref_ambient, float roughness, vec3 ambient, vec3 skybox, inout highp vec4 reflection_accum, inout highp vec4 ambient_accum) {
+
+ vec4 reflection;
+
+#ifdef USE_VERTEX_LIGHTING
+
+ reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb;
+
+ float blend = ref_blend; //crappier blend formula for vertex
+ blend *= blend;
+ blend = max(0.0, 1.0 - blend);
+
+#else //fragment lighting
+
+ vec3 local_pos = (local_matrix * vec4(vertex, 1.0)).xyz;
+
+ if (any(greaterThan(abs(local_pos), box_extents))) { //out of the reflection box
+ return;
+ }
+
+ vec3 inner_pos = abs(local_pos / box_extents);
+ float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z));
+ blend = mix(length(inner_pos), blend, blend);
+ blend *= blend;
+ blend = max(0.0, 1.0 - blend);
+
+ //reflect and make local
+ vec3 ref_normal = normalize(reflect(vertex, normal));
+ ref_normal = (local_matrix * vec4(ref_normal, 0.0)).xyz;
+
+ if (use_box_project) { //box project
+
+ vec3 nrdir = normalize(ref_normal);
+ vec3 rbmax = (box_extents - local_pos) / nrdir;
+ vec3 rbmin = (-box_extents - local_pos) / nrdir;
+
+ vec3 rbminmax = mix(rbmin, rbmax, vec3(greaterThan(nrdir, vec3(0.0, 0.0, 0.0))));
+
+ float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
+ vec3 posonbox = local_pos + nrdir * fa;
+ ref_normal = posonbox - box_offset.xyz;
+ }
+
+ reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb;
+#endif
+
+ if (exterior) {
+ reflection.rgb = mix(skybox, reflection.rgb, blend);
+ }
+ reflection.rgb *= intensity;
+ reflection.a = blend;
+ reflection.rgb *= blend;
+
+ reflection_accum += reflection;
+
+
+#ifndef USE_LIGHTMAP
+
+ vec4 ambient_out;
+#ifndef USE_VERTEX_LIGHTING
+
+ vec3 amb_normal = (local_matrix * vec4(normal, 0.0)).xyz;
+#endif
+
+ ambient_out.rgb = textureCubeLod(reflection_map, amb_normal, RADIANCE_MAX_LOD).rgb;
+ ambient_out.rgb = mix(ref_ambient.rgb, ambient_out.rgb, ref_ambient.a);
+ if (exterior) {
+ ambient_out.rgb = mix(ambient, ambient_out.rgb, blend);
+ }
+
+ ambient_out.a = blend;
+ ambient_out.rgb *= blend;
+ ambient_accum += ambient_out;
+
+#endif
+}
+
+#endif //use refprobe 1 or 2
+
+#ifdef USE_LIGHTMAP
+uniform mediump sampler2D lightmap; //texunit:-4
+uniform mediump float lightmap_energy;
+#endif
+
+#ifdef USE_LIGHTMAP_CAPTURE
+uniform mediump vec4[12] lightmap_captures;
+uniform bool lightmap_capture_sky;
+
+#endif
+
+#ifdef USE_RADIANCE_MAP
+
+
uniform samplerCube radiance_map; // texunit:-2
uniform mat4 radiance_inverse_xform;
@@ -640,15 +871,15 @@ varying vec3 tangent_interp;
varying vec3 binormal_interp;
#endif
-#ifdef ENABLE_COLOR_INTERP
+#if defined(ENABLE_COLOR_INTERP)
varying vec4 color_interp;
#endif
-#ifdef ENABLE_UV_INTERP
+#if defined(ENABLE_UV_INTERP)
varying vec2 uv_interp;
#endif
-#ifdef ENABLE_UV2_INTERP
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
varying vec2 uv2_interp;
#endif
@@ -660,6 +891,8 @@ vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) {
return mix(vec3(dielectric), albedo, metallic); // TODO: reference?
}
+
+
/* clang-format off */
FRAGMENT_SHADER_GLOBALS
@@ -1057,11 +1290,11 @@ void main() {
#endif
float normaldepth = 1.0;
-#ifdef ALPHA_SCISSOR_USED
+#if defined(ALPHA_SCISSOR_USED)
float alpha_scissor = 0.5;
#endif
-#ifdef SCREEN_UV_USED
+#if defined(SCREEN_UV_USED)
vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
#endif
@@ -1091,7 +1324,7 @@ FRAGMENT_SHADER_CODE
vec3 eye_position = -normalize(vertex_interp);
-#ifdef ALPHA_SCISSOR_USED
+#if defined(ALPHA_SCISSOR_USED)
if (alpha < alpha_scissor) {
discard;
}
@@ -1123,6 +1356,105 @@ FRAGMENT_SHADER_CODE
ambient_light *= ambient_energy;
+
+#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2)
+
+
+
+ vec4 ambient_accum = vec4(0.0);
+ vec4 reflection_accum = vec4(0.0);
+
+#ifdef USE_REFLECTION_PROBE1
+
+ reflection_process(reflection_probe1,
+#ifdef USE_VERTEX_LIGHTING
+ refprobe1_reflection_normal_blend.rgb,
+#ifndef USE_LIGHTMAP
+ refprobe1_ambient_normal,
+#endif
+ refprobe1_reflection_normal_blend.a,
+#else
+ normal_interp,vertex_interp,refprobe1_local_matrix,
+ refprobe1_use_box_project,refprobe1_box_extents,refprobe1_box_offset,
+#endif
+ refprobe1_exterior,refprobe1_intensity, refprobe1_ambient, roughness,
+ ambient_light, specular_light, reflection_accum, ambient_accum);
+
+
+
+#endif // USE_REFLECTION_PROBE1
+
+#ifdef USE_REFLECTION_PROBE2
+
+ reflection_process(reflection_probe2,
+#ifdef USE_VERTEX_LIGHTING
+ refprobe2_reflection_normal_blend.rgb,
+#ifndef USE_LIGHTMAP
+ refprobe2_ambient_normal,
+#endif
+ refprobe2_reflection_normal_blend.a,
+#else
+ normal_interp,vertex_interp,refprobe2_local_matrix,
+ refprobe2_use_box_project,refprobe2_box_extents,refprobe2_box_offset,
+#endif
+ refprobe2_exterior,refprobe2_intensity, refprobe2_ambient, roughness,
+ ambient_light, specular_light, reflection_accum, ambient_accum);
+
+#endif // USE_REFLECTION_PROBE2
+
+ if (reflection_accum.a > 0.0) {
+ specular_light = reflection_accum.rgb / reflection_accum.a;
+ }
+
+#ifndef USE_LIGHTMAP
+ if (ambient_accum.a > 0.0) {
+ ambient_light = ambient_accum.rgb / ambient_accum.a;
+ }
+#endif
+
+#endif // defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2)
+
+#ifdef USE_LIGHTMAP
+ //ambient light will come entirely from lightmap is lightmap is used
+ ambient_light = texture2D(lightmap, uv2_interp).rgb * lightmap_energy;
+#endif
+
+#ifdef USE_LIGHTMAP_CAPTURE
+ {
+ vec3 cone_dirs[12] = vec3[](
+ vec3(0, 0, 1),
+ vec3(0.866025, 0, 0.5),
+ vec3(0.267617, 0.823639, 0.5),
+ vec3(-0.700629, 0.509037, 0.5),
+ vec3(-0.700629, -0.509037, 0.5),
+ vec3(0.267617, -0.823639, 0.5),
+ vec3(0, 0, -1),
+ vec3(0.866025, 0, -0.5),
+ vec3(0.267617, 0.823639, -0.5),
+ vec3(-0.700629, 0.509037, -0.5),
+ vec3(-0.700629, -0.509037, -0.5),
+ vec3(0.267617, -0.823639, -0.5));
+
+ vec3 local_normal = normalize(camera_matrix * vec4(normal, 0.0)).xyz;
+ vec4 captured = vec4(0.0);
+ float sum = 0.0;
+ for (int i = 0; i < 12; i++) {
+ float amount = max(0.0, dot(local_normal, cone_dirs[i])); //not correct, but creates a nice wrap around effect
+ captured += lightmap_captures[i] * amount;
+ sum += amount;
+ }
+
+ captured /= sum;
+
+ if (lightmap_capture_sky) {
+ ambient_light = mix(ambient_light, captured.rgb, captured.a);
+ } else {
+ ambient_light = captured.rgb;
+ }
+ }
+#endif
+
+
#endif //BASE PASS
//
diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h
index 0a264caf8f..543011aff3 100644
--- a/drivers/gles3/rasterizer_gles3.h
+++ b/drivers/gles3/rasterizer_gles3.h
@@ -66,6 +66,8 @@ public:
static void make_current();
static void register_config();
+ virtual bool is_low_end() const { return false; }
+
RasterizerGLES3();
~RasterizerGLES3();
};
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index e74fe113c0..1fcd4e02ac 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -1282,6 +1282,8 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m
case ShaderLanguage::TYPE_SAMPLER2DARRAY: {
// TODO
} break;
+
+ default: {}
}
}
@@ -1509,6 +1511,7 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo
}
} break;
+ default: {}
}
}
@@ -1830,6 +1833,7 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) {
}
} break;
+ default: {}
}
}
@@ -3210,6 +3214,7 @@ void RasterizerSceneGLES3::_fill_render_list(InstanceBase **p_cull_result, int p
}
} break;
+ default: {}
}
}
}
@@ -4296,7 +4301,6 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
if (env) {
switch (env->bg_mode) {
case VS::ENV_BG_COLOR_SKY:
-
case VS::ENV_BG_SKY:
sky = storage->sky_owner.getornull(env->sky);
@@ -4334,6 +4338,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
break;
+ default: {}
}
}
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index 67863ac63e..797441c3a1 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -1029,7 +1029,7 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer)
PoolVector<uint8_t> data;
- int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1 ? -1 : 0);
+ int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1);
data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers
PoolVector<uint8_t>::Write wb = data.write();
@@ -1905,6 +1905,7 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
actions = &shaders.actions_particles;
actions->uniforms = &p_shader->uniforms;
} break;
+ case VS::SHADER_MAX: break; // Can't happen, but silences warning
}
Error err = shaders.compiler.compile(p_shader->mode, p_shader->code, actions, p_shader->path, gen_code);
@@ -2028,6 +2029,14 @@ void RasterizerStorageGLES3::shader_get_param_list(RID p_shader, List<PropertyIn
pi.hint = PROPERTY_HINT_RESOURCE_TYPE;
pi.hint_string = "Texture";
} break;
+ case ShaderLanguage::TYPE_SAMPLER2DARRAY:
+ case ShaderLanguage::TYPE_ISAMPLER2DARRAY:
+ case ShaderLanguage::TYPE_USAMPLER2DARRAY: {
+
+ pi.type = Variant::OBJECT;
+ pi.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ pi.hint_string = "TextureArray";
+ } break;
case ShaderLanguage::TYPE_SAMPLER3D:
case ShaderLanguage::TYPE_ISAMPLER3D:
case ShaderLanguage::TYPE_USAMPLER3D: {
@@ -4961,6 +4970,7 @@ void RasterizerStorageGLES3::light_set_param(RID p_light, VS::LightParam p_param
light->version++;
light->instance_change_notify();
} break;
+ default: {}
}
light->param[p_param] = p_value;
@@ -5285,6 +5295,9 @@ void RasterizerStorageGLES3::reflection_probe_set_cull_mask(RID p_probe, uint32_
reflection_probe->instance_change_notify();
}
+void RasterizerStorageGLES3::reflection_probe_set_resolution(RID p_probe, int p_resolution) {
+}
+
AABB RasterizerStorageGLES3::reflection_probe_get_aabb(RID p_probe) const {
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
ERR_FAIL_COND_V(!reflection_probe, AABB());
@@ -7351,7 +7364,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
GIProbeData *gi_probe_data = gi_probe_data_owner.get(p_rid);
glDeleteTextures(1, &gi_probe_data->tex_id);
- gi_probe_owner.free(p_rid);
+ gi_probe_data_owner.free(p_rid);
memdelete(gi_probe_data);
} else if (lightmap_capture_data_owner.owns(p_rid)) {
@@ -7359,7 +7372,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get(p_rid);
lightmap_capture->instance_remove_deps();
- gi_probe_owner.free(p_rid);
+ lightmap_capture_data_owner.free(p_rid);
memdelete(lightmap_capture);
} else if (canvas_occluder_owner.owns(p_rid)) {
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index 0bd9c22be5..b31c5ff7bb 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -1005,6 +1005,7 @@ public:
virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable);
virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable);
virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers);
+ virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution);
virtual AABB reflection_probe_get_aabb(RID p_probe) const;
virtual VS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const;
diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp
index 350107de69..dbc8507951 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -79,6 +79,12 @@ static int _get_datatype_size(SL::DataType p_type) {
case SL::TYPE_SAMPLER2D: return 16;
case SL::TYPE_ISAMPLER2D: return 16;
case SL::TYPE_USAMPLER2D: return 16;
+ case SL::TYPE_SAMPLER2DARRAY: return 16;
+ case SL::TYPE_ISAMPLER2DARRAY: return 16;
+ case SL::TYPE_USAMPLER2DARRAY: return 16;
+ case SL::TYPE_SAMPLER3D: return 16;
+ case SL::TYPE_ISAMPLER3D: return 16;
+ case SL::TYPE_USAMPLER3D: return 16;
case SL::TYPE_SAMPLERCUBE: return 16;
}
@@ -112,6 +118,12 @@ static int _get_datatype_alignment(SL::DataType p_type) {
case SL::TYPE_SAMPLER2D: return 16;
case SL::TYPE_ISAMPLER2D: return 16;
case SL::TYPE_USAMPLER2D: return 16;
+ case SL::TYPE_SAMPLER2DARRAY: return 16;
+ case SL::TYPE_ISAMPLER2DARRAY: return 16;
+ case SL::TYPE_USAMPLER2DARRAY: return 16;
+ case SL::TYPE_SAMPLER3D: return 16;
+ case SL::TYPE_ISAMPLER3D: return 16;
+ case SL::TYPE_USAMPLER3D: return 16;
case SL::TYPE_SAMPLERCUBE: return 16;
}
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index 7da20dfa00..bcaf4a57a8 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -1136,8 +1136,9 @@ float sample_shadow(highp sampler2DShadow shadow, vec2 shadow_pixel_size, vec2 p
avg += textureProj(shadow, vec4(pos + vec2(0.0, shadow_pixel_size.y * 2.0), depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(0.0, -shadow_pixel_size.y * 2.0), depth, 1.0));
return avg * (1.0 / 13.0);
+#endif
-#elif defined(SHADOW_MODE_PCF_5)
+#ifdef SHADOW_MODE_PCF_5
float avg = textureProj(shadow, vec4(pos, depth, 1.0));
avg += textureProj(shadow, vec4(pos + vec2(shadow_pixel_size.x, 0.0), depth, 1.0));
@@ -1146,7 +1147,9 @@ float sample_shadow(highp sampler2DShadow shadow, vec2 shadow_pixel_size, vec2 p
avg += textureProj(shadow, vec4(pos + vec2(0.0, -shadow_pixel_size.y), depth, 1.0));
return avg * (1.0 / 5.0);
-#else
+#endif
+
+#if !defined(SHADOW_MODE_PCF_5) || !defined(SHADOW_MODE_PCF_13)
return textureProj(shadow, vec4(pos, depth, 1.0));
diff --git a/drivers/png/SCsub b/drivers/png/SCsub
index 39480351a6..986c36c67c 100644
--- a/drivers/png/SCsub
+++ b/drivers/png/SCsub
@@ -26,14 +26,22 @@ if env['builtin_libpng']:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_png.add_source_files(env.drivers_sources, thirdparty_sources)
env_png.Append(CPPPATH=[thirdparty_dir])
# Currently .ASM filter_neon.S does not compile on NT.
import os
- if ("neon_enabled" in env and env["neon_enabled"]) and os.name != "nt":
+ use_neon = "neon_enabled" in env and env["neon_enabled"] and os.name != "nt"
+ if use_neon:
env_png.Append(CPPFLAGS=["-DPNG_ARM_NEON_OPT=2"])
- env_neon = env_png.Clone()
+ else:
+ env_png.Append(CPPFLAGS=["-DPNG_ARM_NEON_OPT=0"])
+
+ env_thirdparty = env_png.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources)
+
+ if use_neon:
+ env_neon = env_thirdparty.Clone()
if "S_compiler" in env:
env_neon['CC'] = env['S_compiler']
neon_sources = []
@@ -41,8 +49,6 @@ if env['builtin_libpng']:
neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon_intrinsics.c"))
neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon.S"))
env.drivers_sources += neon_sources
- else:
- env_png.Append(CPPFLAGS=["-DPNG_ARM_NEON_OPT=0"])
# Godot source files
env_png.add_source_files(env.drivers_sources, "*.cpp")
diff --git a/drivers/pulseaudio/SCsub b/drivers/pulseaudio/SCsub
index ee39fd2631..28b315ae66 100644
--- a/drivers/pulseaudio/SCsub
+++ b/drivers/pulseaudio/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.drivers_sources, "*.cpp")
-
-Export('env')
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 5ca9e79ab9..9c02549e39 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -43,10 +43,13 @@ void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) {
case PA_CONTEXT_FAILED:
ad->pa_ready = -1;
break;
-
case PA_CONTEXT_READY:
ad->pa_ready = 1;
break;
+ default:
+ // TODO: Check if we want to handle some of the other
+ // PA context states like PA_CONTEXT_UNCONNECTED.
+ break;
}
}
diff --git a/drivers/rtaudio/SCsub b/drivers/rtaudio/SCsub
index 2b0a602965..285658073c 100644
--- a/drivers/rtaudio/SCsub
+++ b/drivers/rtaudio/SCsub
@@ -11,9 +11,12 @@ thirdparty_sources = [
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env.add_source_files(env.drivers_sources, thirdparty_sources)
env.Append(CPPPATH=[thirdparty_dir])
+env_thirdparty = env.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources)
+
# Driver source files
env.add_source_files(env.drivers_sources, "*.cpp")
diff --git a/drivers/unix/SCsub b/drivers/unix/SCsub
index ada8255580..4888f56099 100644
--- a/drivers/unix/SCsub
+++ b/drivers/unix/SCsub
@@ -5,5 +5,3 @@ Import('env')
env.add_source_files(env.drivers_sources, "*.cpp")
env["check_c_headers"] = [ [ "mntent.h", "HAVE_MNTENT" ] ]
-
-Export('env')
diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp
index e433efcc65..3f03175403 100644
--- a/drivers/unix/net_socket_posix.cpp
+++ b/drivers/unix/net_socket_posix.cpp
@@ -94,7 +94,7 @@
#endif
-static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type) {
+size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type) {
memset(p_addr, 0, sizeof(struct sockaddr_storage));
if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket
@@ -126,12 +126,12 @@ static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IP_Addres
addr4->sin_addr.s_addr = INADDR_ANY;
}
- copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 16);
+ copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4);
return sizeof(sockaddr_in);
}
}
-static void _set_ip_port(IP_Address &r_ip, uint16_t &r_port, struct sockaddr_storage *p_addr) {
+void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port) {
if (p_addr->ss_family == AF_INET) {
@@ -612,7 +612,7 @@ Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) {
SOCKET_TYPE fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size);
ERR_FAIL_COND_V(fd == SOCK_EMPTY, out);
- _set_ip_port(r_ip, r_port, &their_addr);
+ _set_ip_port(&their_addr, r_ip, r_port);
NetSocketPosix *ns = memnew(NetSocketPosix);
ns->_set_socket(fd, _ip_type, _is_stream);
diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h
index 8177e01987..010f2ea6e0 100644
--- a/drivers/unix/net_socket_posix.h
+++ b/drivers/unix/net_socket_posix.h
@@ -39,6 +39,7 @@
#define SOCKET_TYPE SOCKET
#else
+#include <sys/socket.h>
#define SOCKET_TYPE int
#endif
@@ -68,6 +69,8 @@ protected:
public:
static void make_default();
static void cleanup();
+ static void _set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port);
+ static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type);
virtual Error open(Type p_sock_type, IP::Type &ip_type);
virtual void close();
diff --git a/drivers/unix/socket_helpers.h b/drivers/unix/socket_helpers.h
deleted file mode 100644
index 5b42c13eae..0000000000
--- a/drivers/unix/socket_helpers.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*************************************************************************/
-/* socket_helpers.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef SOCKET_HELPERS_H
-#define SOCKET_HELPERS_H
-
-#include <string.h>
-
-#if defined(__MINGW32__) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 4)
-// Workaround for mingw-w64 < 4.0
-#ifndef IPV6_V6ONLY
-#define IPV6_V6ONLY 27
-#endif
-#endif
-
-// helpers for sockaddr -> IP_Address and back, should work for posix and winsock. All implementations should use this
-
-static size_t _set_sockaddr(struct sockaddr_storage *p_addr, const IP_Address &p_ip, int p_port, IP::Type p_sock_type = IP::TYPE_ANY) {
-
- memset(p_addr, 0, sizeof(struct sockaddr_storage));
-
- ERR_FAIL_COND_V(!p_ip.is_valid(), 0);
-
- // IPv6 socket
- if (p_sock_type == IP::TYPE_IPV6 || p_sock_type == IP::TYPE_ANY) {
-
- // IPv6 only socket with IPv4 address
- ERR_FAIL_COND_V(p_sock_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0);
-
- struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
- addr6->sin6_family = AF_INET6;
- addr6->sin6_port = htons(p_port);
- copymem(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16);
- return sizeof(sockaddr_in6);
-
- } else { // IPv4 socket
-
- // IPv4 socket with IPv6 address
- ERR_FAIL_COND_V(!p_ip.is_ipv4(), 0);
-
- struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
- addr4->sin_family = AF_INET;
- addr4->sin_port = htons(p_port); // short, network byte order
- copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 16);
- return sizeof(sockaddr_in);
- };
-};
-
-static size_t _set_listen_sockaddr(struct sockaddr_storage *p_addr, int p_port, IP::Type p_sock_type, const IP_Address p_bind_address) {
-
- memset(p_addr, 0, sizeof(struct sockaddr_storage));
- if (p_sock_type == IP::TYPE_IPV4) {
- struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
- addr4->sin_family = AF_INET;
- addr4->sin_port = htons(p_port);
- if (p_bind_address.is_valid()) {
- copymem(&addr4->sin_addr.s_addr, p_bind_address.get_ipv4(), 4);
- } else {
- addr4->sin_addr.s_addr = INADDR_ANY;
- }
- return sizeof(sockaddr_in);
- } else {
- struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
-
- addr6->sin6_family = AF_INET6;
- addr6->sin6_port = htons(p_port);
- if (p_bind_address.is_valid()) {
- copymem(&addr6->sin6_addr.s6_addr, p_bind_address.get_ipv6(), 16);
- } else {
- addr6->sin6_addr = in6addr_any;
- }
- return sizeof(sockaddr_in6);
- };
-};
-
-static int _socket_create(IP::Type &p_type, int type, int protocol) {
-
- ERR_FAIL_COND_V(p_type > IP::TYPE_ANY || p_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);
-
- int family = p_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6;
- int sockfd = socket(family, type, protocol);
-
- if (sockfd == -1 && p_type == IP::TYPE_ANY) {
- // Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket
- // in place of a dual stack one, and further calls to _set_sock_addr will work as expected.
- p_type = IP::TYPE_IPV4;
- family = AF_INET;
- sockfd = socket(family, type, protocol);
- }
-
- ERR_FAIL_COND_V(sockfd == -1, -1);
-
- if (family == AF_INET6) {
- // Select IPv4 over IPv6 mapping
- int opt = p_type != IP::TYPE_ANY;
- if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&opt, sizeof(opt)) != 0) {
- WARN_PRINT("Unable to set/unset IPv4 address mapping over IPv6");
- }
- }
- if (protocol == IPPROTO_UDP && p_type != IP::TYPE_IPV6) {
- // Enable broadcasting for UDP sockets if it's not IPv6 only (IPv6 has no broadcast option).
- int broadcast = 1;
- if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast)) != 0) {
- WARN_PRINT("Error when enabling broadcasting");
- }
- }
-
- return sockfd;
-}
-
-static void _set_ip_addr_port(IP_Address &r_ip, int &r_port, struct sockaddr_storage *p_addr) {
-
- if (p_addr->ss_family == AF_INET) {
-
- struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
- r_ip.set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr));
-
- r_port = ntohs(addr4->sin_port);
-
- } else if (p_addr->ss_family == AF_INET6) {
-
- struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
- r_ip.set_ipv6(addr6->sin6_addr.s6_addr);
-
- r_port = ntohs(addr6->sin6_port);
- };
-};
-
-#endif
diff --git a/drivers/wasapi/SCsub b/drivers/wasapi/SCsub
index 233593b0f9..4c24925192 100644
--- a/drivers/wasapi/SCsub
+++ b/drivers/wasapi/SCsub
@@ -4,5 +4,3 @@ Import('env')
# Driver source files
env.add_source_files(env.drivers_sources, "*.cpp")
-
-Export('env')
diff --git a/drivers/windows/SCsub b/drivers/windows/SCsub
index ee39fd2631..28b315ae66 100644
--- a/drivers/windows/SCsub
+++ b/drivers/windows/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.drivers_sources, "*.cpp")
-
-Export('env')
diff --git a/drivers/winmidi/SCsub b/drivers/winmidi/SCsub
index 233593b0f9..4c24925192 100644
--- a/drivers/winmidi/SCsub
+++ b/drivers/winmidi/SCsub
@@ -4,5 +4,3 @@ Import('env')
# Driver source files
env.add_source_files(env.drivers_sources, "*.cpp")
-
-Export('env')
diff --git a/drivers/xaudio2/SCsub b/drivers/xaudio2/SCsub
index cb780a893b..3dca95b429 100644
--- a/drivers/xaudio2/SCsub
+++ b/drivers/xaudio2/SCsub
@@ -5,5 +5,3 @@ Import('env')
env.add_source_files(env.drivers_sources, "*.cpp")
env.Append(CXXFLAGS=['-DXAUDIO2_ENABLED'])
env.Append(LINKFLAGS=['xaudio2_8.lib'])
-
-Export('env')
diff --git a/drivers/zlib/SCsub b/drivers/zlib/SCsub
deleted file mode 100644
index 407deb5f6e..0000000000
--- a/drivers/zlib/SCsub
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python
-
-Import('env')
-
-# Not cloning the env, the includes need to be accessible for core/
-
-# Thirdparty source files
-# No check here as already done in drivers/SCsub
-thirdparty_dir = "#thirdparty/zlib/"
-thirdparty_sources = [
- "adler32.c",
- "compress.c",
- "crc32.c",
- "deflate.c",
- "infback.c",
- "inffast.c",
- "inflate.c",
- "inftrees.c",
- "trees.c",
- "uncompr.c",
- "zutil.c",
-]
-thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
-env.add_source_files(env.drivers_sources, thirdparty_sources)
-env.Append(CPPPATH=[thirdparty_dir])
diff --git a/editor/SCsub b/editor/SCsub
index 82b982eef2..d5ac8c7008 100644
--- a/editor/SCsub
+++ b/editor/SCsub
@@ -1,6 +1,7 @@
#!/usr/bin/env python
Import('env')
+
env.editor_sources = []
import os
@@ -89,5 +90,3 @@ if env['tools']:
lib = env.add_library("editor", env.editor_sources)
env.Prepend(LIBS=[lib])
-
- Export('env')
diff --git a/editor/collada/SCsub b/editor/collada/SCsub
index 04c9a827ef..2b1e889fb0 100644
--- a/editor/collada/SCsub
+++ b/editor/collada/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.editor_sources, "*.cpp")
-
-Export('env')
diff --git a/editor/doc/SCsub b/editor/doc/SCsub
index 04c9a827ef..2b1e889fb0 100644
--- a/editor/doc/SCsub
+++ b/editor/doc/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.editor_sources, "*.cpp")
-
-Export('env')
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index 08fd8a1319..38bdba31ea 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -209,6 +209,10 @@ void EditorFileDialog::update_dir() {
case MODE_OPEN_DIR:
get_ok()->set_text(TTR("Select Current Folder"));
break;
+ case MODE_OPEN_ANY:
+ case MODE_SAVE_FILE:
+ // FIXME: Implement, or refactor to avoid duplication with set_mode
+ break;
}
}
@@ -504,6 +508,11 @@ void EditorFileDialog::_items_clear_selection() {
get_ok()->set_disabled(false);
get_ok()->set_text(TTR("Select Current Folder"));
break;
+
+ case MODE_OPEN_ANY:
+ case MODE_SAVE_FILE:
+ // FIXME: Implement, or refactor to avoid duplication with set_mode
+ break;
}
}
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 49d9dca701..10fc15e468 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -1449,6 +1449,9 @@ void EditorInspector::update_tree() {
} else if (!(p.usage & PROPERTY_USAGE_EDITOR))
continue;
+ if (p.usage & PROPERTY_USAGE_HIGH_END_GFX && VS::get_singleton()->is_low_end())
+ continue; //do not show this property in low end gfx
+
if (p.name == "script" && (hide_script || bool(object->call("_hide_script_from_inspector")))) {
continue;
}
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 3fc35810df..5f5c46f4a7 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -49,11 +49,6 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f
err_str = String(p_file) + ":" + itos(p_line) + " - " + String(p_error);
}
- /*
- if (!self->is_visible_in_tree())
- self->emit_signal("show_request");
- */
-
if (p_type == ERR_HANDLER_WARNING) {
self->add_message(err_str, MSG_TYPE_WARNING);
} else {
@@ -76,17 +71,6 @@ void EditorLog::_notification(int p_what) {
}
}
}
-
- /*if (p_what==NOTIFICATION_DRAW) {
-
- RID ci = get_canvas_item();
- get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size()));
- int top_ofs = 20;
- int border_ofs=4;
- Ref<StyleBox> style = get_stylebox("normal","TextEdit");
-
- style->draw(ci,Rect2( Point2(border_ofs,top_ofs),get_size()-Size2(border_ofs*2,top_ofs+border_ofs)));
- }*/
}
void EditorLog::_clear_request() {
@@ -105,6 +89,8 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) {
bool restore = p_type != MSG_TYPE_STD;
switch (p_type) {
+ case MSG_TYPE_STD: {
+ } break;
case MSG_TYPE_ERROR: {
log->push_color(get_color("error_color", "Editor"));
Ref<Texture> icon = get_icon("Error", "EditorIcons");
@@ -122,7 +108,6 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) {
}
log->add_text(p_msg);
- //button->set_text(p_msg);
if (restore)
log->pop();
@@ -132,21 +117,6 @@ void EditorLog::set_tool_button(ToolButton *p_tool_button) {
tool_button = p_tool_button;
}
-/*
-void EditorLog::_dragged(const Point2& p_ofs) {
-
- int ofs = ec->get_minsize().height;
- ofs = ofs-p_ofs.y;
- if (ofs<50)
- ofs=50;
- if (ofs>300)
- ofs=300;
- ec->set_minsize(Size2(ec->get_minsize().width,ofs));
- minimum_size_changed();
-
-}
-*/
-
void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) {
EditorLog *self = (EditorLog *)p_self;
@@ -156,7 +126,6 @@ void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) {
void EditorLog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_clear_request"), &EditorLog::_clear_request);
- //ClassDB::bind_method(D_METHOD("_dragged"),&EditorLog::_dragged );
ADD_SIGNAL(MethodInfo("clear_request"));
}
@@ -187,7 +156,6 @@ EditorLog::EditorLog() {
log->set_h_size_flags(SIZE_EXPAND_FILL);
vb->add_child(log);
add_message(VERSION_FULL_NAME " (c) 2007-2018 Juan Linietsky, Ariel Manzur & Godot Contributors.");
- //log->add_text("Initialization Complete.\n"); //because it looks cool.
eh.errfunc = _error_handler;
eh.userdata = this;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 03746fb8b7..330173a8c3 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -4523,6 +4523,16 @@ void EditorNode::_bottom_panel_raise_toggled(bool p_pressed) {
}
}
+void EditorNode::_update_video_driver_color() {
+
+ //todo probably should de-harcode this and add to editor settings
+ if (video_driver->get_text() == "GLES2") {
+ video_driver->add_color_override("font_color", Color::hex(0x5586a4ff));
+ } else if (video_driver->get_text() == "GLES3") {
+ video_driver->add_color_override("font_color", Color::hex(0xa5557dff));
+ }
+}
+
void EditorNode::_video_driver_selected(int p_which) {
String driver = video_driver->get_item_metadata(p_which);
@@ -4536,6 +4546,7 @@ void EditorNode::_video_driver_selected(int p_which) {
video_driver_request = driver;
video_restart_dialog->popup_centered_minsize();
video_driver->select(video_driver_current);
+ _update_video_driver_color();
}
void EditorNode::_bind_methods() {
@@ -5398,6 +5409,7 @@ EditorNode::EditorNode() {
video_driver->set_focus_mode(Control::FOCUS_NONE);
video_driver->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
video_driver->connect("item_selected", this, "_video_driver_selected");
+ video_driver->add_font_override("font", gui_base->get_font("bold", "EditorFonts"));
menu_hb->add_child(video_driver);
String video_drivers = ProjectSettings::get_singleton()->get_custom_property_info()["rendering/quality/driver/driver_name"].hint_string;
@@ -5414,6 +5426,8 @@ EditorNode::EditorNode() {
}
}
+ _update_video_driver_color();
+
video_restart_dialog = memnew(ConfirmationDialog);
video_restart_dialog->set_text(TTR("Changing the video driver requires restarting the editor."));
video_restart_dialog->get_ok()->set_text(TTR("Save & Restart"));
diff --git a/editor/editor_node.h b/editor/editor_node.h
index bdbe0a245b..0096748ed1 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -208,6 +208,7 @@ private:
int video_driver_current;
String video_driver_request;
void _video_driver_selected(int);
+ void _update_video_driver_color();
// Split containers
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 8df49b4516..f6937386c9 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -819,10 +819,10 @@ void EditorPropertyInteger::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyInteger::_value_changed);
}
-void EditorPropertyInteger::setup(int p_min, int p_max, bool p_allow_greater, bool p_allow_lesser) {
+void EditorPropertyInteger::setup(int p_min, int p_max, int p_step, bool p_allow_greater, bool p_allow_lesser) {
spin->set_min(p_min);
spin->set_max(p_max);
- spin->set_step(1);
+ spin->set_step(p_step);
spin->set_allow_greater(p_allow_greater);
spin->set_allow_lesser(p_allow_lesser);
}
@@ -2665,7 +2665,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
} else {
EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
- int min = 0, max = 65535;
+ int min = 0, max = 65535, step = 1;
bool greater = true, lesser = true;
if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
@@ -2673,6 +2673,11 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
lesser = false;
min = p_hint_text.get_slice(",", 0).to_int();
max = p_hint_text.get_slice(",", 1).to_int();
+
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_int();
+ }
+
for (int i = 2; i < p_hint_text.get_slice_count(","); i++) {
String slice = p_hint_text.get_slice(",", i).strip_edges();
if (slice == "or_greater") {
@@ -2684,7 +2689,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
}
}
- editor->setup(min, max, greater, lesser);
+ editor->setup(min, max, step, greater, lesser);
add_property_editor(p_path, editor);
}
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index cfc433b880..18e70345aa 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -265,7 +265,7 @@ protected:
public:
virtual void update_property();
- void setup(int p_min, int p_max, bool p_allow_greater, bool p_allow_lesser);
+ void setup(int p_min, int p_max, int p_step, bool p_allow_greater, bool p_allow_lesser);
EditorPropertyInteger();
};
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 20c34e9092..4e638cb4ac 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -337,7 +337,7 @@ void EditorPropertyArray::update_property() {
} break;
case Variant::INT: {
EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
- editor->setup(-100000, 100000, true, true);
+ editor->setup(-100000, 100000, 1, true, true);
prop = editor;
} break;
@@ -800,7 +800,7 @@ void EditorPropertyDictionary::update_property() {
} break;
case Variant::INT: {
EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
- editor->setup(-100000, 100000, true, true);
+ editor->setup(-100000, 100000, 1, true, true);
prop = editor;
} break;
diff --git a/editor/fileserver/SCsub b/editor/fileserver/SCsub
index f1fa50148f..2b1e889fb0 100644
--- a/editor/fileserver/SCsub
+++ b/editor/fileserver/SCsub
@@ -1,5 +1,5 @@
#!/usr/bin/env python
Import('env')
-Export('env')
+
env.add_source_files(env.editor_sources, "*.cpp")
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 4794d4d8a0..42fd38b654 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -255,7 +255,7 @@ void FileSystemDock::_update_display_mode() {
button_tree->show();
file_list_vb->show();
- _update_files(true);
+ _update_file_list(true);
break;
case DISPLAY_MODE_SPLIT:
@@ -267,7 +267,7 @@ void FileSystemDock::_update_display_mode() {
_update_tree(_compute_uncollapsed_paths());
file_list_vb->show();
- _update_files(true);
+ _update_file_list(true);
break;
}
}
@@ -384,7 +384,7 @@ void FileSystemDock::_notification(int p_what) {
}
if (should_update_files) {
- _update_files(true);
+ _update_file_list(true);
}
// Change full tree mode
@@ -415,7 +415,7 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s
// Update the file list
if (!updating_tree && display_mode == DISPLAY_MODE_SPLIT) {
- _update_files(false);
+ _update_file_list(false);
}
}
@@ -453,7 +453,7 @@ void FileSystemDock::navigate_to_path(const String &p_path) {
if (display_mode == DISPLAY_MODE_SPLIT) {
_update_tree(_compute_uncollapsed_paths());
- _update_files(false);
+ _update_file_list(false);
} else if (display_mode == DISPLAY_MODE_TREE_ONLY) {
if (path.ends_with("/")) {
_go_to_file_list();
@@ -461,7 +461,7 @@ void FileSystemDock::navigate_to_path(const String &p_path) {
_update_tree(_compute_uncollapsed_paths());
}
} else { // DISPLAY_MODE_FILE_LIST_ONLY
- _update_files(true);
+ _update_file_list(true);
}
String file_name = p_path.get_file();
@@ -482,8 +482,14 @@ void FileSystemDock::_file_list_thumbnail_done(const String &p_path, const Ref<T
Array uarr = p_udata;
int idx = uarr[0];
String file = uarr[1];
- if (idx < files->get_item_count() && files->get_item_text(idx) == file && files->get_item_metadata(idx) == p_path)
- files->set_item_icon(idx, p_preview);
+ if (idx < files->get_item_count() && files->get_item_text(idx) == file && files->get_item_metadata(idx) == p_path) {
+ if (file_list_display_mode == FILE_LIST_DISPLAY_LIST) {
+ if (p_small_preview.is_valid())
+ files->set_item_icon(idx, p_small_preview);
+ } else {
+ files->set_item_icon(idx, p_preview);
+ }
+ }
}
}
@@ -527,7 +533,7 @@ void FileSystemDock::_change_file_display() {
EditorSettings::get_singleton()->set("docks/filesystem/files_display_mode", file_list_display_mode);
- _update_files(true);
+ _update_file_list(true);
}
void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> *matches, int p_max_items) {
@@ -558,7 +564,7 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> *
}
}
-void FileSystemDock::_update_files(bool p_keep_selection) {
+void FileSystemDock::_update_file_list(bool p_keep_selection) {
// Register the previously selected items
Set<String> cselection;
@@ -598,7 +604,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
bool use_folders = searched_string.length() == 0 && ((display_mode == DISPLAY_MODE_FILE_LIST_ONLY || display_mode == DISPLAY_MODE_TREE_ONLY) || always_show_folders);
if (use_thumbnails) {
-
+ // Thumbnails mode
files->set_max_columns(0);
files->set_icon_mode(ItemList::ICON_MODE_TOP);
files->set_fixed_column_width(thumbnail_size * 3 / 2);
@@ -616,6 +622,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
}
} else {
+ // No thumbnails
files->set_icon_mode(ItemList::ICON_MODE_LEFT);
files->set_max_columns(1);
files->set_max_text_lines(1);
@@ -624,6 +631,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
}
if (use_folders) {
+ // Display folders in the list
Ref<Texture> folderIcon = (use_thumbnails) ? folder_thumbnail : get_icon("folder", "FileDialog");
if (directory != "res://") {
@@ -651,13 +659,13 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
}
List<FileInfo> filelist;
-
if (searched_string.length() > 0) {
-
+ // Display the search results
_search(EditorFileSystem::get_singleton()->get_filesystem(), &filelist, 128);
filelist.sort();
} else {
+ // Display the folder content
for (int i = 0; i < efd->get_file_count(); i++) {
FileInfo fi;
@@ -673,7 +681,6 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
}
String oi = "Object";
-
for (List<FileInfo>::Element *E = filelist.front(); E; E = E->next()) {
FileInfo *finfo = &(E->get());
String fname = finfo->name;
@@ -685,6 +692,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
String tooltip = fname;
+ // Select the icons
if (!finfo->import_broken) {
type_icon = (has_icon(ftype, ei)) ? get_icon(ftype, ei) : get_icon(oi, ei);
big_icon = file_thumbnail;
@@ -694,25 +702,30 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
tooltip += "\n" + TTR("Status: Import of file failed. Please fix file and reimport manually.");
}
+ // Add the item to the ItemList
int item_index;
if (use_thumbnails) {
files->add_item(fname, big_icon, true);
item_index = files->get_item_count() - 1;
files->set_item_metadata(item_index, fpath);
files->set_item_tag_icon(item_index, type_icon);
- if (!finfo->import_broken) {
- Array udata;
- udata.resize(2);
- udata[0] = item_index;
- udata[1] = fname;
- EditorResourcePreview::get_singleton()->queue_resource_preview(fpath, this, "_file_list_thumbnail_done", udata);
- }
+
} else {
files->add_item(fname, type_icon, true);
item_index = files->get_item_count() - 1;
files->set_item_metadata(item_index, fpath);
}
+ // Generate the preview
+ if (!finfo->import_broken) {
+ Array udata;
+ udata.resize(2);
+ udata[0] = item_index;
+ udata[1] = fname;
+ EditorResourcePreview::get_singleton()->queue_resource_preview(fpath, this, "_file_list_thumbnail_done", udata);
+ }
+
+ // Select the items
if (cselection.has(fname))
files->select(item_index, false);
@@ -721,6 +734,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
files->ensure_current_is_visible();
}
+ // Tooltip
if (finfo->sources.size()) {
for (int j = 0; j < finfo->sources.size(); j++) {
tooltip += "\nSource: " + finfo->sources[j];
@@ -766,7 +780,7 @@ void FileSystemDock::_go_to_file_list() {
} else {
bool collapsed = tree->get_selected()->is_collapsed();
tree->get_selected()->set_collapsed(!collapsed);
- _update_files(false);
+ _update_file_list(false);
}
}
@@ -809,7 +823,7 @@ void FileSystemDock::_fs_changed() {
}
if (file_list_vb->is_visible()) {
- _update_files(true);
+ _update_file_list(true);
}
set_process(false);
@@ -855,7 +869,7 @@ void FileSystemDock::_update_history() {
}
if (file_list_vb->is_visible()) {
- _update_files(false);
+ _update_file_list(false);
}
button_hist_prev->set_disabled(history_pos == 0);
@@ -1592,13 +1606,13 @@ void FileSystemDock::_search_changed(const String &p_text, const Control *p_from
switch (display_mode) {
case DISPLAY_MODE_FILE_LIST_ONLY: {
- _update_files(false);
+ _update_file_list(false);
} break;
case DISPLAY_MODE_TREE_ONLY: {
_update_tree(searched_string.length() == 0 ? uncollapsed_paths_before_search : Vector<String>());
} break;
case DISPLAY_MODE_SPLIT: {
- _update_files(false);
+ _update_file_list(false);
_update_tree(searched_string.length() == 0 ? uncollapsed_paths_before_search : Vector<String>());
} break;
}
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 51c8791b25..5208b2c667 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -188,7 +188,7 @@ private:
void _file_list_gui_input(Ref<InputEvent> p_event);
void _tree_gui_input(Ref<InputEvent> p_event);
- void _update_files(bool p_keep_selection);
+ void _update_file_list(bool p_keep_selection);
void _update_file_list_display_mode_button();
void _change_file_display();
void _fs_changed();
diff --git a/editor/icons/SCsub b/editor/icons/SCsub
index 31bf8f116a..109e1aa83b 100644
--- a/editor/icons/SCsub
+++ b/editor/icons/SCsub
@@ -1,15 +1,13 @@
#!/usr/bin/env python
Import('env')
+
from platform_methods import run_in_subprocess
import editor_icons_builders
-
make_editor_icons_builder = Builder(action=run_in_subprocess(editor_icons_builders.make_editor_icons_action),
suffix='.h',
src_suffix='.svg')
env['BUILDERS']['MakeEditorIconsBuilder'] = make_editor_icons_builder
env.Alias('editor_icons', [env.MakeEditorIconsBuilder('#editor/editor_icons.gen.h', Glob("*.svg"))])
-
-Export('env')
diff --git a/editor/import/SCsub b/editor/import/SCsub
index f1fa50148f..2b1e889fb0 100644
--- a/editor/import/SCsub
+++ b/editor/import/SCsub
@@ -1,5 +1,5 @@
#!/usr/bin/env python
Import('env')
-Export('env')
+
env.add_source_files(env.editor_sources, "*.cpp")
diff --git a/editor/plugins/SCsub b/editor/plugins/SCsub
index f1fa50148f..2b1e889fb0 100644
--- a/editor/plugins/SCsub
+++ b/editor/plugins/SCsub
@@ -1,5 +1,5 @@
#!/usr/bin/env python
Import('env')
-Export('env')
+
env.add_source_files(env.editor_sources, "*.cpp")
diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp
index 59b79bd070..e65a697857 100644
--- a/editor/plugins/baked_lightmap_editor_plugin.cpp
+++ b/editor/plugins/baked_lightmap_editor_plugin.cpp
@@ -50,6 +50,7 @@ void BakedLightmapEditorPlugin::_bake() {
case BakedLightmap::BAKE_ERROR_CANT_CREATE_IMAGE:
EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable."));
break;
+ default: {}
}
}
}
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index e0c8cf41ff..f937744d45 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -767,6 +767,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
node->set_polygon(uv_new);
}
} break;
+ default: {}
}
if (bone_painting) {
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index 356bfbe2b2..271f753003 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -997,6 +997,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
set_message(TTR("View Plane Transform."), 2);
} break;
+ case TRANSFORM_YZ:
+ case TRANSFORM_XZ:
+ case TRANSFORM_XY: {
+ } break;
}
}
} break;
@@ -1545,6 +1549,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2));
axis = Vector3(0, 0, 1);
break;
+ case TRANSFORM_YZ:
+ case TRANSFORM_XZ:
+ case TRANSFORM_XY:
+ break;
}
Vector3 intersection;
diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
index a6a256f0d6..3de2284cea 100644
--- a/editor/plugins/tile_set_editor_plugin.cpp
+++ b/editor/plugins/tile_set_editor_plugin.cpp
@@ -795,6 +795,7 @@ void TileSetEditor::_on_workspace_draw() {
spin_priority->set_suffix(" / " + String::num(total, 0));
draw_highlight_subtile(edited_shape_coord, queue_others);
} break;
+ default: {}
}
draw_tile_subdivision(get_current_tile(), Color(0.347214, 0.722656, 0.617063));
@@ -1365,6 +1366,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
}
}
} break;
+ default: {}
}
}
}
@@ -1434,6 +1436,7 @@ void TileSetEditor::_on_tool_clicked(int p_tool) {
workspace->update();
}
} break;
+ default: {}
}
}
} else if (p_tool == ZOOM_OUT) {
@@ -1862,6 +1865,7 @@ void TileSetEditor::draw_polygon_shapes() {
}
}
} break;
+ default: {}
}
if (creating_shape) {
for (int j = 0; j < current_shape.size() - 1; j++) {
diff --git a/gles_builders.py b/gles_builders.py
index 8ed9f39393..1e63a53f1a 100644
--- a/gles_builders.py
+++ b/gles_builders.py
@@ -68,12 +68,9 @@ def include_file_in_legacygl_header(filename, header_data, depth):
line = fs.readline()
- if line.find("#ifdef ") != -1 or line.find("#elif defined(") != -1:
+ if line.find("#ifdef ") != -1:
if line.find("#ifdef ") != -1:
ifdefline = line.replace("#ifdef ", "").strip()
- else:
- ifdefline = line.replace("#elif defined(", "").strip()
- ifdefline = ifdefline.replace(")", "").strip()
if line.find("_EN_") != -1:
enumbase = ifdefline[:ifdefline.find("_EN_")]
diff --git a/main/SCsub b/main/SCsub
index 9af102600e..e7fe6ab4e1 100644
--- a/main/SCsub
+++ b/main/SCsub
@@ -1,6 +1,7 @@
#!/usr/bin/env python
Import('env')
+
from platform_methods import run_in_subprocess
import main_builders
@@ -15,8 +16,6 @@ env.CommandNoCache("#main/default_controller_mappings.gen.cpp", controller_datab
env.main_sources.append("#main/default_controller_mappings.gen.cpp")
-Export('env')
-
env.Depends("#main/splash.gen.h", "#main/splash.png")
env.CommandNoCache("#main/splash.gen.h", "#main/splash.png", run_in_subprocess(main_builders.make_splash))
diff --git a/main/tests/SCsub b/main/tests/SCsub
index 26a0819ee8..437d9ed777 100644
--- a/main/tests/SCsub
+++ b/main/tests/SCsub
@@ -5,9 +5,5 @@ Import('env')
env.tests_sources = []
env.add_source_files(env.tests_sources, "*.cpp")
-Export('env')
-
-# SConscript('math/SCsub');
-
lib = env.add_library("tests", env.tests_sources)
env.Prepend(LIBS=[lib])
diff --git a/main/tests/test_gdscript.cpp b/main/tests/test_gdscript.cpp
index 412e809732..4d2fa2a26d 100644
--- a/main/tests/test_gdscript.cpp
+++ b/main/tests/test_gdscript.cpp
@@ -357,6 +357,9 @@ static void _parser_show_block(const GDScriptParser::BlockNode *p_block, int p_i
_parser_show_block(cf_node->body, p_indent + 1);
} break;
+ case GDScriptParser::ControlFlowNode::CF_MATCH: {
+ // FIXME: Implement
+ } break;
case GDScriptParser::ControlFlowNode::CF_SWITCH: {
} break;
diff --git a/main/tests/test_shader_lang.cpp b/main/tests/test_shader_lang.cpp
index 2cd39d0208..9df5973376 100644
--- a/main/tests/test_shader_lang.cpp
+++ b/main/tests/test_shader_lang.cpp
@@ -194,6 +194,9 @@ static String dump_node_code(SL::Node *p_node, int p_level) {
code = vnode->name;
} break;
+ case SL::Node::TYPE_VARIABLE_DECLARATION: {
+ // FIXME: Implement
+ } break;
case SL::Node::TYPE_CONSTANT: {
SL::ConstantNode *cnode = (SL::ConstantNode *)p_node;
return get_constant_text(cnode->datatype, cnode->values);
diff --git a/methods.py b/methods.py
index 00c477635e..3add9b1f18 100644
--- a/methods.py
+++ b/methods.py
@@ -19,6 +19,14 @@ def add_source_files(self, sources, filetype, lib_env=None, shared=False):
sources.append(self.Object(path))
+def disable_warnings(self):
+ # 'self' is the environment
+ if self.msvc:
+ self.Append(CCFLAGS=['/w'])
+ else:
+ self.Append(CCFLAGS=['-w'])
+
+
def add_module_version_string(self,s):
self.module_version_string += "." + s
diff --git a/modules/SCsub b/modules/SCsub
index 74a5267355..67f5893db4 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -9,7 +9,6 @@ Export('env_modules')
env.modules_sources = [
"register_module_types.gen.cpp",
]
-Export('env')
for x in env.module_list:
if (x in env.disabled_modules):
@@ -20,7 +19,6 @@ for x in env.module_list:
if env.split_modules:
env.split_lib("modules", env_lib = env_modules)
else:
-
lib = env_modules.add_library("modules", env.modules_sources)
env.Prepend(LIBS=[lib])
diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub
index 2557e8cb1d..11ce18449b 100644
--- a/modules/bullet/SCsub
+++ b/modules/bullet/SCsub
@@ -186,8 +186,12 @@ if env['builtin_bullet']:
thirdparty_sources = [thirdparty_dir + file for file in bullet2_src]
- env_bullet.add_source_files(env.modules_sources, thirdparty_sources)
env_bullet.Append(CPPPATH=[thirdparty_dir])
+ env_thirdparty = env_bullet.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
+
# Godot source files
env_bullet.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
index 3200b4a214..a3ba3aa0bf 100644
--- a/modules/bullet/area_bullet.cpp
+++ b/modules/bullet/area_bullet.cpp
@@ -46,7 +46,6 @@
AreaBullet::AreaBullet() :
RigidCollisionObjectBullet(CollisionObjectBullet::TYPE_AREA),
monitorable(true),
- isScratched(false),
spOv_mode(PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED),
spOv_gravityPoint(false),
spOv_gravityPointDistanceScale(0),
@@ -55,7 +54,8 @@ AreaBullet::AreaBullet() :
spOv_gravityMag(10),
spOv_linearDump(0.1),
spOv_angularDump(1),
- spOv_priority(0) {
+ spOv_priority(0),
+ isScratched(false) {
btGhost = bulletnew(btGhostObject);
btGhost->setCollisionShape(BulletPhysicsServer::get_empty_shape());
@@ -94,6 +94,9 @@ void AreaBullet::dispatch_callbacks() {
otherObj.object->on_exit_area(this);
overlappingObjects.remove(i); // Remove after callback
break;
+ case OVERLAP_STATE_DIRTY:
+ case OVERLAP_STATE_INSIDE:
+ break;
}
}
}
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index df67f8d7ab..61834b8e3f 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -66,13 +66,13 @@ void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_s
CollisionObjectBullet::CollisionObjectBullet(Type p_type) :
RIDBullet(),
- space(NULL),
type(p_type),
collisionsEnabled(true),
m_isStatic(false),
bt_collision_object(NULL),
body_scale(1., 1., 1.),
- force_shape_reset(false) {}
+ force_shape_reset(false),
+ space(NULL) {}
CollisionObjectBullet::~CollisionObjectBullet() {
// Remove all overlapping, notify is not required since godot take care of it
diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp
index 53d0ab7e3c..27ee44d1bd 100644
--- a/modules/bullet/godot_ray_world_algorithm.cpp
+++ b/modules/bullet/godot_ray_world_algorithm.cpp
@@ -49,9 +49,9 @@ GodotRayWorldAlgorithm::SwappedCreateFunc::SwappedCreateFunc(const btDiscreteDyn
GodotRayWorldAlgorithm::GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped) :
btActivatingCollisionAlgorithm(ci, body0Wrap, body1Wrap),
+ m_world(world),
m_manifoldPtr(mf),
m_ownManifold(false),
- m_world(world),
m_isSwapped(isSwapped) {}
GodotRayWorldAlgorithm::~GodotRayWorldAlgorithm() {
diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h
index 8e70b72841..73e1fc9627 100644
--- a/modules/bullet/godot_result_callbacks.h
+++ b/modules/bullet/godot_result_callbacks.h
@@ -87,13 +87,13 @@ struct GodotAllConvexResultCallback : public btCollisionWorld::ConvexResultCallb
public:
PhysicsDirectSpaceState::ShapeResult *m_results;
int m_resultMax;
- int count;
const Set<RID> *m_exclude;
+ int count;
GodotAllConvexResultCallback(PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude) :
m_results(p_results),
- m_exclude(p_exclude),
m_resultMax(p_resultMax),
+ m_exclude(p_exclude),
count(0) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
@@ -125,6 +125,7 @@ public:
GodotClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld),
m_exclude(p_exclude),
+ m_shapeId(0),
collide_with_bodies(p_collide_with_bodies),
collide_with_areas(p_collide_with_areas) {}
@@ -138,8 +139,8 @@ public:
const btCollisionObject *m_self_object;
PhysicsDirectSpaceState::ShapeResult *m_results;
int m_resultMax;
- int m_count;
const Set<RID> *m_exclude;
+ int m_count;
bool collide_with_bodies;
bool collide_with_areas;
@@ -147,8 +148,8 @@ public:
GodotAllContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
m_self_object(p_self_object),
m_results(p_results),
- m_exclude(p_exclude),
m_resultMax(p_resultMax),
+ m_exclude(p_exclude),
m_count(0),
collide_with_bodies(p_collide_with_bodies),
collide_with_areas(p_collide_with_areas) {}
@@ -164,8 +165,8 @@ public:
const btCollisionObject *m_self_object;
Vector3 *m_results;
int m_resultMax;
- int m_count;
const Set<RID> *m_exclude;
+ int m_count;
bool collide_with_bodies;
bool collide_with_areas;
@@ -173,8 +174,8 @@ public:
GodotContactPairContactResultCallback(btCollisionObject *p_self_object, Vector3 *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
m_self_object(p_self_object),
m_results(p_results),
- m_exclude(p_exclude),
m_resultMax(p_resultMax),
+ m_exclude(p_exclude),
m_count(0),
collide_with_bodies(p_collide_with_bodies),
collide_with_areas(p_collide_with_areas) {}
@@ -188,11 +189,11 @@ struct GodotRestInfoContactResultCallback : public btCollisionWorld::ContactResu
public:
const btCollisionObject *m_self_object;
PhysicsDirectSpaceState::ShapeRestInfo *m_result;
+ const Set<RID> *m_exclude;
bool m_collided;
real_t m_min_distance;
const btCollisionObject *m_rest_info_collision_object;
btVector3 m_rest_info_bt_point;
- const Set<RID> *m_exclude;
bool collide_with_bodies;
bool collide_with_areas;
diff --git a/modules/bullet/hinge_joint_bullet.cpp b/modules/bullet/hinge_joint_bullet.cpp
index 86c6a632cd..3a4459a581 100644
--- a/modules/bullet/hinge_joint_bullet.cpp
+++ b/modules/bullet/hinge_joint_bullet.cpp
@@ -117,7 +117,7 @@ void HingeJointBullet::set_param(PhysicsServer::HingeJointParam p_param, real_t
hingeConstraint->setMaxMotorImpulse(p_value);
break;
default:
- ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
+ ERR_EXPLAIN("The HingeJoint parameter " + itos(p_param) + " is deprecated.");
WARN_DEPRECATED
break;
}
@@ -143,7 +143,7 @@ real_t HingeJointBullet::get_param(PhysicsServer::HingeJointParam p_param) const
case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE:
return hingeConstraint->getMaxMotorImpulse();
default:
- ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
+ ERR_EXPLAIN("The HingeJoint parameter " + itos(p_param) + " is deprecated.");
WARN_DEPRECATED;
return 0;
}
@@ -159,6 +159,7 @@ void HingeJointBullet::set_flag(PhysicsServer::HingeJointFlag p_flag, bool p_val
case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR:
hingeConstraint->enableMotor(p_value);
break;
+ case PhysicsServer::HINGE_JOINT_FLAG_MAX: break; // Can't happen, but silences warning
}
}
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index f24c8670a3..d9a77885b3 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -259,21 +259,21 @@ RigidBodyBullet::RigidBodyBullet() :
RigidCollisionObjectBullet(CollisionObjectBullet::TYPE_RIGID_BODY),
kinematic_utilities(NULL),
locked_axis(0),
- gravity_scale(1),
mass(1),
+ gravity_scale(1),
linearDamp(0),
angularDamp(0),
can_sleep(true),
omit_forces_integration(false),
- force_integration_callback(NULL),
- isTransformChanged(false),
- previousActiveState(true),
maxCollisionsDetection(0),
collisionsCount(0),
maxAreasWhereIam(10),
areaWhereIamCount(0),
countGravityPointSpaces(0),
- isScratchedSpaceOverrideModificator(false) {
+ isScratchedSpaceOverrideModificator(false),
+ isTransformChanged(false),
+ previousActiveState(true),
+ force_integration_callback(NULL) {
godotMotionState = bulletnew(GodotMotionState(this));
@@ -535,20 +535,18 @@ void RigidBodyBullet::set_mode(PhysicsServer::BodyMode p_mode) {
reload_axis_lock();
_internal_set_mass(0);
break;
- case PhysicsServer::BODY_MODE_RIGID: {
+ case PhysicsServer::BODY_MODE_RIGID:
mode = PhysicsServer::BODY_MODE_RIGID;
reload_axis_lock();
_internal_set_mass(0 == mass ? 1 : mass);
scratch_space_override_modificator();
break;
- }
- case PhysicsServer::BODY_MODE_CHARACTER: {
+ case PhysicsServer::BODY_MODE_CHARACTER:
mode = PhysicsServer::BODY_MODE_CHARACTER;
reload_axis_lock();
_internal_set_mass(0 == mass ? 1 : mass);
scratch_space_override_modificator();
break;
- }
}
btBody->setAngularVelocity(btVector3(0, 0, 0));
@@ -927,10 +925,10 @@ void RigidBodyBullet::reload_space_override_modificator() {
}
switch (currentArea->get_spOv_mode()) {
- ///case PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED:
- /// This area does not affect gravity/damp. These are generally areas
- /// that exist only to detect collisions, and objects entering or exiting them.
- /// break;
+ case PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED:
+ /// This area does not affect gravity/damp. These are generally areas
+ /// that exist only to detect collisions, and objects entering or exiting them.
+ break;
case PhysicsServer::AREA_SPACE_OVERRIDE_COMBINE:
/// This area adds its gravity/damp values to whatever has been
/// calculated so far. This way, many overlapping areas can combine
diff --git a/modules/bullet/slider_joint_bullet.cpp b/modules/bullet/slider_joint_bullet.cpp
index 9e1cd23989..9016ec3bf5 100644
--- a/modules/bullet/slider_joint_bullet.cpp
+++ b/modules/bullet/slider_joint_bullet.cpp
@@ -366,6 +366,7 @@ void SliderJointBullet::set_param(PhysicsServer::SliderJointParam p_param, real_
case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: setSoftnessOrthoAng(p_value); break;
case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: setRestitutionOrthoAng(p_value); break;
case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: setDampingOrthoAng(p_value); break;
+ case PhysicsServer::SLIDER_JOINT_MAX: break; // Can't happen, but silences warning
}
}
diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp
index 9fc7230f91..f373ce5db4 100644
--- a/modules/bullet/soft_body_bullet.cpp
+++ b/modules/bullet/soft_body_bullet.cpp
@@ -37,17 +37,17 @@
SoftBodyBullet::SoftBodyBullet() :
CollisionObjectBullet(CollisionObjectBullet::TYPE_SOFT_BODY),
- total_mass(1),
+ bt_soft_body(NULL),
+ isScratched(false),
simulation_precision(5),
+ total_mass(1.),
linear_stiffness(0.5),
areaAngular_stiffness(0.5),
volume_stiffness(0.5),
pressure_coefficient(0.),
pose_matching_coefficient(0.),
damping_coefficient(0.01),
- drag_coefficient(0.),
- bt_soft_body(NULL),
- isScratched(false) {}
+ drag_coefficient(0.) {}
SoftBodyBullet::~SoftBodyBullet() {
}
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 329e12cfff..ba4c72f4c7 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -332,16 +332,17 @@ Vector3 BulletPhysicsDirectSpaceState::get_closest_point_to_object_volume(RID p_
SpaceBullet::SpaceBullet() :
broadphase(NULL),
+ collisionConfiguration(NULL),
dispatcher(NULL),
solver(NULL),
- collisionConfiguration(NULL),
dynamicsWorld(NULL),
soft_body_world_info(NULL),
ghostPairCallback(NULL),
godotFilterCallback(NULL),
gravityDirection(0, -1, 0),
gravityMagnitude(10),
- contactDebugCount(0) {
+ contactDebugCount(0),
+ delta_time(0.) {
create_empty_world(GLOBAL_DEF("physics/3d/active_soft_world", true));
direct_access = memnew(BulletPhysicsDirectSpaceState(this));
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
index 67ab5c610d..d815898ffd 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -96,9 +96,9 @@ class SpaceBullet : public RIDBullet {
btCollisionDispatcher *dispatcher;
btConstraintSolver *solver;
btDiscreteDynamicsWorld *dynamicsWorld;
+ btSoftBodyWorldInfo *soft_body_world_info;
btGhostPairCallback *ghostPairCallback;
GodotFilterCallback *godotFilterCallback;
- btSoftBodyWorldInfo *soft_body_world_info;
btGjkEpaPenetrationDepthSolver *gjk_epa_pen_solver;
btVoronoiSimplexSolver *gjk_simplex_solver;
diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub
index 5c396482aa..fcc69d8371 100644
--- a/modules/cvtt/SCsub
+++ b/modules/cvtt/SCsub
@@ -14,8 +14,11 @@ if env['builtin_squish']:
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_cvtt.add_source_files(env.modules_sources, thirdparty_sources)
env_cvtt.Append(CPPPATH=[thirdparty_dir])
+ env_thirdparty = env_cvtt.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot source files
env_cvtt.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/enet/SCsub b/modules/enet/SCsub
index 7caeafa1d6..a57a4b29ea 100644
--- a/modules/enet/SCsub
+++ b/modules/enet/SCsub
@@ -21,8 +21,11 @@ if env['builtin_enet']:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_enet.add_source_files(env.modules_sources, thirdparty_sources)
env_enet.Append(CPPPATH=[thirdparty_dir])
env_enet.Append(CPPFLAGS=["-DGODOT_ENET"])
+ env_thirdparty = env_enet.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
env_enet.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/etc/SCsub b/modules/etc/SCsub
index 31d8f00ef3..d2c77d6e3c 100644
--- a/modules/etc/SCsub
+++ b/modules/etc/SCsub
@@ -27,16 +27,20 @@ thirdparty_sources = [
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env_etc.add_source_files(env.modules_sources, thirdparty_sources)
env_etc.Append(CPPPATH=[thirdparty_dir])
-# Godot source files
-env_etc.add_source_files(env.modules_sources, "*.cpp")
-
# upstream uses c++11
-if (not env_etc.msvc):
+if not env.msvc:
env_etc.Append(CCFLAGS="-std=c++11")
-# -ffast-math seems to be incompatible with ec2comp on recent versions of
+
+# -ffast-math seems to be incompatible with etc2comp on recent versions of
# GCC and Clang
if '-ffast-math' in env_etc['CCFLAGS']:
env_etc['CCFLAGS'].remove('-ffast-math')
+
+env_thirdparty = env_etc.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
+# Godot source files
+env_etc.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp
index fd17c7b81f..57f5b68c61 100644
--- a/modules/etc/image_etc.cpp
+++ b/modules/etc/image_etc.cpp
@@ -47,13 +47,14 @@ static Image::Format _get_etc2_mode(Image::DetectChannels format) {
case Image::DETECTED_RGB:
return Image::FORMAT_ETC2_RGB8;
- default:
+ case Image::DETECTED_RGBA:
return Image::FORMAT_ETC2_RGBA8;
- // TODO: would be nice if we could use FORMAT_ETC2_RGB8A1 for FORMAT_RGBA5551
+ // TODO: would be nice if we could use FORMAT_ETC2_RGB8A1 for FORMAT_RGBA5551
+ default:
+ // TODO: Kept for compatibility, but should be investigated whether it's correct or if it should error out
+ return Image::FORMAT_ETC2_RGBA8;
}
-
- ERR_FAIL_COND_V(true, Image::FORMAT_MAX);
}
static Etc::Image::Format _image_format_to_etc2comp_format(Image::Format format) {
@@ -81,9 +82,10 @@ static Etc::Image::Format _image_format_to_etc2comp_format(Image::Format format)
case Image::FORMAT_ETC2_RGB8A1:
return Etc::Image::Format::RGB8A1;
- }
- ERR_FAIL_COND_V(true, Etc::Image::Format::UNKNOWN);
+ default:
+ ERR_FAIL_V(Etc::Image::Format::UNKNOWN);
+ }
}
static void _decompress_etc1(Image *p_img) {
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index c86e78ccee..d2f0ad042a 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -73,6 +73,8 @@ if env['builtin_freetype']:
if env['builtin_libpng']:
env.Append(CPPPATH=["#thirdparty/libpng"])
+ # FIXME: Find a way to build this in a separate env nevertheless
+ # so that we can disable warnings on thirdparty code
lib = env.add_library("freetype_builtin", thirdparty_sources)
# Needs to be appended to arrive after libscene in the linker call,
# but we don't want it to arrive *after* system libs, so manual hack
diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub
index 46b2a832f1..fe2d8c7ce9 100644
--- a/modules/gdnative/SCsub
+++ b/modules/gdnative/SCsub
@@ -1,35 +1,38 @@
#!/usr/bin/env python
Import('env')
+Import('env_modules')
-gdn_env = env.Clone()
-gdn_env.add_source_files(env.modules_sources, "gdnative.cpp")
-gdn_env.add_source_files(env.modules_sources, "register_types.cpp")
-gdn_env.add_source_files(env.modules_sources, "android/*.cpp")
-gdn_env.add_source_files(env.modules_sources, "gdnative/*.cpp")
-gdn_env.add_source_files(env.modules_sources, "nativescript/*.cpp")
-gdn_env.add_source_files(env.modules_sources, "gdnative_library_singleton_editor.cpp")
-gdn_env.add_source_files(env.modules_sources, "gdnative_library_editor_plugin.cpp")
+env_gdnative = env_modules.Clone()
+env_gdnative.add_source_files(env.modules_sources, "gdnative.cpp")
+env_gdnative.add_source_files(env.modules_sources, "register_types.cpp")
+env_gdnative.add_source_files(env.modules_sources, "android/*.cpp")
+env_gdnative.add_source_files(env.modules_sources, "gdnative/*.cpp")
+env_gdnative.add_source_files(env.modules_sources, "nativescript/*.cpp")
+env_gdnative.add_source_files(env.modules_sources, "gdnative_library_singleton_editor.cpp")
+env_gdnative.add_source_files(env.modules_sources, "gdnative_library_editor_plugin.cpp")
-gdn_env.Append(CPPPATH=['#modules/gdnative/include/'])
+env_gdnative.Append(CPPPATH=['#modules/gdnative/include/'])
+
+Export('env_gdnative')
SConscript("net/SCsub")
SConscript("arvr/SCsub")
SConscript("pluginscript/SCsub")
+
from platform_methods import run_in_subprocess
import gdnative_builders
-
-_, gensource = gdn_env.CommandNoCache(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'],
+_, gensource = env_gdnative.CommandNoCache(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'],
'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_api_struct))
-gdn_env.add_source_files(env.modules_sources, [gensource])
+env_gdnative.add_source_files(env.modules_sources, [gensource])
env.use_ptrcall = True
if ARGUMENTS.get('gdnative_wrapper', False):
- gensource, = gdn_env.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_wrapper_code))
+ gensource, = env_gdnative.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_wrapper_code))
gd_wrapper_env = env.Clone()
gd_wrapper_env.Append(CPPPATH=['#modules/gdnative/include/'])
diff --git a/modules/gdnative/arvr/SCsub b/modules/gdnative/arvr/SCsub
index ecc5996108..20eaa99592 100644
--- a/modules/gdnative/arvr/SCsub
+++ b/modules/gdnative/arvr/SCsub
@@ -1,13 +1,6 @@
#!/usr/bin/env python
-import os
-import methods
-
Import('env')
-Import('env_modules')
-
-env_arvr_gdnative = env_modules.Clone()
-
-env_arvr_gdnative.Append(CPPPATH=['#modules/gdnative/include/'])
-env_arvr_gdnative.add_source_files(env.modules_sources, '*.cpp')
+Import('env_gdnative')
+env_gdnative.add_source_files(env.modules_sources, '*.cpp')
diff --git a/modules/gdnative/nativescript/SCsub b/modules/gdnative/nativescript/SCsub
index ee3b9c351d..5841ad5531 100644
--- a/modules/gdnative/nativescript/SCsub
+++ b/modules/gdnative/nativescript/SCsub
@@ -1,12 +1,10 @@
#!/usr/bin/env python
Import('env')
+Import('env_gdnative')
-mod_env = env.Clone()
-mod_env.add_source_files(env.modules_sources, "*.cpp")
-mod_env.Append(CPPFLAGS=['-DGDAPI_BUILT_IN'])
+env_gdnative.add_source_files(env.modules_sources, '*.cpp')
+env_gdnative.Append(CPPFLAGS=['-DGDAPI_BUILT_IN'])
if "platform" in env and env["platform"] in ["x11", "iphone"]:
env.Append(LINKFLAGS=["-rdynamic"])
-
-Export('mod_env')
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index a96fe5c5e3..ade8ffd280 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -70,8 +70,6 @@ struct NativeScriptDesc {
String documentation;
};
- String documentation;
-
Map<StringName, Method> methods;
OrderedHashMap<StringName, Property> properties;
Map<StringName, Signal> signals_; // QtCreator doesn't like the name signals
@@ -81,6 +79,8 @@ struct NativeScriptDesc {
godot_instance_create_func create_func;
godot_instance_destroy_func destroy_func;
+ String documentation;
+
const void *type_tag;
bool is_tool;
diff --git a/modules/gdnative/net/SCsub b/modules/gdnative/net/SCsub
index 53f9271128..e915703935 100644
--- a/modules/gdnative/net/SCsub
+++ b/modules/gdnative/net/SCsub
@@ -1,12 +1,7 @@
#!/usr/bin/env python
-import os
-import methods
-
Import('env')
-Import('env_modules')
+Import('env_gdnative')
-env_net_gdnative = env_modules.Clone()
+env_gdnative.add_source_files(env.modules_sources, '*.cpp')
-env_net_gdnative.Append(CPPPATH=['#modules/gdnative/include/'])
-env_net_gdnative.add_source_files(env.modules_sources, '*.cpp')
diff --git a/modules/gdnative/pluginscript/SCsub b/modules/gdnative/pluginscript/SCsub
index 2031a4236b..20eaa99592 100644
--- a/modules/gdnative/pluginscript/SCsub
+++ b/modules/gdnative/pluginscript/SCsub
@@ -1,9 +1,6 @@
#!/usr/bin/env python
Import('env')
-Import('env_modules')
+Import('env_gdnative')
-env_pluginscript = env_modules.Clone()
-
-env_pluginscript.Append(CPPPATH=['#modules/gdnative/include/'])
-env_pluginscript.add_source_files(env.modules_sources, '*.cpp')
+env_gdnative.add_source_files(env.modules_sources, '*.cpp')
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
index 73f09f1659..6904154953 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -9,5 +9,3 @@ env_gdscript.add_source_files(env.modules_sources, "*.cpp")
if env['tools']:
env_gdscript.add_source_files(env.modules_sources, "./editor/*.cpp")
-
-Export('env')
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index b0d5422afe..48c1760662 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1981,6 +1981,7 @@ String GDScriptWarning::get_message() const {
CHECK_SYMBOLS(2);
return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'.";
} break;
+ case WARNING_MAX: break; // Can't happen, but silences warning
}
ERR_EXPLAIN("Invalid GDScript warning code: " + get_name_from_code(code));
ERR_FAIL_V(String());
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 4a74b0712a..f344beba9f 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -301,8 +301,8 @@ struct GDScriptWarning {
static Code get_code_from_name(const String &p_name);
GDScriptWarning() :
- line(-1),
- code(WARNING_MAX) {}
+ code(WARNING_MAX),
+ line(-1) {}
};
#endif // DEBUG_ENABLED
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 3194f19aa9..55bc3d2359 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -1110,6 +1110,7 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c
} break;
}
} break;
+ default: {}
}
// It may have found a null, but that's never useful
@@ -3354,6 +3355,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
return OK;
}
} break;
+ default: {}
}
return ERR_CANT_RESOLVE;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index bff608e56b..5facfe7869 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -5219,6 +5219,8 @@ String GDScriptParser::DataType::to_string() const {
}
return class_type->name.operator String();
} break;
+ case UNRESOLVED: {
+ } break;
}
return "Unresolved";
@@ -5791,7 +5793,10 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data
expr_native = base->base_type.native_type;
expr_script = base->base_type.script_type;
}
- }
+ } break;
+ case DataType::BUILTIN: // Already handled above
+ case DataType::UNRESOLVED: // Not allowed, see above
+ break;
}
switch (p_container.kind) {
@@ -5834,7 +5839,10 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data
expr_class = expr_class->base_type.class_type;
}
return false;
- }
+ } break;
+ case DataType::BUILTIN: // Already handled above
+ case DataType::UNRESOLVED: // Not allowed, see above
+ break;
}
return false;
@@ -6228,6 +6236,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
case Variant::COLOR: {
error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING;
} break;
+ default: {}
}
}
if (error) {
@@ -6345,6 +6354,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
}
}
} break;
+ default: {}
}
p_node->set_datatype(_resolve_type(node_type, p_node->line));
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index cd68072499..c7813a2144 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -88,6 +88,8 @@ public:
case CLASS: {
return class_type == other.class_type;
} break;
+ case UNRESOLVED: {
+ } break;
}
return false;
}
diff --git a/modules/gridmap/SCsub b/modules/gridmap/SCsub
index 2ffe15cd33..62b8a0ff93 100644
--- a/modules/gridmap/SCsub
+++ b/modules/gridmap/SCsub
@@ -6,5 +6,3 @@ Import('env_modules')
env_gridmap = env_modules.Clone()
env_gridmap.add_source_files(env.modules_sources, "*.cpp")
-
-Export('env')
diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub
index e72dc6a1ca..d5f87905eb 100644
--- a/modules/jpg/SCsub
+++ b/modules/jpg/SCsub
@@ -13,8 +13,11 @@ thirdparty_sources = [
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env_jpg.add_source_files(env.modules_sources, thirdparty_sources)
env_jpg.Append(CPPPATH=[thirdparty_dir])
+env_thirdparty = env_jpg.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot's own source files
env_jpg.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub
index d11d7a7ec7..0c6c703e16 100755
--- a/modules/mbedtls/SCsub
+++ b/modules/mbedtls/SCsub
@@ -91,8 +91,12 @@ if env['builtin_mbedtls']:
thirdparty_dir = "#thirdparty/mbedtls/library/"
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_mbed_tls.add_source_files(env.modules_sources, thirdparty_sources)
+
env_mbed_tls.Prepend(CPPPATH=["#thirdparty/mbedtls/include/"])
+ env_thirdparty = env_mbed_tls.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Module sources
env_mbed_tls.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/mobile_vr/SCsub b/modules/mobile_vr/SCsub
index b4e2edcca1..e5725ceb6f 100644
--- a/modules/mobile_vr/SCsub
+++ b/modules/mobile_vr/SCsub
@@ -1,8 +1,5 @@
#!/usr/bin/env python
-import os
-import methods
-
Import('env')
Import('env_modules')
diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index e2c630565f..2cabc7bd59 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -297,6 +297,7 @@ bool MobileVRInterface::initialize() {
mag_current_min = Vector3(0, 0, 0);
mag_current_max = Vector3(0, 0, 0);
+#if !defined(SERVER_ENABLED)
// build our shader
if (lens_shader == NULL) {
///@TODO need to switch between GLES2 and GLES3 version, Reduz suggested moving this into our drivers and making this a core shader
@@ -337,6 +338,7 @@ bool MobileVRInterface::initialize() {
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
+#endif
// reset our orientation
orientation = Basis();
@@ -360,6 +362,7 @@ void MobileVRInterface::uninitialize() {
arvr_server->clear_primary_interface_if(this);
}
+#if !defined(SERVER_ENABLED)
// cleanup our shader and buffers
if (lens_shader != NULL) {
glDeleteVertexArrays(1, &half_screen_array);
@@ -368,6 +371,7 @@ void MobileVRInterface::uninitialize() {
delete lens_shader;
lens_shader = NULL;
}
+#endif
initialized = false;
};
@@ -470,6 +474,7 @@ void MobileVRInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_t
// get our render target
RID eye_texture = VSG::storage->render_target_get_texture(p_render_target);
uint32_t texid = VS::get_singleton()->texture_get_texid(eye_texture);
+#if !defined(SERVER_ENABLED)
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texid);
@@ -484,6 +489,7 @@ void MobileVRInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_t
glBindVertexArray(half_screen_array);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindVertexArray(0);
+#endif
};
void MobileVRInterface::process() {
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index cee0cca90e..63cad4c738 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -34,7 +34,9 @@
#include "servers/arvr/arvr_interface.h"
#include "servers/arvr/arvr_positional_tracker.h"
+#if !defined(SERVER_ENABLED)
#include "shaders/lens_distorted.glsl.gen.h"
+#endif
/**
@author Bastiaan Olij <mux213@gmail.com>
@@ -58,9 +60,13 @@ private:
float eye_height;
uint64_t last_ticks;
+#if !defined(SERVER_ENABLED)
LensDistortedShaderGLES3 *lens_shader;
GLuint half_screen_quad;
GLuint half_screen_array;
+#else
+ void *lens_shader;
+#endif
real_t intraocular_dist;
real_t display_width;
diff --git a/modules/mobile_vr/shaders/SCsub b/modules/mobile_vr/shaders/SCsub
index cf53c9ebe0..97a3598598 100644
--- a/modules/mobile_vr/shaders/SCsub
+++ b/modules/mobile_vr/shaders/SCsub
@@ -4,4 +4,3 @@ Import('env')
if 'GLES3_GLSL' in env['BUILDERS']:
env.GLES3_GLSL('lens_distorted.glsl');
-
diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub
index 5e559bd4db..765a9fc11a 100644
--- a/modules/ogg/SCsub
+++ b/modules/ogg/SCsub
@@ -14,8 +14,11 @@ if env['builtin_libogg']:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_ogg.add_source_files(env.modules_sources, thirdparty_sources)
env_ogg.Append(CPPPATH=[thirdparty_dir])
+ env_thirdparty = env_ogg.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot source files
env_ogg.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/opensimplex/SCsub b/modules/opensimplex/SCsub
index be9c8b091f..4235f6a0b9 100644
--- a/modules/opensimplex/SCsub
+++ b/modules/opensimplex/SCsub
@@ -1,4 +1,22 @@
#!/usr/bin/env python
Import('env')
-env.add_source_files(env.modules_sources, ["register_types.cpp", "simplex_noise.cpp", "noise_texture.cpp", "#thirdparty/misc/open-simplex-noise.c"])
+Import('env_modules')
+
+env_opensimplex = env_modules.Clone()
+
+# Thirdparty source files
+thirdparty_dir = "#thirdparty/misc/"
+thirdparty_sources = [
+ "open-simplex-noise.c",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_opensimplex.Append(CPPPATH=[thirdparty_dir])
+
+env_thirdparty = env_opensimplex.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
+# Godot's own source files
+env_opensimplex.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml
index c7dc373f59..6af58e7a6b 100644
--- a/modules/opensimplex/doc_classes/NoiseTexture.xml
+++ b/modules/opensimplex/doc_classes/NoiseTexture.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NoiseTexture" inherits="Texture" category="Core" version="3.1">
<brief_description>
- [SimplexNoise] filled texture.
+ [OpenSimplexNoise] filled texture.
</brief_description>
<description>
- Uses a [SimplexNoise] to fill the texture data. You can specify the texture size but keep in mind that larger textures will take longer to generate and seamless noise only works with square sized textures.
+ Uses an [OpenSimplexNoise] to fill the texture data. You can specify the texture size but keep in mind that larger textures will take longer to generate and seamless noise only works with square sized textures.
NoiseTexture can also generate normalmap textures.
</description>
<tutorials>
@@ -35,8 +35,8 @@
<member name="as_normalmap" type="bool" setter="set_as_normalmap" getter="is_normalmap">
If true, the resulting texture contains a normal map created from the original noise interpreted as a bump map.
</member>
- <member name="noise" type="SimplexNoise" setter="set_noise" getter="get_noise">
- The [SimplexNoise] instance used to generate the noise.
+ <member name="noise" type="OpenSimplexNoise" setter="set_noise" getter="get_noise">
+ The [OpenSimplexNoise] instance used to generate the noise.
</member>
<member name="seamless" type="bool" setter="set_seamless" getter="get_seamless">
Whether the texture can be tiled without visible seams or not. Seamless textures take longer to generate.
diff --git a/modules/opensimplex/doc_classes/SimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
index e5e0c15b3c..31f13f341c 100644
--- a/modules/opensimplex/doc_classes/SimplexNoise.xml
+++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="SimplexNoise" inherits="Resource" category="Core" version="3.1">
+<class name="OpenSimplexNoise" inherits="Resource" category="Core" version="3.1">
<brief_description>
Noise generator based on Open Simplex.
</brief_description>
<description>
- This resource allows you to configure and sample a fractal noise space. Here is a brief usage example that configures a SimplexNoise and gets samples at various positions and dimensions:
+ This resource allows you to configure and sample a fractal noise space. Here is a brief usage example that configures an OpenSimplexNoise and gets samples at various positions and dimensions:
[codeblock]
- var noise = SimplexNoise.new()
+ var noise = OpenSimplexNoise.new()
# Configure
noise.seed = randi()
@@ -109,7 +109,7 @@
Difference in period between [member octaves].
</member>
<member name="octaves" type="int" setter="set_octaves" getter="get_octaves">
- Number of Simplex noise layers that are sampled to get the fractal noise.
+ Number of OpenSimplex noise layers that are sampled to get the fractal noise.
</member>
<member name="period" type="float" setter="set_period" getter="get_period">
Period of the base octave. A lower period results in a higher-frequency noise (more value changes across the same distance).
diff --git a/modules/opensimplex/noise_texture.cpp b/modules/opensimplex/noise_texture.cpp
index b82b0b453f..6f2723e43b 100644
--- a/modules/opensimplex/noise_texture.cpp
+++ b/modules/opensimplex/noise_texture.cpp
@@ -43,7 +43,7 @@ NoiseTexture::NoiseTexture() {
as_normalmap = false;
flags = FLAGS_DEFAULT;
- noise = Ref<SimplexNoise>();
+ noise = Ref<OpenSimplexNoise>();
texture = VS::get_singleton()->texture_create();
@@ -76,7 +76,7 @@ void NoiseTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normalmap"), "set_as_normalmap", "is_normalmap");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "SimplexNoise"), "set_noise", "get_noise");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise");
}
void NoiseTexture::_set_texture_data(const Ref<Image> &p_image) {
@@ -159,7 +159,7 @@ void NoiseTexture::_update_texture() {
}
}
-void NoiseTexture::set_noise(Ref<SimplexNoise> p_noise) {
+void NoiseTexture::set_noise(Ref<OpenSimplexNoise> p_noise) {
if (p_noise == noise)
return;
if (noise.is_valid()) {
@@ -172,7 +172,7 @@ void NoiseTexture::set_noise(Ref<SimplexNoise> p_noise) {
_queue_update();
}
-Ref<SimplexNoise> NoiseTexture::get_noise() {
+Ref<OpenSimplexNoise> NoiseTexture::get_noise() {
return noise;
}
diff --git a/modules/opensimplex/noise_texture.h b/modules/opensimplex/noise_texture.h
index 108e471fc1..78a02cda9f 100644
--- a/modules/opensimplex/noise_texture.h
+++ b/modules/opensimplex/noise_texture.h
@@ -31,7 +31,7 @@
#ifndef NOISE_TEXTURE_H
#define NOISE_TEXTURE_H
-#include "simplex_noise.h"
+#include "open_simplex_noise.h"
#include "core/image.h"
#include "core/reference.h"
@@ -54,7 +54,7 @@ private:
RID texture;
uint32_t flags;
- Ref<SimplexNoise> noise;
+ Ref<OpenSimplexNoise> noise;
Vector2i size;
bool seamless;
bool as_normalmap;
@@ -71,8 +71,8 @@ protected:
static void _bind_methods();
public:
- void set_noise(Ref<SimplexNoise> p_noise);
- Ref<SimplexNoise> get_noise();
+ void set_noise(Ref<OpenSimplexNoise> p_noise);
+ Ref<OpenSimplexNoise> get_noise();
void set_width(int p_width);
void set_height(int p_hieght);
diff --git a/modules/opensimplex/simplex_noise.cpp b/modules/opensimplex/open_simplex_noise.cpp
index c99fd7b123..bfc2732ff4 100644
--- a/modules/opensimplex/simplex_noise.cpp
+++ b/modules/opensimplex/open_simplex_noise.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* simplex_noise.cpp */
+/* open_simplex_noise.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "simplex_noise.h"
+#include "open_simplex_noise.h"
#include "core/core_string_names.h"
-SimplexNoise::SimplexNoise() {
+OpenSimplexNoise::OpenSimplexNoise() {
seed = 0;
persistence = 0.5;
@@ -43,16 +43,16 @@ SimplexNoise::SimplexNoise() {
_init_seeds();
}
-SimplexNoise::~SimplexNoise() {
+OpenSimplexNoise::~OpenSimplexNoise() {
}
-void SimplexNoise::_init_seeds() {
+void OpenSimplexNoise::_init_seeds() {
for (int i = 0; i < 6; ++i) {
open_simplex_noise(seed + i * 2, &(contexts[i]));
}
}
-void SimplexNoise::set_seed(int p_seed) {
+void OpenSimplexNoise::set_seed(int p_seed) {
if (seed == p_seed)
return;
@@ -64,36 +64,36 @@ void SimplexNoise::set_seed(int p_seed) {
emit_changed();
}
-int SimplexNoise::get_seed() {
+int OpenSimplexNoise::get_seed() {
return seed;
}
-void SimplexNoise::set_octaves(int p_octaves) {
+void OpenSimplexNoise::set_octaves(int p_octaves) {
if (p_octaves == octaves) return;
octaves = CLAMP(p_octaves, 1, 6);
emit_changed();
}
-void SimplexNoise::set_period(float p_period) {
+void OpenSimplexNoise::set_period(float p_period) {
if (p_period == period) return;
period = p_period;
emit_changed();
}
-void SimplexNoise::set_persistence(float p_persistence) {
+void OpenSimplexNoise::set_persistence(float p_persistence) {
if (p_persistence == persistence) return;
persistence = p_persistence;
emit_changed();
}
-void SimplexNoise::set_lacunarity(float p_lacunarity) {
+void OpenSimplexNoise::set_lacunarity(float p_lacunarity) {
if (p_lacunarity == lacunarity) return;
lacunarity = p_lacunarity;
emit_changed();
}
-Ref<Image> SimplexNoise::get_image(int p_width, int p_height) {
+Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) {
PoolVector<uint8_t> data;
data.resize(p_width * p_height * 4);
@@ -116,7 +116,7 @@ Ref<Image> SimplexNoise::get_image(int p_width, int p_height) {
return image;
}
-Ref<Image> SimplexNoise::get_seamless_image(int p_size) {
+Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) {
PoolVector<uint8_t> data;
data.resize(p_size * p_size * 4);
@@ -153,32 +153,32 @@ Ref<Image> SimplexNoise::get_seamless_image(int p_size) {
return image;
}
-void SimplexNoise::_bind_methods() {
+void OpenSimplexNoise::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_seed"), &SimplexNoise::get_seed);
- ClassDB::bind_method(D_METHOD("set_seed", "seed"), &SimplexNoise::set_seed);
+ ClassDB::bind_method(D_METHOD("get_seed"), &OpenSimplexNoise::get_seed);
+ ClassDB::bind_method(D_METHOD("set_seed", "seed"), &OpenSimplexNoise::set_seed);
- ClassDB::bind_method(D_METHOD("set_octaves", "octave_count"), &SimplexNoise::set_octaves);
- ClassDB::bind_method(D_METHOD("get_octaves"), &SimplexNoise::get_octaves);
+ ClassDB::bind_method(D_METHOD("set_octaves", "octave_count"), &OpenSimplexNoise::set_octaves);
+ ClassDB::bind_method(D_METHOD("get_octaves"), &OpenSimplexNoise::get_octaves);
- ClassDB::bind_method(D_METHOD("set_period", "period"), &SimplexNoise::set_period);
- ClassDB::bind_method(D_METHOD("get_period"), &SimplexNoise::get_period);
+ ClassDB::bind_method(D_METHOD("set_period", "period"), &OpenSimplexNoise::set_period);
+ ClassDB::bind_method(D_METHOD("get_period"), &OpenSimplexNoise::get_period);
- ClassDB::bind_method(D_METHOD("set_persistence", "persistence"), &SimplexNoise::set_persistence);
- ClassDB::bind_method(D_METHOD("get_persistence"), &SimplexNoise::get_persistence);
+ ClassDB::bind_method(D_METHOD("set_persistence", "persistence"), &OpenSimplexNoise::set_persistence);
+ ClassDB::bind_method(D_METHOD("get_persistence"), &OpenSimplexNoise::get_persistence);
- ClassDB::bind_method(D_METHOD("set_lacunarity", "lacunarity"), &SimplexNoise::set_lacunarity);
- ClassDB::bind_method(D_METHOD("get_lacunarity"), &SimplexNoise::get_lacunarity);
+ ClassDB::bind_method(D_METHOD("set_lacunarity", "lacunarity"), &OpenSimplexNoise::set_lacunarity);
+ ClassDB::bind_method(D_METHOD("get_lacunarity"), &OpenSimplexNoise::get_lacunarity);
- ClassDB::bind_method(D_METHOD("get_image", "width", "height"), &SimplexNoise::get_image);
- ClassDB::bind_method(D_METHOD("get_seamless_image", "size"), &SimplexNoise::get_seamless_image);
+ ClassDB::bind_method(D_METHOD("get_image", "width", "height"), &OpenSimplexNoise::get_image);
+ ClassDB::bind_method(D_METHOD("get_seamless_image", "size"), &OpenSimplexNoise::get_seamless_image);
- ClassDB::bind_method(D_METHOD("get_noise_2d", "x", "y"), &SimplexNoise::get_noise_2d);
- ClassDB::bind_method(D_METHOD("get_noise_3d", "x", "y", "z"), &SimplexNoise::get_noise_3d);
- ClassDB::bind_method(D_METHOD("get_noise_4d", "x", "y", "z", "w"), &SimplexNoise::get_noise_4d);
+ ClassDB::bind_method(D_METHOD("get_noise_2d", "x", "y"), &OpenSimplexNoise::get_noise_2d);
+ ClassDB::bind_method(D_METHOD("get_noise_3d", "x", "y", "z"), &OpenSimplexNoise::get_noise_3d);
+ ClassDB::bind_method(D_METHOD("get_noise_4d", "x", "y", "z", "w"), &OpenSimplexNoise::get_noise_4d);
- ClassDB::bind_method(D_METHOD("get_noise_2dv", "pos"), &SimplexNoise::get_noise_2dv);
- ClassDB::bind_method(D_METHOD("get_noise_3dv", "pos"), &SimplexNoise::get_noise_3dv);
+ ClassDB::bind_method(D_METHOD("get_noise_2dv", "pos"), &OpenSimplexNoise::get_noise_2dv);
+ ClassDB::bind_method(D_METHOD("get_noise_3dv", "pos"), &OpenSimplexNoise::get_noise_3dv);
ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "octaves", PROPERTY_HINT_RANGE, "1,6,1"), "set_octaves", "get_octaves");
@@ -187,7 +187,7 @@ void SimplexNoise::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "lacunarity", PROPERTY_HINT_RANGE, "0.1,4.0,0.01"), "set_lacunarity", "get_lacunarity");
}
-float SimplexNoise::get_noise_2d(float x, float y) {
+float OpenSimplexNoise::get_noise_2d(float x, float y) {
x /= period;
y /= period;
@@ -208,7 +208,7 @@ float SimplexNoise::get_noise_2d(float x, float y) {
return sum / max;
}
-float SimplexNoise::get_noise_3d(float x, float y, float z) {
+float OpenSimplexNoise::get_noise_3d(float x, float y, float z) {
x /= period;
y /= period;
@@ -231,7 +231,7 @@ float SimplexNoise::get_noise_3d(float x, float y, float z) {
return sum / max;
}
-float SimplexNoise::get_noise_4d(float x, float y, float z, float w) {
+float OpenSimplexNoise::get_noise_4d(float x, float y, float z, float w) {
x /= period;
y /= period;
diff --git a/modules/opensimplex/simplex_noise.h b/modules/opensimplex/open_simplex_noise.h
index 9a48dbf809..a9bee266e8 100644
--- a/modules/opensimplex/simplex_noise.h
+++ b/modules/opensimplex/open_simplex_noise.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* simplex_noise.h */
+/* open_simplex_noise.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SIMPLEX_NOISE_H
-#define SIMPLEX_NOISE_H
+#ifndef OPEN_SIMPLEX_NOISE_H
+#define OPEN_SIMPLEX_NOISE_H
#include "core/image.h"
#include "core/reference.h"
@@ -37,9 +37,9 @@
#include "thirdparty/misc/open-simplex-noise.h"
-class SimplexNoise : public Resource {
- GDCLASS(SimplexNoise, Resource)
- OBJ_SAVE_TYPE(SimplexNoise);
+class OpenSimplexNoise : public Resource {
+ GDCLASS(OpenSimplexNoise, Resource)
+ OBJ_SAVE_TYPE(OpenSimplexNoise);
osn_context contexts[6];
@@ -50,8 +50,8 @@ class SimplexNoise : public Resource {
float lacunarity; // Controls period change across octaves. 2 is usually a good value to address all detail levels.
public:
- SimplexNoise();
- ~SimplexNoise();
+ OpenSimplexNoise();
+ ~OpenSimplexNoise();
void _init_seeds();
@@ -90,4 +90,4 @@ protected:
static void _bind_methods();
};
-#endif // OPENSIMPLEX_NOISE_H
+#endif // OPEN_SIMPLEX_NOISE_H
diff --git a/modules/opensimplex/register_types.cpp b/modules/opensimplex/register_types.cpp
index 9e4af99651..d1c77da257 100644
--- a/modules/opensimplex/register_types.cpp
+++ b/modules/opensimplex/register_types.cpp
@@ -30,11 +30,11 @@
#include "register_types.h"
#include "noise_texture.h"
-#include "simplex_noise.h"
+#include "open_simplex_noise.h"
void register_opensimplex_types() {
- ClassDB::register_class<SimplexNoise>();
+ ClassDB::register_class<OpenSimplexNoise>();
ClassDB::register_class<NoiseTexture>();
}
diff --git a/modules/opus/SCsub b/modules/opus/SCsub
index 6f643ef08c..508aec7057 100644
--- a/modules/opus/SCsub
+++ b/modules/opus/SCsub
@@ -3,7 +3,6 @@
Import('env')
Import('env_modules')
-
stub = True
env_opus = env_modules.Clone()
@@ -198,7 +197,10 @@ if env['builtin_opus']:
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources + opus_sources_silk]
- env_opus.add_source_files(env.modules_sources, thirdparty_sources)
+ # also requires libogg
+ if env['builtin_libogg']:
+ env_opus.Append(CPPPATH=["#thirdparty/libogg"])
+
env_opus.Append(CFLAGS=["-DHAVE_CONFIG_H"])
thirdparty_include_paths = [
@@ -211,9 +213,9 @@ if env['builtin_opus']:
]
env_opus.Append(CPPPATH=[thirdparty_dir + "/" + dir for dir in thirdparty_include_paths])
- # also requires libogg
- if env['builtin_libogg']:
- env_opus.Append(CPPPATH=["#thirdparty/libogg"])
+ env_thirdparty = env_opus.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
if not stub:
# Module files
diff --git a/modules/pvr/SCsub b/modules/pvr/SCsub
index ddca7a794e..2e4a792a36 100644
--- a/modules/pvr/SCsub
+++ b/modules/pvr/SCsub
@@ -17,8 +17,11 @@ thirdparty_sources = [
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env_pvr.add_source_files(env.modules_sources, thirdparty_sources)
env_pvr.Append(CPPPATH=[thirdparty_dir])
+env_thirdparty = env_pvr.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot source files
env_pvr.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/recast/SCsub b/modules/recast/SCsub
index f56be72b24..4a06653968 100644
--- a/modules/recast/SCsub
+++ b/modules/recast/SCsub
@@ -23,10 +23,11 @@ if env['builtin_recast']:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_recast.add_source_files(env.modules_sources, thirdparty_sources)
env_recast.Append(CPPPATH=[thirdparty_dir + "/Include"])
+ env_thirdparty = env_recast.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot source files
env_recast.add_source_files(env.modules_sources, "*.cpp")
-
-Export('env')
diff --git a/modules/regex/SCsub b/modules/regex/SCsub
index 4b8d5e9283..99c25add45 100644
--- a/modules/regex/SCsub
+++ b/modules/regex/SCsub
@@ -4,15 +4,16 @@ Import('env')
Import('env_modules')
env_regex = env_modules.Clone()
-env_regex.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=0"])
-env_regex.add_source_files(env.modules_sources, "*.cpp")
if env['builtin_pcre2']:
jit_blacklist = ['javascript', 'uwp']
+
thirdparty_dir = '#thirdparty/pcre2/src/'
thirdparty_flags = ['-DPCRE2_STATIC', '-DHAVE_CONFIG_H']
+
if 'platform' in env and env['platform'] not in jit_blacklist:
thirdparty_flags.append('-DSUPPORT_JIT')
+
thirdparty_sources = [
"pcre2_auto_possess.c",
"pcre2_chartables.c",
@@ -42,15 +43,21 @@ if env['builtin_pcre2']:
"pcre2_valid_utf.c",
"pcre2_xclass.c",
]
+
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
env_regex.Append(CPPPATH=[thirdparty_dir])
env_regex.Append(CPPFLAGS=thirdparty_flags)
+
def pcre2_builtin(width):
- env_pcre2 = env_modules.Clone()
+ env_pcre2 = env_regex.Clone()
+ env_pcre2.disable_warnings()
env_pcre2["OBJSUFFIX"] = "_" + width + env_pcre2["OBJSUFFIX"]
- env_pcre2.Append(CPPPATH=[thirdparty_dir])
env_pcre2.add_source_files(env.modules_sources, thirdparty_sources)
- env_pcre2.Append(CPPFLAGS=thirdparty_flags)
env_pcre2.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=" + width])
+
pcre2_builtin("16")
pcre2_builtin("32")
+
+env_regex.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=0"])
+env_regex.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/squish/SCsub b/modules/squish/SCsub
index 127f22d798..3be85a1efa 100644
--- a/modules/squish/SCsub
+++ b/modules/squish/SCsub
@@ -22,8 +22,11 @@ if env['builtin_squish']:
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_squish.add_source_files(env.modules_sources, thirdparty_sources)
env_squish.Append(CPPPATH=[thirdparty_dir])
+ env_thirdparty = env_squish.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot source files
env_squish.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/svg/SCsub b/modules/svg/SCsub
index a41e0703bd..d14191056f 100644
--- a/modules/svg/SCsub
+++ b/modules/svg/SCsub
@@ -9,11 +9,12 @@ thirdparty_sources = [
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env.add_source_files(env.modules_sources, thirdparty_sources)
env.Append(CPPPATH=[thirdparty_dir])
env.Append(CCFLAGS=["-DSVG_ENABLED"])
+env_thirdparty = env.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot's own source files
env.add_source_files(env.modules_sources, "*.cpp")
-
-Export('env')
diff --git a/modules/thekla_unwrap/SCsub b/modules/thekla_unwrap/SCsub
index d23ba10d4c..c47c760d5f 100644
--- a/modules/thekla_unwrap/SCsub
+++ b/modules/thekla_unwrap/SCsub
@@ -54,11 +54,10 @@ if env['builtin_thekla_atlas']:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_thekla_unwrap.add_source_files(env.modules_sources, thirdparty_sources)
- env_thekla_unwrap.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/poshlib", thirdparty_dir + "/nvcore", thirdparty_dir + "/nvmesh"])
+ env_thekla_unwrap.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "poshlib", thirdparty_dir + "nvcore", thirdparty_dir + "nvmesh"])
# upstream uses c++11
- if (not env_thekla_unwrap.msvc):
+ if (not env.msvc):
env_thekla_unwrap.Append(CXXFLAGS="-std=c++11")
if env["platform"] == 'x11':
@@ -78,5 +77,9 @@ if env['builtin_thekla_atlas']:
env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_MINGW", "-DNV_CC_GNUC", "-DPOSH_COMPILER_GCC", "-U__STRICT_ANSI__"])
env.Append(LIBS=["dbghelp"])
+ env_thirdparty = env_thekla_unwrap.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot source files
env_thekla_unwrap.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/thekla_unwrap/config.py b/modules/thekla_unwrap/config.py
index bd092bdc16..fad6095064 100644
--- a/modules/thekla_unwrap/config.py
+++ b/modules/thekla_unwrap/config.py
@@ -1,5 +1,6 @@
def can_build(env, platform):
- return (env['tools'] and platform not in ["android", "ios"])
+ #return (env['tools'] and platform not in ["android", "ios"])
+ return False
def configure(env):
pass
diff --git a/modules/theora/SCsub b/modules/theora/SCsub
index 9015c2c354..98c4274a7e 100644
--- a/modules/theora/SCsub
+++ b/modules/theora/SCsub
@@ -70,7 +70,6 @@ if env['builtin_libtheora']:
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_theora.add_source_files(env.modules_sources, thirdparty_sources)
env_theora.Append(CPPPATH=[thirdparty_dir])
# also requires libogg and libvorbis
@@ -79,5 +78,9 @@ if env['builtin_libtheora']:
if env['builtin_libvorbis']:
env_theora.Append(CPPPATH=["#thirdparty/libvorbis"])
+ env_thirdparty = env_theora.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot source files
env_theora.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/tinyexr/SCsub b/modules/tinyexr/SCsub
index 38fd00cc65..3e7bda2bca 100644
--- a/modules/tinyexr/SCsub
+++ b/modules/tinyexr/SCsub
@@ -13,8 +13,11 @@ thirdparty_sources = [
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env_tinyexr.add_source_files(env.modules_sources, thirdparty_sources)
env_tinyexr.Append(CPPPATH=[thirdparty_dir])
+env_thirdparty = env_tinyexr.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot's own source files
env_tinyexr.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub
index cde231867f..2b15f7aee2 100644
--- a/modules/upnp/SCsub
+++ b/modules/upnp/SCsub
@@ -25,8 +25,12 @@ if env['builtin_miniupnpc']:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_upnp.add_source_files(env.modules_sources, thirdparty_sources)
env_upnp.Append(CPPPATH=[thirdparty_dir])
env_upnp.Append(CPPFLAGS=["-DMINIUPNP_STATICLIB"])
+ env_thirdparty = env_upnp.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
+# Godot source files
env_upnp.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/visual_script/SCsub b/modules/visual_script/SCsub
index 96ee911ba0..3c3d2caa57 100644
--- a/modules/visual_script/SCsub
+++ b/modules/visual_script/SCsub
@@ -6,5 +6,3 @@ Import('env_modules')
env_vs = env_modules.Clone()
env_vs.add_source_files(env.modules_sources, "*.cpp")
-
-Export('env')
diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub
index 55a112585b..19587563ab 100644
--- a/modules/vorbis/SCsub
+++ b/modules/vorbis/SCsub
@@ -40,13 +40,16 @@ if env['builtin_libvorbis']:
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_vorbis.add_source_files(env.modules_sources, thirdparty_sources)
env_vorbis.Append(CPPPATH=[thirdparty_dir])
# also requires libogg
if env['builtin_libogg']:
env_vorbis.Append(CPPPATH=["#thirdparty/libogg"])
+ env_thirdparty = env_vorbis.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
if not stub:
# Module files
env_vorbis.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/webm/SCsub b/modules/webm/SCsub
index 33561da098..cb35b926ab 100644
--- a/modules/webm/SCsub
+++ b/modules/webm/SCsub
@@ -6,17 +6,16 @@ Import('env_modules')
env_webm = env_modules.Clone()
# Thirdparty source files
-thirdparty_libsimplewebm_dir = "#thirdparty/libsimplewebm/"
-thirdparty_libsimplewebm_sources = [
+thirdparty_dir = "#thirdparty/libsimplewebm/"
+thirdparty_sources = [
"libwebm/mkvparser/mkvparser.cc",
"OpusVorbisDecoder.cpp",
"VPXDecoder.cpp",
"WebMDemuxer.cpp",
]
-thirdparty_libsimplewebm_sources = [thirdparty_libsimplewebm_dir + file for file in thirdparty_libsimplewebm_sources]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env_webm.add_source_files(env.modules_sources, thirdparty_libsimplewebm_sources)
-env_webm.Append(CPPPATH=[thirdparty_libsimplewebm_dir, thirdparty_libsimplewebm_dir + "libwebm/"])
+env_webm.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "libwebm/"])
# upstream uses c++11
if (not env_webm.msvc):
@@ -31,8 +30,12 @@ if env['builtin_opus']:
env_webm.Append(CPPPATH=["#thirdparty/opus"])
if env['builtin_libvpx']:
- Export('env_webm')
+ env_webm.Append(CPPPATH=["#thirdparty/libvpx"])
SConscript("libvpx/SCsub")
+env_thirdparty = env_webm.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot source files
env_webm.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub
index 711000bd9f..98e38b9027 100644
--- a/modules/webm/libvpx/SCsub
+++ b/modules/webm/libvpx/SCsub
@@ -1,5 +1,10 @@
#!/usr/bin/env python
+Import('env')
+Import('env_modules')
+
+# Thirdparty sources
+
libvpx_dir = "#thirdparty/libvpx/"
libvpx_sources = [
@@ -249,12 +254,8 @@ libvpx_sources_arm_neon_armasm_ms = [libvpx_dir + file for file in libvpx_source
libvpx_sources_arm_neon_gas_apple = [libvpx_dir + file for file in libvpx_sources_arm_neon_gas_apple]
-Import('env')
-Import('env_webm')
-
-env_webm.Append(CPPPATH=[libvpx_dir])
-
-env_libvpx = env.Clone()
+env_libvpx = env_modules.Clone()
+env_libvpx.disable_warnings()
env_libvpx.Append(CPPPATH=[libvpx_dir])
webm_multithread = env["platform"] != 'javascript'
diff --git a/modules/webp/SCsub b/modules/webp/SCsub
index 21ae0ce7c2..8a4307fbe1 100644
--- a/modules/webp/SCsub
+++ b/modules/webp/SCsub
@@ -127,8 +127,11 @@ if env['builtin_libwebp']:
]
thirdparty_sources = [thirdparty_dir + "src/" + file for file in thirdparty_sources]
- env_webp.add_source_files(env.modules_sources, thirdparty_sources)
env_webp.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "src/"])
+ env_thirdparty = env_webp.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
# Godot source files
env_webp.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index c0985b3245..b67a836fe8 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -7,7 +7,7 @@ Import('env_modules')
env_lws = env_modules.Clone()
-if env['builtin_libwebsockets']:
+if env['builtin_libwebsockets'] and not env["platform"] == "javascript": # already builtin for javascript
thirdparty_dir = "#thirdparty/libwebsockets/"
helper_dir = "win32helpers/"
thirdparty_sources = [
@@ -61,34 +61,33 @@ if env['builtin_libwebsockets']:
"tls/mbedtls/mbedtls-server.c"
]
- if env_lws["platform"] == "android": # Builtin getifaddrs
+ if env["platform"] == "android": # Builtin getifaddrs
thirdparty_sources += ["misc/getifaddrs.c"]
- if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp": # Winsock
+ if env["platform"] == "windows" or env["platform"] == "uwp": # Winsock
thirdparty_sources += ["plat/lws-plat-win.c", helper_dir + "getopt.c", helper_dir + "getopt_long.c", helper_dir + "gettimeofday.c"]
else: # Unix socket
thirdparty_sources += ["plat/lws-plat-unix.c"]
-
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- if env_lws["platform"] == "javascript": # No need to add third party libraries at all
- pass
- else:
- env_lws.add_source_files(env.modules_sources, thirdparty_sources)
- env_lws.Append(CPPPATH=[thirdparty_dir])
+ env_lws.Append(CPPPATH=[thirdparty_dir])
+
+ if env['builtin_mbedtls']:
+ mbedtls_includes = "#thirdparty/mbedtls/include"
+ env_lws.Prepend(CPPPATH=[mbedtls_includes])
- wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
- env_lws.Prepend(CPPPATH=wrapper_includes)
+ wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
+ env_lws.Prepend(CPPPATH=wrapper_includes)
- if env['builtin_mbedtls']:
- mbedtls_includes = "#thirdparty/mbedtls/include"
- env_lws.Prepend(CPPPATH=[mbedtls_includes])
+ if env["platform"] == "windows" or env["platform"] == "uwp":
+ env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir])
- if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp":
- env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir])
+ if env["platform"] == "uwp":
+ env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"])
- if env_lws["platform"] == "uwp":
- env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"])
+ env_thirdparty = env_lws.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
env_lws.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp
index 2f022bcfc9..b5c130b308 100644
--- a/modules/websocket/lws_peer.cpp
+++ b/modules/websocket/lws_peer.cpp
@@ -39,7 +39,7 @@
#include <sys/socket.h>
#endif
-#include "drivers/unix/socket_helpers.h"
+#include "drivers/unix/net_socket_posix.h"
void LWSPeer::set_wsi(struct lws *p_wsi) {
ERR_FAIL_COND(wsi != NULL);
@@ -232,7 +232,7 @@ IP_Address LWSPeer::get_connected_host() const {
ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address());
IP_Address ip;
- int port = 0;
+ uint16_t port = 0;
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
@@ -243,7 +243,7 @@ IP_Address LWSPeer::get_connected_host() const {
int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
ERR_FAIL_COND_V(ret != 0, IP_Address());
- _set_ip_addr_port(ip, port, &addr);
+ NetSocketPosix::_set_ip_port(&addr, ip, port);
return ip;
};
@@ -253,7 +253,7 @@ uint16_t LWSPeer::get_connected_port() const {
ERR_FAIL_COND_V(!is_connected_to_host(), 0);
IP_Address ip;
- int port = 0;
+ uint16_t port = 0;
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
@@ -264,7 +264,7 @@ uint16_t LWSPeer::get_connected_port() const {
int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
ERR_FAIL_COND_V(ret != 0, 0);
- _set_ip_addr_port(ip, port, &addr);
+ NetSocketPosix::_set_ip_port(&addr, ip, port);
return port;
};
diff --git a/modules/xatlas_unwrap/SCsub b/modules/xatlas_unwrap/SCsub
new file mode 100644
index 0000000000..ad364d5aaf
--- /dev/null
+++ b/modules/xatlas_unwrap/SCsub
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+
+import platform
+
+Import('env')
+Import('env_modules')
+
+env_xatlas_unwrap = env_modules.Clone()
+
+# Thirdparty source files
+if env['builtin_xatlas']:
+ thirdparty_dir = "#thirdparty/xatlas/"
+ thirdparty_sources = [
+ "xatlas.cpp",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_xatlas_unwrap.Append(CPPPATH=[thirdparty_dir])
+
+ # upstream uses c++11
+ if (not env.msvc):
+ env_xatlas_unwrap.Append(CXXFLAGS="-std=c++11")
+
+ if env["platform"] == 'x11':
+ # if not specifically one of the *BSD, then use LINUX as default
+ if platform.system() == "FreeBSD":
+ env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_FREEBSD", "-DPOSH_COMPILER_GCC"])
+ elif platform.system() == "OpenBSD":
+ env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_OPENBSD", "-DPOSH_COMPILER_GCC"])
+ else:
+ env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_LINUX", "-DPOSH_COMPILER_GCC"])
+ elif env["platform"] == 'osx':
+ env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_DARWIN", "-DPOSH_COMPILER_GCC"])
+ elif env["platform"] == 'windows':
+ if env.msvc:
+ env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_WIN32", "-DNV_CC_MSVC", "-DPOSH_COMPILER_MSVC" ])
+ else:
+ env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_MINGW", "-DNV_CC_GNUC", "-DPOSH_COMPILER_GCC", "-U__STRICT_ANSI__"])
+ env.Append(LIBS=["dbghelp"])
+
+ env_thirdparty = env_xatlas_unwrap.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
+# Godot source files
+env_xatlas_unwrap.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/xatlas_unwrap/config.py b/modules/xatlas_unwrap/config.py
new file mode 100644
index 0000000000..bd092bdc16
--- /dev/null
+++ b/modules/xatlas_unwrap/config.py
@@ -0,0 +1,5 @@
+def can_build(env, platform):
+ return (env['tools'] and platform not in ["android", "ios"])
+
+def configure(env):
+ pass
diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp
new file mode 100644
index 0000000000..9df16aac70
--- /dev/null
+++ b/modules/xatlas_unwrap/register_types.cpp
@@ -0,0 +1,133 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_types.h"
+#include "core/error_macros.h"
+#include "thirdparty/xatlas/xatlas.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y);
+
+bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) {
+
+ //set up input mesh
+ xatlas::InputMesh input_mesh;
+ input_mesh.indexData = malloc(sizeof(int) * p_index_count);
+ input_mesh.indexCount = p_index_count;
+ input_mesh.indexFormat = xatlas::IndexFormat::Float; //really xatlas?
+ input_mesh.faceMaterialData = (uint16_t *)malloc(sizeof(uint16_t) * p_index_count);
+
+ for (int i = 0; i < p_index_count; i++) {
+ int *index = (int *)input_mesh.indexData;
+ index[i] = p_indices[i];
+ }
+ for (int i = 0; i < p_index_count / 3; i++) {
+ uint16_t *mat_index = (uint16_t *)input_mesh.faceMaterialData;
+ mat_index[i] = p_face_materials[i];
+ }
+
+ input_mesh.vertexCount = p_vertex_count;
+ input_mesh.vertexPositionData = malloc(sizeof(float) * p_vertex_count * 3);
+ input_mesh.vertexPositionStride = sizeof(float) * 3;
+ input_mesh.vertexNormalData = malloc(sizeof(float) * p_vertex_count * 3);
+ input_mesh.vertexNormalStride = sizeof(float) * 3;
+
+ //material is a better hint than this i guess?
+ input_mesh.vertexUvData = NULL;
+ input_mesh.vertexUvStride = 0;
+
+ for (int i = 0; i < p_vertex_count * 3; i++) {
+ float *vertex_ptr = (float *)input_mesh.vertexPositionData;
+ float *normal_ptr = (float *)input_mesh.vertexNormalData;
+
+ vertex_ptr[i] = p_vertices[i];
+ normal_ptr[i] = p_normals[i];
+ }
+
+ xatlas::CharterOptions chart_options;
+ xatlas::PackerOptions pack_options;
+
+ pack_options.texelArea = 1.0 / p_texel_size;
+ pack_options.quality = 4;
+
+ xatlas::Atlas *atlas = xatlas::Create();
+ printf("adding mesh..\n");
+ xatlas::AddMeshError err = xatlas::AddMesh(atlas, input_mesh);
+ ERR_EXPLAINC(xatlas::StringForEnum(err.code));
+ ERR_FAIL_COND_V(err.code != xatlas::AddMeshErrorCode::Success, false);
+
+ printf("generate..\n");
+ xatlas::Generate(atlas, chart_options, pack_options);
+
+ *r_size_hint_x = xatlas::GetWidth(atlas);
+ *r_size_hint_y = xatlas::GetHeight(atlas);
+
+ float w = *r_size_hint_x;
+ float h = *r_size_hint_y;
+
+ printf("final texsize: %f,%f\n", w, h);
+ const xatlas::OutputMesh *const *output_meshes = xatlas::GetOutputMeshes(atlas);
+
+ const xatlas::OutputMesh *output = output_meshes[0];
+
+ *r_vertex = (int *)malloc(sizeof(int) * output->vertexCount);
+ *r_uv = (float *)malloc(sizeof(float) * output->vertexCount * 2);
+ *r_index = (int *)malloc(sizeof(int) * output->indexCount);
+
+ for (int i = 0; i < output->vertexCount; i++) {
+ (*r_vertex)[i] = output->vertexArray[i].xref;
+ (*r_uv)[i * 2 + 0] = output->vertexArray[i].uv[0];
+ (*r_uv)[i * 2 + 1] = output->vertexArray[i].uv[1];
+ }
+ *r_vertex_count = output->vertexCount;
+
+ for (int i = 0; i < output->indexCount; i++) {
+ (*r_index)[i] = output->indexArray[i];
+ }
+
+ *r_index_count = output->indexCount;
+
+ //xatlas::Destroy(atlas);
+ free((void *)input_mesh.indexData);
+ free((void *)input_mesh.vertexPositionData);
+ free((void *)input_mesh.vertexNormalData);
+ free((void *)input_mesh.faceMaterialData);
+ printf("done");
+ return true;
+}
+
+void register_xatlas_unwrap_types() {
+
+ array_mesh_lightmap_unwrap_callback = xatlas_mesh_lightmap_unwrap_callback;
+}
+
+void unregister_xatlas_unwrap_types() {
+}
diff --git a/modules/xatlas_unwrap/register_types.h b/modules/xatlas_unwrap/register_types.h
new file mode 100644
index 0000000000..fd8d56fa53
--- /dev/null
+++ b/modules/xatlas_unwrap/register_types.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+void register_xatlas_unwrap_types();
+void unregister_xatlas_unwrap_types();
diff --git a/platform/SCsub b/platform/SCsub
index 0f9c2047a0..aa83154ee0 100644
--- a/platform/SCsub
+++ b/platform/SCsub
@@ -29,5 +29,3 @@ platform_sources.append('register_platform_apis.gen.cpp')
lib = env.add_library('platform', platform_sources)
env.Prepend(LIBS=lib)
-
-Export('env')
diff --git a/platform/android/SCsub b/platform/android/SCsub
index 31fee5722c..807506000f 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -1,12 +1,12 @@
#!/usr/bin/env python
+Import('env')
+
import shutil
from compat import open_utf8
from distutils.version import LooseVersion
from detect import get_ndk_version
-Import('env')
-
android_files = [
'os_android.cpp',
@@ -26,10 +26,6 @@ android_files = [
# 'power_android.cpp'
]
-# env.Depends('#core/math/vector3.h', 'vector3_psp.h')
-
-#obj = env.SharedObject('godot_android.cpp')
-
env_android = env.Clone()
if env['target'] == "profile":
env_android.Append(CPPFLAGS=['-DPROFILER_ENABLED'])
@@ -174,4 +170,4 @@ if lib_arch_dir != '':
ndk_version = get_ndk_version(env["ANDROID_NDK_ROOT"])
if ndk_version != None and LooseVersion(ndk_version) >= LooseVersion("15.0.4075724"):
stl_lib_path = str(env['ANDROID_NDK_ROOT']) + '/sources/cxx-stl/llvm-libc++/libs/' + lib_arch_dir + '/libc++_shared.so'
- env_android.Command(out_dir + '/libc++_shared.so', stl_lib_path, Copy("$TARGET", "$SOURCE")) \ No newline at end of file
+ env_android.Command(out_dir + '/libc++_shared.so', stl_lib_path, Copy("$TARGET", "$SOURCE"))
diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java
index 92c9be5d43..c23037f3e0 100644
--- a/platform/android/java/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/src/org/godotengine/godot/Godot.java
@@ -184,6 +184,9 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
protected void onMainActivityResult(int requestCode, int resultCode, Intent data) {
}
+ protected void onMainRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ }
+
protected void onMainPause() {}
protected void onMainResume() {}
protected void onMainDestroy() {}
@@ -251,6 +254,13 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
}
};
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ for (int i = 0; i < singleton_count; i++) {
+ singletons[i].onMainRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ };
+
public void onVideoInit() {
boolean use_gl3 = getGLESVersionCode() >= 0x00030000;
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index b96bec16b4..debf051eda 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -1,8 +1,9 @@
#!/usr/bin/env python
-import os
Import('env')
+import os
+
iphone_lib = [
'godot_iphone.cpp',
'os_iphone.cpp',
diff --git a/platform/osx/SCsub b/platform/osx/SCsub
index 5c973c30c2..dc407eee9e 100644
--- a/platform/osx/SCsub
+++ b/platform/osx/SCsub
@@ -1,8 +1,8 @@
#!/usr/bin/env python
-import os
Import('env')
+import os
from platform_methods import run_in_subprocess
import platform_osx_builders
diff --git a/platform/server/SCsub b/platform/server/SCsub
index 0788ad75ae..c9082f9b3a 100644
--- a/platform/server/SCsub
+++ b/platform/server/SCsub
@@ -2,11 +2,9 @@
Import('env')
-
common_server = [\
"os_server.cpp",\
"#platform/x11/crash_handler_x11.cpp",
"#platform/x11/power_x11.cpp",
]
-
prog = env.add_program('#bin/godot_server', ['godot_server.cpp'] + common_server)
diff --git a/platform/server/detect.py b/platform/server/detect.py
index e921bf4a5f..597a2ff6a0 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -29,9 +29,7 @@ def get_opts():
def get_flags():
- return [
- ("module_mobile_vr_enabled", False),
- ]
+ return []
def configure(env):
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 5dfb1592e0..e07d373c4b 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -1,8 +1,8 @@
#!/usr/bin/env python
-import os
Import('env')
+import os
from platform_methods import run_in_subprocess
import platform_windows_builders
@@ -19,9 +19,7 @@ common_win = [
]
res_file = 'godot_res.rc'
-
res_target = "godot_res" + env["OBJSUFFIX"]
-
res_obj = env.RES(res_target, res_file)
prog = env.add_program('#bin/godot', common_win + res_obj, PROGSUFFIX=env["PROGSUFFIX"])
diff --git a/platform/x11/SCsub b/platform/x11/SCsub
index d3901eb798..97d3d1b514 100644
--- a/platform/x11/SCsub
+++ b/platform/x11/SCsub
@@ -1,8 +1,8 @@
#!/usr/bin/env python
-import os
Import('env')
+import os
from platform_methods import run_in_subprocess
import platform_x11_builders
diff --git a/scene/2d/SCsub b/scene/2d/SCsub
index bf9125be7f..b01e2fd54d 100644
--- a/scene/2d/SCsub
+++ b/scene/2d/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.scene_sources, "*.cpp")
-
-Export('env')
diff --git a/scene/3d/SCsub b/scene/3d/SCsub
index 4008f4f196..35cc7479d8 100644
--- a/scene/3d/SCsub
+++ b/scene/3d/SCsub
@@ -2,9 +2,7 @@
Import('env')
-
if env['disable_3d']:
-
env.scene_sources.append("3d/spatial.cpp")
env.scene_sources.append("3d/skeleton.cpp")
env.scene_sources.append("3d/particles.cpp")
@@ -12,5 +10,3 @@ if env['disable_3d']:
env.scene_sources.append("3d/scenario_fx.cpp")
else:
env.add_source_files(env.scene_sources, "*.cpp")
-
-Export('env')
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index d7bd89625f..0fb0869979 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -2287,6 +2287,8 @@ void PhysicalBone::_reload_joint() {
}
} break;
+ case JOINT_TYPE_NONE: {
+ } break;
}
}
@@ -2342,6 +2344,8 @@ void PhysicalBone::set_joint_type(JointType p_joint_type) {
case JOINT_TYPE_6DOF:
joint_data = memnew(SixDOFJointData);
break;
+ case JOINT_TYPE_NONE:
+ break;
}
_reload_joint();
@@ -2505,12 +2509,12 @@ PhysicalBone::PhysicalBone() :
gizmo_move_joint(false),
#endif
joint_data(NULL),
+ parent_skeleton(NULL),
static_body(false),
- simulate_physics(false),
_internal_static_body(false),
+ simulate_physics(false),
_internal_simulate_physics(false),
bone_id(-1),
- parent_skeleton(NULL),
bone_name(""),
bounce(0),
mass(1),
diff --git a/scene/3d/spring_arm.cpp b/scene/3d/spring_arm.cpp
index 818e7f9217..f74784c2f9 100644
--- a/scene/3d/spring_arm.cpp
+++ b/scene/3d/spring_arm.cpp
@@ -36,8 +36,9 @@
SpringArm::SpringArm() :
spring_length(1),
- mask(1),
current_spring_length(0),
+ keep_child_basis(false),
+ mask(1),
margin(0.01) {}
void SpringArm::_notification(int p_what) {
diff --git a/scene/3d/spring_arm.h b/scene/3d/spring_arm.h
index 24d912d371..e0c3f2992d 100644
--- a/scene/3d/spring_arm.h
+++ b/scene/3d/spring_arm.h
@@ -39,8 +39,8 @@ class SpringArm : public Spatial {
Ref<Shape> shape;
Set<RID> excluded_objects;
float spring_length;
- bool keep_child_basis;
float current_spring_length;
+ bool keep_child_basis;
uint32_t mask;
float margin;
diff --git a/scene/SCsub b/scene/SCsub
index 5d81e818ba..d8839ce3a8 100644
--- a/scene/SCsub
+++ b/scene/SCsub
@@ -4,16 +4,19 @@ Import('env')
env.scene_sources = []
-
# Thirdparty code
thirdparty_dir = "#thirdparty/misc/"
thirdparty_sources = [
+ # C++ sources
+ "easing_equations.cpp",
# C sources
"mikktspace.c",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env.add_source_files(env.scene_sources, thirdparty_sources)
+env_thirdparty = env.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(env.scene_sources, thirdparty_sources)
# Godot's own sources
env.add_source_files(env.scene_sources, "*.cpp")
@@ -32,5 +35,3 @@ SConscript('resources/SCsub')
# Build it all as a library
lib = env.add_library("scene", env.scene_sources)
env.Prepend(LIBS=[lib])
-
-Export('env')
diff --git a/scene/animation/SCsub b/scene/animation/SCsub
index bf9125be7f..b01e2fd54d 100644
--- a/scene/animation/SCsub
+++ b/scene/animation/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.scene_sources, "*.cpp")
-
-Export('env')
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 7d91703cf8..102f05a146 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -1451,6 +1451,7 @@ void AnimationPlayer::_set_process(bool p_process, bool p_force) {
case ANIMATION_PROCESS_PHYSICS: set_physics_process_internal(p_process && active); break;
case ANIMATION_PROCESS_IDLE: set_process_internal(p_process && active); break;
+ case ANIMATION_PROCESS_MANUAL: break;
}
processing = p_process;
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 179f5d9698..524784df53 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -877,6 +877,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
tr.track->object->call(method, args[0], args[1], args[2], args[3], args[4]);
}
} break;
+ default: {}
}
}
}
diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp
index 69975e6195..83f45afac8 100644
--- a/scene/animation/skeleton_ik.cpp
+++ b/scene/animation/skeleton_ik.cpp
@@ -418,11 +418,11 @@ void SkeletonIK::_notification(int p_what) {
SkeletonIK::SkeletonIK() :
Node(),
interpolation(1),
- skeleton(NULL),
- target_node_override(NULL),
use_magnet(false),
min_distance(0.01),
max_iterations(10),
+ skeleton(NULL),
+ target_node_override(NULL),
task(NULL) {
set_process_priority(1);
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 82e2bb93e2..3521782417 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -277,7 +277,10 @@ Variant &Tween::_get_initial_val(InterpolateData &p_data) {
ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, p_data.initial_val);
}
return initial_val;
- } break;
+ }
+
+ case INTER_CALLBACK:
+ break;
}
return p_data.delta_val;
}
@@ -313,7 +316,7 @@ Variant &Tween::_get_delta_val(InterpolateData &p_data) {
if (final_val.get_type() == Variant::INT) final_val = final_val.operator real_t();
_calc_delta_val(p_data.initial_val, final_val, p_data.delta_val);
return p_data.delta_val;
- } break;
+ }
case TARGETING_PROPERTY:
case TARGETING_METHOD: {
@@ -325,7 +328,10 @@ Variant &Tween::_get_delta_val(InterpolateData &p_data) {
//_calc_delta_val(p_data.initial_val, p_data.final_val, p_data.delta_val);
_calc_delta_val(initial_val, p_data.final_val, p_data.delta_val);
return p_data.delta_val;
- } break;
+ }
+
+ case INTER_CALLBACK:
+ break;
}
return p_data.initial_val;
}
@@ -857,12 +863,8 @@ bool Tween::seek(real_t p_time) {
data.finish = false;
}
- switch (data.type) {
- case INTER_PROPERTY:
- case INTER_METHOD:
- break;
- case INTER_CALLBACK:
- continue;
+ if (data.type == INTER_CALLBACK) {
+ continue;
}
Variant result = _run_equation(data);
diff --git a/scene/audio/SCsub b/scene/audio/SCsub
index bf9125be7f..b01e2fd54d 100644
--- a/scene/audio/SCsub
+++ b/scene/audio/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.scene_sources, "*.cpp")
-
-Export('env')
diff --git a/scene/gui/SCsub b/scene/gui/SCsub
index bf9125be7f..b01e2fd54d 100644
--- a/scene/gui/SCsub
+++ b/scene/gui/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.scene_sources, "*.cpp")
-
-Export('env')
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 59590ea67b..895e76d516 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -360,7 +360,9 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const {
return DRAW_DISABLED;
};
- if (status.press_attempt == false && status.hovering && !status.pressed) {
+ if (status.press_attempt == false && status.hovering) {
+ if (status.pressed)
+ return DRAW_HOVER_PRESSED;
return DRAW_HOVER;
} else {
@@ -536,6 +538,7 @@ void BaseButton::_bind_methods() {
BIND_ENUM_CONSTANT(DRAW_PRESSED);
BIND_ENUM_CONSTANT(DRAW_HOVER);
BIND_ENUM_CONSTANT(DRAW_DISABLED);
+ BIND_ENUM_CONSTANT(DRAW_HOVER_PRESSED);
BIND_ENUM_CONSTANT(ACTION_MODE_BUTTON_PRESS);
BIND_ENUM_CONSTANT(ACTION_MODE_BUTTON_RELEASE);
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index 79638bbcce..176d9fc213 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -85,6 +85,7 @@ public:
DRAW_PRESSED,
DRAW_HOVER,
DRAW_DISABLED,
+ DRAW_HOVER_PRESSED,
};
DrawMode get_draw_mode() const;
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index dd6d66ac62..2d17fb1391 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -88,6 +88,21 @@ void Button::_notification(int p_what) {
if (has_color("icon_color_normal"))
color_icon = get_color("icon_color_normal");
} break;
+ case DRAW_HOVER_PRESSED: {
+ if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) {
+ style = get_stylebox("hover_pressed");
+ if (!flat)
+ style->draw(ci, Rect2(Point2(0, 0), size));
+ if (has_color("font_color_hover_pressed"))
+ color = get_color("font_color_hover_pressed");
+ else
+ color = get_color("font_color");
+ if (has_color("icon_color_hover_pressed"))
+ color_icon = get_color("icon_color_hover_pressed");
+
+ break;
+ }
+ }
case DRAW_PRESSED: {
style = get_stylebox("pressed");
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 283d66d8de..1e9f4df4a3 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -330,6 +330,10 @@ void FileDialog::deselect_items() {
case MODE_OPEN_DIR:
get_ok()->set_text(RTR("Select Current Folder"));
break;
+ case MODE_OPEN_ANY:
+ case MODE_SAVE_FILE:
+ // FIXME: Implement, or refactor to avoid duplication with set_mode
+ break;
}
}
}
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index 09d8664240..e862743934 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -30,6 +30,19 @@
#include "range.h"
+String Range::get_configuration_warning() const {
+ String warning = Control::get_configuration_warning();
+
+ if (shared->exp_ratio && shared->min <= 0) {
+ if (warning != String()) {
+ warning += "\n";
+ }
+ warning += TTR("If exp_edit is true min_value must be > 0.");
+ }
+
+ return warning;
+}
+
void Range::_value_changed_notify() {
_value_changed(shared->val);
@@ -66,10 +79,11 @@ void Range::Shared::emit_changed(const char *p_what) {
}
void Range::set_value(double p_val) {
+ if (shared->step > 0)
+ p_val = Math::round(p_val / shared->step) * shared->step;
- if (_rounded_values) {
+ if (_rounded_values)
p_val = Math::round(p_val);
- }
if (!shared->allow_greater && p_val > shared->max - shared->page)
p_val = shared->max - shared->page;
@@ -90,6 +104,8 @@ void Range::set_min(double p_min) {
set_value(shared->val);
shared->emit_changed("min");
+
+ update_configuration_warning();
}
void Range::set_max(double p_max) {
@@ -277,6 +293,8 @@ bool Range::is_using_rounded_values() const {
void Range::set_exp_ratio(bool p_enable) {
shared->exp_ratio = p_enable;
+
+ update_configuration_warning();
}
bool Range::is_ratio_exp() const {
diff --git a/scene/gui/range.h b/scene/gui/range.h
index 125f559248..58f15c8aa8 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -97,6 +97,8 @@ public:
void share(Range *p_range);
void unshare();
+ virtual String get_configuration_warning() const;
+
Range();
~Range();
};
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 64567a3d0f..632a686256 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -3794,7 +3794,7 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const {
int tab_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width;
while (col < line_text.length()) {
- char c = line_text[col];
+ CharType c = line_text[col];
int w = text.get_char_width(c, line_text[col + 1], px + word_px);
int indent_ofs = (cur_wrap_index != 0 ? tab_offset_px : 0);
@@ -5864,7 +5864,7 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
if (select_word(s, col, beg, end)) {
bool inside_quotes = false;
- char selected_quote = '\0';
+ CharType selected_quote = '\0';
int qbegin = 0, qend = 0;
for (int i = 0; i < s.length(); i++) {
if (s[i] == '"' || s[i] == '\'') {
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index 6bd3b26280..a6a57b010f 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -88,6 +88,9 @@ bool TextureButton::has_point(const Point2 &p_point) const {
scale.y = min;
ofs -= _texture_region.position / min;
} break;
+ default: {
+ // FIXME: Why a switch if we only handle one enum value?
+ }
}
// offset and scale the new point position to adjust it to the bitmask size
diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp
index 8188d1dcf8..d28b4065fb 100644
--- a/scene/gui/texture_progress.cpp
+++ b/scene/gui/texture_progress.cpp
@@ -229,6 +229,17 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F
first_section_size = topleft.y;
last_section_size = bottomright.y;
} break;
+ case FILL_BILINEAR_LEFT_AND_RIGHT: {
+ // TODO: Implement
+ } break;
+ case FILL_BILINEAR_TOP_AND_BOTTOM: {
+ // TODO: Implement
+ } break;
+ case FILL_CLOCKWISE:
+ case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE:
+ case FILL_COUNTER_CLOCKWISE: {
+ // Those modes are circular, not relevant for nine patch
+ } break;
}
double width_filled = width_total * p_ratio;
@@ -263,6 +274,17 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F
dst_rect.size.y = width_filled;
topleft.y = last_section_size;
} break;
+ case FILL_BILINEAR_LEFT_AND_RIGHT: {
+ // TODO: Implement
+ } break;
+ case FILL_BILINEAR_TOP_AND_BOTTOM: {
+ // TODO: Implement
+ } break;
+ case FILL_CLOCKWISE:
+ case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE:
+ case FILL_COUNTER_CLOCKWISE: {
+ // Those modes are circular, not relevant for nine patch
+ } break;
}
}
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 551600109e..34138acb85 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -53,7 +53,6 @@ public:
CELL_MODE_STRING, ///< just a string
CELL_MODE_CHECK, ///< string + check
CELL_MODE_RANGE, ///< Contains a range
- CELL_MODE_RANGE_EXPRESSION, ///< Contains a range
CELL_MODE_ICON, ///< Contains an icon, not editable
CELL_MODE_CUSTOM, ///< Contains a custom value, show a string, and an edit button
};
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp
index 17ab234551..39e7c73390 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_player.cpp
@@ -102,6 +102,10 @@ void VideoPlayer::_mix_audio() {
}
} break;
+ case AudioServer::SPEAKER_SURROUND_31: {
+
+ // FIXME: Implement
+ } break;
case AudioServer::SPEAKER_SURROUND_51: {
AudioFrame *targets[2] = {
diff --git a/scene/main/SCsub b/scene/main/SCsub
index bf9125be7f..b01e2fd54d 100644
--- a/scene/main/SCsub
+++ b/scene/main/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.scene_sources, "*.cpp")
-
-Export('env')
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index aebc96aad7..fdbe3b57f0 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1197,6 +1197,9 @@ void SceneTree::_update_root_rect() {
}
switch (stretch_mode) {
+ case STRETCH_MODE_DISABLED: {
+ // Already handled above
+ } break;
case STRETCH_MODE_2D: {
root->set_size((screen_size / stretch_shrink).floor());
diff --git a/scene/resources/SCsub b/scene/resources/SCsub
index 2ad90247a7..5e5b6f8fd5 100644
--- a/scene/resources/SCsub
+++ b/scene/resources/SCsub
@@ -4,6 +4,4 @@ Import('env')
env.add_source_files(env.scene_sources, "*.cpp")
-Export('env')
-
SConscript("default_theme/SCsub")
diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub
index bf9125be7f..b01e2fd54d 100644
--- a/scene/resources/default_theme/SCsub
+++ b/scene/resources/default_theme/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.scene_sources, "*.cpp")
-
-Export('env')
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 20fa1d6e2b..0dc2ef105b 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -364,6 +364,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("pressed", "CheckBox", cbx_empty);
theme->set_stylebox("disabled", "CheckBox", cbx_empty);
theme->set_stylebox("hover", "CheckBox", cbx_empty);
+ theme->set_stylebox("hover_pressed", "CheckBox", cbx_empty);
theme->set_stylebox("focus", "CheckBox", cbx_focus);
theme->set_icon("checked", "CheckBox", make_icon(checked_png));
@@ -376,6 +377,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "CheckBox", control_font_color);
theme->set_color("font_color_pressed", "CheckBox", control_font_color_pressed);
theme->set_color("font_color_hover", "CheckBox", control_font_color_hover);
+ theme->set_color("font_color_hover_pressed", "CheckBox", control_font_color_pressed);
theme->set_color("font_color_disabled", "CheckBox", control_font_color_disabled);
theme->set_constant("hseparation", "CheckBox", 4 * scale);
@@ -393,6 +395,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("pressed", "CheckButton", cb_empty);
theme->set_stylebox("disabled", "CheckButton", cb_empty);
theme->set_stylebox("hover", "CheckButton", cb_empty);
+ theme->set_stylebox("hover_pressed", "CheckButton", cb_empty);
theme->set_stylebox("focus", "CheckButton", focus);
theme->set_icon("on", "CheckButton", make_icon(toggle_on_png));
@@ -403,6 +406,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "CheckButton", control_font_color);
theme->set_color("font_color_pressed", "CheckButton", control_font_color_pressed);
theme->set_color("font_color_hover", "CheckButton", control_font_color_hover);
+ theme->set_color("font_color_hover_pressed", "CheckButton", control_font_color_pressed);
theme->set_color("font_color_disabled", "CheckButton", control_font_color_disabled);
theme->set_constant("hseparation", "CheckButton", 4 * scale);
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index ce801c8763..b671b1f874 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -610,11 +610,11 @@ void SpatialMaterial::_update_shader() {
code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat_world;\n";
//handle animation
- code += "\tint particle_total_frames = particles_anim_h_frames * particles_anim_v_frames;\n";
- code += "\tint particle_frame = int(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";
- code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0,particle_total_frames-1); else particle_frame=abs(particle_frame)%particle_total_frames;\n";
+ code += "\tfloat particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n";
+ code += "\tfloat particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";
+ code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0.0,particle_total_frames-1.0); else particle_frame=mod(particle_frame,float(particle_total_frames));\n";
code += "\tUV /= vec2(float(particles_anim_h_frames),float(particles_anim_v_frames));\n";
- code += "\tUV += vec2(float(particle_frame % particles_anim_h_frames) / float(particles_anim_h_frames),float(particle_frame / particles_anim_h_frames) / float(particles_anim_v_frames));\n";
+ code += "\tUV += vec2(mod(particle_frame,float(particles_anim_h_frames)) / float(particles_anim_h_frames),particle_frame / float(particles_anim_h_frames) / float(particles_anim_v_frames));\n";
} break;
}
@@ -1369,6 +1369,12 @@ void SpatialMaterial::_validate_feature(const String &text, Feature feature, Pro
}
}
+void SpatialMaterial::_validate_high_end(const String &text, PropertyInfo &property) const {
+ if (property.name.begins_with(text)) {
+ property.usage |= PROPERTY_USAGE_HIGH_END_GFX;
+ }
+}
+
void SpatialMaterial::_validate_property(PropertyInfo &property) const {
_validate_feature("normal", FEATURE_NORMAL_MAPPING, property);
_validate_feature("emission", FEATURE_EMISSION, property);
@@ -1382,6 +1388,12 @@ void SpatialMaterial::_validate_property(PropertyInfo &property) const {
_validate_feature("refraction", FEATURE_REFRACTION, property);
_validate_feature("detail", FEATURE_DETAIL, property);
+ _validate_high_end("refraction", property);
+ _validate_high_end("subsurf_scatter", property);
+ _validate_high_end("anisotropy", property);
+ _validate_high_end("clearcoat", property);
+ _validate_high_end("depth", property);
+
if (property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) {
property.usage = 0;
}
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 4a2a813341..0154874ae4 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -442,6 +442,8 @@ private:
static Ref<SpatialMaterial> materials_for_2d[MAX_MATERIALS_FOR_2D]; //used by Sprite3D and other stuff
+ void _validate_high_end(const String &text, PropertyInfo &property) const;
+
protected:
static void _bind_methods();
void _validate_property(PropertyInfo &property) const;
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index 364ec9bb19..6f67ba8af1 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -691,6 +691,7 @@ void ParticlesMaterial::set_param(Parameter p_param, float p_value) {
case PARAM_ANIM_OFFSET: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset, p_value);
} break;
+ case PARAM_MAX: break; // Can't happen, but silences warning
}
}
float ParticlesMaterial::get_param(Parameter p_param) const {
@@ -743,6 +744,7 @@ void ParticlesMaterial::set_param_randomness(Parameter p_param, float p_value) {
case PARAM_ANIM_OFFSET: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_random, p_value);
} break;
+ case PARAM_MAX: break; // Can't happen, but silences warning
}
}
float ParticlesMaterial::get_param_randomness(Parameter p_param) const {
@@ -819,6 +821,7 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture>
case PARAM_ANIM_OFFSET: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, p_texture);
} break;
+ case PARAM_MAX: break; // Can't happen, but silences warning
}
_queue_shader_change();
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 16b4ed31df..f0e3979f13 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -207,6 +207,9 @@ void ImageTexture::set_flags(uint32_t p_flags) {
flags=p_flags|cube; */
flags = p_flags;
+ if (w == 0 || h == 0) {
+ return; //uninitialized, do not set to texture
+ }
VisualServer::get_singleton()->texture_set_flags(texture, p_flags);
}
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 74dcd47c48..1802bf12b6 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -80,8 +80,8 @@ public:
struct AutotileData {
BitmaskMode bitmask_mode;
- int spacing;
Size2 size;
+ int spacing;
Vector2 icon_coord;
Map<Vector2, uint16_t> flags;
Map<Vector2, Ref<OccluderPolygon2D> > occluder_map;
@@ -90,11 +90,10 @@ public:
// Default size to prevent invalid value
explicit AutotileData() :
+ bitmask_mode(BITMASK_2X2),
size(64, 64),
spacing(0),
- icon_coord(0, 0) {
- bitmask_mode = BITMASK_2X2;
- }
+ icon_coord(0, 0) {}
};
private:
@@ -111,8 +110,8 @@ private:
Vector2 navigation_polygon_offset;
Ref<NavigationPolygon> navigation_polygon;
Ref<ShaderMaterial> material;
- Color modulate;
TileMode tile_mode;
+ Color modulate;
AutotileData autotile_data;
int z_index;
diff --git a/servers/SCsub b/servers/SCsub
index 252a18ffd3..f4af347fe6 100644
--- a/servers/SCsub
+++ b/servers/SCsub
@@ -5,8 +5,6 @@ Import('env')
env.servers_sources = []
env.add_source_files(env.servers_sources, "*.cpp")
-Export('env')
-
SConscript('arvr/SCsub')
SConscript('physics/SCsub')
SConscript('physics_2d/SCsub')
diff --git a/servers/arvr/SCsub b/servers/arvr/SCsub
index ccc76e823f..d730144861 100644
--- a/servers/arvr/SCsub
+++ b/servers/arvr/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.servers_sources, "*.cpp")
-
-Export('env')
diff --git a/servers/audio/SCsub b/servers/audio/SCsub
index afaffcfe93..3c18c18043 100644
--- a/servers/audio/SCsub
+++ b/servers/audio/SCsub
@@ -4,6 +4,4 @@ Import('env')
env.add_source_files(env.servers_sources, "*.cpp")
-Export('env')
-
SConscript("effects/SCsub")
diff --git a/servers/audio/effects/SCsub b/servers/audio/effects/SCsub
index ccc76e823f..d730144861 100644
--- a/servers/audio/effects/SCsub
+++ b/servers/audio/effects/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.servers_sources, "*.cpp")
-
-Export('env')
diff --git a/servers/physics/SCsub b/servers/physics/SCsub
index c0ee2cc739..c5cc889112 100644
--- a/servers/physics/SCsub
+++ b/servers/physics/SCsub
@@ -4,6 +4,4 @@ Import('env')
env.add_source_files(env.servers_sources, "*.cpp")
-Export('env')
-
SConscript("joints/SCsub")
diff --git a/servers/physics/body_sw.cpp b/servers/physics/body_sw.cpp
index cc9681193c..36511f78ce 100644
--- a/servers/physics/body_sw.cpp
+++ b/servers/physics/body_sw.cpp
@@ -755,10 +755,10 @@ void BodySW::set_kinematic_margin(real_t p_margin) {
BodySW::BodySW() :
CollisionObjectSW(TYPE_BODY),
+ locked_axis(0),
active_list(this),
inertia_update_list(this),
- direct_state_query_list(this),
- locked_axis(0) {
+ direct_state_query_list(this) {
mode = PhysicsServer::BODY_MODE_RIGID;
active = true;
diff --git a/servers/physics/joints/SCsub b/servers/physics/joints/SCsub
index ccc76e823f..d730144861 100644
--- a/servers/physics/joints/SCsub
+++ b/servers/physics/joints/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.servers_sources, "*.cpp")
-
-Export('env')
diff --git a/servers/physics/joints/cone_twist_joint_sw.cpp b/servers/physics/joints/cone_twist_joint_sw.cpp
index c06f27cc57..37fcde4b76 100644
--- a/servers/physics/joints/cone_twist_joint_sw.cpp
+++ b/servers/physics/joints/cone_twist_joint_sw.cpp
@@ -332,6 +332,7 @@ void ConeTwistJointSW::set_param(PhysicsServer::ConeTwistJointParam p_param, rea
m_relaxationFactor = p_value;
} break;
+ case PhysicsServer::CONE_TWIST_MAX: break; // Can't happen, but silences warning
}
}
@@ -358,6 +359,7 @@ real_t ConeTwistJointSW::get_param(PhysicsServer::ConeTwistJointParam p_param) c
return m_relaxationFactor;
} break;
+ case PhysicsServer::CONE_TWIST_MAX: break; // Can't happen, but silences warning
}
return 0;
diff --git a/servers/physics/joints/generic_6dof_joint_sw.cpp b/servers/physics/joints/generic_6dof_joint_sw.cpp
index c95e5cef32..9b1a41e80d 100644
--- a/servers/physics/joints/generic_6dof_joint_sw.cpp
+++ b/servers/physics/joints/generic_6dof_joint_sw.cpp
@@ -497,6 +497,13 @@ void Generic6DOFJointSW::set_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJoi
m_angularLimits[p_axis].m_maxLimitForce = p_value;
} break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_MAX: break; // Can't happen, but silences warning
}
}
@@ -572,6 +579,13 @@ real_t Generic6DOFJointSW::get_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJ
return m_angularLimits[p_axis].m_maxMotorForce;
} break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_MAX: break; // Can't happen, but silences warning
}
return 0;
}
@@ -593,6 +607,10 @@ void Generic6DOFJointSW::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJoin
m_angularLimits[p_axis].m_enableMotor = p_value;
} break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_MAX: break; // Can't happen, but silences warning
}
}
bool Generic6DOFJointSW::get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisFlag p_flag) const {
@@ -611,6 +629,10 @@ bool Generic6DOFJointSW::get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJoin
return m_angularLimits[p_axis].m_enableMotor;
} break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_MAX: break; // Can't happen, but silences warning
}
return 0;
diff --git a/servers/physics/joints/hinge_joint_sw.cpp b/servers/physics/joints/hinge_joint_sw.cpp
index 368a349632..50de0e871e 100644
--- a/servers/physics/joints/hinge_joint_sw.cpp
+++ b/servers/physics/joints/hinge_joint_sw.cpp
@@ -409,6 +409,7 @@ void HingeJointSW::set_param(PhysicsServer::HingeJointParam p_param, real_t p_va
case PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION: m_relaxationFactor = p_value; break;
case PhysicsServer::HINGE_JOINT_MOTOR_TARGET_VELOCITY: m_motorTargetVelocity = p_value; break;
case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE: m_maxMotorImpulse = p_value; break;
+ case PhysicsServer::HINGE_JOINT_MAX: break; // Can't happen, but silences warning
}
}
@@ -424,6 +425,7 @@ real_t HingeJointSW::get_param(PhysicsServer::HingeJointParam p_param) const {
case PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION: return m_relaxationFactor;
case PhysicsServer::HINGE_JOINT_MOTOR_TARGET_VELOCITY: return m_motorTargetVelocity;
case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE: return m_maxMotorImpulse;
+ case PhysicsServer::HINGE_JOINT_MAX: break; // Can't happen, but silences warning
}
return 0;
@@ -434,6 +436,7 @@ void HingeJointSW::set_flag(PhysicsServer::HingeJointFlag p_flag, bool p_value)
switch (p_flag) {
case PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT: m_useLimit = p_value; break;
case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR: m_enableAngularMotor = p_value; break;
+ case PhysicsServer::HINGE_JOINT_FLAG_MAX: break; // Can't happen, but silences warning
}
}
bool HingeJointSW::get_flag(PhysicsServer::HingeJointFlag p_flag) const {
@@ -441,6 +444,7 @@ bool HingeJointSW::get_flag(PhysicsServer::HingeJointFlag p_flag) const {
switch (p_flag) {
case PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT: return m_useLimit;
case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR: return m_enableAngularMotor;
+ case PhysicsServer::HINGE_JOINT_FLAG_MAX: break; // Can't happen, but silences warning
}
return false;
diff --git a/servers/physics/joints/slider_joint_sw.cpp b/servers/physics/joints/slider_joint_sw.cpp
index c0e9660b22..30700d45f1 100644
--- a/servers/physics/joints/slider_joint_sw.cpp
+++ b/servers/physics/joints/slider_joint_sw.cpp
@@ -404,6 +404,8 @@ void SliderJointSW::set_param(PhysicsServer::SliderJointParam p_param, real_t p_
case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: m_softnessOrthoAng = p_value; break;
case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: m_restitutionOrthoAng = p_value; break;
case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: m_dampingOrthoAng = p_value; break;
+
+ case PhysicsServer::SLIDER_JOINT_MAX: break; // Can't happen, but silences warning
}
}
@@ -433,6 +435,8 @@ real_t SliderJointSW::get_param(PhysicsServer::SliderJointParam p_param) const {
case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: return m_softnessOrthoAng;
case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: return m_restitutionOrthoAng;
case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: return m_dampingOrthoAng;
+
+ case PhysicsServer::SLIDER_JOINT_MAX: break; // Can't happen, but silences warning
}
return 0;
diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h
index fc2a228723..7c23fcdaea 100644
--- a/servers/physics_2d_server.h
+++ b/servers/physics_2d_server.h
@@ -372,7 +372,6 @@ public:
BODY_MODE_KINEMATIC,
BODY_MODE_RIGID,
BODY_MODE_CHARACTER
- //BODY_MODE_SOFT ??
};
virtual RID body_create() = 0;
@@ -581,9 +580,7 @@ public:
INFO_ACTIVE_OBJECTS,
INFO_COLLISION_PAIRS,
- INFO_ISLAND_COUNT,
- INFO_STEP_TIME,
- INFO_BROAD_PHASE_TIME
+ INFO_ISLAND_COUNT
};
virtual int get_process_info(ProcessInfo p_info) = 0;
diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp
index 0660c84d09..888e16e0c3 100644
--- a/servers/physics_server.cpp
+++ b/servers/physics_server.cpp
@@ -678,7 +678,6 @@ void PhysicsServer::_bind_methods() {
BIND_ENUM_CONSTANT(BODY_MODE_STATIC);
BIND_ENUM_CONSTANT(BODY_MODE_KINEMATIC);
BIND_ENUM_CONSTANT(BODY_MODE_RIGID);
- BIND_ENUM_CONSTANT(BODY_MODE_SOFT);
BIND_ENUM_CONSTANT(BODY_MODE_CHARACTER);
BIND_ENUM_CONSTANT(BODY_PARAM_BOUNCE);
diff --git a/servers/physics_server.h b/servers/physics_server.h
index d0d2ec16f0..d80d76305a 100644
--- a/servers/physics_server.h
+++ b/servers/physics_server.h
@@ -360,7 +360,6 @@ public:
BODY_MODE_STATIC,
BODY_MODE_KINEMATIC,
BODY_MODE_RIGID,
- BODY_MODE_SOFT,
BODY_MODE_CHARACTER
};
diff --git a/servers/visual/SCsub b/servers/visual/SCsub
index ccc76e823f..d730144861 100644
--- a/servers/visual/SCsub
+++ b/servers/visual/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.servers_sources, "*.cpp")
-
-Export('env')
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
index 6eeaf12dfc..207f13ac1a 100644
--- a/servers/visual/rasterizer.h
+++ b/servers/visual/rasterizer.h
@@ -397,6 +397,7 @@ public:
virtual RID reflection_probe_create() = 0;
virtual void reflection_probe_set_update_mode(RID p_probe, VS::ReflectionProbeUpdateMode p_mode) = 0;
+ virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0;
virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) = 0;
virtual void reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient) = 0;
virtual void reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) = 0;
@@ -1106,6 +1107,8 @@ public:
virtual void end_frame(bool p_swap_buffers) = 0;
virtual void finalize() = 0;
+ virtual bool is_low_end() const = 0;
+
virtual ~Rasterizer() {}
};
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
index 171b66c606..67a810bf1c 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -2366,6 +2366,21 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::C
value = Variant(t);
break;
}
+ case ShaderLanguage::TYPE_ISAMPLER2DARRAY:
+ case ShaderLanguage::TYPE_ISAMPLER2D:
+ case ShaderLanguage::TYPE_ISAMPLER3D:
+ case ShaderLanguage::TYPE_SAMPLER2DARRAY:
+ case ShaderLanguage::TYPE_SAMPLER2D:
+ case ShaderLanguage::TYPE_SAMPLER3D:
+ case ShaderLanguage::TYPE_USAMPLER2DARRAY:
+ case ShaderLanguage::TYPE_USAMPLER2D:
+ case ShaderLanguage::TYPE_USAMPLER3D:
+ case ShaderLanguage::TYPE_SAMPLERCUBE: {
+ // Texture types, likely not relevant here.
+ break;
+ }
+ case ShaderLanguage::TYPE_VOID:
+ break;
}
return value;
}
@@ -3005,6 +3020,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
case TYPE_IVEC2: member_type = TYPE_INT; break;
case TYPE_UVEC2: member_type = TYPE_UINT; break;
case TYPE_MAT2: member_type = TYPE_VEC2; break;
+ default: break;
}
break;
@@ -3030,6 +3046,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
case TYPE_IVEC3: member_type = TYPE_INT; break;
case TYPE_UVEC3: member_type = TYPE_UINT; break;
case TYPE_MAT3: member_type = TYPE_VEC3; break;
+ default: break;
}
break;
case TYPE_BVEC4:
@@ -3054,6 +3071,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
case TYPE_IVEC4: member_type = TYPE_INT; break;
case TYPE_UVEC4: member_type = TYPE_UINT; break;
case TYPE_MAT4: member_type = TYPE_VEC4; break;
+ default: break;
}
break;
default: {
diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp
index 6e41197e21..677c323216 100644
--- a/servers/visual/visual_server_raster.cpp
+++ b/servers/visual/visual_server_raster.cpp
@@ -189,6 +189,9 @@ void VisualServerRaster::call_set_use_vsync(bool p_enable) {
OS::get_singleton()->_set_use_vsync(p_enable);
}
+bool VisualServerRaster::is_low_end() const {
+ return VSG::rasterizer->is_low_end();
+}
VisualServerRaster::VisualServerRaster() {
VSG::canvas = memnew(VisualServerCanvas);
diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h
index b54e150656..ef2b767a8a 100644
--- a/servers/visual/visual_server_raster.h
+++ b/servers/visual/visual_server_raster.h
@@ -337,6 +337,7 @@ public:
BIND2(reflection_probe_set_enable_box_projection, RID, bool)
BIND2(reflection_probe_set_enable_shadows, RID, bool)
BIND2(reflection_probe_set_cull_mask, RID, uint32_t)
+ BIND2(reflection_probe_set_resolution, RID, int)
/* BAKED LIGHT API */
@@ -689,6 +690,8 @@ public:
virtual void call_set_use_vsync(bool p_enable);
+ virtual bool is_low_end() const;
+
VisualServerRaster();
~VisualServerRaster();
diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp
index 5ac54623f2..cd0702d20b 100644
--- a/servers/visual/visual_server_scene.cpp
+++ b/servers/visual/visual_server_scene.cpp
@@ -398,6 +398,7 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) {
VSG::scene_render->free(gi_probe->probe_instance);
} break;
+ default: {}
}
if (instance->base_data) {
@@ -471,6 +472,7 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) {
gi_probe->probe_instance = VSG::scene_render->gi_probe_instance_create();
} break;
+ default: {}
}
VSG::storage->instance_add_dependency(p_base, instance);
@@ -518,6 +520,7 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) {
gi_probe_update_list.remove(&gi_probe->update_element);
}
} break;
+ default: {}
}
instance->scenario = NULL;
@@ -549,6 +552,7 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) {
gi_probe_update_list.add(&gi_probe->update_element);
}
} break;
+ default: {}
}
_instance_queue_update(instance, true, true);
@@ -649,6 +653,7 @@ void VisualServerScene::instance_set_visible(RID p_instance, bool p_visible) {
}
} break;
+ default: {}
}
}
inline bool is_geometry_instance(VisualServer::InstanceType p_type) {
@@ -825,6 +830,7 @@ void VisualServerScene::instance_geometry_set_flag(RID p_instance, VS::InstanceF
instance->redraw_if_visible = p_enabled;
} break;
+ default: {}
}
}
void VisualServerScene::instance_geometry_set_cast_shadows_setting(RID p_instance, VS::ShadowCastingSetting p_shadow_casting_setting) {
@@ -1016,7 +1022,6 @@ void VisualServerScene::_update_instance_aabb(Instance *p_instance) {
new_aabb = VSG::storage->lightmap_capture_get_bounds(p_instance->base);
} break;
-
default: {}
}
@@ -2136,6 +2141,8 @@ bool VisualServerScene::_render_reflection_probe_step(Instance *p_instance, int
Scenario *scenario = p_instance->scenario;
ERR_FAIL_COND_V(!scenario, true);
+ VisualServerRaster::redraw_request(); //update, so it updates in editor
+
if (p_step == 0) {
if (!VSG::scene_render->reflection_probe_instance_begin_render(reflection_probe->instance, scenario->reflection_atlas)) {
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
index b8f86d7123..e4d69121f0 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -271,6 +271,7 @@ public:
FUNC2(reflection_probe_set_enable_box_projection, RID, bool)
FUNC2(reflection_probe_set_enable_shadows, RID, bool)
FUNC2(reflection_probe_set_cull_mask, RID, uint32_t)
+ FUNC2(reflection_probe_set_resolution, RID, int)
/* BAKED LIGHT API */
@@ -608,6 +609,10 @@ public:
static void set_use_vsync_callback(bool p_enable);
+ virtual bool is_low_end() const {
+ return visual_server->is_low_end();
+ }
+
VisualServerWrapMT(VisualServer *p_contained, bool p_create_thread);
~VisualServerWrapMT();
diff --git a/servers/visual_server.h b/servers/visual_server.h
index 6a1f2c3550..100bc06db6 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -490,6 +490,7 @@ public:
virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) = 0;
virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) = 0;
virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) = 0;
+ virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0;
/* GI PROBE API */
@@ -1036,6 +1037,8 @@ public:
virtual void call_set_use_vsync(bool p_enable) = 0;
+ virtual bool is_low_end() const = 0;
+
VisualServer();
virtual ~VisualServer();
};
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 71053de016..7fdd9e20b0 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -375,6 +375,10 @@ Collection of single-file libraries used in Godot components.
### scene
+- `easing_equations.cpp`
+ * Upstream: http://robertpenner.com/easing/ via https://github.com/jesusgollonet/ofpennereasing (modified to fit Godot types)
+ * Version: git (af72c14, 2008) + Godot types and style changes
+ * License: BSD-3-Clause
- `mikktspace.{c,h}`
* Upstream: https://wiki.blender.org/index.php/Dev:Shading/Tangent_Space_Normal_Maps
* Version: 1.0
diff --git a/scene/animation/tween_interpolaters.cpp b/thirdparty/misc/easing_equations.cpp
index 52aa7403c0..bc84564b19 100644
--- a/scene/animation/tween_interpolaters.cpp
+++ b/thirdparty/misc/easing_equations.cpp
@@ -1,40 +1,10 @@
-/*************************************************************************/
-/* tween_interpolaters.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
/**
* Adapted from Penner Easing equations' C++ port.
* Source: https://github.com/jesusgollonet/ofpennereasing
* License: BSD-3-clause
*/
-#include "tween.h"
+#include "scene/animation/tween.h"
const real_t pi = 3.1415926535898;
diff --git a/thirdparty/xatlas/xatlas.cpp b/thirdparty/xatlas/xatlas.cpp
new file mode 100644
index 0000000000..f6a9ce64dc
--- /dev/null
+++ b/thirdparty/xatlas/xatlas.cpp
@@ -0,0 +1,7384 @@
+// This code is in the public domain -- castanyo@yahoo.es
+#include "xatlas.h"
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <algorithm>
+#include <cmath>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#undef min
+#undef max
+
+#ifndef xaAssert
+#define xaAssert(exp) \
+ if (!(exp)) { \
+ xaPrint("%s %s %s\n", #exp, __FILE__, __LINE__); \
+ }
+#endif
+#ifndef xaDebugAssert
+#define xaDebugAssert(exp) assert(exp)
+#endif
+#ifndef xaPrint
+#define xaPrint(...) \
+ if (xatlas::internal::s_print) { \
+ xatlas::internal::s_print(__VA_ARGS__); \
+ }
+#endif
+
+#ifdef _MSC_VER
+// Ignore gcc attributes.
+#define __attribute__(X)
+#endif
+
+#ifdef _MSC_VER
+#define restrict
+#define NV_FORCEINLINE __forceinline
+#else
+#define restrict __restrict__
+#define NV_FORCEINLINE __attribute__((always_inline)) inline
+#endif
+
+#define NV_UINT32_MAX 0xffffffff
+#define NV_FLOAT_MAX 3.402823466e+38F
+
+#ifndef PI
+#define PI float(3.1415926535897932384626433833)
+#endif
+
+#define NV_EPSILON (0.0001f)
+#define NV_NORMAL_EPSILON (0.001f)
+
+namespace xatlas {
+namespace internal {
+
+static PrintFunc s_print = NULL;
+
+static int align(int x, int a) {
+ return (x + a - 1) & ~(a - 1);
+}
+
+static bool isAligned(int x, int a) {
+ return (x & (a - 1)) == 0;
+}
+
+/// Return the maximum of the three arguments.
+template <typename T>
+static T max3(const T &a, const T &b, const T &c) {
+ return std::max(a, std::max(b, c));
+}
+
+/// Return the maximum of the three arguments.
+template <typename T>
+static T min3(const T &a, const T &b, const T &c) {
+ return std::min(a, std::min(b, c));
+}
+
+/// Clamp between two values.
+template <typename T>
+static T clamp(const T &x, const T &a, const T &b) {
+ return std::min(std::max(x, a), b);
+}
+
+static float saturate(float f) {
+ return clamp(f, 0.0f, 1.0f);
+}
+
+// Robust floating point comparisons:
+// http://realtimecollisiondetection.net/blog/?p=89
+static bool equal(const float f0, const float f1, const float epsilon = NV_EPSILON) {
+ //return fabs(f0-f1) <= epsilon;
+ return fabs(f0 - f1) <= epsilon * max3(1.0f, fabsf(f0), fabsf(f1));
+}
+
+NV_FORCEINLINE static int ftoi_floor(float val) {
+ return (int)val;
+}
+
+NV_FORCEINLINE static int ftoi_ceil(float val) {
+ return (int)ceilf(val);
+}
+
+NV_FORCEINLINE static int ftoi_round(float f) {
+ return int(floorf(f + 0.5f));
+}
+
+static bool isZero(const float f, const float epsilon = NV_EPSILON) {
+ return fabs(f) <= epsilon;
+}
+
+static float lerp(float f0, float f1, float t) {
+ const float s = 1.0f - t;
+ return f0 * s + f1 * t;
+}
+
+static float square(float f) {
+ return f * f;
+}
+
+static int square(int i) {
+ return i * i;
+}
+
+/** Return the next power of two.
+* @see http://graphics.stanford.edu/~seander/bithacks.html
+* @warning Behaviour for 0 is undefined.
+* @note isPowerOfTwo(x) == true -> nextPowerOfTwo(x) == x
+* @note nextPowerOfTwo(x) = 2 << log2(x-1)
+*/
+static uint32_t nextPowerOfTwo(uint32_t x) {
+ xaDebugAssert(x != 0);
+ // On modern CPUs this is supposed to be as fast as using the bsr instruction.
+ x--;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return x + 1;
+}
+
+static uint64_t nextPowerOfTwo(uint64_t x) {
+ xaDebugAssert(x != 0);
+ uint32_t p = 1;
+ while (x > p) {
+ p += p;
+ }
+ return p;
+}
+
+static uint32_t sdbmHash(const void *data_in, uint32_t size, uint32_t h = 5381) {
+ const uint8_t *data = (const uint8_t *)data_in;
+ uint32_t i = 0;
+ while (i < size) {
+ h = (h << 16) + (h << 6) - h + (uint32_t)data[i++];
+ }
+ return h;
+}
+
+// Note that this hash does not handle NaN properly.
+static uint32_t sdbmFloatHash(const float *f, uint32_t count, uint32_t h = 5381) {
+ for (uint32_t i = 0; i < count; i++) {
+ union {
+ float f;
+ uint32_t i;
+ } x = { f[i] };
+ if (x.i == 0x80000000) x.i = 0;
+ h = sdbmHash(&x, 4, h);
+ }
+ return h;
+}
+
+template <typename T>
+static uint32_t hash(const T &t, uint32_t h = 5381) {
+ return sdbmHash(&t, sizeof(T), h);
+}
+
+static uint32_t hash(const float &f, uint32_t h) {
+ return sdbmFloatHash(&f, 1, h);
+}
+
+// Functors for hash table:
+template <typename Key>
+struct Hash {
+ uint32_t operator()(const Key &k) const { return hash(k); }
+};
+
+template <typename Key>
+struct Equal {
+ bool operator()(const Key &k0, const Key &k1) const { return k0 == k1; }
+};
+
+class Vector2 {
+public:
+ typedef Vector2 const &Arg;
+
+ Vector2() {}
+ explicit Vector2(float f) :
+ x(f),
+ y(f) {}
+ Vector2(float x, float y) :
+ x(x),
+ y(y) {}
+ Vector2(Vector2::Arg v) :
+ x(v.x),
+ y(v.y) {}
+
+ const Vector2 &operator=(Vector2::Arg v) {
+ x = v.x;
+ y = v.y;
+ return *this;
+ }
+ const float *ptr() const { return &x; }
+
+ void set(float _x, float _y) {
+ x = _x;
+ y = _y;
+ }
+
+ Vector2 operator-() const {
+ return Vector2(-x, -y);
+ }
+
+ void operator+=(Vector2::Arg v) {
+ x += v.x;
+ y += v.y;
+ }
+
+ void operator-=(Vector2::Arg v) {
+ x -= v.x;
+ y -= v.y;
+ }
+
+ void operator*=(float s) {
+ x *= s;
+ y *= s;
+ }
+
+ void operator*=(Vector2::Arg v) {
+ x *= v.x;
+ y *= v.y;
+ }
+
+ friend bool operator==(Vector2::Arg a, Vector2::Arg b) {
+ return a.x == b.x && a.y == b.y;
+ }
+
+ friend bool operator!=(Vector2::Arg a, Vector2::Arg b) {
+ return a.x != b.x || a.y != b.y;
+ }
+
+ union {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4201)
+#endif
+ struct
+ {
+ float x, y;
+ };
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ float component[2];
+ };
+};
+
+Vector2 operator+(Vector2::Arg a, Vector2::Arg b) {
+ return Vector2(a.x + b.x, a.y + b.y);
+}
+
+Vector2 operator-(Vector2::Arg a, Vector2::Arg b) {
+ return Vector2(a.x - b.x, a.y - b.y);
+}
+
+Vector2 operator*(Vector2::Arg v, float s) {
+ return Vector2(v.x * s, v.y * s);
+}
+
+Vector2 operator*(Vector2::Arg v1, Vector2::Arg v2) {
+ return Vector2(v1.x * v2.x, v1.y * v2.y);
+}
+
+Vector2 operator/(Vector2::Arg v, float s) {
+ return Vector2(v.x / s, v.y / s);
+}
+
+Vector2 lerp(Vector2::Arg v1, Vector2::Arg v2, float t) {
+ const float s = 1.0f - t;
+ return Vector2(v1.x * s + t * v2.x, v1.y * s + t * v2.y);
+}
+
+float dot(Vector2::Arg a, Vector2::Arg b) {
+ return a.x * b.x + a.y * b.y;
+}
+
+float lengthSquared(Vector2::Arg v) {
+ return v.x * v.x + v.y * v.y;
+}
+
+float length(Vector2::Arg v) {
+ return sqrtf(lengthSquared(v));
+}
+
+float distance(Vector2::Arg a, Vector2::Arg b) {
+ return length(a - b);
+}
+
+bool isNormalized(Vector2::Arg v, float epsilon = NV_NORMAL_EPSILON) {
+ return equal(length(v), 1, epsilon);
+}
+
+Vector2 normalize(Vector2::Arg v, float epsilon = NV_EPSILON) {
+ float l = length(v);
+ xaDebugAssert(!isZero(l, epsilon));
+#ifdef NDEBUG
+ epsilon = 0; // silence unused parameter warning
+#endif
+ Vector2 n = v * (1.0f / l);
+ xaDebugAssert(isNormalized(n));
+ return n;
+}
+
+Vector2 normalizeSafe(Vector2::Arg v, Vector2::Arg fallback, float epsilon = NV_EPSILON) {
+ float l = length(v);
+ if (isZero(l, epsilon)) {
+ return fallback;
+ }
+ return v * (1.0f / l);
+}
+
+bool equal(Vector2::Arg v1, Vector2::Arg v2, float epsilon = NV_EPSILON) {
+ return equal(v1.x, v2.x, epsilon) && equal(v1.y, v2.y, epsilon);
+}
+
+Vector2 max(Vector2::Arg a, Vector2::Arg b) {
+ return Vector2(std::max(a.x, b.x), std::max(a.y, b.y));
+}
+
+bool isFinite(Vector2::Arg v) {
+ return std::isfinite(v.x) && std::isfinite(v.y);
+}
+
+// Note, this is the area scaled by 2!
+float triangleArea(Vector2::Arg v0, Vector2::Arg v1) {
+ return (v0.x * v1.y - v0.y * v1.x); // * 0.5f;
+}
+float triangleArea(Vector2::Arg a, Vector2::Arg b, Vector2::Arg c) {
+ // IC: While it may be appealing to use the following expression:
+ //return (c.x * a.y + a.x * b.y + b.x * c.y - b.x * a.y - c.x * b.y - a.x * c.y); // * 0.5f;
+ // That's actually a terrible idea. Small triangles far from the origin can end up producing fairly large floating point
+ // numbers and the results becomes very unstable and dependent on the order of the factors.
+ // Instead, it's preferable to subtract the vertices first, and multiply the resulting small values together. The result
+ // in this case is always much more accurate (as long as the triangle is small) and less dependent of the location of
+ // the triangle.
+ //return ((a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x)); // * 0.5f;
+ return triangleArea(a - c, b - c);
+}
+
+float triangleArea2(Vector2::Arg v1, Vector2::Arg v2, Vector2::Arg v3) {
+ return 0.5f * (v3.x * v1.y + v1.x * v2.y + v2.x * v3.y - v2.x * v1.y - v3.x * v2.y - v1.x * v3.y);
+}
+
+static uint32_t hash(const Vector2 &v, uint32_t h) {
+ return sdbmFloatHash(v.component, 2, h);
+}
+
+class Vector3 {
+public:
+ typedef Vector3 const &Arg;
+
+ Vector3() {}
+ explicit Vector3(float f) :
+ x(f),
+ y(f),
+ z(f) {}
+ Vector3(float x, float y, float z) :
+ x(x),
+ y(y),
+ z(z) {}
+ Vector3(Vector2::Arg v, float z) :
+ x(v.x),
+ y(v.y),
+ z(z) {}
+ Vector3(Vector3::Arg v) :
+ x(v.x),
+ y(v.y),
+ z(v.z) {}
+
+ const Vector3 &operator=(Vector3::Arg v) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ return *this;
+ }
+
+ Vector2 xy() const {
+ return Vector2(x, y);
+ }
+
+ const float *ptr() const { return &x; }
+
+ void set(float _x, float _y, float _z) {
+ x = _x;
+ y = _y;
+ z = _z;
+ }
+
+ Vector3 operator-() const {
+ return Vector3(-x, -y, -z);
+ }
+
+ void operator+=(Vector3::Arg v) {
+ x += v.x;
+ y += v.y;
+ z += v.z;
+ }
+
+ void operator-=(Vector3::Arg v) {
+ x -= v.x;
+ y -= v.y;
+ z -= v.z;
+ }
+
+ void operator*=(float s) {
+ x *= s;
+ y *= s;
+ z *= s;
+ }
+
+ void operator/=(float s) {
+ float is = 1.0f / s;
+ x *= is;
+ y *= is;
+ z *= is;
+ }
+
+ void operator*=(Vector3::Arg v) {
+ x *= v.x;
+ y *= v.y;
+ z *= v.z;
+ }
+
+ void operator/=(Vector3::Arg v) {
+ x /= v.x;
+ y /= v.y;
+ z /= v.z;
+ }
+
+ friend bool operator==(Vector3::Arg a, Vector3::Arg b) {
+ return a.x == b.x && a.y == b.y && a.z == b.z;
+ }
+
+ friend bool operator!=(Vector3::Arg a, Vector3::Arg b) {
+ return a.x != b.x || a.y != b.y || a.z != b.z;
+ }
+
+ union {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4201)
+#endif
+ struct
+ {
+ float x, y, z;
+ };
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ float component[3];
+ };
+};
+
+Vector3 add(Vector3::Arg a, Vector3::Arg b) {
+ return Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
+}
+Vector3 add(Vector3::Arg a, float b) {
+ return Vector3(a.x + b, a.y + b, a.z + b);
+}
+Vector3 operator+(Vector3::Arg a, Vector3::Arg b) {
+ return add(a, b);
+}
+Vector3 operator+(Vector3::Arg a, float b) {
+ return add(a, b);
+}
+
+Vector3 sub(Vector3::Arg a, Vector3::Arg b) {
+ return Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
+}
+
+Vector3 sub(Vector3::Arg a, float b) {
+ return Vector3(a.x - b, a.y - b, a.z - b);
+}
+
+Vector3 operator-(Vector3::Arg a, Vector3::Arg b) {
+ return sub(a, b);
+}
+
+Vector3 operator-(Vector3::Arg a, float b) {
+ return sub(a, b);
+}
+
+Vector3 cross(Vector3::Arg a, Vector3::Arg b) {
+ return Vector3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
+}
+
+Vector3 operator*(Vector3::Arg v, float s) {
+ return Vector3(v.x * s, v.y * s, v.z * s);
+}
+
+Vector3 operator*(float s, Vector3::Arg v) {
+ return Vector3(v.x * s, v.y * s, v.z * s);
+}
+
+Vector3 operator*(Vector3::Arg v, Vector3::Arg s) {
+ return Vector3(v.x * s.x, v.y * s.y, v.z * s.z);
+}
+
+Vector3 operator/(Vector3::Arg v, float s) {
+ return v * (1.0f / s);
+}
+
+Vector3 lerp(Vector3::Arg v1, Vector3::Arg v2, float t) {
+ const float s = 1.0f - t;
+ return Vector3(v1.x * s + t * v2.x, v1.y * s + t * v2.y, v1.z * s + t * v2.z);
+}
+
+float dot(Vector3::Arg a, Vector3::Arg b) {
+ return a.x * b.x + a.y * b.y + a.z * b.z;
+}
+
+float lengthSquared(Vector3::Arg v) {
+ return v.x * v.x + v.y * v.y + v.z * v.z;
+}
+
+float length(Vector3::Arg v) {
+ return sqrtf(lengthSquared(v));
+}
+
+float distance(Vector3::Arg a, Vector3::Arg b) {
+ return length(a - b);
+}
+
+float distanceSquared(Vector3::Arg a, Vector3::Arg b) {
+ return lengthSquared(a - b);
+}
+
+bool isNormalized(Vector3::Arg v, float epsilon = NV_NORMAL_EPSILON) {
+ return equal(length(v), 1, epsilon);
+}
+
+Vector3 normalize(Vector3::Arg v, float epsilon = NV_EPSILON) {
+ float l = length(v);
+ xaDebugAssert(!isZero(l, epsilon));
+#ifdef NDEBUG
+ epsilon = 0; // silence unused parameter warning
+#endif
+ Vector3 n = v * (1.0f / l);
+ xaDebugAssert(isNormalized(n));
+ return n;
+}
+
+Vector3 normalizeSafe(Vector3::Arg v, Vector3::Arg fallback, float epsilon = NV_EPSILON) {
+ float l = length(v);
+ if (isZero(l, epsilon)) {
+ return fallback;
+ }
+ return v * (1.0f / l);
+}
+
+bool equal(Vector3::Arg v1, Vector3::Arg v2, float epsilon = NV_EPSILON) {
+ return equal(v1.x, v2.x, epsilon) && equal(v1.y, v2.y, epsilon) && equal(v1.z, v2.z, epsilon);
+}
+
+Vector3 min(Vector3::Arg a, Vector3::Arg b) {
+ return Vector3(std::min(a.x, b.x), std::min(a.y, b.y), std::min(a.z, b.z));
+}
+
+Vector3 max(Vector3::Arg a, Vector3::Arg b) {
+ return Vector3(std::max(a.x, b.x), std::max(a.y, b.y), std::max(a.z, b.z));
+}
+
+Vector3 clamp(Vector3::Arg v, float min, float max) {
+ return Vector3(clamp(v.x, min, max), clamp(v.y, min, max), clamp(v.z, min, max));
+}
+
+Vector3 saturate(Vector3::Arg v) {
+ return Vector3(saturate(v.x), saturate(v.y), saturate(v.z));
+}
+
+Vector3 floor(Vector3::Arg v) {
+ return Vector3(floorf(v.x), floorf(v.y), floorf(v.z));
+}
+
+bool isFinite(Vector3::Arg v) {
+ return std::isfinite(v.x) && std::isfinite(v.y) && std::isfinite(v.z);
+}
+
+static uint32_t hash(const Vector3 &v, uint32_t h) {
+ return sdbmFloatHash(v.component, 3, h);
+}
+
+/// Basis class to compute tangent space basis, ortogonalizations and to
+/// transform vectors from one space to another.
+class Basis {
+public:
+ /// Create a null basis.
+ Basis() :
+ tangent(0, 0, 0),
+ bitangent(0, 0, 0),
+ normal(0, 0, 0) {}
+
+ void buildFrameForDirection(Vector3::Arg d, float angle = 0) {
+ xaAssert(isNormalized(d));
+ normal = d;
+ // Choose minimum axis.
+ if (fabsf(normal.x) < fabsf(normal.y) && fabsf(normal.x) < fabsf(normal.z)) {
+ tangent = Vector3(1, 0, 0);
+ } else if (fabsf(normal.y) < fabsf(normal.z)) {
+ tangent = Vector3(0, 1, 0);
+ } else {
+ tangent = Vector3(0, 0, 1);
+ }
+ // Ortogonalize
+ tangent -= normal * dot(normal, tangent);
+ tangent = normalize(tangent);
+ bitangent = cross(normal, tangent);
+ // Rotate frame around normal according to angle.
+ if (angle != 0.0f) {
+ float c = cosf(angle);
+ float s = sinf(angle);
+ Vector3 tmp = c * tangent - s * bitangent;
+ bitangent = s * tangent + c * bitangent;
+ tangent = tmp;
+ }
+ }
+
+ Vector3 tangent;
+ Vector3 bitangent;
+ Vector3 normal;
+};
+
+// Simple bit array.
+class BitArray {
+public:
+ BitArray() :
+ m_size(0) {}
+ BitArray(uint32_t sz) {
+ resize(sz);
+ }
+
+ uint32_t size() const {
+ return m_size;
+ }
+
+ void clear() {
+ resize(0);
+ }
+
+ void resize(uint32_t new_size) {
+ m_size = new_size;
+ m_wordArray.resize((m_size + 31) >> 5);
+ }
+
+ /// Get bit.
+ bool bitAt(uint32_t b) const {
+ xaDebugAssert(b < m_size);
+ return (m_wordArray[b >> 5] & (1 << (b & 31))) != 0;
+ }
+
+ // Set a bit.
+ void setBitAt(uint32_t idx) {
+ xaDebugAssert(idx < m_size);
+ m_wordArray[idx >> 5] |= (1 << (idx & 31));
+ }
+
+ // Toggle a bit.
+ void toggleBitAt(uint32_t idx) {
+ xaDebugAssert(idx < m_size);
+ m_wordArray[idx >> 5] ^= (1 << (idx & 31));
+ }
+
+ // Set a bit to the given value. @@ Rename modifyBitAt?
+ void setBitAt(uint32_t idx, bool b) {
+ xaDebugAssert(idx < m_size);
+ m_wordArray[idx >> 5] = setBits(m_wordArray[idx >> 5], 1 << (idx & 31), b);
+ xaDebugAssert(bitAt(idx) == b);
+ }
+
+ // Clear all the bits.
+ void clearAll() {
+ memset(m_wordArray.data(), 0, m_wordArray.size() * sizeof(uint32_t));
+ }
+
+ // Set all the bits.
+ void setAll() {
+ memset(m_wordArray.data(), 0xFF, m_wordArray.size() * sizeof(uint32_t));
+ }
+
+private:
+ // See "Conditionally set or clear bits without branching" at http://graphics.stanford.edu/~seander/bithacks.html
+ uint32_t setBits(uint32_t w, uint32_t m, bool b) {
+ return (w & ~m) | (-int(b) & m);
+ }
+
+ // Number of bits stored.
+ uint32_t m_size;
+
+ // Array of bits.
+ std::vector<uint32_t> m_wordArray;
+};
+
+/// Bit map. This should probably be called BitImage.
+class BitMap {
+public:
+ BitMap() :
+ m_width(0),
+ m_height(0) {}
+ BitMap(uint32_t w, uint32_t h) :
+ m_width(w),
+ m_height(h),
+ m_bitArray(w * h) {}
+
+ uint32_t width() const {
+ return m_width;
+ }
+ uint32_t height() const {
+ return m_height;
+ }
+
+ void resize(uint32_t w, uint32_t h, bool initValue) {
+ BitArray tmp(w * h);
+ if (initValue)
+ tmp.setAll();
+ else
+ tmp.clearAll();
+ // @@ Copying one bit at a time. This could be much faster.
+ for (uint32_t y = 0; y < m_height; y++) {
+ for (uint32_t x = 0; x < m_width; x++) {
+ //tmp.setBitAt(y*w + x, bitAt(x, y));
+ if (bitAt(x, y) != initValue) tmp.toggleBitAt(y * w + x);
+ }
+ }
+ std::swap(m_bitArray, tmp);
+ m_width = w;
+ m_height = h;
+ }
+
+ bool bitAt(uint32_t x, uint32_t y) const {
+ xaDebugAssert(x < m_width && y < m_height);
+ return m_bitArray.bitAt(y * m_width + x);
+ }
+
+ void setBitAt(uint32_t x, uint32_t y) {
+ xaDebugAssert(x < m_width && y < m_height);
+ m_bitArray.setBitAt(y * m_width + x);
+ }
+
+ void clearAll() {
+ m_bitArray.clearAll();
+ }
+
+private:
+ uint32_t m_width;
+ uint32_t m_height;
+ BitArray m_bitArray;
+};
+
+// Axis Aligned Bounding Box.
+class Box {
+public:
+ Box() {}
+ Box(const Box &b) :
+ minCorner(b.minCorner),
+ maxCorner(b.maxCorner) {}
+ Box(const Vector3 &mins, const Vector3 &maxs) :
+ minCorner(mins),
+ maxCorner(maxs) {}
+
+ operator const float *() const {
+ return reinterpret_cast<const float *>(this);
+ }
+
+ // Clear the bounds.
+ void clearBounds() {
+ minCorner.set(FLT_MAX, FLT_MAX, FLT_MAX);
+ maxCorner.set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
+ }
+
+ // Return extents of the box.
+ Vector3 extents() const {
+ return (maxCorner - minCorner) * 0.5f;
+ }
+
+ // Add a point to this box.
+ void addPointToBounds(const Vector3 &p) {
+ minCorner = min(minCorner, p);
+ maxCorner = max(maxCorner, p);
+ }
+
+ // Get the volume of the box.
+ float volume() const {
+ Vector3 d = extents();
+ return 8.0f * (d.x * d.y * d.z);
+ }
+
+ Vector3 minCorner;
+ Vector3 maxCorner;
+};
+
+class Fit {
+public:
+ static Vector3 computeCentroid(int n, const Vector3 *__restrict points) {
+ Vector3 centroid(0.0f);
+ for (int i = 0; i < n; i++) {
+ centroid += points[i];
+ }
+ centroid /= float(n);
+ return centroid;
+ }
+
+ static Vector3 computeCovariance(int n, const Vector3 *__restrict points, float *__restrict covariance) {
+ // compute the centroid
+ Vector3 centroid = computeCentroid(n, points);
+ // compute covariance matrix
+ for (int i = 0; i < 6; i++) {
+ covariance[i] = 0.0f;
+ }
+ for (int i = 0; i < n; i++) {
+ Vector3 v = points[i] - centroid;
+ covariance[0] += v.x * v.x;
+ covariance[1] += v.x * v.y;
+ covariance[2] += v.x * v.z;
+ covariance[3] += v.y * v.y;
+ covariance[4] += v.y * v.z;
+ covariance[5] += v.z * v.z;
+ }
+ return centroid;
+ }
+
+ static bool isPlanar(int n, const Vector3 *points, float epsilon = NV_EPSILON) {
+ // compute the centroid and covariance
+ float matrix[6];
+ computeCovariance(n, points, matrix);
+ float eigenValues[3];
+ Vector3 eigenVectors[3];
+ if (!eigenSolveSymmetric3(matrix, eigenValues, eigenVectors)) {
+ return false;
+ }
+ return eigenValues[2] < epsilon;
+ }
+
+ // Tridiagonal solver from Charles Bloom.
+ // Householder transforms followed by QL decomposition.
+ // Seems to be based on the code from Numerical Recipes in C.
+ static bool eigenSolveSymmetric3(const float matrix[6], float eigenValues[3], Vector3 eigenVectors[3]) {
+ xaDebugAssert(matrix != NULL && eigenValues != NULL && eigenVectors != NULL);
+ float subd[3];
+ float diag[3];
+ float work[3][3];
+ work[0][0] = matrix[0];
+ work[0][1] = work[1][0] = matrix[1];
+ work[0][2] = work[2][0] = matrix[2];
+ work[1][1] = matrix[3];
+ work[1][2] = work[2][1] = matrix[4];
+ work[2][2] = matrix[5];
+ EigenSolver3_Tridiagonal(work, diag, subd);
+ if (!EigenSolver3_QLAlgorithm(work, diag, subd)) {
+ for (int i = 0; i < 3; i++) {
+ eigenValues[i] = 0;
+ eigenVectors[i] = Vector3(0);
+ }
+ return false;
+ }
+ for (int i = 0; i < 3; i++) {
+ eigenValues[i] = (float)diag[i];
+ }
+ // eigenvectors are the columns; make them the rows :
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ eigenVectors[j].component[i] = (float)work[i][j];
+ }
+ }
+ // shuffle to sort by singular value :
+ if (eigenValues[2] > eigenValues[0] && eigenValues[2] > eigenValues[1]) {
+ std::swap(eigenValues[0], eigenValues[2]);
+ std::swap(eigenVectors[0], eigenVectors[2]);
+ }
+ if (eigenValues[1] > eigenValues[0]) {
+ std::swap(eigenValues[0], eigenValues[1]);
+ std::swap(eigenVectors[0], eigenVectors[1]);
+ }
+ if (eigenValues[2] > eigenValues[1]) {
+ std::swap(eigenValues[1], eigenValues[2]);
+ std::swap(eigenVectors[1], eigenVectors[2]);
+ }
+ xaDebugAssert(eigenValues[0] >= eigenValues[1] && eigenValues[0] >= eigenValues[2]);
+ xaDebugAssert(eigenValues[1] >= eigenValues[2]);
+ return true;
+ }
+
+private:
+ static void EigenSolver3_Tridiagonal(float mat[3][3], float *diag, float *subd) {
+ // Householder reduction T = Q^t M Q
+ // Input:
+ // mat, symmetric 3x3 matrix M
+ // Output:
+ // mat, orthogonal matrix Q
+ // diag, diagonal entries of T
+ // subd, subdiagonal entries of T (T is symmetric)
+ const float epsilon = 1e-08f;
+ float a = mat[0][0];
+ float b = mat[0][1];
+ float c = mat[0][2];
+ float d = mat[1][1];
+ float e = mat[1][2];
+ float f = mat[2][2];
+ diag[0] = a;
+ subd[2] = 0.f;
+ if (fabsf(c) >= epsilon) {
+ const float ell = sqrtf(b * b + c * c);
+ b /= ell;
+ c /= ell;
+ const float q = 2 * b * e + c * (f - d);
+ diag[1] = d + c * q;
+ diag[2] = f - c * q;
+ subd[0] = ell;
+ subd[1] = e - b * q;
+ mat[0][0] = 1;
+ mat[0][1] = 0;
+ mat[0][2] = 0;
+ mat[1][0] = 0;
+ mat[1][1] = b;
+ mat[1][2] = c;
+ mat[2][0] = 0;
+ mat[2][1] = c;
+ mat[2][2] = -b;
+ } else {
+ diag[1] = d;
+ diag[2] = f;
+ subd[0] = b;
+ subd[1] = e;
+ mat[0][0] = 1;
+ mat[0][1] = 0;
+ mat[0][2] = 0;
+ mat[1][0] = 0;
+ mat[1][1] = 1;
+ mat[1][2] = 0;
+ mat[2][0] = 0;
+ mat[2][1] = 0;
+ mat[2][2] = 1;
+ }
+ }
+
+ static bool EigenSolver3_QLAlgorithm(float mat[3][3], float *diag, float *subd) {
+ // QL iteration with implicit shifting to reduce matrix from tridiagonal
+ // to diagonal
+ const int maxiter = 32;
+ for (int ell = 0; ell < 3; ell++) {
+ int iter;
+ for (iter = 0; iter < maxiter; iter++) {
+ int m;
+ for (m = ell; m <= 1; m++) {
+ float dd = fabsf(diag[m]) + fabsf(diag[m + 1]);
+ if (fabsf(subd[m]) + dd == dd)
+ break;
+ }
+ if (m == ell)
+ break;
+ float g = (diag[ell + 1] - diag[ell]) / (2 * subd[ell]);
+ float r = sqrtf(g * g + 1);
+ if (g < 0)
+ g = diag[m] - diag[ell] + subd[ell] / (g - r);
+ else
+ g = diag[m] - diag[ell] + subd[ell] / (g + r);
+ float s = 1, c = 1, p = 0;
+ for (int i = m - 1; i >= ell; i--) {
+ float f = s * subd[i], b = c * subd[i];
+ if (fabsf(f) >= fabsf(g)) {
+ c = g / f;
+ r = sqrtf(c * c + 1);
+ subd[i + 1] = f * r;
+ c *= (s = 1 / r);
+ } else {
+ s = f / g;
+ r = sqrtf(s * s + 1);
+ subd[i + 1] = g * r;
+ s *= (c = 1 / r);
+ }
+ g = diag[i + 1] - p;
+ r = (diag[i] - g) * s + 2 * b * c;
+ p = s * r;
+ diag[i + 1] = g + p;
+ g = c * r - b;
+ for (int k = 0; k < 3; k++) {
+ f = mat[k][i + 1];
+ mat[k][i + 1] = s * mat[k][i] + c * f;
+ mat[k][i] = c * mat[k][i] - s * f;
+ }
+ }
+ diag[ell] -= p;
+ subd[ell] = g;
+ subd[m] = 0;
+ }
+ if (iter == maxiter)
+ // should not get here under normal circumstances
+ return false;
+ }
+ return true;
+ }
+};
+
+/// Fixed size vector class.
+class FullVector {
+public:
+ FullVector(uint32_t dim) { m_array.resize(dim); }
+ FullVector(const FullVector &v) :
+ m_array(v.m_array) {}
+
+ const FullVector &operator=(const FullVector &v) {
+ xaAssert(dimension() == v.dimension());
+ m_array = v.m_array;
+ return *this;
+ }
+
+ uint32_t dimension() const { return m_array.size(); }
+ const float &operator[](uint32_t index) const { return m_array[index]; }
+ float &operator[](uint32_t index) { return m_array[index]; }
+
+ void fill(float f) {
+ const uint32_t dim = dimension();
+ for (uint32_t i = 0; i < dim; i++) {
+ m_array[i] = f;
+ }
+ }
+
+ void operator+=(const FullVector &v) {
+ xaDebugAssert(dimension() == v.dimension());
+ const uint32_t dim = dimension();
+ for (uint32_t i = 0; i < dim; i++) {
+ m_array[i] += v.m_array[i];
+ }
+ }
+
+ void operator-=(const FullVector &v) {
+ xaDebugAssert(dimension() == v.dimension());
+ const uint32_t dim = dimension();
+ for (uint32_t i = 0; i < dim; i++) {
+ m_array[i] -= v.m_array[i];
+ }
+ }
+
+ void operator*=(const FullVector &v) {
+ xaDebugAssert(dimension() == v.dimension());
+ const uint32_t dim = dimension();
+ for (uint32_t i = 0; i < dim; i++) {
+ m_array[i] *= v.m_array[i];
+ }
+ }
+
+ void operator+=(float f) {
+ const uint32_t dim = dimension();
+ for (uint32_t i = 0; i < dim; i++) {
+ m_array[i] += f;
+ }
+ }
+
+ void operator-=(float f) {
+ const uint32_t dim = dimension();
+ for (uint32_t i = 0; i < dim; i++) {
+ m_array[i] -= f;
+ }
+ }
+
+ void operator*=(float f) {
+ const uint32_t dim = dimension();
+ for (uint32_t i = 0; i < dim; i++) {
+ m_array[i] *= f;
+ }
+ }
+
+private:
+ std::vector<float> m_array;
+};
+
+namespace halfedge {
+class Face;
+class Vertex;
+
+class Edge {
+public:
+ uint32_t id;
+ Edge *next;
+ Edge *prev; // This is not strictly half-edge, but makes algorithms easier and faster.
+ Edge *pair;
+ Vertex *vertex;
+ Face *face;
+
+ // Default constructor.
+ Edge(uint32_t id) :
+ id(id),
+ next(NULL),
+ prev(NULL),
+ pair(NULL),
+ vertex(NULL),
+ face(NULL) {}
+
+ // Vertex queries.
+ const Vertex *from() const {
+ return vertex;
+ }
+
+ Vertex *from() {
+ return vertex;
+ }
+
+ const Vertex *to() const {
+ return pair->vertex; // This used to be 'next->vertex', but that changed often when the connectivity of the mesh changes.
+ }
+
+ Vertex *to() {
+ return pair->vertex;
+ }
+
+ // Edge queries.
+ void setNext(Edge *e) {
+ next = e;
+ if (e != NULL) e->prev = this;
+ }
+ void setPrev(Edge *e) {
+ prev = e;
+ if (e != NULL) e->next = this;
+ }
+
+ // @@ It would be more simple to only check m_pair == NULL
+ // Face queries.
+ bool isBoundary() const {
+ return !(face && pair->face);
+ }
+
+ // @@ This is not exactly accurate, we should compare the texture coordinates...
+ bool isSeam() const {
+ return vertex != pair->next->vertex || next->vertex != pair->vertex;
+ }
+
+ bool isNormalSeam() const;
+ bool isTextureSeam() const;
+
+ bool isValid() const {
+ // null face is OK.
+ if (next == NULL || prev == NULL || pair == NULL || vertex == NULL) return false;
+ if (next->prev != this) return false;
+ if (prev->next != this) return false;
+ if (pair->pair != this) return false;
+ return true;
+ }
+
+ float length() const;
+
+ // Return angle between this edge and the previous one.
+ float angle() const;
+};
+
+class Vertex {
+public:
+ uint32_t id;
+ uint32_t original_id;
+ Edge *edge;
+ Vertex *next;
+ Vertex *prev;
+ Vector3 pos;
+ Vector3 nor;
+ Vector2 tex;
+
+ Vertex(uint32_t id) :
+ id(id),
+ original_id(id),
+ edge(NULL),
+ pos(0.0f),
+ nor(0.0f),
+ tex(0.0f) {
+ next = this;
+ prev = this;
+ }
+
+ // Set first edge of all colocals.
+ void setEdge(Edge *e) {
+ for (VertexIterator it(colocals()); !it.isDone(); it.advance()) {
+ it.current()->edge = e;
+ }
+ }
+
+ // Update position of all colocals.
+ void setPos(const Vector3 &p) {
+ for (VertexIterator it(colocals()); !it.isDone(); it.advance()) {
+ it.current()->pos = p;
+ }
+ }
+
+ bool isFirstColocal() const {
+ return firstColocal() == this;
+ }
+
+ const Vertex *firstColocal() const {
+ uint32_t firstId = id;
+ const Vertex *vertex = this;
+ for (ConstVertexIterator it(colocals()); !it.isDone(); it.advance()) {
+ if (it.current()->id < firstId) {
+ firstId = vertex->id;
+ vertex = it.current();
+ }
+ }
+ return vertex;
+ }
+
+ Vertex *firstColocal() {
+ Vertex *vertex = this;
+ uint32_t firstId = id;
+ for (VertexIterator it(colocals()); !it.isDone(); it.advance()) {
+ if (it.current()->id < firstId) {
+ firstId = vertex->id;
+ vertex = it.current();
+ }
+ }
+ return vertex;
+ }
+
+ bool isColocal(const Vertex *v) const {
+ if (this == v) return true;
+ if (pos != v->pos) return false;
+ for (ConstVertexIterator it(colocals()); !it.isDone(); it.advance()) {
+ if (v == it.current()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void linkColocal(Vertex *v) {
+ next->prev = v;
+ v->next = next;
+ next = v;
+ v->prev = this;
+ }
+ void unlinkColocal() {
+ next->prev = prev;
+ prev->next = next;
+ next = this;
+ prev = this;
+ }
+
+ // @@ Note: This only works if linkBoundary has been called.
+ bool isBoundary() const {
+ return (edge && !edge->face);
+ }
+
+ // Iterator that visits the edges around this vertex in counterclockwise order.
+ class EdgeIterator //: public Iterator<Edge *>
+ {
+ public:
+ EdgeIterator(Edge *e) :
+ m_end(NULL),
+ m_current(e) {}
+
+ virtual void advance() {
+ if (m_end == NULL) m_end = m_current;
+ m_current = m_current->pair->next;
+ //m_current = m_current->prev->pair;
+ }
+
+ virtual bool isDone() const {
+ return m_end == m_current;
+ }
+ virtual Edge *current() const {
+ return m_current;
+ }
+ Vertex *vertex() const {
+ return m_current->vertex;
+ }
+
+ private:
+ Edge *m_end;
+ Edge *m_current;
+ };
+
+ EdgeIterator edges() {
+ return EdgeIterator(edge);
+ }
+ EdgeIterator edges(Edge *e) {
+ return EdgeIterator(e);
+ }
+
+ // Iterator that visits the edges around this vertex in counterclockwise order.
+ class ConstEdgeIterator //: public Iterator<Edge *>
+ {
+ public:
+ ConstEdgeIterator(const Edge *e) :
+ m_end(NULL),
+ m_current(e) {}
+ ConstEdgeIterator(EdgeIterator it) :
+ m_end(NULL),
+ m_current(it.current()) {}
+
+ virtual void advance() {
+ if (m_end == NULL) m_end = m_current;
+ m_current = m_current->pair->next;
+ //m_current = m_current->prev->pair;
+ }
+
+ virtual bool isDone() const {
+ return m_end == m_current;
+ }
+ virtual const Edge *current() const {
+ return m_current;
+ }
+ const Vertex *vertex() const {
+ return m_current->to();
+ }
+
+ private:
+ const Edge *m_end;
+ const Edge *m_current;
+ };
+
+ ConstEdgeIterator edges() const {
+ return ConstEdgeIterator(edge);
+ }
+ ConstEdgeIterator edges(const Edge *e) const {
+ return ConstEdgeIterator(e);
+ }
+
+ // Iterator that visits all the colocal vertices.
+ class VertexIterator //: public Iterator<Edge *>
+ {
+ public:
+ VertexIterator(Vertex *v) :
+ m_end(NULL),
+ m_current(v) {}
+
+ virtual void advance() {
+ if (m_end == NULL) m_end = m_current;
+ m_current = m_current->next;
+ }
+
+ virtual bool isDone() const {
+ return m_end == m_current;
+ }
+ virtual Vertex *current() const {
+ return m_current;
+ }
+
+ private:
+ Vertex *m_end;
+ Vertex *m_current;
+ };
+
+ VertexIterator colocals() {
+ return VertexIterator(this);
+ }
+
+ // Iterator that visits all the colocal vertices.
+ class ConstVertexIterator //: public Iterator<Edge *>
+ {
+ public:
+ ConstVertexIterator(const Vertex *v) :
+ m_end(NULL),
+ m_current(v) {}
+
+ virtual void advance() {
+ if (m_end == NULL) m_end = m_current;
+ m_current = m_current->next;
+ }
+
+ virtual bool isDone() const {
+ return m_end == m_current;
+ }
+ virtual const Vertex *current() const {
+ return m_current;
+ }
+
+ private:
+ const Vertex *m_end;
+ const Vertex *m_current;
+ };
+
+ ConstVertexIterator colocals() const {
+ return ConstVertexIterator(this);
+ }
+};
+
+bool Edge::isNormalSeam() const {
+ return (vertex->nor != pair->next->vertex->nor || next->vertex->nor != pair->vertex->nor);
+}
+
+bool Edge::isTextureSeam() const {
+ return (vertex->tex != pair->next->vertex->tex || next->vertex->tex != pair->vertex->tex);
+}
+
+float Edge::length() const {
+ return internal::length(to()->pos - from()->pos);
+}
+
+float Edge::angle() const {
+ Vector3 p = vertex->pos;
+ Vector3 a = prev->vertex->pos;
+ Vector3 b = next->vertex->pos;
+ Vector3 v0 = a - p;
+ Vector3 v1 = b - p;
+ return acosf(dot(v0, v1) / (internal::length(v0) * internal::length(v1)));
+}
+
+class Face {
+public:
+ uint32_t id;
+ uint16_t group;
+ uint16_t material;
+ Edge *edge;
+
+ Face(uint32_t id) :
+ id(id),
+ group(uint16_t(~0)),
+ material(uint16_t(~0)),
+ edge(NULL) {}
+
+ float area() const {
+ float area = 0;
+ const Vector3 &v0 = edge->from()->pos;
+ for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance()) {
+ const Edge *e = it.current();
+ const Vector3 &v1 = e->vertex->pos;
+ const Vector3 &v2 = e->next->vertex->pos;
+ area += length(cross(v1 - v0, v2 - v0));
+ }
+ return area * 0.5f;
+ }
+
+ float parametricArea() const {
+ float area = 0;
+ const Vector2 &v0 = edge->from()->tex;
+ for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance()) {
+ const Edge *e = it.current();
+ const Vector2 &v1 = e->vertex->tex;
+ const Vector2 &v2 = e->next->vertex->tex;
+ area += triangleArea(v0, v1, v2);
+ }
+ return area * 0.5f;
+ }
+
+ Vector3 normal() const {
+ Vector3 n(0);
+ const Vertex *vertex0 = NULL;
+ for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) {
+ const Edge *e = it.current();
+ xaAssert(e != NULL);
+ if (vertex0 == NULL) {
+ vertex0 = e->vertex;
+ } else if (e->next->vertex != vertex0) {
+ const halfedge::Vertex *vertex1 = e->from();
+ const halfedge::Vertex *vertex2 = e->to();
+ const Vector3 &p0 = vertex0->pos;
+ const Vector3 &p1 = vertex1->pos;
+ const Vector3 &p2 = vertex2->pos;
+ Vector3 v10 = p1 - p0;
+ Vector3 v20 = p2 - p0;
+ n += cross(v10, v20);
+ }
+ }
+ return normalizeSafe(n, Vector3(0, 0, 1), 0.0f);
+ }
+
+ Vector3 centroid() const {
+ Vector3 sum(0.0f);
+ uint32_t count = 0;
+ for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) {
+ const Edge *e = it.current();
+ sum += e->from()->pos;
+ count++;
+ }
+ return sum / float(count);
+ }
+
+ // Unnormalized face normal assuming it's a triangle.
+ Vector3 triangleNormal() const {
+ Vector3 p0 = edge->vertex->pos;
+ Vector3 p1 = edge->next->vertex->pos;
+ Vector3 p2 = edge->next->next->vertex->pos;
+ Vector3 e0 = p2 - p0;
+ Vector3 e1 = p1 - p0;
+ return normalizeSafe(cross(e0, e1), Vector3(0), 0.0f);
+ }
+
+ Vector3 triangleNormalAreaScaled() const {
+ Vector3 p0 = edge->vertex->pos;
+ Vector3 p1 = edge->next->vertex->pos;
+ Vector3 p2 = edge->next->next->vertex->pos;
+ Vector3 e0 = p2 - p0;
+ Vector3 e1 = p1 - p0;
+ return cross(e0, e1);
+ }
+
+ // Average of the edge midpoints weighted by the edge length.
+ // I want a point inside the triangle, but closer to the cirumcenter.
+ Vector3 triangleCenter() const {
+ Vector3 p0 = edge->vertex->pos;
+ Vector3 p1 = edge->next->vertex->pos;
+ Vector3 p2 = edge->next->next->vertex->pos;
+ float l0 = length(p1 - p0);
+ float l1 = length(p2 - p1);
+ float l2 = length(p0 - p2);
+ Vector3 m0 = (p0 + p1) * l0 / (l0 + l1 + l2);
+ Vector3 m1 = (p1 + p2) * l1 / (l0 + l1 + l2);
+ Vector3 m2 = (p2 + p0) * l2 / (l0 + l1 + l2);
+ return m0 + m1 + m2;
+ }
+
+ bool isValid() const {
+ uint32_t count = 0;
+ for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) {
+ const Edge *e = it.current();
+ if (e->face != this) return false;
+ if (!e->isValid()) return false;
+ if (!e->pair->isValid()) return false;
+ count++;
+ }
+ if (count < 3) return false;
+ return true;
+ }
+
+ bool contains(const Edge *e) const {
+ for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) {
+ if (it.current() == e) return true;
+ }
+ return false;
+ }
+
+ uint32_t edgeCount() const {
+ uint32_t count = 0;
+ for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) {
+ ++count;
+ }
+ return count;
+ }
+
+ // The iterator that visits the edges of this face in clockwise order.
+ class EdgeIterator //: public Iterator<Edge *>
+ {
+ public:
+ EdgeIterator(Edge *e) :
+ m_end(NULL),
+ m_current(e) {}
+
+ virtual void advance() {
+ if (m_end == NULL) m_end = m_current;
+ m_current = m_current->next;
+ }
+
+ virtual bool isDone() const {
+ return m_end == m_current;
+ }
+ virtual Edge *current() const {
+ return m_current;
+ }
+ Vertex *vertex() const {
+ return m_current->vertex;
+ }
+
+ private:
+ Edge *m_end;
+ Edge *m_current;
+ };
+
+ EdgeIterator edges() {
+ return EdgeIterator(edge);
+ }
+ EdgeIterator edges(Edge *e) {
+ xaDebugAssert(contains(e));
+ return EdgeIterator(e);
+ }
+
+ // The iterator that visits the edges of this face in clockwise order.
+ class ConstEdgeIterator //: public Iterator<const Edge *>
+ {
+ public:
+ ConstEdgeIterator(const Edge *e) :
+ m_end(NULL),
+ m_current(e) {}
+ ConstEdgeIterator(const EdgeIterator &it) :
+ m_end(NULL),
+ m_current(it.current()) {}
+
+ virtual void advance() {
+ if (m_end == NULL) m_end = m_current;
+ m_current = m_current->next;
+ }
+
+ virtual bool isDone() const {
+ return m_end == m_current;
+ }
+ virtual const Edge *current() const {
+ return m_current;
+ }
+ const Vertex *vertex() const {
+ return m_current->vertex;
+ }
+
+ private:
+ const Edge *m_end;
+ const Edge *m_current;
+ };
+
+ ConstEdgeIterator edges() const {
+ return ConstEdgeIterator(edge);
+ }
+ ConstEdgeIterator edges(const Edge *e) const {
+ xaDebugAssert(contains(e));
+ return ConstEdgeIterator(e);
+ }
+};
+
+/// Simple half edge mesh designed for dynamic mesh manipulation.
+class Mesh {
+public:
+ Mesh() :
+ m_colocalVertexCount(0) {}
+
+ Mesh(const Mesh *mesh) {
+ // Copy mesh vertices.
+ const uint32_t vertexCount = mesh->vertexCount();
+ m_vertexArray.resize(vertexCount);
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ const Vertex *vertex = mesh->vertexAt(v);
+ xaDebugAssert(vertex->id == v);
+ m_vertexArray[v] = new Vertex(v);
+ m_vertexArray[v]->pos = vertex->pos;
+ m_vertexArray[v]->nor = vertex->nor;
+ m_vertexArray[v]->tex = vertex->tex;
+ }
+ m_colocalVertexCount = vertexCount;
+ // Copy mesh faces.
+ const uint32_t faceCount = mesh->faceCount();
+ std::vector<uint32_t> indexArray;
+ indexArray.reserve(3);
+ for (uint32_t f = 0; f < faceCount; f++) {
+ const Face *face = mesh->faceAt(f);
+ for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const Vertex *vertex = it.current()->from();
+ indexArray.push_back(vertex->id);
+ }
+ addFace(indexArray);
+ indexArray.clear();
+ }
+ }
+
+ ~Mesh() {
+ clear();
+ }
+
+ void clear() {
+ for (size_t i = 0; i < m_vertexArray.size(); i++)
+ delete m_vertexArray[i];
+ m_vertexArray.clear();
+ for (auto it = m_edgeMap.begin(); it != m_edgeMap.end(); it++)
+ delete it->second;
+ m_edgeArray.clear();
+ m_edgeMap.clear();
+ for (size_t i = 0; i < m_faceArray.size(); i++)
+ delete m_faceArray[i];
+ m_faceArray.clear();
+ }
+
+ Vertex *addVertex(const Vector3 &pos) {
+ xaDebugAssert(isFinite(pos));
+ Vertex *v = new Vertex(m_vertexArray.size());
+ v->pos = pos;
+ m_vertexArray.push_back(v);
+ return v;
+ }
+
+ /// Link colocal vertices based on geometric location only.
+ void linkColocals() {
+ xaPrint("--- Linking colocals:\n");
+ const uint32_t vertexCount = this->vertexCount();
+ std::unordered_map<Vector3, Vertex *, Hash<Vector3>, Equal<Vector3> > vertexMap;
+ vertexMap.reserve(vertexCount);
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ Vertex *vertex = vertexAt(v);
+ Vertex *colocal = vertexMap[vertex->pos];
+ if (colocal) {
+ colocal->linkColocal(vertex);
+ } else {
+ vertexMap[vertex->pos] = vertex;
+ }
+ }
+ m_colocalVertexCount = vertexMap.size();
+ xaPrint("--- %d vertex positions.\n", m_colocalVertexCount);
+ // @@ Remove duplicated vertices? or just leave them as colocals?
+ }
+
+ void linkColocalsWithCanonicalMap(const std::vector<uint32_t> &canonicalMap) {
+ xaPrint("--- Linking colocals:\n");
+ uint32_t vertexMapSize = 0;
+ for (uint32_t i = 0; i < canonicalMap.size(); i++) {
+ vertexMapSize = std::max(vertexMapSize, canonicalMap[i] + 1);
+ }
+ std::vector<Vertex *> vertexMap;
+ vertexMap.resize(vertexMapSize, NULL);
+ m_colocalVertexCount = 0;
+ const uint32_t vertexCount = this->vertexCount();
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ Vertex *vertex = vertexAt(v);
+ Vertex *colocal = vertexMap[canonicalMap[v]];
+ if (colocal != NULL) {
+ xaDebugAssert(vertex->pos == colocal->pos);
+ colocal->linkColocal(vertex);
+ } else {
+ vertexMap[canonicalMap[v]] = vertex;
+ m_colocalVertexCount++;
+ }
+ }
+ xaPrint("--- %d vertex positions.\n", m_colocalVertexCount);
+ }
+
+ Face *addFace() {
+ Face *f = new Face(m_faceArray.size());
+ m_faceArray.push_back(f);
+ return f;
+ }
+
+ Face *addFace(uint32_t v0, uint32_t v1, uint32_t v2) {
+ uint32_t indexArray[3];
+ indexArray[0] = v0;
+ indexArray[1] = v1;
+ indexArray[2] = v2;
+ return addFace(indexArray, 3, 0, 3);
+ }
+
+ Face *addUniqueFace(uint32_t v0, uint32_t v1, uint32_t v2) {
+
+ int base_vertex = m_vertexArray.size();
+
+ uint32_t ids[3] = { v0, v1, v2 };
+
+ Vector3 base[3] = {
+ m_vertexArray[v0]->pos,
+ m_vertexArray[v1]->pos,
+ m_vertexArray[v2]->pos,
+ };
+
+ //make sure its not a degenerate
+ bool degenerate = distanceSquared(base[0], base[1]) < NV_EPSILON || distanceSquared(base[0], base[2]) < NV_EPSILON || distanceSquared(base[1], base[2]) < NV_EPSILON;
+ xaDebugAssert(!degenerate);
+
+ float min_x = 0;
+
+ for (int i = 0; i < 3; i++) {
+ if (i == 0 || m_vertexArray[v0]->pos.x < min_x) {
+ min_x = m_vertexArray[v0]->pos.x;
+ }
+ }
+
+ float max_x = 0;
+
+ for (int j = 0; j < m_vertexArray.size(); j++) {
+ if (j == 0 || m_vertexArray[j]->pos.x > max_x) { //vertex already exists
+ max_x = m_vertexArray[j]->pos.x;
+ }
+ }
+
+ //separate from everything else, in x axis
+ for (int i = 0; i < 3; i++) {
+
+ base[i].x -= min_x;
+ base[i].x += max_x + 10.0;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ Vertex *v = new Vertex(m_vertexArray.size());
+ v->pos = base[i];
+ v->nor = m_vertexArray[ids[i]]->nor,
+ v->tex = m_vertexArray[ids[i]]->tex,
+
+ v->original_id = ids[i];
+ m_vertexArray.push_back(v);
+ }
+
+ uint32_t indexArray[3];
+ indexArray[0] = base_vertex + 0;
+ indexArray[1] = base_vertex + 1;
+ indexArray[2] = base_vertex + 2;
+ return addFace(indexArray, 3, 0, 3);
+ }
+
+ Face *addFace(uint32_t v0, uint32_t v1, uint32_t v2, uint32_t v3) {
+ uint32_t indexArray[4];
+ indexArray[0] = v0;
+ indexArray[1] = v1;
+ indexArray[2] = v2;
+ indexArray[3] = v3;
+ return addFace(indexArray, 4, 0, 4);
+ }
+
+ Face *addFace(const std::vector<uint32_t> &indexArray) {
+ return addFace(indexArray, 0, indexArray.size());
+ }
+
+ Face *addFace(const std::vector<uint32_t> &indexArray, uint32_t first, uint32_t num) {
+ return addFace(indexArray.data(), (uint32_t)indexArray.size(), first, num);
+ }
+
+ Face *addFace(const uint32_t *indexArray, uint32_t indexCount, uint32_t first, uint32_t num) {
+ xaDebugAssert(first < indexCount);
+ xaDebugAssert(num <= indexCount - first);
+ xaDebugAssert(num > 2);
+ if (!canAddFace(indexArray, first, num)) {
+ return NULL;
+ }
+ Face *f = new Face(m_faceArray.size());
+ Edge *firstEdge = NULL;
+ Edge *last = NULL;
+ Edge *current = NULL;
+ for (uint32_t i = 0; i < num - 1; i++) {
+ current = addEdge(indexArray[first + i], indexArray[first + i + 1]);
+ xaAssert(current != NULL && current->face == NULL);
+ current->face = f;
+ if (last != NULL)
+ last->setNext(current);
+ else
+ firstEdge = current;
+ last = current;
+ }
+ current = addEdge(indexArray[first + num - 1], indexArray[first]);
+ xaAssert(current != NULL && current->face == NULL);
+ current->face = f;
+ last->setNext(current);
+ current->setNext(firstEdge);
+ f->edge = firstEdge;
+ m_faceArray.push_back(f);
+ return f;
+ }
+
+ // These functions disconnect the given element from the mesh and delete it.
+
+ // @@ We must always disconnect edge pairs simultaneously.
+ void disconnect(Edge *edge) {
+ xaDebugAssert(edge != NULL);
+ // Remove from edge list.
+ if ((edge->id & 1) == 0) {
+ xaDebugAssert(m_edgeArray[edge->id / 2] == edge);
+ m_edgeArray[edge->id / 2] = NULL;
+ }
+ // Remove edge from map. @@ Store map key inside edge?
+ xaDebugAssert(edge->from() != NULL && edge->to() != NULL);
+ size_t removed = m_edgeMap.erase(Key(edge->from()->id, edge->to()->id));
+ xaDebugAssert(removed == 1);
+#ifdef NDEBUG
+ removed = 0; // silence unused parameter warning
+#endif
+ // Disconnect from vertex.
+ if (edge->vertex != NULL) {
+ if (edge->vertex->edge == edge) {
+ if (edge->prev && edge->prev->pair) {
+ edge->vertex->edge = edge->prev->pair;
+ } else if (edge->pair && edge->pair->next) {
+ edge->vertex->edge = edge->pair->next;
+ } else {
+ edge->vertex->edge = NULL;
+ // @@ Remove disconnected vertex?
+ }
+ }
+ }
+ // Disconnect from face.
+ if (edge->face != NULL) {
+ if (edge->face->edge == edge) {
+ if (edge->next != NULL && edge->next != edge) {
+ edge->face->edge = edge->next;
+ } else if (edge->prev != NULL && edge->prev != edge) {
+ edge->face->edge = edge->prev;
+ } else {
+ edge->face->edge = NULL;
+ // @@ Remove disconnected face?
+ }
+ }
+ }
+ // Disconnect from previous.
+ if (edge->prev) {
+ if (edge->prev->next == edge) {
+ edge->prev->setNext(NULL);
+ }
+ //edge->setPrev(NULL);
+ }
+ // Disconnect from next.
+ if (edge->next) {
+ if (edge->next->prev == edge) {
+ edge->next->setPrev(NULL);
+ }
+ //edge->setNext(NULL);
+ }
+ }
+
+ void remove(Edge *edge) {
+ xaDebugAssert(edge != NULL);
+ disconnect(edge);
+ delete edge;
+ }
+
+ void remove(Vertex *vertex) {
+ xaDebugAssert(vertex != NULL);
+ // Remove from vertex list.
+ m_vertexArray[vertex->id] = NULL;
+ // Disconnect from colocals.
+ vertex->unlinkColocal();
+ // Disconnect from edges.
+ if (vertex->edge != NULL) {
+ // @@ Removing a connected vertex is asking for trouble...
+ if (vertex->edge->vertex == vertex) {
+ // @@ Connect edge to a colocal?
+ vertex->edge->vertex = NULL;
+ }
+ vertex->setEdge(NULL);
+ }
+ delete vertex;
+ }
+
+ void remove(Face *face) {
+ xaDebugAssert(face != NULL);
+ // Remove from face list.
+ m_faceArray[face->id] = NULL;
+ // Disconnect from edges.
+ if (face->edge != NULL) {
+ xaDebugAssert(face->edge->face == face);
+ face->edge->face = NULL;
+ face->edge = NULL;
+ }
+ delete face;
+ }
+
+ // Triangulate in place.
+ void triangulate() {
+ bool all_triangles = true;
+ const uint32_t faceCount = m_faceArray.size();
+ for (uint32_t f = 0; f < faceCount; f++) {
+ Face *face = m_faceArray[f];
+ if (face->edgeCount() != 3) {
+ all_triangles = false;
+ break;
+ }
+ }
+ if (all_triangles) {
+ return;
+ }
+ // Do not touch vertices, but rebuild edges and faces.
+ std::vector<Edge *> edgeArray;
+ std::vector<Face *> faceArray;
+ std::swap(edgeArray, m_edgeArray);
+ std::swap(faceArray, m_faceArray);
+ m_edgeMap.clear();
+ for (uint32_t f = 0; f < faceCount; f++) {
+ Face *face = faceArray[f];
+ // Trivial fan-like triangulation.
+ const uint32_t v0 = face->edge->vertex->id;
+ uint32_t v2, v1 = (uint32_t)-1;
+ for (Face::EdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ Edge *edge = it.current();
+ v2 = edge->to()->id;
+ if (v2 == v0) break;
+ if (v1 != -1) addFace(v0, v1, v2);
+ v1 = v2;
+ }
+ }
+ xaDebugAssert(m_faceArray.size() > faceCount); // triangle count > face count
+ linkBoundary();
+ for (size_t i = 0; i < edgeArray.size(); i++)
+ delete edgeArray[i];
+ for (size_t i = 0; i < faceArray.size(); i++)
+ delete faceArray[i];
+ }
+
+ /// Link boundary edges once the mesh has been created.
+ void linkBoundary() {
+ xaPrint("--- Linking boundaries:\n");
+ int num = 0;
+ // Create boundary edges.
+ uint32_t edgeCount = this->edgeCount();
+ for (uint32_t e = 0; e < edgeCount; e++) {
+ Edge *edge = edgeAt(e);
+ if (edge != NULL && edge->pair == NULL) {
+ Edge *pair = new Edge(edge->id + 1);
+ uint32_t i = edge->from()->id;
+ uint32_t j = edge->next->from()->id;
+ Key key(j, i);
+ xaAssert(m_edgeMap.find(key) == m_edgeMap.end());
+ pair->vertex = m_vertexArray[j];
+ m_edgeMap[key] = pair;
+ edge->pair = pair;
+ pair->pair = edge;
+ num++;
+ }
+ }
+ // Link boundary edges.
+ for (uint32_t e = 0; e < edgeCount; e++) {
+ Edge *edge = edgeAt(e);
+ if (edge != NULL && edge->pair->face == NULL) {
+ linkBoundaryEdge(edge->pair);
+ }
+ }
+ xaPrint("--- %d boundary edges.\n", num);
+ }
+
+ /*
+ Fixing T-junctions.
+
+ - Find T-junctions. Find vertices that are on an edge.
+ - This test is approximate.
+ - Insert edges on a spatial index to speedup queries.
+ - Consider only open edges, that is edges that have no pairs.
+ - Consider only vertices on boundaries.
+ - Close T-junction.
+ - Split edge.
+
+ */
+ bool splitBoundaryEdges() // Returns true if any split was made.
+ {
+ std::vector<Vertex *> boundaryVertices;
+ for (uint32_t i = 0; i < m_vertexArray.size(); i++) {
+ Vertex *v = m_vertexArray[i];
+ if (v->isBoundary()) {
+ boundaryVertices.push_back(v);
+ }
+ }
+ xaPrint("Fixing T-junctions:\n");
+ int splitCount = 0;
+ for (uint32_t v = 0; v < boundaryVertices.size(); v++) {
+ Vertex *vertex = boundaryVertices[v];
+ Vector3 x0 = vertex->pos;
+ // Find edges that this vertex overlaps with.
+ for (uint32_t e = 0; e < m_edgeArray.size(); e++) {
+ Edge *edge = m_edgeArray[e];
+ if (edge != NULL && edge->isBoundary()) {
+ if (edge->from() == vertex || edge->to() == vertex) {
+ continue;
+ }
+ Vector3 x1 = edge->from()->pos;
+ Vector3 x2 = edge->to()->pos;
+ Vector3 v01 = x0 - x1;
+ Vector3 v21 = x2 - x1;
+ float l = length(v21);
+ float d = length(cross(v01, v21)) / l;
+ if (isZero(d)) {
+ float t = dot(v01, v21) / (l * l);
+ if (t > 0.0f + NV_EPSILON && t < 1.0f - NV_EPSILON) {
+ xaDebugAssert(equal(lerp(x1, x2, t), x0));
+ Vertex *splitVertex = splitBoundaryEdge(edge, t, x0);
+ vertex->linkColocal(splitVertex); // @@ Should we do this here?
+ splitCount++;
+ }
+ }
+ }
+ }
+ }
+ xaPrint(" - %d edges split.\n", splitCount);
+ xaDebugAssert(isValid());
+ return splitCount != 0;
+ }
+
+ // Vertices
+ uint32_t vertexCount() const {
+ return m_vertexArray.size();
+ }
+ const Vertex *vertexAt(int i) const {
+ return m_vertexArray[i];
+ }
+ Vertex *vertexAt(int i) {
+ return m_vertexArray[i];
+ }
+
+ uint32_t colocalVertexCount() const {
+ return m_colocalVertexCount;
+ }
+
+ // Faces
+ uint32_t faceCount() const {
+ return m_faceArray.size();
+ }
+ const Face *faceAt(int i) const {
+ return m_faceArray[i];
+ }
+ Face *faceAt(int i) {
+ return m_faceArray[i];
+ }
+
+ // Edges
+ uint32_t edgeCount() const {
+ return m_edgeArray.size();
+ }
+ const Edge *edgeAt(int i) const {
+ return m_edgeArray[i];
+ }
+ Edge *edgeAt(int i) {
+ return m_edgeArray[i];
+ }
+
+ class ConstVertexIterator;
+
+ class VertexIterator {
+ friend class ConstVertexIterator;
+
+ public:
+ VertexIterator(Mesh *mesh) :
+ m_mesh(mesh),
+ m_current(0) {}
+
+ virtual void advance() {
+ m_current++;
+ }
+ virtual bool isDone() const {
+ return m_current == m_mesh->vertexCount();
+ }
+ virtual Vertex *current() const {
+ return m_mesh->vertexAt(m_current);
+ }
+
+ private:
+ halfedge::Mesh *m_mesh;
+ uint32_t m_current;
+ };
+ VertexIterator vertices() {
+ return VertexIterator(this);
+ }
+
+ class ConstVertexIterator {
+ public:
+ ConstVertexIterator(const Mesh *mesh) :
+ m_mesh(mesh),
+ m_current(0) {}
+ ConstVertexIterator(class VertexIterator &it) :
+ m_mesh(it.m_mesh),
+ m_current(it.m_current) {}
+
+ virtual void advance() {
+ m_current++;
+ }
+ virtual bool isDone() const {
+ return m_current == m_mesh->vertexCount();
+ }
+ virtual const Vertex *current() const {
+ return m_mesh->vertexAt(m_current);
+ }
+
+ private:
+ const halfedge::Mesh *m_mesh;
+ uint32_t m_current;
+ };
+ ConstVertexIterator vertices() const {
+ return ConstVertexIterator(this);
+ }
+
+ class ConstFaceIterator;
+
+ class FaceIterator {
+ friend class ConstFaceIterator;
+
+ public:
+ FaceIterator(Mesh *mesh) :
+ m_mesh(mesh),
+ m_current(0) {}
+
+ virtual void advance() {
+ m_current++;
+ }
+ virtual bool isDone() const {
+ return m_current == m_mesh->faceCount();
+ }
+ virtual Face *current() const {
+ return m_mesh->faceAt(m_current);
+ }
+
+ private:
+ halfedge::Mesh *m_mesh;
+ uint32_t m_current;
+ };
+ FaceIterator faces() {
+ return FaceIterator(this);
+ }
+
+ class ConstFaceIterator {
+ public:
+ ConstFaceIterator(const Mesh *mesh) :
+ m_mesh(mesh),
+ m_current(0) {}
+ ConstFaceIterator(const FaceIterator &it) :
+ m_mesh(it.m_mesh),
+ m_current(it.m_current) {}
+
+ virtual void advance() {
+ m_current++;
+ }
+ virtual bool isDone() const {
+ return m_current == m_mesh->faceCount();
+ }
+ virtual const Face *current() const {
+ return m_mesh->faceAt(m_current);
+ }
+
+ private:
+ const halfedge::Mesh *m_mesh;
+ uint32_t m_current;
+ };
+ ConstFaceIterator faces() const {
+ return ConstFaceIterator(this);
+ }
+
+ class ConstEdgeIterator;
+
+ class EdgeIterator {
+ friend class ConstEdgeIterator;
+
+ public:
+ EdgeIterator(Mesh *mesh) :
+ m_mesh(mesh),
+ m_current(0) {}
+
+ virtual void advance() {
+ m_current++;
+ }
+ virtual bool isDone() const {
+ return m_current == m_mesh->edgeCount();
+ }
+ virtual Edge *current() const {
+ return m_mesh->edgeAt(m_current);
+ }
+
+ private:
+ halfedge::Mesh *m_mesh;
+ uint32_t m_current;
+ };
+ EdgeIterator edges() {
+ return EdgeIterator(this);
+ }
+
+ class ConstEdgeIterator {
+ public:
+ ConstEdgeIterator(const Mesh *mesh) :
+ m_mesh(mesh),
+ m_current(0) {}
+ ConstEdgeIterator(const EdgeIterator &it) :
+ m_mesh(it.m_mesh),
+ m_current(it.m_current) {}
+
+ virtual void advance() {
+ m_current++;
+ }
+ virtual bool isDone() const {
+ return m_current == m_mesh->edgeCount();
+ }
+ virtual const Edge *current() const {
+ return m_mesh->edgeAt(m_current);
+ }
+
+ private:
+ const halfedge::Mesh *m_mesh;
+ uint32_t m_current;
+ };
+ ConstEdgeIterator edges() const {
+ return ConstEdgeIterator(this);
+ }
+
+ // @@ Add half-edge iterator.
+
+ bool isValid() const {
+ // Make sure all edges are valid.
+ const uint32_t edgeCount = m_edgeArray.size();
+ for (uint32_t e = 0; e < edgeCount; e++) {
+ Edge *edge = m_edgeArray[e];
+ if (edge != NULL) {
+ if (edge->id != 2 * e) {
+ return false;
+ }
+ if (!edge->isValid()) {
+ return false;
+ }
+ if (edge->pair->id != 2 * e + 1) {
+ return false;
+ }
+ if (!edge->pair->isValid()) {
+ return false;
+ }
+ }
+ }
+ // @@ Make sure all faces are valid.
+ // @@ Make sure all vertices are valid.
+ return true;
+ }
+
+ // Error status:
+
+ struct ErrorCode {
+ enum Enum {
+ AlreadyAddedEdge,
+ DegenerateColocalEdge,
+ DegenerateEdge,
+ DuplicateEdge
+ };
+ };
+
+ mutable ErrorCode::Enum errorCode;
+ mutable uint32_t errorIndex0;
+ mutable uint32_t errorIndex1;
+
+private:
+ // Return true if the face can be added to the manifold mesh.
+ bool canAddFace(const std::vector<uint32_t> &indexArray, uint32_t first, uint32_t num) const {
+ return canAddFace(indexArray.data(), first, num);
+ }
+
+ bool canAddFace(const uint32_t *indexArray, uint32_t first, uint32_t num) const {
+ for (uint32_t j = num - 1, i = 0; i < num; j = i++) {
+ if (!canAddEdge(indexArray[first + j], indexArray[first + i])) {
+ errorIndex0 = indexArray[first + j];
+ errorIndex1 = indexArray[first + i];
+ return false;
+ }
+ }
+ // We also have to make sure the face does not have any duplicate edge!
+ for (uint32_t i = 0; i < num; i++) {
+ int i0 = indexArray[first + i + 0];
+ int i1 = indexArray[first + (i + 1) % num];
+ for (uint32_t j = i + 1; j < num; j++) {
+ int j0 = indexArray[first + j + 0];
+ int j1 = indexArray[first + (j + 1) % num];
+ if (i0 == j0 && i1 == j1) {
+ errorCode = ErrorCode::DuplicateEdge;
+ errorIndex0 = i0;
+ errorIndex1 = i1;
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ // Return true if the edge doesn't exist or doesn't have any adjacent face.
+ bool canAddEdge(uint32_t i, uint32_t j) const {
+ if (i == j) {
+ // Skip degenerate edges.
+ errorCode = ErrorCode::DegenerateEdge;
+ return false;
+ }
+ // Same check, but taking into account colocal vertices.
+ const Vertex *v0 = vertexAt(i);
+ const Vertex *v1 = vertexAt(j);
+ for (Vertex::ConstVertexIterator it(v0->colocals()); !it.isDone(); it.advance()) {
+ if (it.current() == v1) {
+ // Skip degenerate edges.
+ errorCode = ErrorCode::DegenerateColocalEdge;
+ return false;
+ }
+ }
+ // Make sure edge has not been added yet.
+ Edge *edge = findEdge(i, j);
+ // We ignore edges that don't have an adjacent face yet, since this face could become the edge's face.
+ if (!(edge == NULL || edge->face == NULL)) {
+ errorCode = ErrorCode::AlreadyAddedEdge;
+ return false;
+ }
+ return true;
+ }
+
+ Edge *addEdge(uint32_t i, uint32_t j) {
+ xaAssert(i != j);
+ Edge *edge = findEdge(i, j);
+ if (edge != NULL) {
+ // Edge may already exist, but its face must not be set.
+ xaDebugAssert(edge->face == NULL);
+ // Nothing else to do!
+ } else {
+ // Add new edge.
+ // Lookup pair.
+ Edge *pair = findEdge(j, i);
+ if (pair != NULL) {
+ // Create edge with same id.
+ edge = new Edge(pair->id + 1);
+ // Link edge pairs.
+ edge->pair = pair;
+ pair->pair = edge;
+ // @@ I'm not sure this is necessary!
+ pair->vertex->setEdge(pair);
+ } else {
+ // Create edge.
+ edge = new Edge(2 * m_edgeArray.size());
+ // Add only unpaired edges.
+ m_edgeArray.push_back(edge);
+ }
+ edge->vertex = m_vertexArray[i];
+ m_edgeMap[Key(i, j)] = edge;
+ }
+ // Face and Next are set by addFace.
+ return edge;
+ }
+
+ /// Find edge, test all colocals.
+ Edge *findEdge(uint32_t i, uint32_t j) const {
+ Edge *edge = NULL;
+ const Vertex *v0 = vertexAt(i);
+ const Vertex *v1 = vertexAt(j);
+ // Test all colocal pairs.
+ for (Vertex::ConstVertexIterator it0(v0->colocals()); !it0.isDone(); it0.advance()) {
+ for (Vertex::ConstVertexIterator it1(v1->colocals()); !it1.isDone(); it1.advance()) {
+ Key key(it0.current()->id, it1.current()->id);
+ if (edge == NULL) {
+ auto edgeIt = m_edgeMap.find(key);
+ if (edgeIt != m_edgeMap.end())
+ edge = (*edgeIt).second;
+#if !defined(_DEBUG)
+ if (edge != NULL) return edge;
+#endif
+ } else {
+ // Make sure that only one edge is found.
+ xaDebugAssert(m_edgeMap.find(key) == m_edgeMap.end());
+ }
+ }
+ }
+ return edge;
+ }
+
+ /// Link this boundary edge.
+ void linkBoundaryEdge(Edge *edge) {
+ xaAssert(edge->face == NULL);
+ // Make sure next pointer has not been set. @@ We want to be able to relink boundary edges after mesh changes.
+ Edge *next = edge;
+ while (next->pair->face != NULL) {
+ // Get pair prev
+ Edge *e = next->pair->next;
+ while (e->next != next->pair) {
+ e = e->next;
+ }
+ next = e;
+ }
+ edge->setNext(next->pair);
+ // Adjust vertex edge, so that it's the boundary edge. (required for isBoundary())
+ if (edge->vertex->edge != edge) {
+ // Multiple boundaries in the same edge.
+ edge->vertex->edge = edge;
+ }
+ }
+
+ Vertex *splitBoundaryEdge(Edge *edge, float t, const Vector3 &pos) {
+ /*
+ We want to go from this configuration:
+
+ + +
+ | ^
+ edge |<->| pair
+ v |
+ + +
+
+ To this one:
+
+ + +
+ | ^
+ e0 |<->| p0
+ v |
+ vertex + +
+ | ^
+ e1 |<->| p1
+ v |
+ + +
+
+ */
+ Edge *pair = edge->pair;
+ // Make sure boundaries are linked.
+ xaDebugAssert(pair != NULL);
+ // Make sure edge is a boundary edge.
+ xaDebugAssert(pair->face == NULL);
+ // Add new vertex.
+ Vertex *vertex = addVertex(pos);
+ vertex->nor = lerp(edge->from()->nor, edge->to()->nor, t);
+ vertex->tex = lerp(edge->from()->tex, edge->to()->tex, t);
+ disconnect(edge);
+ disconnect(pair);
+ // Add edges.
+ Edge *e0 = addEdge(edge->from()->id, vertex->id);
+ Edge *p0 = addEdge(vertex->id, pair->to()->id);
+ Edge *e1 = addEdge(vertex->id, edge->to()->id);
+ Edge *p1 = addEdge(pair->from()->id, vertex->id);
+ // Link edges.
+ e0->setNext(e1);
+ p1->setNext(p0);
+ e0->setPrev(edge->prev);
+ e1->setNext(edge->next);
+ p1->setPrev(pair->prev);
+ p0->setNext(pair->next);
+ xaDebugAssert(e0->next == e1);
+ xaDebugAssert(e1->prev == e0);
+ xaDebugAssert(p1->next == p0);
+ xaDebugAssert(p0->prev == p1);
+ xaDebugAssert(p0->pair == e0);
+ xaDebugAssert(e0->pair == p0);
+ xaDebugAssert(p1->pair == e1);
+ xaDebugAssert(e1->pair == p1);
+ // Link faces.
+ e0->face = edge->face;
+ e1->face = edge->face;
+ // Link vertices.
+ edge->from()->setEdge(e0);
+ vertex->setEdge(e1);
+ delete edge;
+ delete pair;
+ return vertex;
+ }
+
+private:
+ std::vector<Vertex *> m_vertexArray;
+ std::vector<Edge *> m_edgeArray;
+ std::vector<Face *> m_faceArray;
+
+ struct Key {
+ Key() {}
+ Key(const Key &k) :
+ p0(k.p0),
+ p1(k.p1) {}
+ Key(uint32_t v0, uint32_t v1) :
+ p0(v0),
+ p1(v1) {}
+ void operator=(const Key &k) {
+ p0 = k.p0;
+ p1 = k.p1;
+ }
+ bool operator==(const Key &k) const {
+ return p0 == k.p0 && p1 == k.p1;
+ }
+
+ uint32_t p0;
+ uint32_t p1;
+ };
+
+ friend struct Hash<Mesh::Key>;
+ std::unordered_map<Key, Edge *, Hash<Key>, Equal<Key> > m_edgeMap;
+ uint32_t m_colocalVertexCount;
+};
+
+class MeshTopology {
+public:
+ MeshTopology(const Mesh *mesh) {
+ buildTopologyInfo(mesh);
+ }
+
+ /// Determine if the mesh is connected.
+ bool isConnected() const {
+ return m_connectedCount == 1;
+ }
+
+ /// Determine if the mesh is closed. (Each edge is shared by two faces)
+ bool isClosed() const {
+ return m_boundaryCount == 0;
+ }
+
+ /// Return true if the mesh has the topology of a disk.
+ bool isDisk() const {
+ return isConnected() && m_boundaryCount == 1 /* && m_eulerNumber == 1*/;
+ }
+
+private:
+ void buildTopologyInfo(const Mesh *mesh) {
+ const uint32_t vertexCount = mesh->colocalVertexCount();
+ const uint32_t faceCount = mesh->faceCount();
+ const uint32_t edgeCount = mesh->edgeCount();
+ xaPrint("--- Building mesh topology:\n");
+ std::vector<uint32_t> stack(faceCount);
+ BitArray bitFlags(faceCount);
+ bitFlags.clearAll();
+ // Compute connectivity.
+ xaPrint("--- Computing connectivity.\n");
+ m_connectedCount = 0;
+ for (uint32_t f = 0; f < faceCount; f++) {
+ if (bitFlags.bitAt(f) == false) {
+ m_connectedCount++;
+ stack.push_back(f);
+ while (!stack.empty()) {
+ const uint32_t top = stack.back();
+ xaAssert(top != uint32_t(~0));
+ stack.pop_back();
+ if (bitFlags.bitAt(top) == false) {
+ bitFlags.setBitAt(top);
+ const Face *face = mesh->faceAt(top);
+ const Edge *firstEdge = face->edge;
+ const Edge *edge = firstEdge;
+ do {
+ const Face *neighborFace = edge->pair->face;
+ if (neighborFace != NULL) {
+ stack.push_back(neighborFace->id);
+ }
+ edge = edge->next;
+ } while (edge != firstEdge);
+ }
+ }
+ }
+ }
+ xaAssert(stack.empty());
+ xaPrint("--- %d connected components.\n", m_connectedCount);
+ // Count boundary loops.
+ xaPrint("--- Counting boundary loops.\n");
+ m_boundaryCount = 0;
+ bitFlags.resize(edgeCount);
+ bitFlags.clearAll();
+ // Don't forget to link the boundary otherwise this won't work.
+ for (uint32_t e = 0; e < edgeCount; e++) {
+ const Edge *startEdge = mesh->edgeAt(e);
+ if (startEdge != NULL && startEdge->isBoundary() && bitFlags.bitAt(e) == false) {
+ xaDebugAssert(startEdge->face != NULL);
+ xaDebugAssert(startEdge->pair->face == NULL);
+ startEdge = startEdge->pair;
+ m_boundaryCount++;
+ const Edge *edge = startEdge;
+ do {
+ bitFlags.setBitAt(edge->id / 2);
+ edge = edge->next;
+ } while (startEdge != edge);
+ }
+ }
+ xaPrint("--- %d boundary loops found.\n", m_boundaryCount);
+ // Compute euler number.
+ m_eulerNumber = vertexCount - edgeCount + faceCount;
+ xaPrint("--- Euler number: %d.\n", m_eulerNumber);
+ // Compute genus. (only valid on closed connected surfaces)
+ m_genus = -1;
+ if (isClosed() && isConnected()) {
+ m_genus = (2 - m_eulerNumber) / 2;
+ xaPrint("--- Genus: %d.\n", m_genus);
+ }
+ }
+
+private:
+ ///< Number of boundary loops.
+ int m_boundaryCount;
+
+ ///< Number of connected components.
+ int m_connectedCount;
+
+ ///< Euler number.
+ int m_eulerNumber;
+
+ /// Mesh genus.
+ int m_genus;
+};
+
+float computeSurfaceArea(const halfedge::Mesh *mesh) {
+ float area = 0;
+ for (halfedge::Mesh::ConstFaceIterator it(mesh->faces()); !it.isDone(); it.advance()) {
+ const halfedge::Face *face = it.current();
+ area += face->area();
+ }
+ xaDebugAssert(area >= 0);
+ return area;
+}
+
+float computeParametricArea(const halfedge::Mesh *mesh) {
+ float area = 0;
+ for (halfedge::Mesh::ConstFaceIterator it(mesh->faces()); !it.isDone(); it.advance()) {
+ const halfedge::Face *face = it.current();
+ area += face->parametricArea();
+ }
+ return area;
+}
+
+uint32_t countMeshTriangles(const Mesh *mesh) {
+ const uint32_t faceCount = mesh->faceCount();
+ uint32_t triangleCount = 0;
+ for (uint32_t f = 0; f < faceCount; f++) {
+ const Face *face = mesh->faceAt(f);
+ uint32_t edgeCount = face->edgeCount();
+ xaDebugAssert(edgeCount > 2);
+ triangleCount += edgeCount - 2;
+ }
+ return triangleCount;
+}
+
+Mesh *unifyVertices(const Mesh *inputMesh) {
+ Mesh *mesh = new Mesh;
+ // Only add the first colocal.
+ const uint32_t vertexCount = inputMesh->vertexCount();
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ const Vertex *vertex = inputMesh->vertexAt(v);
+ if (vertex->isFirstColocal()) {
+ mesh->addVertex(vertex->pos);
+ }
+ }
+ std::vector<uint32_t> indexArray;
+ // Add new faces pointing to first colocals.
+ uint32_t faceCount = inputMesh->faceCount();
+ for (uint32_t f = 0; f < faceCount; f++) {
+ const Face *face = inputMesh->faceAt(f);
+ indexArray.clear();
+ for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const Edge *edge = it.current();
+ const Vertex *vertex = edge->vertex->firstColocal();
+ indexArray.push_back(vertex->id);
+ }
+ mesh->addFace(indexArray);
+ }
+ mesh->linkBoundary();
+ return mesh;
+}
+
+static bool pointInTriangle(const Vector2 &p, const Vector2 &a, const Vector2 &b, const Vector2 &c) {
+ return triangleArea(a, b, p) >= 0.00001f &&
+ triangleArea(b, c, p) >= 0.00001f &&
+ triangleArea(c, a, p) >= 0.00001f;
+}
+
+// This is doing a simple ear-clipping algorithm that skips invalid triangles. Ideally, we should
+// also sort the ears by angle, start with the ones that have the smallest angle and proceed in order.
+Mesh *triangulate(const Mesh *inputMesh) {
+ Mesh *mesh = new Mesh;
+ // Add all vertices.
+ const uint32_t vertexCount = inputMesh->vertexCount();
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ const Vertex *vertex = inputMesh->vertexAt(v);
+ mesh->addVertex(vertex->pos);
+ }
+ std::vector<int> polygonVertices;
+ std::vector<float> polygonAngles;
+ std::vector<Vector2> polygonPoints;
+ const uint32_t faceCount = inputMesh->faceCount();
+ for (uint32_t f = 0; f < faceCount; f++) {
+ const Face *face = inputMesh->faceAt(f);
+ xaDebugAssert(face != NULL);
+ const uint32_t edgeCount = face->edgeCount();
+ xaDebugAssert(edgeCount >= 3);
+ polygonVertices.clear();
+ polygonVertices.reserve(edgeCount);
+ if (edgeCount == 3) {
+ // Simple case for triangles.
+ for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const Edge *edge = it.current();
+ const Vertex *vertex = edge->vertex;
+ polygonVertices.push_back(vertex->id);
+ }
+ int v0 = polygonVertices[0];
+ int v1 = polygonVertices[1];
+ int v2 = polygonVertices[2];
+ mesh->addFace(v0, v1, v2);
+ } else {
+ // Build 2D polygon projecting vertices onto normal plane.
+ // Faces are not necesarily planar, this is for example the case, when the face comes from filling a hole. In such cases
+ // it's much better to use the best fit plane.
+ const Vector3 fn = face->normal();
+ Basis basis;
+ basis.buildFrameForDirection(fn);
+ polygonPoints.clear();
+ polygonPoints.reserve(edgeCount);
+ polygonAngles.clear();
+ polygonAngles.reserve(edgeCount);
+ for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const Edge *edge = it.current();
+ const Vertex *vertex = edge->vertex;
+ polygonVertices.push_back(vertex->id);
+ Vector2 p;
+ p.x = dot(basis.tangent, vertex->pos);
+ p.y = dot(basis.bitangent, vertex->pos);
+ polygonPoints.push_back(p);
+ }
+ polygonAngles.resize(edgeCount);
+ while (polygonVertices.size() > 2) {
+ uint32_t size = polygonVertices.size();
+ // Update polygon angles. @@ Update only those that have changed.
+ float minAngle = 2 * PI;
+ uint32_t bestEar = 0; // Use first one if none of them is valid.
+ bool bestIsValid = false;
+ for (uint32_t i = 0; i < size; i++) {
+ uint32_t i0 = i;
+ uint32_t i1 = (i + 1) % size; // Use Sean's polygon interation trick.
+ uint32_t i2 = (i + 2) % size;
+ Vector2 p0 = polygonPoints[i0];
+ Vector2 p1 = polygonPoints[i1];
+ Vector2 p2 = polygonPoints[i2];
+
+ bool degenerate = distance(p0, p1) < NV_EPSILON || distance(p0, p2) < NV_EPSILON || distance(p1, p2) < NV_EPSILON;
+ if (degenerate) {
+ continue;
+ }
+
+ float d = clamp(dot(p0 - p1, p2 - p1) / (length(p0 - p1) * length(p2 - p1)), -1.0f, 1.0f);
+ float angle = acosf(d);
+ float area = triangleArea(p0, p1, p2);
+ if (area < 0.0f) angle = 2.0f * PI - angle;
+ polygonAngles[i1] = angle;
+ if (angle < minAngle || !bestIsValid) {
+ // Make sure this is a valid ear, if not, skip this point.
+ bool valid = true;
+ for (uint32_t j = 0; j < size; j++) {
+ if (j == i0 || j == i1 || j == i2) continue;
+ Vector2 p = polygonPoints[j];
+ if (pointInTriangle(p, p0, p1, p2)) {
+ valid = false;
+ break;
+ }
+ }
+ if (valid || !bestIsValid) {
+ minAngle = angle;
+ bestEar = i1;
+ bestIsValid = valid;
+ }
+ }
+ }
+ if (!bestIsValid)
+ break;
+
+ xaDebugAssert(minAngle <= 2 * PI);
+ // Clip best ear:
+ uint32_t i0 = (bestEar + size - 1) % size;
+ uint32_t i1 = (bestEar + 0) % size;
+ uint32_t i2 = (bestEar + 1) % size;
+ int v0 = polygonVertices[i0];
+ int v1 = polygonVertices[i1];
+ int v2 = polygonVertices[i2];
+ mesh->addFace(v0, v1, v2);
+ polygonVertices.erase(polygonVertices.begin() + i1);
+ polygonPoints.erase(polygonPoints.begin() + i1);
+ polygonAngles.erase(polygonAngles.begin() + i1);
+ }
+ }
+ }
+ mesh->linkBoundary();
+ return mesh;
+}
+
+} // namespace halfedge
+
+/// Mersenne twister random number generator.
+class MTRand {
+public:
+ enum time_e { Time };
+ enum { N = 624 }; // length of state vector
+ enum { M = 397 };
+
+ /// Constructor that uses the current time as the seed.
+ MTRand(time_e) {
+ seed((uint32_t)time(NULL));
+ }
+
+ /// Constructor that uses the given seed.
+ MTRand(uint32_t s = 0) {
+ seed(s);
+ }
+
+ /// Provide a new seed.
+ void seed(uint32_t s) {
+ initialize(s);
+ reload();
+ }
+
+ /// Get a random number between 0 - 65536.
+ uint32_t get() {
+ // Pull a 32-bit integer from the generator state
+ // Every other access function simply transforms the numbers extracted here
+ if (left == 0) {
+ reload();
+ }
+ left--;
+ uint32_t s1;
+ s1 = *next++;
+ s1 ^= (s1 >> 11);
+ s1 ^= (s1 << 7) & 0x9d2c5680U;
+ s1 ^= (s1 << 15) & 0xefc60000U;
+ return (s1 ^ (s1 >> 18));
+ };
+
+ /// Get a random number on [0, max] interval.
+ uint32_t getRange(uint32_t max) {
+ if (max == 0) return 0;
+ if (max == NV_UINT32_MAX) return get();
+ const uint32_t np2 = nextPowerOfTwo(max + 1); // @@ This fails if max == NV_UINT32_MAX
+ const uint32_t mask = np2 - 1;
+ uint32_t n;
+ do {
+ n = get() & mask;
+ } while (n > max);
+ return n;
+ }
+
+private:
+ void initialize(uint32_t seed) {
+ // Initialize generator state with seed
+ // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
+ // In previous versions, most significant bits (MSBs) of the seed affect
+ // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto.
+ uint32_t *s = state;
+ uint32_t *r = state;
+ int i = 1;
+ *s++ = seed & 0xffffffffUL;
+ for (; i < N; ++i) {
+ *s++ = (1812433253UL * (*r ^ (*r >> 30)) + i) & 0xffffffffUL;
+ r++;
+ }
+ }
+
+ void reload() {
+ // Generate N new values in state
+ // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com)
+ uint32_t *p = state;
+ int i;
+ for (i = N - M; i--; ++p)
+ *p = twist(p[M], p[0], p[1]);
+ for (i = M; --i; ++p)
+ *p = twist(p[M - N], p[0], p[1]);
+ *p = twist(p[M - N], p[0], state[0]);
+ left = N, next = state;
+ }
+
+ uint32_t hiBit(uint32_t u) const {
+ return u & 0x80000000U;
+ }
+ uint32_t loBit(uint32_t u) const {
+ return u & 0x00000001U;
+ }
+ uint32_t loBits(uint32_t u) const {
+ return u & 0x7fffffffU;
+ }
+ uint32_t mixBits(uint32_t u, uint32_t v) const {
+ return hiBit(u) | loBits(v);
+ }
+ uint32_t twist(uint32_t m, uint32_t s0, uint32_t s1) const {
+ return m ^ (mixBits(s0, s1) >> 1) ^ ((~loBit(s1) + 1) & 0x9908b0dfU);
+ }
+
+ uint32_t state[N]; // internal state
+ uint32_t *next; // next value to get from state
+ int left; // number of values left before reload needed
+};
+
+namespace morton {
+// Code from ryg:
+// http://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
+
+// Inverse of part1By1 - "delete" all odd-indexed bits
+uint32_t compact1By1(uint32_t x) {
+ x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
+ x = (x ^ (x >> 1)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10
+ x = (x ^ (x >> 2)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
+ x = (x ^ (x >> 4)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
+ x = (x ^ (x >> 8)) & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210
+ return x;
+}
+
+// Inverse of part1By2 - "delete" all bits not at positions divisible by 3
+uint32_t compact1By2(uint32_t x) {
+ x &= 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0
+ x = (x ^ (x >> 2)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10
+ x = (x ^ (x >> 4)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210
+ x = (x ^ (x >> 8)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210
+ x = (x ^ (x >> 16)) & 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210
+ return x;
+}
+
+uint32_t decodeMorton2X(uint32_t code) {
+ return compact1By1(code >> 0);
+}
+
+uint32_t decodeMorton2Y(uint32_t code) {
+ return compact1By1(code >> 1);
+}
+
+uint32_t decodeMorton3X(uint32_t code) {
+ return compact1By2(code >> 0);
+}
+
+uint32_t decodeMorton3Y(uint32_t code) {
+ return compact1By2(code >> 1);
+}
+
+uint32_t decodeMorton3Z(uint32_t code) {
+ return compact1By2(code >> 2);
+}
+} // namespace morton
+
+// A simple, dynamic proximity grid based on Jon's code.
+// Instead of storing pointers here I store indices.
+struct ProximityGrid {
+ void init(const Box &box, uint32_t count) {
+ cellArray.clear();
+ // Determine grid size.
+ float cellWidth;
+ Vector3 diagonal = box.extents() * 2.f;
+ float volume = box.volume();
+ if (equal(volume, 0)) {
+ // Degenerate box, treat like a quad.
+ Vector2 quad;
+ if (diagonal.x < diagonal.y && diagonal.x < diagonal.z) {
+ quad.x = diagonal.y;
+ quad.y = diagonal.z;
+ } else if (diagonal.y < diagonal.x && diagonal.y < diagonal.z) {
+ quad.x = diagonal.x;
+ quad.y = diagonal.z;
+ } else {
+ quad.x = diagonal.x;
+ quad.y = diagonal.y;
+ }
+ float cellArea = quad.x * quad.y / count;
+ cellWidth = sqrtf(cellArea); // pow(cellArea, 1.0f / 2.0f);
+ } else {
+ // Ideally we want one cell per point.
+ float cellVolume = volume / count;
+ cellWidth = powf(cellVolume, 1.0f / 3.0f);
+ }
+ xaDebugAssert(cellWidth != 0);
+ sx = std::max(1, ftoi_ceil(diagonal.x / cellWidth));
+ sy = std::max(1, ftoi_ceil(diagonal.y / cellWidth));
+ sz = std::max(1, ftoi_ceil(diagonal.z / cellWidth));
+ invCellSize.x = float(sx) / diagonal.x;
+ invCellSize.y = float(sy) / diagonal.y;
+ invCellSize.z = float(sz) / diagonal.z;
+ cellArray.resize(sx * sy * sz);
+ corner = box.minCorner; // @@ Align grid better?
+ }
+
+ int index_x(float x) const {
+ return clamp(ftoi_floor((x - corner.x) * invCellSize.x), 0, sx - 1);
+ }
+
+ int index_y(float y) const {
+ return clamp(ftoi_floor((y - corner.y) * invCellSize.y), 0, sy - 1);
+ }
+
+ int index_z(float z) const {
+ return clamp(ftoi_floor((z - corner.z) * invCellSize.z), 0, sz - 1);
+ }
+
+ int index(int x, int y, int z) const {
+ xaDebugAssert(x >= 0 && x < sx);
+ xaDebugAssert(y >= 0 && y < sy);
+ xaDebugAssert(z >= 0 && z < sz);
+ int idx = (z * sy + y) * sx + x;
+ xaDebugAssert(idx >= 0 && uint32_t(idx) < cellArray.size());
+ return idx;
+ }
+
+ uint32_t mortonCount() const {
+ uint64_t s = uint64_t(max3(sx, sy, sz));
+ s = nextPowerOfTwo(s);
+ if (s > 1024) {
+ return uint32_t(s * s * min3(sx, sy, sz));
+ }
+ return uint32_t(s * s * s);
+ }
+
+ int mortonIndex(uint32_t code) const {
+ uint32_t x, y, z;
+ uint32_t s = uint32_t(max3(sx, sy, sz));
+ if (s > 1024) {
+ // Use layered two-dimensional morton order.
+ s = nextPowerOfTwo(s);
+ uint32_t layer = code / (s * s);
+ code = code % (s * s);
+ uint32_t layer_count = uint32_t(min3(sx, sy, sz));
+ if (sx == (int)layer_count) {
+ x = layer;
+ y = morton::decodeMorton2X(code);
+ z = morton::decodeMorton2Y(code);
+ } else if (sy == (int)layer_count) {
+ x = morton::decodeMorton2Y(code);
+ y = layer;
+ z = morton::decodeMorton2X(code);
+ } else { /*if (sz == layer_count)*/
+ x = morton::decodeMorton2X(code);
+ y = morton::decodeMorton2Y(code);
+ z = layer;
+ }
+ } else {
+ x = morton::decodeMorton3X(code);
+ y = morton::decodeMorton3Y(code);
+ z = morton::decodeMorton3Z(code);
+ }
+ if (x >= uint32_t(sx) || y >= uint32_t(sy) || z >= uint32_t(sz)) {
+ return -1;
+ }
+ return index(x, y, z);
+ }
+
+ void add(const Vector3 &pos, uint32_t key) {
+ int x = index_x(pos.x);
+ int y = index_y(pos.y);
+ int z = index_z(pos.z);
+ uint32_t idx = index(x, y, z);
+ cellArray[idx].indexArray.push_back(key);
+ }
+
+ // Gather all points inside the given sphere.
+ // Radius is assumed to be small, so we don't bother culling the cells.
+ void gather(const Vector3 &position, float radius, std::vector<uint32_t> &indexArray) {
+ int x0 = index_x(position.x - radius);
+ int x1 = index_x(position.x + radius);
+ int y0 = index_y(position.y - radius);
+ int y1 = index_y(position.y + radius);
+ int z0 = index_z(position.z - radius);
+ int z1 = index_z(position.z + radius);
+ for (int z = z0; z <= z1; z++) {
+ for (int y = y0; y <= y1; y++) {
+ for (int x = x0; x <= x1; x++) {
+ int idx = index(x, y, z);
+ indexArray.insert(indexArray.begin(), cellArray[idx].indexArray.begin(), cellArray[idx].indexArray.end());
+ }
+ }
+ }
+ }
+
+ struct Cell {
+ std::vector<uint32_t> indexArray;
+ };
+
+ std::vector<Cell> cellArray;
+
+ Vector3 corner;
+ Vector3 invCellSize;
+ int sx, sy, sz;
+};
+
+// Based on Pierre Terdiman's and Michael Herf's source code.
+// http://www.codercorner.com/RadixSortRevisited.htm
+// http://www.stereopsis.com/radix.html
+class RadixSort {
+public:
+ RadixSort() :
+ m_size(0),
+ m_ranks(NULL),
+ m_ranks2(NULL),
+ m_validRanks(false) {}
+ ~RadixSort() {
+ // Release everything
+ free(m_ranks2);
+ free(m_ranks);
+ }
+
+ RadixSort &sort(const float *input, uint32_t count) {
+ if (input == NULL || count == 0) return *this;
+ // Resize lists if needed
+ if (count != m_size) {
+ if (count > m_size) {
+ m_ranks2 = (uint32_t *)realloc(m_ranks2, sizeof(uint32_t) * count);
+ m_ranks = (uint32_t *)realloc(m_ranks, sizeof(uint32_t) * count);
+ }
+ m_size = count;
+ m_validRanks = false;
+ }
+ if (count < 32) {
+ insertionSort(input, count);
+ } else {
+ // @@ Avoid touching the input multiple times.
+ for (uint32_t i = 0; i < count; i++) {
+ FloatFlip((uint32_t &)input[i]);
+ }
+ radixSort<uint32_t>((const uint32_t *)input, count);
+ for (uint32_t i = 0; i < count; i++) {
+ IFloatFlip((uint32_t &)input[i]);
+ }
+ }
+ return *this;
+ }
+
+ RadixSort &sort(const std::vector<float> &input) {
+ return sort(input.data(), input.size());
+ }
+
+ // Access to results. m_ranks is a list of indices in sorted order, i.e. in the order you may further process your data
+ const uint32_t *ranks() const {
+ xaDebugAssert(m_validRanks);
+ return m_ranks;
+ }
+ uint32_t *ranks() {
+ xaDebugAssert(m_validRanks);
+ return m_ranks;
+ }
+
+private:
+ uint32_t m_size;
+ uint32_t *m_ranks;
+ uint32_t *m_ranks2;
+ bool m_validRanks;
+
+ void FloatFlip(uint32_t &f) {
+ int32_t mask = (int32_t(f) >> 31) | 0x80000000; // Warren Hunt, Manchor Ko.
+ f ^= mask;
+ }
+
+ void IFloatFlip(uint32_t &f) {
+ uint32_t mask = ((f >> 31) - 1) | 0x80000000; // Michael Herf.
+ f ^= mask;
+ }
+
+ template <typename T>
+ void createHistograms(const T *buffer, uint32_t count, uint32_t *histogram) {
+ const uint32_t bucketCount = sizeof(T); // (8 * sizeof(T)) / log2(radix)
+ // Init bucket pointers.
+ uint32_t *h[bucketCount];
+ for (uint32_t i = 0; i < bucketCount; i++) {
+ h[i] = histogram + 256 * i;
+ }
+ // Clear histograms.
+ memset(histogram, 0, 256 * bucketCount * sizeof(uint32_t));
+ // @@ Add support for signed integers.
+ // Build histograms.
+ const uint8_t *p = (const uint8_t *)buffer; // @@ Does this break aliasing rules?
+ const uint8_t *pe = p + count * sizeof(T);
+ while (p != pe) {
+ h[0][*p++]++, h[1][*p++]++, h[2][*p++]++, h[3][*p++]++;
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4127)
+#endif
+ if (bucketCount == 8) h[4][*p++]++, h[5][*p++]++, h[6][*p++]++, h[7][*p++]++;
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ }
+ }
+
+ template <typename T>
+ void insertionSort(const T *input, uint32_t count) {
+ if (!m_validRanks) {
+ m_ranks[0] = 0;
+ for (uint32_t i = 1; i != count; ++i) {
+ int rank = m_ranks[i] = i;
+ uint32_t j = i;
+ while (j != 0 && input[rank] < input[m_ranks[j - 1]]) {
+ m_ranks[j] = m_ranks[j - 1];
+ --j;
+ }
+ if (i != j) {
+ m_ranks[j] = rank;
+ }
+ }
+ m_validRanks = true;
+ } else {
+ for (uint32_t i = 1; i != count; ++i) {
+ int rank = m_ranks[i];
+ uint32_t j = i;
+ while (j != 0 && input[rank] < input[m_ranks[j - 1]]) {
+ m_ranks[j] = m_ranks[j - 1];
+ --j;
+ }
+ if (i != j) {
+ m_ranks[j] = rank;
+ }
+ }
+ }
+ }
+
+ template <typename T>
+ void radixSort(const T *input, uint32_t count) {
+ const uint32_t P = sizeof(T); // pass count
+ // Allocate histograms & offsets on the stack
+ uint32_t histogram[256 * P];
+ uint32_t *link[256];
+ createHistograms(input, count, histogram);
+ // Radix sort, j is the pass number (0=LSB, P=MSB)
+ for (uint32_t j = 0; j < P; j++) {
+ // Pointer to this bucket.
+ const uint32_t *h = &histogram[j * 256];
+ const uint8_t *inputBytes = (const uint8_t *)input; // @@ Is this aliasing legal?
+ inputBytes += j;
+ if (h[inputBytes[0]] == count) {
+ // Skip this pass, all values are the same.
+ continue;
+ }
+ // Create offsets
+ link[0] = m_ranks2;
+ for (uint32_t i = 1; i < 256; i++)
+ link[i] = link[i - 1] + h[i - 1];
+ // Perform Radix Sort
+ if (!m_validRanks) {
+ for (uint32_t i = 0; i < count; i++) {
+ *link[inputBytes[i * P]]++ = i;
+ }
+ m_validRanks = true;
+ } else {
+ for (uint32_t i = 0; i < count; i++) {
+ const uint32_t idx = m_ranks[i];
+ *link[inputBytes[idx * P]]++ = idx;
+ }
+ }
+ // Swap pointers for next pass. Valid indices - the most recent ones - are in m_ranks after the swap.
+ std::swap(m_ranks, m_ranks2);
+ }
+ // All values were equal, generate linear ranks.
+ if (!m_validRanks) {
+ for (uint32_t i = 0; i < count; i++) {
+ m_ranks[i] = i;
+ }
+ m_validRanks = true;
+ }
+ }
+};
+
+namespace raster {
+class ClippedTriangle {
+public:
+ ClippedTriangle(Vector2::Arg a, Vector2::Arg b, Vector2::Arg c) {
+ m_numVertices = 3;
+ m_activeVertexBuffer = 0;
+ m_verticesA[0] = a;
+ m_verticesA[1] = b;
+ m_verticesA[2] = c;
+ m_vertexBuffers[0] = m_verticesA;
+ m_vertexBuffers[1] = m_verticesB;
+ }
+
+ uint32_t vertexCount() {
+ return m_numVertices;
+ }
+
+ const Vector2 *vertices() {
+ return m_vertexBuffers[m_activeVertexBuffer];
+ }
+
+ void clipHorizontalPlane(float offset, float clipdirection) {
+ Vector2 *v = m_vertexBuffers[m_activeVertexBuffer];
+ m_activeVertexBuffer ^= 1;
+ Vector2 *v2 = m_vertexBuffers[m_activeVertexBuffer];
+ v[m_numVertices] = v[0];
+ float dy2, dy1 = offset - v[0].y;
+ int dy2in, dy1in = clipdirection * dy1 >= 0;
+ uint32_t p = 0;
+ for (uint32_t k = 0; k < m_numVertices; k++) {
+ dy2 = offset - v[k + 1].y;
+ dy2in = clipdirection * dy2 >= 0;
+ if (dy1in) v2[p++] = v[k];
+ if (dy1in + dy2in == 1) { // not both in/out
+ float dx = v[k + 1].x - v[k].x;
+ float dy = v[k + 1].y - v[k].y;
+ v2[p++] = Vector2(v[k].x + dy1 * (dx / dy), offset);
+ }
+ dy1 = dy2;
+ dy1in = dy2in;
+ }
+ m_numVertices = p;
+ //for (uint32_t k=0; k<m_numVertices; k++) printf("(%f, %f)\n", v2[k].x, v2[k].y); printf("\n");
+ }
+
+ void clipVerticalPlane(float offset, float clipdirection) {
+ Vector2 *v = m_vertexBuffers[m_activeVertexBuffer];
+ m_activeVertexBuffer ^= 1;
+ Vector2 *v2 = m_vertexBuffers[m_activeVertexBuffer];
+ v[m_numVertices] = v[0];
+ float dx2, dx1 = offset - v[0].x;
+ int dx2in, dx1in = clipdirection * dx1 >= 0;
+ uint32_t p = 0;
+ for (uint32_t k = 0; k < m_numVertices; k++) {
+ dx2 = offset - v[k + 1].x;
+ dx2in = clipdirection * dx2 >= 0;
+ if (dx1in) v2[p++] = v[k];
+ if (dx1in + dx2in == 1) { // not both in/out
+ float dx = v[k + 1].x - v[k].x;
+ float dy = v[k + 1].y - v[k].y;
+ v2[p++] = Vector2(offset, v[k].y + dx1 * (dy / dx));
+ }
+ dx1 = dx2;
+ dx1in = dx2in;
+ }
+ m_numVertices = p;
+ }
+
+ void computeAreaCentroid() {
+ Vector2 *v = m_vertexBuffers[m_activeVertexBuffer];
+ v[m_numVertices] = v[0];
+ m_area = 0;
+ float centroidx = 0, centroidy = 0;
+ for (uint32_t k = 0; k < m_numVertices; k++) {
+ // http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
+ float f = v[k].x * v[k + 1].y - v[k + 1].x * v[k].y;
+ m_area += f;
+ centroidx += f * (v[k].x + v[k + 1].x);
+ centroidy += f * (v[k].y + v[k + 1].y);
+ }
+ m_area = 0.5f * fabsf(m_area);
+ if (m_area == 0) {
+ m_centroid = Vector2(0.0f);
+ } else {
+ m_centroid = Vector2(centroidx / (6 * m_area), centroidy / (6 * m_area));
+ }
+ }
+
+ void clipAABox(float x0, float y0, float x1, float y1) {
+ clipVerticalPlane(x0, -1);
+ clipHorizontalPlane(y0, -1);
+ clipVerticalPlane(x1, 1);
+ clipHorizontalPlane(y1, 1);
+ computeAreaCentroid();
+ }
+
+ Vector2 centroid() {
+ return m_centroid;
+ }
+
+ float area() {
+ return m_area;
+ }
+
+private:
+ Vector2 m_verticesA[7 + 1];
+ Vector2 m_verticesB[7 + 1];
+ Vector2 *m_vertexBuffers[2];
+ uint32_t m_numVertices;
+ uint32_t m_activeVertexBuffer;
+ float m_area;
+ Vector2 m_centroid;
+};
+
+/// A callback to sample the environment. Return false to terminate rasterization.
+typedef bool (*SamplingCallback)(void *param, int x, int y, Vector3::Arg bar, Vector3::Arg dx, Vector3::Arg dy, float coverage);
+
+/// A triangle for rasterization.
+struct Triangle {
+ Triangle(Vector2::Arg v0, Vector2::Arg v1, Vector2::Arg v2, Vector3::Arg t0, Vector3::Arg t1, Vector3::Arg t2) {
+ // Init vertices.
+ this->v1 = v0;
+ this->v2 = v2;
+ this->v3 = v1;
+ // Set barycentric coordinates.
+ this->t1 = t0;
+ this->t2 = t2;
+ this->t3 = t1;
+ // make sure every triangle is front facing.
+ flipBackface();
+ // Compute deltas.
+ valid = computeDeltas();
+ computeUnitInwardNormals();
+ }
+
+ /// Compute texture space deltas.
+ /// This method takes two edge vectors that form a basis, determines the
+ /// coordinates of the canonic vectors in that basis, and computes the
+ /// texture gradient that corresponds to those vectors.
+ bool computeDeltas() {
+ Vector2 e0 = v3 - v1;
+ Vector2 e1 = v2 - v1;
+ Vector3 de0 = t3 - t1;
+ Vector3 de1 = t2 - t1;
+ float denom = 1.0f / (e0.y * e1.x - e1.y * e0.x);
+ if (!std::isfinite(denom)) {
+ return false;
+ }
+ float lambda1 = -e1.y * denom;
+ float lambda2 = e0.y * denom;
+ float lambda3 = e1.x * denom;
+ float lambda4 = -e0.x * denom;
+ dx = de0 * lambda1 + de1 * lambda2;
+ dy = de0 * lambda3 + de1 * lambda4;
+ return true;
+ }
+
+ bool draw(const Vector2 &extents, bool enableScissors, SamplingCallback cb, void *param) {
+ // 28.4 fixed-point coordinates
+ const int Y1 = ftoi_round(16.0f * v1.y);
+ const int Y2 = ftoi_round(16.0f * v2.y);
+ const int Y3 = ftoi_round(16.0f * v3.y);
+ const int X1 = ftoi_round(16.0f * v1.x);
+ const int X2 = ftoi_round(16.0f * v2.x);
+ const int X3 = ftoi_round(16.0f * v3.x);
+ // Deltas
+ const int DX12 = X1 - X2;
+ const int DX23 = X2 - X3;
+ const int DX31 = X3 - X1;
+ const int DY12 = Y1 - Y2;
+ const int DY23 = Y2 - Y3;
+ const int DY31 = Y3 - Y1;
+ // Fixed-point deltas
+ const int FDX12 = DX12 << 4;
+ const int FDX23 = DX23 << 4;
+ const int FDX31 = DX31 << 4;
+ const int FDY12 = DY12 << 4;
+ const int FDY23 = DY23 << 4;
+ const int FDY31 = DY31 << 4;
+ int minx, miny, maxx, maxy;
+ if (enableScissors) {
+ int frustumX0 = 0 << 4;
+ int frustumY0 = 0 << 4;
+ int frustumX1 = (int)extents.x << 4;
+ int frustumY1 = (int)extents.y << 4;
+ // Bounding rectangle
+ minx = (std::max(min3(X1, X2, X3), frustumX0) + 0xF) >> 4;
+ miny = (std::max(min3(Y1, Y2, Y3), frustumY0) + 0xF) >> 4;
+ maxx = (std::min(max3(X1, X2, X3), frustumX1) + 0xF) >> 4;
+ maxy = (std::min(max3(Y1, Y2, Y3), frustumY1) + 0xF) >> 4;
+ } else {
+ // Bounding rectangle
+ minx = (min3(X1, X2, X3) + 0xF) >> 4;
+ miny = (min3(Y1, Y2, Y3) + 0xF) >> 4;
+ maxx = (max3(X1, X2, X3) + 0xF) >> 4;
+ maxy = (max3(Y1, Y2, Y3) + 0xF) >> 4;
+ }
+ // Block size, standard 8x8 (must be power of two)
+ const int q = 8;
+ // @@ This won't work when minx,miny are negative. This code path is not used. Leaving as is for now.
+ xaAssert(minx >= 0);
+ xaAssert(miny >= 0);
+ // Start in corner of 8x8 block
+ minx &= ~(q - 1);
+ miny &= ~(q - 1);
+ // Half-edge constants
+ int C1 = DY12 * X1 - DX12 * Y1;
+ int C2 = DY23 * X2 - DX23 * Y2;
+ int C3 = DY31 * X3 - DX31 * Y3;
+ // Correct for fill convention
+ if (DY12 < 0 || (DY12 == 0 && DX12 > 0)) C1++;
+ if (DY23 < 0 || (DY23 == 0 && DX23 > 0)) C2++;
+ if (DY31 < 0 || (DY31 == 0 && DX31 > 0)) C3++;
+ // Loop through blocks
+ for (int y = miny; y < maxy; y += q) {
+ for (int x = minx; x < maxx; x += q) {
+ // Corners of block
+ int x0 = x << 4;
+ int x1 = (x + q - 1) << 4;
+ int y0 = y << 4;
+ int y1 = (y + q - 1) << 4;
+ // Evaluate half-space functions
+ bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0;
+ bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0;
+ bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0;
+ bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0;
+ int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3);
+ bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0;
+ bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0;
+ bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0;
+ bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0;
+ int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3);
+ bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0;
+ bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0;
+ bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0;
+ bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0;
+ int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3);
+ // Skip block when outside an edge
+ if (a == 0x0 || b == 0x0 || c == 0x0) continue;
+ // Accept whole block when totally covered
+ if (a == 0xF && b == 0xF && c == 0xF) {
+ Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x);
+ for (int iy = y; iy < y + q; iy++) {
+ Vector3 tex = texRow;
+ for (int ix = x; ix < x + q; ix++) {
+ //Vector3 tex = t1 + dx * (ix - v1.x) + dy * (iy - v1.y);
+ if (!cb(param, ix, iy, tex, dx, dy, 1.0)) {
+ // early out.
+ return false;
+ }
+ tex += dx;
+ }
+ texRow += dy;
+ }
+ } else { // Partially covered block
+ int CY1 = C1 + DX12 * y0 - DY12 * x0;
+ int CY2 = C2 + DX23 * y0 - DY23 * x0;
+ int CY3 = C3 + DX31 * y0 - DY31 * x0;
+ Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x);
+ for (int iy = y; iy < y + q; iy++) {
+ int CX1 = CY1;
+ int CX2 = CY2;
+ int CX3 = CY3;
+ Vector3 tex = texRow;
+ for (int ix = x; ix < x + q; ix++) {
+ if (CX1 > 0 && CX2 > 0 && CX3 > 0) {
+ if (!cb(param, ix, iy, tex, dx, dy, 1.0)) {
+ // early out.
+ return false;
+ }
+ }
+ CX1 -= FDY12;
+ CX2 -= FDY23;
+ CX3 -= FDY31;
+ tex += dx;
+ }
+ CY1 += FDX12;
+ CY2 += FDX23;
+ CY3 += FDX31;
+ texRow += dy;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ // extents has to be multiple of BK_SIZE!!
+ bool drawAA(const Vector2 &extents, bool enableScissors, SamplingCallback cb, void *param) {
+ const float PX_INSIDE = 1.0f / sqrt(2.0f);
+ const float PX_OUTSIDE = -1.0f / sqrt(2.0f);
+ const float BK_SIZE = 8;
+ const float BK_INSIDE = sqrt(BK_SIZE * BK_SIZE / 2.0f);
+ const float BK_OUTSIDE = -sqrt(BK_SIZE * BK_SIZE / 2.0f);
+
+ float minx, miny, maxx, maxy;
+ if (enableScissors) {
+ // Bounding rectangle
+ minx = floorf(std::max(min3(v1.x, v2.x, v3.x), 0.0f));
+ miny = floorf(std::max(min3(v1.y, v2.y, v3.y), 0.0f));
+ maxx = ceilf(std::min(max3(v1.x, v2.x, v3.x), extents.x - 1.0f));
+ maxy = ceilf(std::min(max3(v1.y, v2.y, v3.y), extents.y - 1.0f));
+ } else {
+ // Bounding rectangle
+ minx = floorf(min3(v1.x, v2.x, v3.x));
+ miny = floorf(min3(v1.y, v2.y, v3.y));
+ maxx = ceilf(max3(v1.x, v2.x, v3.x));
+ maxy = ceilf(max3(v1.y, v2.y, v3.y));
+ }
+ // There's no reason to align the blocks to the viewport, instead we align them to the origin of the triangle bounds.
+ minx = floorf(minx);
+ miny = floorf(miny);
+ //minx = (float)(((int)minx) & (~((int)BK_SIZE - 1))); // align to blocksize (we don't need to worry about blocks partially out of viewport)
+ //miny = (float)(((int)miny) & (~((int)BK_SIZE - 1)));
+ minx += 0.5;
+ miny += 0.5; // sampling at texel centers!
+ maxx += 0.5;
+ maxy += 0.5;
+ // Half-edge constants
+ float C1 = n1.x * (-v1.x) + n1.y * (-v1.y);
+ float C2 = n2.x * (-v2.x) + n2.y * (-v2.y);
+ float C3 = n3.x * (-v3.x) + n3.y * (-v3.y);
+ // Loop through blocks
+ for (float y0 = miny; y0 <= maxy; y0 += BK_SIZE) {
+ for (float x0 = minx; x0 <= maxx; x0 += BK_SIZE) {
+ // Corners of block
+ float xc = (x0 + (BK_SIZE - 1) / 2.0f);
+ float yc = (y0 + (BK_SIZE - 1) / 2.0f);
+ // Evaluate half-space functions
+ float aC = C1 + n1.x * xc + n1.y * yc;
+ float bC = C2 + n2.x * xc + n2.y * yc;
+ float cC = C3 + n3.x * xc + n3.y * yc;
+ // Skip block when outside an edge
+ if ((aC <= BK_OUTSIDE) || (bC <= BK_OUTSIDE) || (cC <= BK_OUTSIDE)) continue;
+ // Accept whole block when totally covered
+ if ((aC >= BK_INSIDE) && (bC >= BK_INSIDE) && (cC >= BK_INSIDE)) {
+ Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x);
+ for (float y = y0; y < y0 + BK_SIZE; y++) {
+ Vector3 tex = texRow;
+ for (float x = x0; x < x0 + BK_SIZE; x++) {
+ if (!cb(param, (int)x, (int)y, tex, dx, dy, 1.0f)) {
+ return false;
+ }
+ tex += dx;
+ }
+ texRow += dy;
+ }
+ } else { // Partially covered block
+ float CY1 = C1 + n1.x * x0 + n1.y * y0;
+ float CY2 = C2 + n2.x * x0 + n2.y * y0;
+ float CY3 = C3 + n3.x * x0 + n3.y * y0;
+ Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x);
+ for (float y = y0; y < y0 + BK_SIZE; y++) { // @@ This is not clipping to scissor rectangle correctly.
+ float CX1 = CY1;
+ float CX2 = CY2;
+ float CX3 = CY3;
+ Vector3 tex = texRow;
+ for (float x = x0; x < x0 + BK_SIZE; x++) { // @@ This is not clipping to scissor rectangle correctly.
+ if (CX1 >= PX_INSIDE && CX2 >= PX_INSIDE && CX3 >= PX_INSIDE) {
+ // pixel completely covered
+ Vector3 tex2 = t1 + dx * (x - v1.x) + dy * (y - v1.y);
+ if (!cb(param, (int)x, (int)y, tex2, dx, dy, 1.0f)) {
+ return false;
+ }
+ } else if ((CX1 >= PX_OUTSIDE) && (CX2 >= PX_OUTSIDE) && (CX3 >= PX_OUTSIDE)) {
+ // triangle partially covers pixel. do clipping.
+ ClippedTriangle ct(v1 - Vector2(x, y), v2 - Vector2(x, y), v3 - Vector2(x, y));
+ ct.clipAABox(-0.5, -0.5, 0.5, 0.5);
+ Vector2 centroid = ct.centroid();
+ float area = ct.area();
+ if (area > 0.0f) {
+ Vector3 texCent = tex - dx * centroid.x - dy * centroid.y;
+ //xaAssert(texCent.x >= -0.1f && texCent.x <= 1.1f); // @@ Centroid is not very exact...
+ //xaAssert(texCent.y >= -0.1f && texCent.y <= 1.1f);
+ //xaAssert(texCent.z >= -0.1f && texCent.z <= 1.1f);
+ //Vector3 texCent2 = t1 + dx * (x - v1.x) + dy * (y - v1.y);
+ if (!cb(param, (int)x, (int)y, texCent, dx, dy, area)) {
+ return false;
+ }
+ }
+ }
+ CX1 += n1.x;
+ CX2 += n2.x;
+ CX3 += n3.x;
+ tex += dx;
+ }
+ CY1 += n1.y;
+ CY2 += n2.y;
+ CY3 += n3.y;
+ texRow += dy;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ void flipBackface() {
+ // check if triangle is backfacing, if so, swap two vertices
+ if (((v3.x - v1.x) * (v2.y - v1.y) - (v3.y - v1.y) * (v2.x - v1.x)) < 0) {
+ Vector2 hv = v1;
+ v1 = v2;
+ v2 = hv; // swap pos
+ Vector3 ht = t1;
+ t1 = t2;
+ t2 = ht; // swap tex
+ }
+ }
+
+ // compute unit inward normals for each edge.
+ void computeUnitInwardNormals() {
+ n1 = v1 - v2;
+ n1 = Vector2(-n1.y, n1.x);
+ n1 = n1 * (1.0f / sqrtf(n1.x * n1.x + n1.y * n1.y));
+ n2 = v2 - v3;
+ n2 = Vector2(-n2.y, n2.x);
+ n2 = n2 * (1.0f / sqrtf(n2.x * n2.x + n2.y * n2.y));
+ n3 = v3 - v1;
+ n3 = Vector2(-n3.y, n3.x);
+ n3 = n3 * (1.0f / sqrtf(n3.x * n3.x + n3.y * n3.y));
+ }
+
+ // Vertices.
+ Vector2 v1, v2, v3;
+ Vector2 n1, n2, n3; // unit inward normals
+ Vector3 t1, t2, t3;
+
+ // Deltas.
+ Vector3 dx, dy;
+
+ float sign;
+ bool valid;
+};
+
+enum Mode {
+ Mode_Nearest,
+ Mode_Antialiased
+};
+
+// Process the given triangle. Returns false if rasterization was interrupted by the callback.
+static bool drawTriangle(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[3], SamplingCallback cb, void *param) {
+ Triangle tri(v[0], v[1], v[2], Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1));
+ // @@ It would be nice to have a conservative drawing mode that enlarges the triangle extents by one texel and is able to handle degenerate triangles.
+ // @@ Maybe the simplest thing to do would be raster triangle edges.
+ if (tri.valid) {
+ if (mode == Mode_Antialiased) {
+ return tri.drawAA(extents, enableScissors, cb, param);
+ }
+ if (mode == Mode_Nearest) {
+ return tri.draw(extents, enableScissors, cb, param);
+ }
+ }
+ return true;
+}
+
+// Process the given quad. Returns false if rasterization was interrupted by the callback.
+static bool drawQuad(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[4], SamplingCallback cb, void *param) {
+ bool sign0 = triangleArea2(v[0], v[1], v[2]) > 0.0f;
+ bool sign1 = triangleArea2(v[0], v[2], v[3]) > 0.0f;
+ // Divide the quad into two non overlapping triangles.
+ if (sign0 == sign1) {
+ Triangle tri0(v[0], v[1], v[2], Vector3(0, 0, 0), Vector3(1, 0, 0), Vector3(1, 1, 0));
+ Triangle tri1(v[0], v[2], v[3], Vector3(0, 0, 0), Vector3(1, 1, 0), Vector3(0, 1, 0));
+ if (tri0.valid && tri1.valid) {
+ if (mode == Mode_Antialiased) {
+ return tri0.drawAA(extents, enableScissors, cb, param) && tri1.drawAA(extents, enableScissors, cb, param);
+ } else {
+ return tri0.draw(extents, enableScissors, cb, param) && tri1.draw(extents, enableScissors, cb, param);
+ }
+ }
+ } else {
+ Triangle tri0(v[0], v[1], v[3], Vector3(0, 0, 0), Vector3(1, 0, 0), Vector3(0, 1, 0));
+ Triangle tri1(v[1], v[2], v[3], Vector3(1, 0, 0), Vector3(1, 1, 0), Vector3(0, 1, 0));
+ if (tri0.valid && tri1.valid) {
+ if (mode == Mode_Antialiased) {
+ return tri0.drawAA(extents, enableScissors, cb, param) && tri1.drawAA(extents, enableScissors, cb, param);
+ } else {
+ return tri0.draw(extents, enableScissors, cb, param) && tri1.draw(extents, enableScissors, cb, param);
+ }
+ }
+ }
+ return true;
+}
+} // namespace raster
+
+// Full and sparse vector and matrix classes. BLAS subset.
+// Pseudo-BLAS interface.
+namespace sparse {
+enum Transpose {
+ NoTransposed = 0,
+ Transposed = 1
+};
+
+/**
+* Sparse matrix class. The matrix is assumed to be sparse and to have
+* very few non-zero elements, for this reason it's stored in indexed
+* format. To multiply column vectors efficiently, the matrix stores
+* the elements in indexed-column order, there is a list of indexed
+* elements for each row of the matrix. As with the FullVector the
+* dimension of the matrix is constant.
+**/
+class Matrix {
+public:
+ // An element of the sparse array.
+ struct Coefficient {
+ uint32_t x; // column
+ float v; // value
+ };
+
+ Matrix(uint32_t d) :
+ m_width(d) { m_array.resize(d); }
+ Matrix(uint32_t w, uint32_t h) :
+ m_width(w) { m_array.resize(h); }
+ Matrix(const Matrix &m) :
+ m_width(m.m_width) { m_array = m.m_array; }
+
+ const Matrix &operator=(const Matrix &m) {
+ xaAssert(width() == m.width());
+ xaAssert(height() == m.height());
+ m_array = m.m_array;
+ return *this;
+ }
+
+ uint32_t width() const { return m_width; }
+ uint32_t height() const { return m_array.size(); }
+ bool isSquare() const { return width() == height(); }
+
+ // x is column, y is row
+ float getCoefficient(uint32_t x, uint32_t y) const {
+ xaDebugAssert(x < width());
+ xaDebugAssert(y < height());
+ const uint32_t count = m_array[y].size();
+ for (uint32_t i = 0; i < count; i++) {
+ if (m_array[y][i].x == x) return m_array[y][i].v;
+ }
+ return 0.0f;
+ }
+
+ void setCoefficient(uint32_t x, uint32_t y, float f) {
+ xaDebugAssert(x < width());
+ xaDebugAssert(y < height());
+ const uint32_t count = m_array[y].size();
+ for (uint32_t i = 0; i < count; i++) {
+ if (m_array[y][i].x == x) {
+ m_array[y][i].v = f;
+ return;
+ }
+ }
+ if (f != 0.0f) {
+ Coefficient c = { x, f };
+ m_array[y].push_back(c);
+ }
+ }
+
+ float dotRow(uint32_t y, const FullVector &v) const {
+ xaDebugAssert(y < height());
+ const uint32_t count = m_array[y].size();
+ float sum = 0;
+ for (uint32_t i = 0; i < count; i++) {
+ sum += m_array[y][i].v * v[m_array[y][i].x];
+ }
+ return sum;
+ }
+
+ void madRow(uint32_t y, float alpha, FullVector &v) const {
+ xaDebugAssert(y < height());
+ const uint32_t count = m_array[y].size();
+ for (uint32_t i = 0; i < count; i++) {
+ v[m_array[y][i].x] += alpha * m_array[y][i].v;
+ }
+ }
+
+ void clearRow(uint32_t y) {
+ xaDebugAssert(y < height());
+ m_array[y].clear();
+ }
+
+ void scaleRow(uint32_t y, float f) {
+ xaDebugAssert(y < height());
+ const uint32_t count = m_array[y].size();
+ for (uint32_t i = 0; i < count; i++) {
+ m_array[y][i].v *= f;
+ }
+ }
+
+ const std::vector<Coefficient> &getRow(uint32_t y) const { return m_array[y]; }
+
+private:
+ /// Number of columns.
+ const uint32_t m_width;
+
+ /// Array of matrix elements.
+ std::vector<std::vector<Coefficient> > m_array;
+};
+
+// y = a * x + y
+static void saxpy(float a, const FullVector &x, FullVector &y) {
+ xaDebugAssert(x.dimension() == y.dimension());
+ const uint32_t dim = x.dimension();
+ for (uint32_t i = 0; i < dim; i++) {
+ y[i] += a * x[i];
+ }
+}
+
+static void copy(const FullVector &x, FullVector &y) {
+ xaDebugAssert(x.dimension() == y.dimension());
+ const uint32_t dim = x.dimension();
+ for (uint32_t i = 0; i < dim; i++) {
+ y[i] = x[i];
+ }
+}
+
+static void scal(float a, FullVector &x) {
+ const uint32_t dim = x.dimension();
+ for (uint32_t i = 0; i < dim; i++) {
+ x[i] *= a;
+ }
+}
+
+static float dot(const FullVector &x, const FullVector &y) {
+ xaDebugAssert(x.dimension() == y.dimension());
+ const uint32_t dim = x.dimension();
+ float sum = 0;
+ for (uint32_t i = 0; i < dim; i++) {
+ sum += x[i] * y[i];
+ }
+ return sum;
+}
+
+static void mult(Transpose TM, const Matrix &M, const FullVector &x, FullVector &y) {
+ const uint32_t w = M.width();
+ const uint32_t h = M.height();
+ if (TM == Transposed) {
+ xaDebugAssert(h == x.dimension());
+ xaDebugAssert(w == y.dimension());
+ y.fill(0.0f);
+ for (uint32_t i = 0; i < h; i++) {
+ M.madRow(i, x[i], y);
+ }
+ } else {
+ xaDebugAssert(w == x.dimension());
+ xaDebugAssert(h == y.dimension());
+ for (uint32_t i = 0; i < h; i++) {
+ y[i] = M.dotRow(i, x);
+ }
+ }
+}
+
+// y = M * x
+static void mult(const Matrix &M, const FullVector &x, FullVector &y) {
+ mult(NoTransposed, M, x, y);
+}
+
+static void sgemv(float alpha, Transpose TA, const Matrix &A, const FullVector &x, float beta, FullVector &y) {
+ const uint32_t w = A.width();
+ const uint32_t h = A.height();
+ if (TA == Transposed) {
+ xaDebugAssert(h == x.dimension());
+ xaDebugAssert(w == y.dimension());
+ for (uint32_t i = 0; i < h; i++) {
+ A.madRow(i, alpha * x[i], y);
+ }
+ } else {
+ xaDebugAssert(w == x.dimension());
+ xaDebugAssert(h == y.dimension());
+ for (uint32_t i = 0; i < h; i++) {
+ y[i] = alpha * A.dotRow(i, x) + beta * y[i];
+ }
+ }
+}
+
+// y = alpha*A*x + beta*y
+static void sgemv(float alpha, const Matrix &A, const FullVector &x, float beta, FullVector &y) {
+ sgemv(alpha, NoTransposed, A, x, beta, y);
+}
+
+// dot y-row of A by x-column of B
+static float dotRowColumn(int y, const Matrix &A, int x, const Matrix &B) {
+ const std::vector<Matrix::Coefficient> &row = A.getRow(y);
+ const uint32_t count = row.size();
+ float sum = 0.0f;
+ for (uint32_t i = 0; i < count; i++) {
+ const Matrix::Coefficient &c = row[i];
+ sum += c.v * B.getCoefficient(x, c.x);
+ }
+ return sum;
+}
+
+// dot y-row of A by x-row of B
+static float dotRowRow(int y, const Matrix &A, int x, const Matrix &B) {
+ const std::vector<Matrix::Coefficient> &row = A.getRow(y);
+ const uint32_t count = row.size();
+ float sum = 0.0f;
+ for (uint32_t i = 0; i < count; i++) {
+ const Matrix::Coefficient &c = row[i];
+ sum += c.v * B.getCoefficient(c.x, x);
+ }
+ return sum;
+}
+
+// dot y-column of A by x-column of B
+static float dotColumnColumn(int y, const Matrix &A, int x, const Matrix &B) {
+ xaDebugAssert(A.height() == B.height());
+ const uint32_t h = A.height();
+ float sum = 0.0f;
+ for (uint32_t i = 0; i < h; i++) {
+ sum += A.getCoefficient(y, i) * B.getCoefficient(x, i);
+ }
+ return sum;
+}
+
+static void transpose(const Matrix &A, Matrix &B) {
+ xaDebugAssert(A.width() == B.height());
+ xaDebugAssert(B.width() == A.height());
+ const uint32_t w = A.width();
+ for (uint32_t x = 0; x < w; x++) {
+ B.clearRow(x);
+ }
+ const uint32_t h = A.height();
+ for (uint32_t y = 0; y < h; y++) {
+ const std::vector<Matrix::Coefficient> &row = A.getRow(y);
+ const uint32_t count = row.size();
+ for (uint32_t i = 0; i < count; i++) {
+ const Matrix::Coefficient &c = row[i];
+ xaDebugAssert(c.x < w);
+ B.setCoefficient(y, c.x, c.v);
+ }
+ }
+}
+
+static void sgemm(float alpha, Transpose TA, const Matrix &A, Transpose TB, const Matrix &B, float beta, Matrix &C) {
+ const uint32_t w = C.width();
+ const uint32_t h = C.height();
+ uint32_t aw = (TA == NoTransposed) ? A.width() : A.height();
+ uint32_t ah = (TA == NoTransposed) ? A.height() : A.width();
+ uint32_t bw = (TB == NoTransposed) ? B.width() : B.height();
+ uint32_t bh = (TB == NoTransposed) ? B.height() : B.width();
+ xaDebugAssert(aw == bh);
+ xaDebugAssert(bw == ah);
+ xaDebugAssert(w == bw);
+ xaDebugAssert(h == ah);
+#ifdef NDEBUG
+ aw = ah = bw = bh = 0; // silence unused parameter warning
+#endif
+ for (uint32_t y = 0; y < h; y++) {
+ for (uint32_t x = 0; x < w; x++) {
+ float c = beta * C.getCoefficient(x, y);
+ if (TA == NoTransposed && TB == NoTransposed) {
+ // dot y-row of A by x-column of B.
+ c += alpha * dotRowColumn(y, A, x, B);
+ } else if (TA == Transposed && TB == Transposed) {
+ // dot y-column of A by x-row of B.
+ c += alpha * dotRowColumn(x, B, y, A);
+ } else if (TA == Transposed && TB == NoTransposed) {
+ // dot y-column of A by x-column of B.
+ c += alpha * dotColumnColumn(y, A, x, B);
+ } else if (TA == NoTransposed && TB == Transposed) {
+ // dot y-row of A by x-row of B.
+ c += alpha * dotRowRow(y, A, x, B);
+ }
+ C.setCoefficient(x, y, c);
+ }
+ }
+}
+
+static void mult(Transpose TA, const Matrix &A, Transpose TB, const Matrix &B, Matrix &C) {
+ sgemm(1.0f, TA, A, TB, B, 0.0f, C);
+}
+
+// C = A * B
+static void mult(const Matrix &A, const Matrix &B, Matrix &C) {
+ mult(NoTransposed, A, NoTransposed, B, C);
+}
+
+} // namespace sparse
+
+class JacobiPreconditioner {
+public:
+ JacobiPreconditioner(const sparse::Matrix &M, bool symmetric) :
+ m_inverseDiagonal(M.width()) {
+ xaAssert(M.isSquare());
+ for (uint32_t x = 0; x < M.width(); x++) {
+ float elem = M.getCoefficient(x, x);
+ //xaDebugAssert( elem != 0.0f ); // This can be zero in the presence of zero area triangles.
+ if (symmetric) {
+ m_inverseDiagonal[x] = (elem != 0) ? 1.0f / sqrtf(fabsf(elem)) : 1.0f;
+ } else {
+ m_inverseDiagonal[x] = (elem != 0) ? 1.0f / elem : 1.0f;
+ }
+ }
+ }
+
+ void apply(const FullVector &x, FullVector &y) const {
+ xaDebugAssert(x.dimension() == m_inverseDiagonal.dimension());
+ xaDebugAssert(y.dimension() == m_inverseDiagonal.dimension());
+ // @@ Wrap vector component-wise product into a separate function.
+ const uint32_t D = x.dimension();
+ for (uint32_t i = 0; i < D; i++) {
+ y[i] = m_inverseDiagonal[i] * x[i];
+ }
+ }
+
+private:
+ FullVector m_inverseDiagonal;
+};
+
+// Linear solvers.
+class Solver {
+public:
+ // Solve the symmetric system: At·A·x = At·b
+ static bool LeastSquaresSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f) {
+ xaDebugAssert(A.width() == x.dimension());
+ xaDebugAssert(A.height() == b.dimension());
+ xaDebugAssert(A.height() >= A.width()); // @@ If height == width we could solve it directly...
+ const uint32_t D = A.width();
+ sparse::Matrix At(A.height(), A.width());
+ sparse::transpose(A, At);
+ FullVector Atb(D);
+ sparse::mult(At, b, Atb);
+ sparse::Matrix AtA(D);
+ sparse::mult(At, A, AtA);
+ return SymmetricSolver(AtA, Atb, x, epsilon);
+ }
+
+ // See section 10.4.3 in: Mesh Parameterization: Theory and Practice, Siggraph Course Notes, August 2007
+ static bool LeastSquaresSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, const uint32_t *lockedParameters, uint32_t lockedCount, float epsilon = 1e-5f) {
+ xaDebugAssert(A.width() == x.dimension());
+ xaDebugAssert(A.height() == b.dimension());
+ xaDebugAssert(A.height() >= A.width() - lockedCount);
+ // @@ This is not the most efficient way of building a system with reduced degrees of freedom. It would be faster to do it on the fly.
+ const uint32_t D = A.width() - lockedCount;
+ xaDebugAssert(D > 0);
+ // Compute: b - Al * xl
+ FullVector b_Alxl(b);
+ for (uint32_t y = 0; y < A.height(); y++) {
+ const uint32_t count = A.getRow(y).size();
+ for (uint32_t e = 0; e < count; e++) {
+ uint32_t column = A.getRow(y)[e].x;
+ bool isFree = true;
+ for (uint32_t i = 0; i < lockedCount; i++) {
+ isFree &= (lockedParameters[i] != column);
+ }
+ if (!isFree) {
+ b_Alxl[y] -= x[column] * A.getRow(y)[e].v;
+ }
+ }
+ }
+ // Remove locked columns from A.
+ sparse::Matrix Af(D, A.height());
+ for (uint32_t y = 0; y < A.height(); y++) {
+ const uint32_t count = A.getRow(y).size();
+ for (uint32_t e = 0; e < count; e++) {
+ uint32_t column = A.getRow(y)[e].x;
+ uint32_t ix = column;
+ bool isFree = true;
+ for (uint32_t i = 0; i < lockedCount; i++) {
+ isFree &= (lockedParameters[i] != column);
+ if (column > lockedParameters[i]) ix--; // shift columns
+ }
+ if (isFree) {
+ Af.setCoefficient(ix, y, A.getRow(y)[e].v);
+ }
+ }
+ }
+ // Remove elements from x
+ FullVector xf(D);
+ for (uint32_t i = 0, j = 0; i < A.width(); i++) {
+ bool isFree = true;
+ for (uint32_t l = 0; l < lockedCount; l++) {
+ isFree &= (lockedParameters[l] != i);
+ }
+ if (isFree) {
+ xf[j++] = x[i];
+ }
+ }
+ // Solve reduced system.
+ bool result = LeastSquaresSolver(Af, b_Alxl, xf, epsilon);
+ // Copy results back to x.
+ for (uint32_t i = 0, j = 0; i < A.width(); i++) {
+ bool isFree = true;
+ for (uint32_t l = 0; l < lockedCount; l++) {
+ isFree &= (lockedParameters[l] != i);
+ }
+ if (isFree) {
+ x[i] = xf[j++];
+ }
+ }
+ return result;
+ }
+
+private:
+ /**
+ * Compute the solution of the sparse linear system Ab=x using the Conjugate
+ * Gradient method.
+ *
+ * Solving sparse linear systems:
+ * (1) A·x = b
+ *
+ * The conjugate gradient algorithm solves (1) only in the case that A is
+ * symmetric and positive definite. It is based on the idea of minimizing the
+ * function
+ *
+ * (2) f(x) = 1/2·x·A·x - b·x
+ *
+ * This function is minimized when its gradient
+ *
+ * (3) df = A·x - b
+ *
+ * is zero, which is equivalent to (1). The minimization is carried out by
+ * generating a succession of search directions p.k and improved minimizers x.k.
+ * At each stage a quantity alfa.k is found that minimizes f(x.k + alfa.k·p.k),
+ * and x.k+1 is set equal to the new point x.k + alfa.k·p.k. The p.k and x.k are
+ * built up in such a way that x.k+1 is also the minimizer of f over the whole
+ * vector space of directions already taken, {p.1, p.2, . . . , p.k}. After N
+ * iterations you arrive at the minimizer over the entire vector space, i.e., the
+ * solution to (1).
+ *
+ * For a really good explanation of the method see:
+ *
+ * "An Introduction to the Conjugate Gradient Method Without the Agonizing Pain",
+ * Jonhathan Richard Shewchuk.
+ *
+ **/
+ static bool ConjugateGradientSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon) {
+ xaDebugAssert(A.isSquare());
+ xaDebugAssert(A.width() == b.dimension());
+ xaDebugAssert(A.width() == x.dimension());
+ int i = 0;
+ const int D = A.width();
+ const int i_max = 4 * D; // Convergence should be linear, but in some cases, it's not.
+ FullVector r(D); // residual
+ FullVector p(D); // search direction
+ FullVector q(D); //
+ float delta_0;
+ float delta_old;
+ float delta_new;
+ float alpha;
+ float beta;
+ // r = b - A·x;
+ sparse::copy(b, r);
+ sparse::sgemv(-1, A, x, 1, r);
+ // p = r;
+ sparse::copy(r, p);
+ delta_new = sparse::dot(r, r);
+ delta_0 = delta_new;
+ while (i < i_max && delta_new > epsilon * epsilon * delta_0) {
+ i++;
+ // q = A·p
+ mult(A, p, q);
+ // alpha = delta_new / p·q
+ alpha = delta_new / sparse::dot(p, q);
+ // x = alfa·p + x
+ sparse::saxpy(alpha, p, x);
+ if ((i & 31) == 0) { // recompute r after 32 steps
+ // r = b - A·x
+ sparse::copy(b, r);
+ sparse::sgemv(-1, A, x, 1, r);
+ } else {
+ // r = r - alpha·q
+ sparse::saxpy(-alpha, q, r);
+ }
+ delta_old = delta_new;
+ delta_new = sparse::dot(r, r);
+ beta = delta_new / delta_old;
+ // p = beta·p + r
+ sparse::scal(beta, p);
+ sparse::saxpy(1, r, p);
+ }
+ return delta_new <= epsilon * epsilon * delta_0;
+ }
+
+ // Conjugate gradient with preconditioner.
+ static bool ConjugateGradientSolver(const JacobiPreconditioner &preconditioner, const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon) {
+ xaDebugAssert(A.isSquare());
+ xaDebugAssert(A.width() == b.dimension());
+ xaDebugAssert(A.width() == x.dimension());
+ int i = 0;
+ const int D = A.width();
+ const int i_max = 4 * D; // Convergence should be linear, but in some cases, it's not.
+ FullVector r(D); // residual
+ FullVector p(D); // search direction
+ FullVector q(D); //
+ FullVector s(D); // preconditioned
+ float delta_0;
+ float delta_old;
+ float delta_new;
+ float alpha;
+ float beta;
+ // r = b - A·x
+ sparse::copy(b, r);
+ sparse::sgemv(-1, A, x, 1, r);
+ // p = M^-1 · r
+ preconditioner.apply(r, p);
+ delta_new = sparse::dot(r, p);
+ delta_0 = delta_new;
+ while (i < i_max && delta_new > epsilon * epsilon * delta_0) {
+ i++;
+ // q = A·p
+ mult(A, p, q);
+ // alpha = delta_new / p·q
+ alpha = delta_new / sparse::dot(p, q);
+ // x = alfa·p + x
+ sparse::saxpy(alpha, p, x);
+ if ((i & 31) == 0) { // recompute r after 32 steps
+ // r = b - A·x
+ sparse::copy(b, r);
+ sparse::sgemv(-1, A, x, 1, r);
+ } else {
+ // r = r - alfa·q
+ sparse::saxpy(-alpha, q, r);
+ }
+ // s = M^-1 · r
+ preconditioner.apply(r, s);
+ delta_old = delta_new;
+ delta_new = sparse::dot(r, s);
+ beta = delta_new / delta_old;
+ // p = s + beta·p
+ sparse::scal(beta, p);
+ sparse::saxpy(1, s, p);
+ }
+ return delta_new <= epsilon * epsilon * delta_0;
+ }
+
+ static bool SymmetricSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f) {
+ xaDebugAssert(A.height() == A.width());
+ xaDebugAssert(A.height() == b.dimension());
+ xaDebugAssert(b.dimension() == x.dimension());
+ JacobiPreconditioner jacobi(A, true);
+ return ConjugateGradientSolver(jacobi, A, b, x, epsilon);
+ }
+};
+
+namespace param {
+class Atlas;
+class Chart;
+
+// Fast sweep in 3 directions
+static bool findApproximateDiameterVertices(halfedge::Mesh *mesh, halfedge::Vertex **a, halfedge::Vertex **b) {
+ xaDebugAssert(mesh != NULL);
+ xaDebugAssert(a != NULL);
+ xaDebugAssert(b != NULL);
+ const uint32_t vertexCount = mesh->vertexCount();
+ halfedge::Vertex *minVertex[3];
+ halfedge::Vertex *maxVertex[3];
+ minVertex[0] = minVertex[1] = minVertex[2] = NULL;
+ maxVertex[0] = maxVertex[1] = maxVertex[2] = NULL;
+ for (uint32_t v = 1; v < vertexCount; v++) {
+ halfedge::Vertex *vertex = mesh->vertexAt(v);
+ xaDebugAssert(vertex != NULL);
+ if (vertex->isBoundary()) {
+ minVertex[0] = minVertex[1] = minVertex[2] = vertex;
+ maxVertex[0] = maxVertex[1] = maxVertex[2] = vertex;
+ break;
+ }
+ }
+ if (minVertex[0] == NULL) {
+ // Input mesh has not boundaries.
+ return false;
+ }
+ for (uint32_t v = 1; v < vertexCount; v++) {
+ halfedge::Vertex *vertex = mesh->vertexAt(v);
+ xaDebugAssert(vertex != NULL);
+ if (!vertex->isBoundary()) {
+ // Skip interior vertices.
+ continue;
+ }
+ if (vertex->pos.x < minVertex[0]->pos.x)
+ minVertex[0] = vertex;
+ else if (vertex->pos.x > maxVertex[0]->pos.x)
+ maxVertex[0] = vertex;
+ if (vertex->pos.y < minVertex[1]->pos.y)
+ minVertex[1] = vertex;
+ else if (vertex->pos.y > maxVertex[1]->pos.y)
+ maxVertex[1] = vertex;
+ if (vertex->pos.z < minVertex[2]->pos.z)
+ minVertex[2] = vertex;
+ else if (vertex->pos.z > maxVertex[2]->pos.z)
+ maxVertex[2] = vertex;
+ }
+ float lengths[3];
+ for (int i = 0; i < 3; i++) {
+ lengths[i] = length(minVertex[i]->pos - maxVertex[i]->pos);
+ }
+ if (lengths[0] > lengths[1] && lengths[0] > lengths[2]) {
+ *a = minVertex[0];
+ *b = maxVertex[0];
+ } else if (lengths[1] > lengths[2]) {
+ *a = minVertex[1];
+ *b = maxVertex[1];
+ } else {
+ *a = minVertex[2];
+ *b = maxVertex[2];
+ }
+ return true;
+}
+
+// Conformal relations from Brecht Van Lommel (based on ABF):
+
+static float vec_angle_cos(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3) {
+ Vector3 d1 = v1 - v2;
+ Vector3 d2 = v3 - v2;
+ return clamp(dot(d1, d2) / (length(d1) * length(d2)), -1.0f, 1.0f);
+}
+
+static float vec_angle(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3) {
+ float dot = vec_angle_cos(v1, v2, v3);
+ return acosf(dot);
+}
+
+static void triangle_angles(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3, float *a1, float *a2, float *a3) {
+ *a1 = vec_angle(v3, v1, v2);
+ *a2 = vec_angle(v1, v2, v3);
+ *a3 = PI - *a2 - *a1;
+}
+
+static void setup_abf_relations(sparse::Matrix &A, int row, const halfedge::Vertex *v0, const halfedge::Vertex *v1, const halfedge::Vertex *v2) {
+ int id0 = v0->id;
+ int id1 = v1->id;
+ int id2 = v2->id;
+ Vector3 p0 = v0->pos;
+ Vector3 p1 = v1->pos;
+ Vector3 p2 = v2->pos;
+ // @@ IC: Wouldn't it be more accurate to return cos and compute 1-cos^2?
+ // It does indeed seem to be a little bit more robust.
+ // @@ Need to revisit this more carefully!
+ float a0, a1, a2;
+ triangle_angles(p0, p1, p2, &a0, &a1, &a2);
+ float s0 = sinf(a0);
+ float s1 = sinf(a1);
+ float s2 = sinf(a2);
+ if (s1 > s0 && s1 > s2) {
+ std::swap(s1, s2);
+ std::swap(s0, s1);
+ std::swap(a1, a2);
+ std::swap(a0, a1);
+ std::swap(id1, id2);
+ std::swap(id0, id1);
+ } else if (s0 > s1 && s0 > s2) {
+ std::swap(s0, s2);
+ std::swap(s0, s1);
+ std::swap(a0, a2);
+ std::swap(a0, a1);
+ std::swap(id0, id2);
+ std::swap(id0, id1);
+ }
+ float c0 = cosf(a0);
+ float ratio = (s2 == 0.0f) ? 1.0f : s1 / s2;
+ float cosine = c0 * ratio;
+ float sine = s0 * ratio;
+ // Note : 2*id + 0 --> u
+ // 2*id + 1 --> v
+ int u0_id = 2 * id0 + 0;
+ int v0_id = 2 * id0 + 1;
+ int u1_id = 2 * id1 + 0;
+ int v1_id = 2 * id1 + 1;
+ int u2_id = 2 * id2 + 0;
+ int v2_id = 2 * id2 + 1;
+ // Real part
+ A.setCoefficient(u0_id, 2 * row + 0, cosine - 1.0f);
+ A.setCoefficient(v0_id, 2 * row + 0, -sine);
+ A.setCoefficient(u1_id, 2 * row + 0, -cosine);
+ A.setCoefficient(v1_id, 2 * row + 0, sine);
+ A.setCoefficient(u2_id, 2 * row + 0, 1);
+ // Imaginary part
+ A.setCoefficient(u0_id, 2 * row + 1, sine);
+ A.setCoefficient(v0_id, 2 * row + 1, cosine - 1.0f);
+ A.setCoefficient(u1_id, 2 * row + 1, -sine);
+ A.setCoefficient(v1_id, 2 * row + 1, -cosine);
+ A.setCoefficient(v2_id, 2 * row + 1, 1);
+}
+
+bool computeLeastSquaresConformalMap(halfedge::Mesh *mesh) {
+ xaDebugAssert(mesh != NULL);
+ // For this to work properly, mesh should not have colocals that have the same
+ // attributes, unless you want the vertices to actually have different texcoords.
+ const uint32_t vertexCount = mesh->vertexCount();
+ const uint32_t D = 2 * vertexCount;
+ const uint32_t N = 2 * halfedge::countMeshTriangles(mesh);
+ // N is the number of equations (one per triangle)
+ // D is the number of variables (one per vertex; there are 2 pinned vertices).
+ if (N < D - 4) {
+ return false;
+ }
+ sparse::Matrix A(D, N);
+ FullVector b(N);
+ FullVector x(D);
+ // Fill b:
+ b.fill(0.0f);
+ // Fill x:
+ halfedge::Vertex *v0;
+ halfedge::Vertex *v1;
+ if (!findApproximateDiameterVertices(mesh, &v0, &v1)) {
+ // Mesh has no boundaries.
+ return false;
+ }
+ if (v0->tex == v1->tex) {
+ // LSCM expects an existing parameterization.
+ return false;
+ }
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ halfedge::Vertex *vertex = mesh->vertexAt(v);
+ xaDebugAssert(vertex != NULL);
+ // Initial solution.
+ x[2 * v + 0] = vertex->tex.x;
+ x[2 * v + 1] = vertex->tex.y;
+ }
+ // Fill A:
+ const uint32_t faceCount = mesh->faceCount();
+ for (uint32_t f = 0, t = 0; f < faceCount; f++) {
+ const halfedge::Face *face = mesh->faceAt(f);
+ xaDebugAssert(face != NULL);
+ xaDebugAssert(face->edgeCount() == 3);
+ const halfedge::Vertex *vertex0 = NULL;
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Edge *edge = it.current();
+ xaAssert(edge != NULL);
+ if (vertex0 == NULL) {
+ vertex0 = edge->vertex;
+ } else if (edge->next->vertex != vertex0) {
+ const halfedge::Vertex *vertex1 = edge->from();
+ const halfedge::Vertex *vertex2 = edge->to();
+ setup_abf_relations(A, t, vertex0, vertex1, vertex2);
+ //setup_conformal_map_relations(A, t, vertex0, vertex1, vertex2);
+ t++;
+ }
+ }
+ }
+ const uint32_t lockedParameters[] = {
+ 2 * v0->id + 0,
+ 2 * v0->id + 1,
+ 2 * v1->id + 0,
+ 2 * v1->id + 1
+ };
+ // Solve
+ Solver::LeastSquaresSolver(A, b, x, lockedParameters, 4, 0.000001f);
+ // Map x back to texcoords:
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ halfedge::Vertex *vertex = mesh->vertexAt(v);
+ xaDebugAssert(vertex != NULL);
+ vertex->tex = Vector2(x[2 * v + 0], x[2 * v + 1]);
+ }
+ return true;
+}
+
+bool computeOrthogonalProjectionMap(halfedge::Mesh *mesh) {
+ Vector3 axis[2];
+ uint32_t vertexCount = mesh->vertexCount();
+ std::vector<Vector3> points(vertexCount);
+ points.resize(vertexCount);
+ for (uint32_t i = 0; i < vertexCount; i++) {
+ points[i] = mesh->vertexAt(i)->pos;
+ }
+ // Avoid redundant computations.
+ float matrix[6];
+ Fit::computeCovariance(vertexCount, points.data(), matrix);
+ if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0) {
+ return false;
+ }
+ float eigenValues[3];
+ Vector3 eigenVectors[3];
+ if (!Fit::eigenSolveSymmetric3(matrix, eigenValues, eigenVectors)) {
+ return false;
+ }
+ axis[0] = normalize(eigenVectors[0]);
+ axis[1] = normalize(eigenVectors[1]);
+ // Project vertices to plane.
+ for (halfedge::Mesh::VertexIterator it(mesh->vertices()); !it.isDone(); it.advance()) {
+ halfedge::Vertex *vertex = it.current();
+ vertex->tex.x = dot(axis[0], vertex->pos);
+ vertex->tex.y = dot(axis[1], vertex->pos);
+ }
+ return true;
+}
+
+void computeSingleFaceMap(halfedge::Mesh *mesh) {
+ xaDebugAssert(mesh != NULL);
+ xaDebugAssert(mesh->faceCount() == 1);
+ halfedge::Face *face = mesh->faceAt(0);
+ xaAssert(face != NULL);
+ Vector3 p0 = face->edge->from()->pos;
+ Vector3 p1 = face->edge->to()->pos;
+ Vector3 X = normalizeSafe(p1 - p0, Vector3(0.0f), 0.0f);
+ Vector3 Z = face->normal();
+ Vector3 Y = normalizeSafe(cross(Z, X), Vector3(0.0f), 0.0f);
+ uint32_t i = 0;
+ for (halfedge::Face::EdgeIterator it(face->edges()); !it.isDone(); it.advance(), i++) {
+ halfedge::Vertex *vertex = it.vertex();
+ xaAssert(vertex != NULL);
+ if (i == 0) {
+ vertex->tex = Vector2(0);
+ } else {
+ Vector3 pn = vertex->pos;
+ float xn = dot((pn - p0), X);
+ float yn = dot((pn - p0), Y);
+ vertex->tex = Vector2(xn, yn);
+ }
+ }
+}
+
+// Dummy implementation of a priority queue using sort at insertion.
+// - Insertion is o(n)
+// - Smallest element goes at the end, so that popping it is o(1).
+// - Resorting is n*log(n)
+// @@ Number of elements in the queue is usually small, and we'd have to rebalance often. I'm not sure it's worth implementing a heap.
+// @@ Searcing at removal would remove the need for sorting when priorities change.
+struct PriorityQueue {
+ PriorityQueue(uint32_t size = UINT_MAX) :
+ maxSize(size) {}
+
+ void push(float priority, uint32_t face) {
+ uint32_t i = 0;
+ const uint32_t count = pairs.size();
+ for (; i < count; i++) {
+ if (pairs[i].priority > priority) break;
+ }
+ Pair p = { priority, face };
+ pairs.insert(pairs.begin() + i, p);
+ if (pairs.size() > maxSize) {
+ pairs.erase(pairs.begin());
+ }
+ }
+
+ // push face out of order, to be sorted later.
+ void push(uint32_t face) {
+ Pair p = { 0.0f, face };
+ pairs.push_back(p);
+ }
+
+ uint32_t pop() {
+ uint32_t f = pairs.back().face;
+ pairs.pop_back();
+ return f;
+ }
+
+ void sort() {
+ //sort(pairs); // @@ My intro sort appears to be much slower than it should!
+ std::sort(pairs.begin(), pairs.end());
+ }
+
+ void clear() {
+ pairs.clear();
+ }
+
+ uint32_t count() const {
+ return pairs.size();
+ }
+
+ float firstPriority() const {
+ return pairs.back().priority;
+ }
+
+ const uint32_t maxSize;
+
+ struct Pair {
+ bool operator<(const Pair &p) const {
+ return priority > p.priority; // !! Sort in inverse priority order!
+ }
+
+ float priority;
+ uint32_t face;
+ };
+
+ std::vector<Pair> pairs;
+};
+
+struct ChartBuildData {
+ ChartBuildData(int p_id) :
+ id(p_id) {
+ planeNormal = Vector3(0);
+ centroid = Vector3(0);
+ coneAxis = Vector3(0);
+ coneAngle = 0;
+ area = 0;
+ boundaryLength = 0;
+ normalSum = Vector3(0);
+ centroidSum = Vector3(0);
+ }
+
+ int id;
+
+ // Proxy info:
+ Vector3 planeNormal;
+ Vector3 centroid;
+ Vector3 coneAxis;
+ float coneAngle;
+
+ float area;
+ float boundaryLength;
+ Vector3 normalSum;
+ Vector3 centroidSum;
+
+ std::vector<uint32_t> seeds; // @@ These could be a pointers to the halfedge faces directly.
+ std::vector<uint32_t> faces;
+ PriorityQueue candidates;
+};
+
+struct AtlasBuilder {
+ AtlasBuilder(const halfedge::Mesh *m) :
+ mesh(m),
+ facesLeft(m->faceCount()) {
+ const uint32_t faceCount = m->faceCount();
+ faceChartArray.resize(faceCount, -1);
+ faceCandidateArray.resize(faceCount, (uint32_t)-1);
+ // @@ Floyd for the whole mesh is too slow. We could compute floyd progressively per patch as the patch grows. We need a better solution to compute most central faces.
+ //computeShortestPaths();
+ // Precompute edge lengths and face areas.
+ uint32_t edgeCount = m->edgeCount();
+ edgeLengths.resize(edgeCount);
+ for (uint32_t i = 0; i < edgeCount; i++) {
+ uint32_t id = m->edgeAt(i)->id;
+ xaDebugAssert(id / 2 == i);
+#ifdef NDEBUG
+ id = 0; // silence unused parameter warning
+#endif
+ edgeLengths[i] = m->edgeAt(i)->length();
+ }
+ faceAreas.resize(faceCount);
+ for (uint32_t i = 0; i < faceCount; i++) {
+ faceAreas[i] = m->faceAt(i)->area();
+ }
+ }
+
+ ~AtlasBuilder() {
+ const uint32_t chartCount = chartArray.size();
+ for (uint32_t i = 0; i < chartCount; i++) {
+ delete chartArray[i];
+ }
+ }
+
+ void markUnchartedFaces(const std::vector<uint32_t> &unchartedFaces) {
+ const uint32_t unchartedFaceCount = unchartedFaces.size();
+ for (uint32_t i = 0; i < unchartedFaceCount; i++) {
+ uint32_t f = unchartedFaces[i];
+ faceChartArray[f] = -2;
+ //faceCandidateArray[f] = -2; // @@ ?
+ removeCandidate(f);
+ }
+ xaDebugAssert(facesLeft >= unchartedFaceCount);
+ facesLeft -= unchartedFaceCount;
+ }
+
+ void computeShortestPaths() {
+ const uint32_t faceCount = mesh->faceCount();
+ shortestPaths.resize(faceCount * faceCount, FLT_MAX);
+ // Fill edges:
+ for (uint32_t i = 0; i < faceCount; i++) {
+ shortestPaths[i * faceCount + i] = 0.0f;
+ const halfedge::Face *face_i = mesh->faceAt(i);
+ Vector3 centroid_i = face_i->centroid();
+ for (halfedge::Face::ConstEdgeIterator it(face_i->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Edge *edge = it.current();
+ if (!edge->isBoundary()) {
+ const halfedge::Face *face_j = edge->pair->face;
+ uint32_t j = face_j->id;
+ Vector3 centroid_j = face_j->centroid();
+ shortestPaths[i * faceCount + j] = shortestPaths[j * faceCount + i] = length(centroid_i - centroid_j);
+ }
+ }
+ }
+ // Use Floyd-Warshall algorithm to compute all paths:
+ for (uint32_t k = 0; k < faceCount; k++) {
+ for (uint32_t i = 0; i < faceCount; i++) {
+ for (uint32_t j = 0; j < faceCount; j++) {
+ shortestPaths[i * faceCount + j] = std::min(shortestPaths[i * faceCount + j], shortestPaths[i * faceCount + k] + shortestPaths[k * faceCount + j]);
+ }
+ }
+ }
+ }
+
+ void placeSeeds(float threshold, uint32_t maxSeedCount) {
+ // Instead of using a predefiened number of seeds:
+ // - Add seeds one by one, growing chart until a certain treshold.
+ // - Undo charts and restart growing process.
+ // @@ How can we give preference to faces far from sharp features as in the LSCM paper?
+ // - those points can be found using a simple flood filling algorithm.
+ // - how do we weight the probabilities?
+ for (uint32_t i = 0; i < maxSeedCount; i++) {
+ if (facesLeft == 0) {
+ // No faces left, stop creating seeds.
+ break;
+ }
+ createRandomChart(threshold);
+ }
+ }
+
+ void createRandomChart(float threshold) {
+ ChartBuildData *chart = new ChartBuildData(chartArray.size());
+ chartArray.push_back(chart);
+ // Pick random face that is not used by any chart yet.
+ uint32_t randomFaceIdx = rand.getRange(facesLeft - 1);
+ uint32_t i = 0;
+ for (uint32_t f = 0; f != randomFaceIdx; f++, i++) {
+ while (faceChartArray[i] != -1)
+ i++;
+ }
+ while (faceChartArray[i] != -1)
+ i++;
+ chart->seeds.push_back(i);
+ addFaceToChart(chart, i, true);
+ // Grow the chart as much as possible within the given threshold.
+ growChart(chart, threshold * 0.5f, facesLeft);
+ //growCharts(threshold - threshold * 0.75f / chartCount(), facesLeft);
+ }
+
+ void addFaceToChart(ChartBuildData *chart, uint32_t f, bool recomputeProxy = false) {
+ // Add face to chart.
+ chart->faces.push_back(f);
+ xaDebugAssert(faceChartArray[f] == -1);
+ faceChartArray[f] = chart->id;
+ facesLeft--;
+ // Update area and boundary length.
+ chart->area = evaluateChartArea(chart, f);
+ chart->boundaryLength = evaluateBoundaryLength(chart, f);
+ chart->normalSum = evaluateChartNormalSum(chart, f);
+ chart->centroidSum = evaluateChartCentroidSum(chart, f);
+ if (recomputeProxy) {
+ // Update proxy and candidate's priorities.
+ updateProxy(chart);
+ }
+ // Update candidates.
+ removeCandidate(f);
+ updateCandidates(chart, f);
+ updatePriorities(chart);
+ }
+
+ // Returns true if any of the charts can grow more.
+ bool growCharts(float threshold, uint32_t faceCount) {
+ // Using one global list.
+ faceCount = std::min(faceCount, facesLeft);
+ for (uint32_t i = 0; i < faceCount; i++) {
+ const Candidate &candidate = getBestCandidate();
+ if (candidate.metric > threshold) {
+ return false; // Can't grow more.
+ }
+ addFaceToChart(candidate.chart, candidate.face);
+ }
+ return facesLeft != 0; // Can continue growing.
+ }
+
+ bool growChart(ChartBuildData *chart, float threshold, uint32_t faceCount) {
+ // Try to add faceCount faces within threshold to chart.
+ for (uint32_t i = 0; i < faceCount;) {
+ if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold) {
+ return false;
+ }
+ uint32_t f = chart->candidates.pop();
+ if (faceChartArray[f] == -1) {
+ addFaceToChart(chart, f);
+ i++;
+ }
+ }
+ if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold) {
+ return false;
+ }
+ return true;
+ }
+
+ void resetCharts() {
+ const uint32_t faceCount = mesh->faceCount();
+ for (uint32_t i = 0; i < faceCount; i++) {
+ faceChartArray[i] = -1;
+ faceCandidateArray[i] = (uint32_t)-1;
+ }
+ facesLeft = faceCount;
+ candidateArray.clear();
+ const uint32_t chartCount = chartArray.size();
+ for (uint32_t i = 0; i < chartCount; i++) {
+ ChartBuildData *chart = chartArray[i];
+ const uint32_t seed = chart->seeds.back();
+ chart->area = 0.0f;
+ chart->boundaryLength = 0.0f;
+ chart->normalSum = Vector3(0);
+ chart->centroidSum = Vector3(0);
+ chart->faces.clear();
+ chart->candidates.clear();
+ addFaceToChart(chart, seed);
+ }
+ }
+
+ void updateCandidates(ChartBuildData *chart, uint32_t f) {
+ const halfedge::Face *face = mesh->faceAt(f);
+ // Traverse neighboring faces, add the ones that do not belong to any chart yet.
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Edge *edge = it.current()->pair;
+ if (!edge->isBoundary()) {
+ uint32_t faceId = edge->face->id;
+ if (faceChartArray[faceId] == -1) {
+ chart->candidates.push(faceId);
+ }
+ }
+ }
+ }
+
+ void updateProxies() {
+ const uint32_t chartCount = chartArray.size();
+ for (uint32_t i = 0; i < chartCount; i++) {
+ updateProxy(chartArray[i]);
+ }
+ }
+
+ void updateProxy(ChartBuildData *chart) {
+ //#pragma message(NV_FILE_LINE "TODO: Use best fit plane instead of average normal.")
+ chart->planeNormal = normalizeSafe(chart->normalSum, Vector3(0), 0.0f);
+ chart->centroid = chart->centroidSum / float(chart->faces.size());
+ }
+
+ bool relocateSeeds() {
+ bool anySeedChanged = false;
+ const uint32_t chartCount = chartArray.size();
+ for (uint32_t i = 0; i < chartCount; i++) {
+ if (relocateSeed(chartArray[i])) {
+ anySeedChanged = true;
+ }
+ }
+ return anySeedChanged;
+ }
+
+ bool relocateSeed(ChartBuildData *chart) {
+ Vector3 centroid = computeChartCentroid(chart);
+ const uint32_t N = 10; // @@ Hardcoded to 10?
+ PriorityQueue bestTriangles(N);
+ // Find the first N triangles that fit the proxy best.
+ const uint32_t faceCount = chart->faces.size();
+ for (uint32_t i = 0; i < faceCount; i++) {
+ float priority = evaluateProxyFitMetric(chart, chart->faces[i]);
+ bestTriangles.push(priority, chart->faces[i]);
+ }
+ // Of those, choose the most central triangle.
+ uint32_t mostCentral;
+ float maxDistance = -1;
+ const uint32_t bestCount = bestTriangles.count();
+ for (uint32_t i = 0; i < bestCount; i++) {
+ const halfedge::Face *face = mesh->faceAt(bestTriangles.pairs[i].face);
+ Vector3 faceCentroid = face->triangleCenter();
+ float distance = length(centroid - faceCentroid);
+ if (distance > maxDistance) {
+ maxDistance = distance;
+ mostCentral = bestTriangles.pairs[i].face;
+ }
+ }
+ xaDebugAssert(maxDistance >= 0);
+ // In order to prevent k-means cyles we record all the previously chosen seeds.
+ uint32_t index = std::find(chart->seeds.begin(), chart->seeds.end(), mostCentral) - chart->seeds.begin();
+ if (index < chart->seeds.size()) {
+ // Move new seed to the end of the seed array.
+ uint32_t last = chart->seeds.size() - 1;
+ std::swap(chart->seeds[index], chart->seeds[last]);
+ return false;
+ } else {
+ // Append new seed.
+ chart->seeds.push_back(mostCentral);
+ return true;
+ }
+ }
+
+ void updatePriorities(ChartBuildData *chart) {
+ // Re-evaluate candidate priorities.
+ uint32_t candidateCount = chart->candidates.count();
+ for (uint32_t i = 0; i < candidateCount; i++) {
+ chart->candidates.pairs[i].priority = evaluatePriority(chart, chart->candidates.pairs[i].face);
+ if (faceChartArray[chart->candidates.pairs[i].face] == -1) {
+ updateCandidate(chart, chart->candidates.pairs[i].face, chart->candidates.pairs[i].priority);
+ }
+ }
+ // Sort candidates.
+ chart->candidates.sort();
+ }
+
+ // Evaluate combined metric.
+ float evaluatePriority(ChartBuildData *chart, uint32_t face) {
+ // Estimate boundary length and area:
+ float newBoundaryLength = evaluateBoundaryLength(chart, face);
+ float newChartArea = evaluateChartArea(chart, face);
+ float F = evaluateProxyFitMetric(chart, face);
+ float C = evaluateRoundnessMetric(chart, face, newBoundaryLength, newChartArea);
+ float P = evaluateStraightnessMetric(chart, face);
+ // Penalize faces that cross seams, reward faces that close seams or reach boundaries.
+ float N = evaluateNormalSeamMetric(chart, face);
+ float T = evaluateTextureSeamMetric(chart, face);
+ //float R = evaluateCompletenessMetric(chart, face);
+ //float D = evaluateDihedralAngleMetric(chart, face);
+ // @@ Add a metric based on local dihedral angle.
+ // @@ Tweaking the normal and texture seam metrics.
+ // - Cause more impedance. Never cross 90 degree edges.
+ // -
+ float cost = float(
+ options.proxyFitMetricWeight * F +
+ options.roundnessMetricWeight * C +
+ options.straightnessMetricWeight * P +
+ options.normalSeamMetricWeight * N +
+ options.textureSeamMetricWeight * T);
+ // Enforce limits strictly:
+ if (newChartArea > options.maxChartArea) cost = FLT_MAX;
+ if (newBoundaryLength > options.maxBoundaryLength) cost = FLT_MAX;
+ // Make sure normal seams are fully respected:
+ if (options.normalSeamMetricWeight >= 1000 && N != 0) cost = FLT_MAX;
+ xaAssert(std::isfinite(cost));
+ return cost;
+ }
+
+ // Returns a value in [0-1].
+ float evaluateProxyFitMetric(ChartBuildData *chart, uint32_t f) {
+ const halfedge::Face *face = mesh->faceAt(f);
+ Vector3 faceNormal = face->triangleNormal();
+ // Use plane fitting metric for now:
+ return 1 - dot(faceNormal, chart->planeNormal); // @@ normal deviations should be weighted by face area
+ }
+
+ float evaluateRoundnessMetric(ChartBuildData *chart, uint32_t /*face*/, float newBoundaryLength, float newChartArea) {
+ float roundness = square(chart->boundaryLength) / chart->area;
+ float newRoundness = square(newBoundaryLength) / newChartArea;
+ if (newRoundness > roundness) {
+ return square(newBoundaryLength) / (newChartArea * 4 * PI);
+ } else {
+ // Offer no impedance to faces that improve roundness.
+ return 0;
+ }
+ }
+
+ float evaluateStraightnessMetric(ChartBuildData *chart, uint32_t f) {
+ float l_out = 0.0f;
+ float l_in = 0.0f;
+ const halfedge::Face *face = mesh->faceAt(f);
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Edge *edge = it.current();
+ float l = edgeLengths[edge->id / 2];
+ if (edge->isBoundary()) {
+ l_out += l;
+ } else {
+ uint32_t neighborFaceId = edge->pair->face->id;
+ if (faceChartArray[neighborFaceId] != chart->id) {
+ l_out += l;
+ } else {
+ l_in += l;
+ }
+ }
+ }
+ xaDebugAssert(l_in != 0.0f); // Candidate face must be adjacent to chart. @@ This is not true if the input mesh has zero-length edges.
+ float ratio = (l_out - l_in) / (l_out + l_in);
+ return std::min(ratio, 0.0f); // Only use the straightness metric to close gaps.
+ }
+
+ float evaluateNormalSeamMetric(ChartBuildData *chart, uint32_t f) {
+ float seamFactor = 0.0f;
+ float totalLength = 0.0f;
+ const halfedge::Face *face = mesh->faceAt(f);
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Edge *edge = it.current();
+ if (edge->isBoundary()) {
+ continue;
+ }
+ const uint32_t neighborFaceId = edge->pair->face->id;
+ if (faceChartArray[neighborFaceId] != chart->id) {
+ continue;
+ }
+ //float l = edge->length();
+ float l = edgeLengths[edge->id / 2];
+ totalLength += l;
+ if (!edge->isSeam()) {
+ continue;
+ }
+ // Make sure it's a normal seam.
+ if (edge->isNormalSeam()) {
+ float d0 = clamp(dot(edge->vertex->nor, edge->pair->next->vertex->nor), 0.0f, 1.0f);
+ float d1 = clamp(dot(edge->next->vertex->nor, edge->pair->vertex->nor), 0.0f, 1.0f);
+ l *= 1 - (d0 + d1) * 0.5f;
+ seamFactor += l;
+ }
+ }
+ if (seamFactor == 0) return 0.0f;
+ return seamFactor / totalLength;
+ }
+
+ float evaluateTextureSeamMetric(ChartBuildData *chart, uint32_t f) {
+ float seamLength = 0.0f;
+ float totalLength = 0.0f;
+ const halfedge::Face *face = mesh->faceAt(f);
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Edge *edge = it.current();
+ if (edge->isBoundary()) {
+ continue;
+ }
+ const uint32_t neighborFaceId = edge->pair->face->id;
+ if (faceChartArray[neighborFaceId] != chart->id) {
+ continue;
+ }
+ //float l = edge->length();
+ float l = edgeLengths[edge->id / 2];
+ totalLength += l;
+ if (!edge->isSeam()) {
+ continue;
+ }
+ // Make sure it's a texture seam.
+ if (edge->isTextureSeam()) {
+ seamLength += l;
+ }
+ }
+ if (seamLength == 0.0f) {
+ return 0.0f; // Avoid division by zero.
+ }
+ return seamLength / totalLength;
+ }
+
+ float evaluateChartArea(ChartBuildData *chart, uint32_t f) {
+ const halfedge::Face *face = mesh->faceAt(f);
+ return chart->area + faceAreas[face->id];
+ }
+
+ float evaluateBoundaryLength(ChartBuildData *chart, uint32_t f) {
+ float boundaryLength = chart->boundaryLength;
+ // Add new edges, subtract edges shared with the chart.
+ const halfedge::Face *face = mesh->faceAt(f);
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Edge *edge = it.current();
+ //float edgeLength = edge->length();
+ float edgeLength = edgeLengths[edge->id / 2];
+ if (edge->isBoundary()) {
+ boundaryLength += edgeLength;
+ } else {
+ uint32_t neighborFaceId = edge->pair->face->id;
+ if (faceChartArray[neighborFaceId] != chart->id) {
+ boundaryLength += edgeLength;
+ } else {
+ boundaryLength -= edgeLength;
+ }
+ }
+ }
+ return std::max(0.0f, boundaryLength); // @@ Hack!
+ }
+
+ Vector3 evaluateChartNormalSum(ChartBuildData *chart, uint32_t f) {
+ const halfedge::Face *face = mesh->faceAt(f);
+ return chart->normalSum + face->triangleNormalAreaScaled();
+ }
+
+ Vector3 evaluateChartCentroidSum(ChartBuildData *chart, uint32_t f) {
+ const halfedge::Face *face = mesh->faceAt(f);
+ return chart->centroidSum + face->centroid();
+ }
+
+ Vector3 computeChartCentroid(const ChartBuildData *chart) {
+ Vector3 centroid(0);
+ const uint32_t faceCount = chart->faces.size();
+ for (uint32_t i = 0; i < faceCount; i++) {
+ const halfedge::Face *face = mesh->faceAt(chart->faces[i]);
+ centroid += face->triangleCenter();
+ }
+ return centroid / float(faceCount);
+ }
+
+ void fillHoles(float threshold) {
+ while (facesLeft > 0)
+ createRandomChart(threshold);
+ }
+
+ void mergeCharts() {
+ std::vector<float> sharedBoundaryLengths;
+ const uint32_t chartCount = chartArray.size();
+ for (int c = chartCount - 1; c >= 0; c--) {
+ sharedBoundaryLengths.clear();
+ sharedBoundaryLengths.resize(chartCount, 0.0f);
+ ChartBuildData *chart = chartArray[c];
+ float externalBoundary = 0.0f;
+ const uint32_t faceCount = chart->faces.size();
+ for (uint32_t i = 0; i < faceCount; i++) {
+ uint32_t f = chart->faces[i];
+ const halfedge::Face *face = mesh->faceAt(f);
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Edge *edge = it.current();
+ //float l = edge->length();
+ float l = edgeLengths[edge->id / 2];
+ if (edge->isBoundary()) {
+ externalBoundary += l;
+ } else {
+ uint32_t neighborFace = edge->pair->face->id;
+ uint32_t neighborChart = faceChartArray[neighborFace];
+ if (neighborChart != (uint32_t)c) {
+ if ((edge->isSeam() && (edge->isNormalSeam() || edge->isTextureSeam())) || neighborChart == -2) {
+ externalBoundary += l;
+ } else {
+ sharedBoundaryLengths[neighborChart] += l;
+ }
+ }
+ }
+ }
+ }
+ for (int cc = chartCount - 1; cc >= 0; cc--) {
+ if (cc == c)
+ continue;
+ ChartBuildData *chart2 = chartArray[cc];
+ if (chart2 == NULL)
+ continue;
+ if (sharedBoundaryLengths[cc] > 0.8 * std::max(0.0f, chart->boundaryLength - externalBoundary)) {
+ // Try to avoid degenerate configurations.
+ if (chart2->boundaryLength > sharedBoundaryLengths[cc]) {
+ if (dot(chart2->planeNormal, chart->planeNormal) > -0.25) {
+ mergeChart(chart2, chart, sharedBoundaryLengths[cc]);
+ delete chart;
+ chartArray[c] = NULL;
+ break;
+ }
+ }
+ }
+ if (sharedBoundaryLengths[cc] > 0.20 * std::max(0.0f, chart->boundaryLength - externalBoundary)) {
+ // Compare proxies.
+ if (dot(chart2->planeNormal, chart->planeNormal) > 0) {
+ mergeChart(chart2, chart, sharedBoundaryLengths[cc]);
+ delete chart;
+ chartArray[c] = NULL;
+ break;
+ }
+ }
+ }
+ }
+ // Remove deleted charts.
+ for (int c = 0; c < int32_t(chartArray.size()); /*do not increment if removed*/) {
+ if (chartArray[c] == NULL) {
+ chartArray.erase(chartArray.begin() + c);
+ // Update faceChartArray.
+ const uint32_t faceCount = faceChartArray.size();
+ for (uint32_t i = 0; i < faceCount; i++) {
+ xaDebugAssert(faceChartArray[i] != -1);
+ xaDebugAssert(faceChartArray[i] != c);
+ xaDebugAssert(faceChartArray[i] <= int32_t(chartArray.size()));
+ if (faceChartArray[i] > c) {
+ faceChartArray[i]--;
+ }
+ }
+ } else {
+ chartArray[c]->id = c;
+ c++;
+ }
+ }
+ }
+
+ // @@ Cleanup.
+ struct Candidate {
+ uint32_t face;
+ ChartBuildData *chart;
+ float metric;
+ };
+
+ // @@ Get N best candidates in one pass.
+ const Candidate &getBestCandidate() const {
+ uint32_t best = 0;
+ float bestCandidateMetric = FLT_MAX;
+ const uint32_t candidateCount = candidateArray.size();
+ xaAssert(candidateCount > 0);
+ for (uint32_t i = 0; i < candidateCount; i++) {
+ const Candidate &candidate = candidateArray[i];
+ if (candidate.metric < bestCandidateMetric) {
+ bestCandidateMetric = candidate.metric;
+ best = i;
+ }
+ }
+ return candidateArray[best];
+ }
+
+ void removeCandidate(uint32_t f) {
+ int c = faceCandidateArray[f];
+ if (c != -1) {
+ faceCandidateArray[f] = (uint32_t)-1;
+ if (c == int(candidateArray.size() - 1)) {
+ candidateArray.pop_back();
+ } else {
+ // Replace with last.
+ candidateArray[c] = candidateArray[candidateArray.size() - 1];
+ candidateArray.pop_back();
+ faceCandidateArray[candidateArray[c].face] = c;
+ }
+ }
+ }
+
+ void updateCandidate(ChartBuildData *chart, uint32_t f, float metric) {
+ if (faceCandidateArray[f] == -1) {
+ const uint32_t index = candidateArray.size();
+ faceCandidateArray[f] = index;
+ candidateArray.resize(index + 1);
+ candidateArray[index].face = f;
+ candidateArray[index].chart = chart;
+ candidateArray[index].metric = metric;
+ } else {
+ int c = faceCandidateArray[f];
+ xaDebugAssert(c != -1);
+ Candidate &candidate = candidateArray[c];
+ xaDebugAssert(candidate.face == f);
+ if (metric < candidate.metric || chart == candidate.chart) {
+ candidate.metric = metric;
+ candidate.chart = chart;
+ }
+ }
+ }
+
+ void mergeChart(ChartBuildData *owner, ChartBuildData *chart, float sharedBoundaryLength) {
+ const uint32_t faceCount = chart->faces.size();
+ for (uint32_t i = 0; i < faceCount; i++) {
+ uint32_t f = chart->faces[i];
+ xaDebugAssert(faceChartArray[f] == chart->id);
+ faceChartArray[f] = owner->id;
+ owner->faces.push_back(f);
+ }
+ // Update adjacencies?
+ owner->area += chart->area;
+ owner->boundaryLength += chart->boundaryLength - sharedBoundaryLength;
+ owner->normalSum += chart->normalSum;
+ owner->centroidSum += chart->centroidSum;
+ updateProxy(owner);
+ }
+
+ uint32_t chartCount() const { return chartArray.size(); }
+ const std::vector<uint32_t> &chartFaces(uint32_t i) const { return chartArray[i]->faces; }
+
+ const halfedge::Mesh *mesh;
+ uint32_t facesLeft;
+ std::vector<int> faceChartArray;
+ std::vector<ChartBuildData *> chartArray;
+ std::vector<float> shortestPaths;
+ std::vector<float> edgeLengths;
+ std::vector<float> faceAreas;
+ std::vector<Candidate> candidateArray; //
+ std::vector<uint32_t> faceCandidateArray; // Map face index to candidate index.
+ MTRand rand;
+ CharterOptions options;
+};
+
+/// A chart is a connected set of faces with a certain topology (usually a disk).
+class Chart {
+public:
+ Chart() :
+ m_isDisk(false),
+ m_isVertexMapped(false) {}
+
+ void build(const halfedge::Mesh *originalMesh, const std::vector<uint32_t> &faceArray) {
+ // Copy face indices.
+ m_faceArray = faceArray;
+ const uint32_t meshVertexCount = originalMesh->vertexCount();
+ m_chartMesh.reset(new halfedge::Mesh());
+ m_unifiedMesh.reset(new halfedge::Mesh());
+ std::vector<uint32_t> chartMeshIndices(meshVertexCount, (uint32_t)~0);
+ std::vector<uint32_t> unifiedMeshIndices(meshVertexCount, (uint32_t)~0);
+ // Add vertices.
+ const uint32_t faceCount = faceArray.size();
+ for (uint32_t f = 0; f < faceCount; f++) {
+ const halfedge::Face *face = originalMesh->faceAt(faceArray[f]);
+ xaDebugAssert(face != NULL);
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Vertex *vertex = it.current()->vertex;
+ const halfedge::Vertex *unifiedVertex = vertex->firstColocal();
+ if (unifiedMeshIndices[unifiedVertex->id] == ~0) {
+ unifiedMeshIndices[unifiedVertex->id] = m_unifiedMesh->vertexCount();
+ xaDebugAssert(vertex->pos == unifiedVertex->pos);
+ m_unifiedMesh->addVertex(vertex->pos);
+ }
+ if (chartMeshIndices[vertex->id] == ~0) {
+ chartMeshIndices[vertex->id] = m_chartMesh->vertexCount();
+ m_chartToOriginalMap.push_back(vertex->original_id);
+ m_chartToUnifiedMap.push_back(unifiedMeshIndices[unifiedVertex->id]);
+ halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos);
+ v->nor = vertex->nor;
+ v->tex = vertex->tex;
+ }
+ }
+ }
+ // This is ignoring the canonical map:
+ // - Is it really necessary to link colocals?
+ m_chartMesh->linkColocals();
+ //m_unifiedMesh->linkColocals(); // Not strictly necessary, no colocals in the unified mesh. # Wrong.
+ // This check is not valid anymore, if the original mesh vertices were linked with a canonical map, then it might have
+ // some colocal vertices that were unlinked. So, the unified mesh might have some duplicate vertices, because firstColocal()
+ // is not guaranteed to return the same vertex for two colocal vertices.
+ //xaAssert(m_chartMesh->colocalVertexCount() == m_unifiedMesh->vertexCount());
+ // Is that OK? What happens in meshes were that happens? Does anything break? Apparently not...
+ std::vector<uint32_t> faceIndices;
+ faceIndices.reserve(7);
+ // Add faces.
+ for (uint32_t f = 0; f < faceCount; f++) {
+ const halfedge::Face *face = originalMesh->faceAt(faceArray[f]);
+ xaDebugAssert(face != NULL);
+ faceIndices.clear();
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Vertex *vertex = it.current()->vertex;
+ xaDebugAssert(vertex != NULL);
+ faceIndices.push_back(chartMeshIndices[vertex->id]);
+ }
+ m_chartMesh->addFace(faceIndices);
+ faceIndices.clear();
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Vertex *vertex = it.current()->vertex;
+ xaDebugAssert(vertex != NULL);
+ vertex = vertex->firstColocal();
+ faceIndices.push_back(unifiedMeshIndices[vertex->id]);
+ }
+ m_unifiedMesh->addFace(faceIndices);
+ }
+ m_chartMesh->linkBoundary();
+ m_unifiedMesh->linkBoundary();
+ //exportMesh(m_unifiedMesh.ptr(), "debug_input.obj");
+ if (m_unifiedMesh->splitBoundaryEdges()) {
+ m_unifiedMesh.reset(halfedge::unifyVertices(m_unifiedMesh.get()));
+ }
+ //exportMesh(m_unifiedMesh.ptr(), "debug_split.obj");
+ // Closing the holes is not always the best solution and does not fix all the problems.
+ // We need to do some analysis of the holes and the genus to:
+ // - Find cuts that reduce genus.
+ // - Find cuts to connect holes.
+ // - Use minimal spanning trees or seamster.
+ if (!closeHoles()) {
+ /*static int pieceCount = 0;
+ StringBuilder fileName;
+ fileName.format("debug_hole_%d.obj", pieceCount++);
+ exportMesh(m_unifiedMesh.ptr(), fileName.str());*/
+ }
+ m_unifiedMesh.reset(halfedge::triangulate(m_unifiedMesh.get()));
+ //exportMesh(m_unifiedMesh.ptr(), "debug_triangulated.obj");
+ // Analyze chart topology.
+ halfedge::MeshTopology topology(m_unifiedMesh.get());
+ m_isDisk = topology.isDisk();
+ }
+
+ void buildVertexMap(const halfedge::Mesh *originalMesh, const std::vector<uint32_t> &unchartedMaterialArray) {
+ xaAssert(m_chartMesh.get() == NULL && m_unifiedMesh.get() == NULL);
+ m_isVertexMapped = true;
+ // Build face indices.
+ m_faceArray.clear();
+ const uint32_t meshFaceCount = originalMesh->faceCount();
+ for (uint32_t f = 0; f < meshFaceCount; f++) {
+ const halfedge::Face *face = originalMesh->faceAt(f);
+ if (std::find(unchartedMaterialArray.begin(), unchartedMaterialArray.end(), face->material) != unchartedMaterialArray.end()) {
+ m_faceArray.push_back(f);
+ }
+ }
+ const uint32_t faceCount = m_faceArray.size();
+ if (faceCount == 0) {
+ return;
+ }
+ // @@ The chartMesh construction is basically the same as with regular charts, don't duplicate!
+ const uint32_t meshVertexCount = originalMesh->vertexCount();
+ m_chartMesh.reset(new halfedge::Mesh());
+ std::vector<uint32_t> chartMeshIndices(meshVertexCount, (uint32_t)~0);
+ // Vertex map mesh only has disconnected vertices.
+ for (uint32_t f = 0; f < faceCount; f++) {
+ const halfedge::Face *face = originalMesh->faceAt(m_faceArray[f]);
+ xaDebugAssert(face != NULL);
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Vertex *vertex = it.current()->vertex;
+ if (chartMeshIndices[vertex->id] == ~0) {
+ chartMeshIndices[vertex->id] = m_chartMesh->vertexCount();
+ m_chartToOriginalMap.push_back(vertex->original_id);
+ halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos);
+ v->nor = vertex->nor;
+ v->tex = vertex->tex; // @@ Not necessary.
+ }
+ }
+ }
+ // @@ Link colocals using the original mesh canonical map? Build canonical map on the fly? Do we need to link colocals at all for this?
+ //m_chartMesh->linkColocals();
+ std::vector<uint32_t> faceIndices;
+ faceIndices.reserve(7);
+ // Add faces.
+ for (uint32_t f = 0; f < faceCount; f++) {
+ const halfedge::Face *face = originalMesh->faceAt(m_faceArray[f]);
+ xaDebugAssert(face != NULL);
+ faceIndices.clear();
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Vertex *vertex = it.current()->vertex;
+ xaDebugAssert(vertex != NULL);
+ xaDebugAssert(chartMeshIndices[vertex->id] != ~0);
+ faceIndices.push_back(chartMeshIndices[vertex->id]);
+ }
+ halfedge::Face *new_face = m_chartMesh->addFace(faceIndices);
+ xaDebugAssert(new_face != NULL);
+#ifdef NDEBUG
+ new_face = NULL; // silence unused parameter warning
+#endif
+ }
+ m_chartMesh->linkBoundary();
+ const uint32_t chartVertexCount = m_chartMesh->vertexCount();
+ Box bounds;
+ bounds.clearBounds();
+ for (uint32_t i = 0; i < chartVertexCount; i++) {
+ halfedge::Vertex *vertex = m_chartMesh->vertexAt(i);
+ bounds.addPointToBounds(vertex->pos);
+ }
+ ProximityGrid grid;
+ grid.init(bounds, chartVertexCount);
+ for (uint32_t i = 0; i < chartVertexCount; i++) {
+ halfedge::Vertex *vertex = m_chartMesh->vertexAt(i);
+ grid.add(vertex->pos, i);
+ }
+ uint32_t texelCount = 0;
+ const float positionThreshold = 0.01f;
+ const float normalThreshold = 0.01f;
+ uint32_t verticesVisited = 0;
+ uint32_t cellsVisited = 0;
+ std::vector<int> vertexIndexArray(chartVertexCount, -1); // Init all indices to -1.
+ // Traverse vertices in morton order. @@ It may be more interesting to sort them based on orientation.
+ const uint32_t cellCodeCount = grid.mortonCount();
+ for (uint32_t cellCode = 0; cellCode < cellCodeCount; cellCode++) {
+ int cell = grid.mortonIndex(cellCode);
+ if (cell < 0) continue;
+ cellsVisited++;
+ const std::vector<uint32_t> &indexArray = grid.cellArray[cell].indexArray;
+ for (uint32_t i = 0; i < indexArray.size(); i++) {
+ uint32_t idx = indexArray[i];
+ halfedge::Vertex *vertex = m_chartMesh->vertexAt(idx);
+ xaDebugAssert(vertexIndexArray[idx] == -1);
+ std::vector<uint32_t> neighbors;
+ grid.gather(vertex->pos, positionThreshold, /*ref*/ neighbors);
+ // Compare against all nearby vertices, cluster greedily.
+ for (uint32_t j = 0; j < neighbors.size(); j++) {
+ uint32_t otherIdx = neighbors[j];
+ if (vertexIndexArray[otherIdx] != -1) {
+ halfedge::Vertex *otherVertex = m_chartMesh->vertexAt(otherIdx);
+ if (distance(vertex->pos, otherVertex->pos) < positionThreshold &&
+ distance(vertex->nor, otherVertex->nor) < normalThreshold) {
+ vertexIndexArray[idx] = vertexIndexArray[otherIdx];
+ break;
+ }
+ }
+ }
+ // If index not assigned, assign new one.
+ if (vertexIndexArray[idx] == -1) {
+ vertexIndexArray[idx] = texelCount++;
+ }
+ verticesVisited++;
+ }
+ }
+ xaDebugAssert(cellsVisited == grid.cellArray.size());
+ xaDebugAssert(verticesVisited == chartVertexCount);
+ vertexMapWidth = ftoi_ceil(sqrtf(float(texelCount)));
+ vertexMapWidth = (vertexMapWidth + 3) & ~3; // Width aligned to 4.
+ vertexMapHeight = vertexMapWidth == 0 ? 0 : (texelCount + vertexMapWidth - 1) / vertexMapWidth;
+ //vertexMapHeight = (vertexMapHeight + 3) & ~3; // Height aligned to 4.
+ xaDebugAssert(vertexMapWidth >= vertexMapHeight);
+ xaPrint("Reduced vertex count from %d to %d.\n", chartVertexCount, texelCount);
+ // Lay down the clustered vertices in morton order.
+ std::vector<uint32_t> texelCodes(texelCount);
+ // For each texel, assign one morton code.
+ uint32_t texelCode = 0;
+ for (uint32_t i = 0; i < texelCount; i++) {
+ uint32_t x, y;
+ do {
+ x = morton::decodeMorton2X(texelCode);
+ y = morton::decodeMorton2Y(texelCode);
+ texelCode++;
+ } while (x >= uint32_t(vertexMapWidth) || y >= uint32_t(vertexMapHeight));
+ texelCodes[i] = texelCode - 1;
+ }
+ for (uint32_t i = 0; i < chartVertexCount; i++) {
+ halfedge::Vertex *vertex = m_chartMesh->vertexAt(i);
+ int idx = vertexIndexArray[i];
+ if (idx != -1) {
+ uint32_t tc = texelCodes[idx];
+ uint32_t x = morton::decodeMorton2X(tc);
+ uint32_t y = morton::decodeMorton2Y(tc);
+ vertex->tex.x = float(x);
+ vertex->tex.y = float(y);
+ }
+ }
+ }
+
+ bool closeHoles() {
+ xaDebugAssert(!m_isVertexMapped);
+ std::vector<halfedge::Edge *> boundaryEdges;
+ getBoundaryEdges(m_unifiedMesh.get(), boundaryEdges);
+ uint32_t boundaryCount = boundaryEdges.size();
+ if (boundaryCount <= 1) {
+ // Nothing to close.
+ return true;
+ }
+ // Compute lengths and areas.
+ std::vector<float> boundaryLengths;
+ for (uint32_t i = 0; i < boundaryCount; i++) {
+ const halfedge::Edge *startEdge = boundaryEdges[i];
+ xaAssert(startEdge->face == NULL);
+ //float boundaryEdgeCount = 0;
+ float boundaryLength = 0.0f;
+ //Vector3 boundaryCentroid(zero);
+ const halfedge::Edge *edge = startEdge;
+ do {
+ Vector3 t0 = edge->from()->pos;
+ Vector3 t1 = edge->to()->pos;
+ //boundaryEdgeCount++;
+ boundaryLength += length(t1 - t0);
+ //boundaryCentroid += edge->vertex()->pos;
+ edge = edge->next;
+ } while (edge != startEdge);
+ boundaryLengths.push_back(boundaryLength);
+ //boundaryCentroids.append(boundaryCentroid / boundaryEdgeCount);
+ }
+ // Find disk boundary.
+ uint32_t diskBoundary = 0;
+ float maxLength = boundaryLengths[0];
+ for (uint32_t i = 1; i < boundaryCount; i++) {
+ if (boundaryLengths[i] > maxLength) {
+ maxLength = boundaryLengths[i];
+ diskBoundary = i;
+ }
+ }
+ // Close holes.
+ for (uint32_t i = 0; i < boundaryCount; i++) {
+ if (diskBoundary == i) {
+ // Skip disk boundary.
+ continue;
+ }
+ halfedge::Edge *startEdge = boundaryEdges[i];
+ xaDebugAssert(startEdge != NULL);
+ xaDebugAssert(startEdge->face == NULL);
+ std::vector<halfedge::Vertex *> vertexLoop;
+ std::vector<halfedge::Edge *> edgeLoop;
+ halfedge::Edge *edge = startEdge;
+ do {
+ halfedge::Vertex *vertex = edge->next->vertex; // edge->to()
+ uint32_t j;
+ for (j = 0; j < vertexLoop.size(); j++) {
+ if (vertex->isColocal(vertexLoop[j])) {
+ break;
+ }
+ }
+ bool isCrossing = (j != vertexLoop.size());
+ if (isCrossing) {
+ halfedge::Edge *prev = edgeLoop[j]; // Previous edge before the loop.
+ halfedge::Edge *next = edge->next; // Next edge after the loop.
+ xaDebugAssert(prev->to()->isColocal(next->from()));
+ // Close loop.
+ edgeLoop.push_back(edge);
+ closeLoop(j + 1, edgeLoop);
+ // Link boundary loop.
+ prev->setNext(next);
+ vertex->setEdge(next);
+ // Start over again.
+ vertexLoop.clear();
+ edgeLoop.clear();
+ edge = startEdge;
+ vertex = edge->to();
+ }
+ vertexLoop.push_back(vertex);
+ edgeLoop.push_back(edge);
+ edge = edge->next;
+ } while (edge != startEdge);
+ closeLoop(0, edgeLoop);
+ }
+ getBoundaryEdges(m_unifiedMesh.get(), boundaryEdges);
+ boundaryCount = boundaryEdges.size();
+ xaDebugAssert(boundaryCount == 1);
+ return boundaryCount == 1;
+ }
+
+ bool isDisk() const {
+ return m_isDisk;
+ }
+ bool isVertexMapped() const {
+ return m_isVertexMapped;
+ }
+
+ uint32_t vertexCount() const {
+ return m_chartMesh->vertexCount();
+ }
+ uint32_t colocalVertexCount() const {
+ return m_unifiedMesh->vertexCount();
+ }
+
+ uint32_t faceCount() const {
+ return m_faceArray.size();
+ }
+ uint32_t faceAt(uint32_t i) const {
+ return m_faceArray[i];
+ }
+
+ const halfedge::Mesh *chartMesh() const {
+ return m_chartMesh.get();
+ }
+ halfedge::Mesh *chartMesh() {
+ return m_chartMesh.get();
+ }
+ const halfedge::Mesh *unifiedMesh() const {
+ return m_unifiedMesh.get();
+ }
+ halfedge::Mesh *unifiedMesh() {
+ return m_unifiedMesh.get();
+ }
+
+ //uint32_t vertexIndex(uint32_t i) const { return m_vertexIndexArray[i]; }
+
+ uint32_t mapChartVertexToOriginalVertex(uint32_t i) const {
+ return m_chartToOriginalMap[i];
+ }
+ uint32_t mapChartVertexToUnifiedVertex(uint32_t i) const {
+ return m_chartToUnifiedMap[i];
+ }
+
+ const std::vector<uint32_t> &faceArray() const {
+ return m_faceArray;
+ }
+
+ // Transfer parameterization from unified mesh to chart mesh.
+ void transferParameterization() {
+ xaDebugAssert(!m_isVertexMapped);
+ uint32_t vertexCount = m_chartMesh->vertexCount();
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ halfedge::Vertex *vertex = m_chartMesh->vertexAt(v);
+ halfedge::Vertex *unifiedVertex = m_unifiedMesh->vertexAt(mapChartVertexToUnifiedVertex(v));
+ vertex->tex = unifiedVertex->tex;
+ }
+ }
+
+ float computeSurfaceArea() const {
+ return halfedge::computeSurfaceArea(m_chartMesh.get()) * scale;
+ }
+
+ float computeParametricArea() const {
+ // This only makes sense in parameterized meshes.
+ xaDebugAssert(m_isDisk);
+ xaDebugAssert(!m_isVertexMapped);
+ return halfedge::computeParametricArea(m_chartMesh.get());
+ }
+
+ Vector2 computeParametricBounds() const {
+ // This only makes sense in parameterized meshes.
+ xaDebugAssert(m_isDisk);
+ xaDebugAssert(!m_isVertexMapped);
+ Box bounds;
+ bounds.clearBounds();
+ uint32_t vertexCount = m_chartMesh->vertexCount();
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ halfedge::Vertex *vertex = m_chartMesh->vertexAt(v);
+ bounds.addPointToBounds(Vector3(vertex->tex, 0));
+ }
+ return bounds.extents().xy();
+ }
+
+ float scale = 1.0f;
+ uint32_t vertexMapWidth;
+ uint32_t vertexMapHeight;
+ bool blockAligned = true;
+
+private:
+ bool closeLoop(uint32_t start, const std::vector<halfedge::Edge *> &loop) {
+ const uint32_t vertexCount = loop.size() - start;
+ xaDebugAssert(vertexCount >= 3);
+ if (vertexCount < 3) return false;
+ xaDebugAssert(loop[start]->vertex->isColocal(loop[start + vertexCount - 1]->to()));
+ // If the hole is planar, then we add a single face that will be properly triangulated later.
+ // If the hole is not planar, we add a triangle fan with a vertex at the hole centroid.
+ // This is still a bit of a hack. There surely are better hole filling algorithms out there.
+ std::vector<Vector3> points(vertexCount);
+ for (uint32_t i = 0; i < vertexCount; i++) {
+ points[i] = loop[start + i]->vertex->pos;
+ }
+ bool isPlanar = Fit::isPlanar(vertexCount, points.data());
+ if (isPlanar) {
+ // Add face and connect edges.
+ halfedge::Face *face = m_unifiedMesh->addFace();
+ for (uint32_t i = 0; i < vertexCount; i++) {
+ halfedge::Edge *edge = loop[start + i];
+ edge->face = face;
+ edge->setNext(loop[start + (i + 1) % vertexCount]);
+ }
+ face->edge = loop[start];
+ xaDebugAssert(face->isValid());
+ } else {
+ // If the polygon is not planar, we just cross our fingers, and hope this will work:
+ // Compute boundary centroid:
+ Vector3 centroidPos(0);
+ for (uint32_t i = 0; i < vertexCount; i++) {
+ centroidPos += points[i];
+ }
+ centroidPos *= (1.0f / vertexCount);
+ halfedge::Vertex *centroid = m_unifiedMesh->addVertex(centroidPos);
+ // Add one pair of edges for each boundary vertex.
+ for (uint32_t j = vertexCount - 1, i = 0; i < vertexCount; j = i++) {
+ halfedge::Face *face = m_unifiedMesh->addFace(centroid->id, loop[start + j]->vertex->id, loop[start + i]->vertex->id);
+ xaDebugAssert(face != NULL);
+#ifdef NDEBUG
+ face = NULL; // silence unused parameter warning
+#endif
+ }
+ }
+ return true;
+ }
+
+ static void getBoundaryEdges(halfedge::Mesh *mesh, std::vector<halfedge::Edge *> &boundaryEdges) {
+ xaDebugAssert(mesh != NULL);
+ const uint32_t edgeCount = mesh->edgeCount();
+ BitArray bitFlags(edgeCount);
+ bitFlags.clearAll();
+ boundaryEdges.clear();
+ // Search for boundary edges. Mark all the edges that belong to the same boundary.
+ for (uint32_t e = 0; e < edgeCount; e++) {
+ halfedge::Edge *startEdge = mesh->edgeAt(e);
+ if (startEdge != NULL && startEdge->isBoundary() && bitFlags.bitAt(e) == false) {
+ xaDebugAssert(startEdge->face != NULL);
+ xaDebugAssert(startEdge->pair->face == NULL);
+ startEdge = startEdge->pair;
+ const halfedge::Edge *edge = startEdge;
+ do {
+ xaDebugAssert(edge->face == NULL);
+ xaDebugAssert(bitFlags.bitAt(edge->id / 2) == false);
+ bitFlags.setBitAt(edge->id / 2);
+ edge = edge->next;
+ } while (startEdge != edge);
+ boundaryEdges.push_back(startEdge);
+ }
+ }
+ }
+
+ // Chart mesh.
+ std::auto_ptr<halfedge::Mesh> m_chartMesh;
+
+ std::auto_ptr<halfedge::Mesh> m_unifiedMesh;
+ bool m_isDisk;
+ bool m_isVertexMapped;
+
+ // List of faces of the original mesh that belong to this chart.
+ std::vector<uint32_t> m_faceArray;
+
+ // Map vertices of the chart mesh to vertices of the original mesh.
+ std::vector<uint32_t> m_chartToOriginalMap;
+
+ std::vector<uint32_t> m_chartToUnifiedMap;
+};
+
+// Estimate quality of existing parameterization.
+class ParameterizationQuality {
+public:
+ ParameterizationQuality() {
+ m_totalTriangleCount = 0;
+ m_flippedTriangleCount = 0;
+ m_zeroAreaTriangleCount = 0;
+ m_parametricArea = 0.0f;
+ m_geometricArea = 0.0f;
+ m_stretchMetric = 0.0f;
+ m_maxStretchMetric = 0.0f;
+ m_conformalMetric = 0.0f;
+ m_authalicMetric = 0.0f;
+ }
+
+ ParameterizationQuality(const halfedge::Mesh *mesh) {
+ xaDebugAssert(mesh != NULL);
+ m_totalTriangleCount = 0;
+ m_flippedTriangleCount = 0;
+ m_zeroAreaTriangleCount = 0;
+ m_parametricArea = 0.0f;
+ m_geometricArea = 0.0f;
+ m_stretchMetric = 0.0f;
+ m_maxStretchMetric = 0.0f;
+ m_conformalMetric = 0.0f;
+ m_authalicMetric = 0.0f;
+ const uint32_t faceCount = mesh->faceCount();
+ for (uint32_t f = 0; f < faceCount; f++) {
+ const halfedge::Face *face = mesh->faceAt(f);
+ const halfedge::Vertex *vertex0 = NULL;
+ Vector3 p[3];
+ Vector2 t[3];
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Edge *edge = it.current();
+ if (vertex0 == NULL) {
+ vertex0 = edge->vertex;
+ p[0] = vertex0->pos;
+ t[0] = vertex0->tex;
+ } else if (edge->to() != vertex0) {
+ p[1] = edge->from()->pos;
+ p[2] = edge->to()->pos;
+ t[1] = edge->from()->tex;
+ t[2] = edge->to()->tex;
+ processTriangle(p, t);
+ }
+ }
+ }
+ if (m_flippedTriangleCount + m_zeroAreaTriangleCount == faceCount) {
+ // If all triangles are flipped, then none is.
+ m_flippedTriangleCount = 0;
+ }
+ xaDebugAssert(std::isfinite(m_parametricArea) && m_parametricArea >= 0);
+ xaDebugAssert(std::isfinite(m_geometricArea) && m_geometricArea >= 0);
+ xaDebugAssert(std::isfinite(m_stretchMetric));
+ xaDebugAssert(std::isfinite(m_maxStretchMetric));
+ xaDebugAssert(std::isfinite(m_conformalMetric));
+ xaDebugAssert(std::isfinite(m_authalicMetric));
+ }
+
+ bool isValid() const {
+ return m_flippedTriangleCount == 0; // @@ Does not test for self-overlaps.
+ }
+
+ float rmsStretchMetric() const {
+ if (m_geometricArea == 0) return 0.0f;
+ float normFactor = sqrtf(m_parametricArea / m_geometricArea);
+ return sqrtf(m_stretchMetric / m_geometricArea) * normFactor;
+ }
+
+ float maxStretchMetric() const {
+ if (m_geometricArea == 0) return 0.0f;
+ float normFactor = sqrtf(m_parametricArea / m_geometricArea);
+ return m_maxStretchMetric * normFactor;
+ }
+
+ float rmsConformalMetric() const {
+ if (m_geometricArea == 0) return 0.0f;
+ return sqrtf(m_conformalMetric / m_geometricArea);
+ }
+
+ float maxAuthalicMetric() const {
+ if (m_geometricArea == 0) return 0.0f;
+ return sqrtf(m_authalicMetric / m_geometricArea);
+ }
+
+ void operator+=(const ParameterizationQuality &pq) {
+ m_totalTriangleCount += pq.m_totalTriangleCount;
+ m_flippedTriangleCount += pq.m_flippedTriangleCount;
+ m_zeroAreaTriangleCount += pq.m_zeroAreaTriangleCount;
+ m_parametricArea += pq.m_parametricArea;
+ m_geometricArea += pq.m_geometricArea;
+ m_stretchMetric += pq.m_stretchMetric;
+ m_maxStretchMetric = std::max(m_maxStretchMetric, pq.m_maxStretchMetric);
+ m_conformalMetric += pq.m_conformalMetric;
+ m_authalicMetric += pq.m_authalicMetric;
+ }
+
+private:
+ void processTriangle(Vector3 q[3], Vector2 p[3]) {
+ m_totalTriangleCount++;
+ // Evaluate texture stretch metric. See:
+ // - "Texture Mapping Progressive Meshes", Sander, Snyder, Gortler & Hoppe
+ // - "Mesh Parameterization: Theory and Practice", Siggraph'07 Course Notes, Hormann, Levy & Sheffer.
+ float t1 = p[0].x;
+ float s1 = p[0].y;
+ float t2 = p[1].x;
+ float s2 = p[1].y;
+ float t3 = p[2].x;
+ float s3 = p[2].y;
+ float geometricArea = length(cross(q[1] - q[0], q[2] - q[0])) / 2;
+ float parametricArea = ((s2 - s1) * (t3 - t1) - (s3 - s1) * (t2 - t1)) / 2;
+ if (isZero(parametricArea)) {
+ m_zeroAreaTriangleCount++;
+ return;
+ }
+ Vector3 Ss = (q[0] * (t2 - t3) + q[1] * (t3 - t1) + q[2] * (t1 - t2)) / (2 * parametricArea);
+ Vector3 St = (q[0] * (s3 - s2) + q[1] * (s1 - s3) + q[2] * (s2 - s1)) / (2 * parametricArea);
+ float a = dot(Ss, Ss); // E
+ float b = dot(Ss, St); // F
+ float c = dot(St, St); // G
+ // Compute eigen-values of the first fundamental form:
+ float sigma1 = sqrtf(0.5f * std::max(0.0f, a + c - sqrtf(square(a - c) + 4 * square(b)))); // gamma uppercase, min eigenvalue.
+ float sigma2 = sqrtf(0.5f * std::max(0.0f, a + c + sqrtf(square(a - c) + 4 * square(b)))); // gamma lowercase, max eigenvalue.
+ xaAssert(sigma2 >= sigma1);
+ // isometric: sigma1 = sigma2 = 1
+ // conformal: sigma1 / sigma2 = 1
+ // authalic: sigma1 * sigma2 = 1
+ float rmsStretch = sqrtf((a + c) * 0.5f);
+ float rmsStretch2 = sqrtf((square(sigma1) + square(sigma2)) * 0.5f);
+ xaDebugAssert(equal(rmsStretch, rmsStretch2, 0.01f));
+#ifdef NDEBUG
+ rmsStretch2 = 0; // silence unused parameter warning
+#endif
+ if (parametricArea < 0.0f) {
+ // Count flipped triangles.
+ m_flippedTriangleCount++;
+ parametricArea = fabsf(parametricArea);
+ }
+ m_stretchMetric += square(rmsStretch) * geometricArea;
+ m_maxStretchMetric = std::max(m_maxStretchMetric, sigma2);
+ if (!isZero(sigma1, 0.000001f)) {
+ // sigma1 is zero when geometricArea is zero.
+ m_conformalMetric += (sigma2 / sigma1) * geometricArea;
+ }
+ m_authalicMetric += (sigma1 * sigma2) * geometricArea;
+ // Accumulate total areas.
+ m_geometricArea += geometricArea;
+ m_parametricArea += parametricArea;
+ //triangleConformalEnergy(q, p);
+ }
+
+ uint32_t m_totalTriangleCount;
+ uint32_t m_flippedTriangleCount;
+ uint32_t m_zeroAreaTriangleCount;
+ float m_parametricArea;
+ float m_geometricArea;
+ float m_stretchMetric;
+ float m_maxStretchMetric;
+ float m_conformalMetric;
+ float m_authalicMetric;
+};
+
+// Set of charts corresponding to a single mesh.
+class MeshCharts {
+public:
+ MeshCharts(const halfedge::Mesh *mesh) :
+ m_mesh(mesh) {}
+
+ ~MeshCharts() {
+ for (size_t i = 0; i < m_chartArray.size(); i++)
+ delete m_chartArray[i];
+ }
+
+ uint32_t chartCount() const {
+ return m_chartArray.size();
+ }
+ uint32_t vertexCount() const {
+ return m_totalVertexCount;
+ }
+
+ const Chart *chartAt(uint32_t i) const {
+ return m_chartArray[i];
+ }
+ Chart *chartAt(uint32_t i) {
+ return m_chartArray[i];
+ }
+
+ // Extract the charts of the input mesh.
+ void extractCharts() {
+ const uint32_t faceCount = m_mesh->faceCount();
+ int first = 0;
+ std::vector<uint32_t> queue;
+ queue.reserve(faceCount);
+ BitArray bitFlags(faceCount);
+ bitFlags.clearAll();
+ for (uint32_t f = 0; f < faceCount; f++) {
+ if (bitFlags.bitAt(f) == false) {
+ // Start new patch. Reset queue.
+ first = 0;
+ queue.clear();
+ queue.push_back(f);
+ bitFlags.setBitAt(f);
+ while (first != (int)queue.size()) {
+ const halfedge::Face *face = m_mesh->faceAt(queue[first]);
+ // Visit face neighbors of queue[first]
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ const halfedge::Edge *edge = it.current();
+ xaDebugAssert(edge->pair != NULL);
+ if (!edge->isBoundary() && /*!edge->isSeam()*/
+ //!(edge->from()->tex() != edge->pair()->to()->tex() || edge->to()->tex() != edge->pair()->from()->tex()))
+ !(edge->from() != edge->pair->to() || edge->to() != edge->pair->from())) { // Preserve existing seams (not just texture seams).
+ const halfedge::Face *neighborFace = edge->pair->face;
+ xaDebugAssert(neighborFace != NULL);
+ if (bitFlags.bitAt(neighborFace->id) == false) {
+ queue.push_back(neighborFace->id);
+ bitFlags.setBitAt(neighborFace->id);
+ }
+ }
+ }
+ first++;
+ }
+ Chart *chart = new Chart();
+ chart->build(m_mesh, queue);
+ m_chartArray.push_back(chart);
+ }
+ }
+ }
+
+ /*
+ Compute charts using a simple segmentation algorithm.
+
+ LSCM:
+ - identify sharp features using local dihedral angles.
+ - identify seed faces farthest from sharp features.
+ - grow charts from these seeds.
+
+ MCGIM:
+ - phase 1: chart growth
+ - grow all charts simultaneously using dijkstra search on the dual graph of the mesh.
+ - graph edges are weighted based on planarity metric.
+ - metric uses distance to global chart normal.
+ - terminate when all faces have been assigned.
+ - phase 2: seed computation:
+ - place new seed of the chart at the most interior face.
+ - most interior is evaluated using distance metric only.
+
+ - method repeates the two phases, until the location of the seeds does not change.
+ - cycles are detected by recording all the previous seeds and chartification terminates.
+
+ D-Charts:
+
+ - Uniaxial conic metric:
+ - N_c = axis of the generalized cone that best fits the chart. (cone can a be cylinder or a plane).
+ - omega_c = angle between the face normals and the axis.
+ - Fitting error between chart C and tringle t: F(c,t) = (N_c*n_t - cos(omega_c))^2
+
+ - Compactness metrics:
+ - Roundness:
+ - C(c,t) = pi * D(S_c,t)^2 / A_c
+ - S_c = chart seed.
+ - D(S_c,t) = length of the shortest path inside the chart betwen S_c and t.
+ - A_c = chart area.
+ - Straightness:
+ - P(c,t) = l_out(c,t) / l_in(c,t)
+ - l_out(c,t) = lenght of the edges not shared between C and t.
+ - l_in(c,t) = lenght of the edges shared between C and t.
+
+ - Combined metric:
+ - Cost(c,t) = F(c,t)^alpha + C(c,t)^beta + P(c,t)^gamma
+ - alpha = 1, beta = 0.7, gamma = 0.5
+
+ Our basic approach:
+ - Just one iteration of k-means?
+ - Avoid dijkstra by greedily growing charts until a threshold is met. Increase threshold and repeat until no faces left.
+ - If distortion metric is too high, split chart, add two seeds.
+ - If chart size is low, try removing chart.
+
+ Postprocess:
+ - If topology is not disk:
+ - Fill holes, if new faces fit proxy.
+ - Find best cut, otherwise.
+ - After parameterization:
+ - If boundary self-intersects:
+ - cut chart along the closest two diametral boundary vertices, repeat parametrization.
+ - what if the overlap is on an appendix? How do we find that out and cut appropiately?
+ - emphasize roundness metrics to prevent those cases.
+ - If interior self-overlaps: preserve boundary parameterization and use mean-value map.
+ */
+ void computeCharts(const CharterOptions &options, const std::vector<uint32_t> &unchartedMaterialArray) {
+ Chart *vertexMap = NULL;
+ if (unchartedMaterialArray.size() != 0) {
+ vertexMap = new Chart();
+ vertexMap->buildVertexMap(m_mesh, unchartedMaterialArray);
+ if (vertexMap->faceCount() == 0) {
+ delete vertexMap;
+ vertexMap = NULL;
+ }
+ }
+ AtlasBuilder builder(m_mesh);
+ if (vertexMap != NULL) {
+ // Mark faces that do not need to be charted.
+ builder.markUnchartedFaces(vertexMap->faceArray());
+ m_chartArray.push_back(vertexMap);
+ }
+ if (builder.facesLeft != 0) {
+ // Tweak these values:
+ const float maxThreshold = 2;
+ const uint32_t growFaceCount = 32;
+ const uint32_t maxIterations = 4;
+ builder.options = options;
+ //builder.options.proxyFitMetricWeight *= 0.75; // relax proxy fit weight during initial seed placement.
+ //builder.options.roundnessMetricWeight = 0;
+ //builder.options.straightnessMetricWeight = 0;
+ // This seems a reasonable estimate.
+ uint32_t maxSeedCount = std::max(6U, builder.facesLeft);
+ // Create initial charts greedely.
+ xaPrint("### Placing seeds\n");
+ builder.placeSeeds(maxThreshold, maxSeedCount);
+ xaPrint("### Placed %d seeds (max = %d)\n", builder.chartCount(), maxSeedCount);
+ builder.updateProxies();
+ builder.mergeCharts();
+#if 1
+ xaPrint("### Relocating seeds\n");
+ builder.relocateSeeds();
+ xaPrint("### Reset charts\n");
+ builder.resetCharts();
+ if (vertexMap != NULL) {
+ builder.markUnchartedFaces(vertexMap->faceArray());
+ }
+ builder.options = options;
+ xaPrint("### Growing charts\n");
+ // Restart process growing charts in parallel.
+ uint32_t iteration = 0;
+ while (true) {
+ if (!builder.growCharts(maxThreshold, growFaceCount)) {
+ xaPrint("### Can't grow anymore\n");
+ // If charts cannot grow more: fill holes, merge charts, relocate seeds and start new iteration.
+ xaPrint("### Filling holes\n");
+ builder.fillHoles(maxThreshold);
+ xaPrint("### Using %d charts now\n", builder.chartCount());
+ builder.updateProxies();
+ xaPrint("### Merging charts\n");
+ builder.mergeCharts();
+ xaPrint("### Using %d charts now\n", builder.chartCount());
+ xaPrint("### Reseeding\n");
+ if (!builder.relocateSeeds()) {
+ xaPrint("### Cannot relocate seeds anymore\n");
+ // Done!
+ break;
+ }
+ if (iteration == maxIterations) {
+ xaPrint("### Reached iteration limit\n");
+ break;
+ }
+ iteration++;
+ xaPrint("### Reset charts\n");
+ builder.resetCharts();
+ if (vertexMap != NULL) {
+ builder.markUnchartedFaces(vertexMap->faceArray());
+ }
+ xaPrint("### Growing charts\n");
+ }
+ };
+#endif
+ // Make sure no holes are left!
+ xaDebugAssert(builder.facesLeft == 0);
+ const uint32_t chartCount = builder.chartArray.size();
+ for (uint32_t i = 0; i < chartCount; i++) {
+ Chart *chart = new Chart();
+ m_chartArray.push_back(chart);
+ chart->build(m_mesh, builder.chartFaces(i));
+ }
+ }
+ const uint32_t chartCount = m_chartArray.size();
+ // Build face indices.
+ m_faceChart.resize(m_mesh->faceCount());
+ m_faceIndex.resize(m_mesh->faceCount());
+ for (uint32_t i = 0; i < chartCount; i++) {
+ const Chart *chart = m_chartArray[i];
+ const uint32_t faceCount = chart->faceCount();
+ for (uint32_t f = 0; f < faceCount; f++) {
+ uint32_t idx = chart->faceAt(f);
+ m_faceChart[idx] = i;
+ m_faceIndex[idx] = f;
+ }
+ }
+ // Build an exclusive prefix sum of the chart vertex counts.
+ m_chartVertexCountPrefixSum.resize(chartCount);
+ if (chartCount > 0) {
+ m_chartVertexCountPrefixSum[0] = 0;
+ for (uint32_t i = 1; i < chartCount; i++) {
+ const Chart *chart = m_chartArray[i - 1];
+ m_chartVertexCountPrefixSum[i] = m_chartVertexCountPrefixSum[i - 1] + chart->vertexCount();
+ }
+ m_totalVertexCount = m_chartVertexCountPrefixSum[chartCount - 1] + m_chartArray[chartCount - 1]->vertexCount();
+ } else {
+ m_totalVertexCount = 0;
+ }
+ }
+
+ void parameterizeCharts() {
+ ParameterizationQuality globalParameterizationQuality;
+ // Parameterize the charts.
+ uint32_t diskCount = 0;
+ const uint32_t chartCount = m_chartArray.size();
+ for (uint32_t i = 0; i < chartCount; i++) {
+ Chart *chart = m_chartArray[i];
+
+ bool isValid = false;
+
+ if (chart->isVertexMapped()) {
+ continue;
+ }
+
+ if (chart->isDisk()) {
+ diskCount++;
+ ParameterizationQuality chartParameterizationQuality;
+ if (chart->faceCount() == 1) {
+ computeSingleFaceMap(chart->unifiedMesh());
+ chartParameterizationQuality = ParameterizationQuality(chart->unifiedMesh());
+ } else {
+ computeOrthogonalProjectionMap(chart->unifiedMesh());
+ ParameterizationQuality orthogonalQuality(chart->unifiedMesh());
+ computeLeastSquaresConformalMap(chart->unifiedMesh());
+ ParameterizationQuality lscmQuality(chart->unifiedMesh());
+ chartParameterizationQuality = lscmQuality;
+ }
+ isValid = chartParameterizationQuality.isValid();
+ if (!isValid) {
+ xaPrint("*** Invalid parameterization.\n");
+ }
+ // @@ Check that parameterization quality is above a certain threshold.
+ // @@ Detect boundary self-intersections.
+ globalParameterizationQuality += chartParameterizationQuality;
+ }
+
+ // Transfer parameterization from unified mesh to chart mesh.
+ chart->transferParameterization();
+ }
+ xaPrint(" Parameterized %d/%d charts.\n", diskCount, chartCount);
+ xaPrint(" RMS stretch metric: %f\n", globalParameterizationQuality.rmsStretchMetric());
+ xaPrint(" MAX stretch metric: %f\n", globalParameterizationQuality.maxStretchMetric());
+ xaPrint(" RMS conformal metric: %f\n", globalParameterizationQuality.rmsConformalMetric());
+ xaPrint(" RMS authalic metric: %f\n", globalParameterizationQuality.maxAuthalicMetric());
+ }
+
+ uint32_t faceChartAt(uint32_t i) const {
+ return m_faceChart[i];
+ }
+ uint32_t faceIndexWithinChartAt(uint32_t i) const {
+ return m_faceIndex[i];
+ }
+
+ uint32_t vertexCountBeforeChartAt(uint32_t i) const {
+ return m_chartVertexCountPrefixSum[i];
+ }
+
+private:
+ const halfedge::Mesh *m_mesh;
+
+ std::vector<Chart *> m_chartArray;
+
+ std::vector<uint32_t> m_chartVertexCountPrefixSum;
+ uint32_t m_totalVertexCount;
+
+ std::vector<uint32_t> m_faceChart; // the chart of every face of the input mesh.
+ std::vector<uint32_t> m_faceIndex; // the index within the chart for every face of the input mesh.
+};
+
+/// An atlas is a set of charts.
+class Atlas {
+public:
+ ~Atlas() {
+ for (size_t i = 0; i < m_meshChartsArray.size(); i++)
+ delete m_meshChartsArray[i];
+ }
+
+ uint32_t meshCount() const {
+ return m_meshChartsArray.size();
+ }
+
+ const MeshCharts *meshAt(uint32_t i) const {
+ return m_meshChartsArray[i];
+ }
+
+ MeshCharts *meshAt(uint32_t i) {
+ return m_meshChartsArray[i];
+ }
+
+ uint32_t chartCount() const {
+ uint32_t count = 0;
+ for (uint32_t c = 0; c < m_meshChartsArray.size(); c++) {
+ count += m_meshChartsArray[c]->chartCount();
+ }
+ return count;
+ }
+
+ const Chart *chartAt(uint32_t i) const {
+ for (uint32_t c = 0; c < m_meshChartsArray.size(); c++) {
+ uint32_t count = m_meshChartsArray[c]->chartCount();
+ if (i < count) {
+ return m_meshChartsArray[c]->chartAt(i);
+ }
+ i -= count;
+ }
+ return NULL;
+ }
+
+ Chart *chartAt(uint32_t i) {
+ for (uint32_t c = 0; c < m_meshChartsArray.size(); c++) {
+ uint32_t count = m_meshChartsArray[c]->chartCount();
+ if (i < count) {
+ return m_meshChartsArray[c]->chartAt(i);
+ }
+ i -= count;
+ }
+ return NULL;
+ }
+
+ // Add mesh charts and takes ownership.
+ // Extract the charts and add to this atlas.
+ void addMeshCharts(MeshCharts *meshCharts) {
+ m_meshChartsArray.push_back(meshCharts);
+ }
+
+ void extractCharts(const halfedge::Mesh *mesh) {
+ MeshCharts *meshCharts = new MeshCharts(mesh);
+ meshCharts->extractCharts();
+ addMeshCharts(meshCharts);
+ }
+
+ void computeCharts(const halfedge::Mesh *mesh, const CharterOptions &options, const std::vector<uint32_t> &unchartedMaterialArray) {
+ MeshCharts *meshCharts = new MeshCharts(mesh);
+ meshCharts->computeCharts(options, unchartedMaterialArray);
+ addMeshCharts(meshCharts);
+ }
+
+ void parameterizeCharts() {
+ for (uint32_t i = 0; i < m_meshChartsArray.size(); i++) {
+ m_meshChartsArray[i]->parameterizeCharts();
+ }
+ }
+
+private:
+ std::vector<MeshCharts *> m_meshChartsArray;
+};
+
+struct AtlasPacker {
+ AtlasPacker(Atlas *atlas) :
+ m_atlas(atlas),
+ m_width(0),
+ m_height(0) {
+ // Save the original uvs.
+ m_originalChartUvs.resize(m_atlas->chartCount());
+ for (uint32_t i = 0; i < m_atlas->chartCount(); i++) {
+ const halfedge::Mesh *mesh = atlas->chartAt(i)->chartMesh();
+ m_originalChartUvs[i].resize(mesh->vertexCount());
+ for (uint32_t j = 0; j < mesh->vertexCount(); j++)
+ m_originalChartUvs[i][j] = mesh->vertexAt(j)->tex;
+ }
+ }
+
+ uint32_t getWidth() const { return m_width; }
+ uint32_t getHeight() const { return m_height; }
+
+ // Pack charts in the smallest possible rectangle.
+ void packCharts(const PackerOptions &options) {
+ const uint32_t chartCount = m_atlas->chartCount();
+ if (chartCount == 0) return;
+ float texelsPerUnit = 1;
+ if (options.method == PackMethod::TexelArea)
+ texelsPerUnit = options.texelArea;
+ for (int iteration = 0;; iteration++) {
+ m_rand = MTRand();
+ std::vector<float> chartOrderArray(chartCount);
+ std::vector<Vector2> chartExtents(chartCount);
+ float meshArea = 0;
+ for (uint32_t c = 0; c < chartCount; c++) {
+ Chart *chart = m_atlas->chartAt(c);
+ if (!chart->isVertexMapped() && !chart->isDisk()) {
+ chartOrderArray[c] = 0;
+ // Skip non-disks.
+ continue;
+ }
+ Vector2 extents(0.0f);
+ if (chart->isVertexMapped()) {
+ // Arrange vertices in a rectangle.
+ extents.x = float(chart->vertexMapWidth);
+ extents.y = float(chart->vertexMapHeight);
+ } else {
+ // Compute surface area to sort charts.
+ float chartArea = chart->computeSurfaceArea();
+ meshArea += chartArea;
+ //chartOrderArray[c] = chartArea;
+ // Compute chart scale
+ float parametricArea = fabsf(chart->computeParametricArea()); // @@ There doesn't seem to be anything preventing parametric area to be negative.
+ if (parametricArea < NV_EPSILON) {
+ // When the parametric area is too small we use a rough approximation to prevent divisions by very small numbers.
+ Vector2 bounds = chart->computeParametricBounds();
+ parametricArea = bounds.x * bounds.y;
+ }
+ float scale = (chartArea / parametricArea) * texelsPerUnit;
+ if (parametricArea == 0) { // < NV_EPSILON)
+ scale = 0;
+ }
+ xaAssert(std::isfinite(scale));
+ // Compute bounding box of chart.
+ Vector2 majorAxis, minorAxis, origin, end;
+ computeBoundingBox(chart, &majorAxis, &minorAxis, &origin, &end);
+ xaAssert(isFinite(majorAxis) && isFinite(minorAxis) && isFinite(origin));
+ // Sort charts by perimeter. @@ This is sometimes producing somewhat unexpected results. Is this right?
+ //chartOrderArray[c] = ((end.x - origin.x) + (end.y - origin.y)) * scale;
+ // Translate, rotate and scale vertices. Compute extents.
+ halfedge::Mesh *mesh = chart->chartMesh();
+ const uint32_t vertexCount = mesh->vertexCount();
+ for (uint32_t i = 0; i < vertexCount; i++) {
+ halfedge::Vertex *vertex = mesh->vertexAt(i);
+ //Vector2 t = vertex->tex - origin;
+ Vector2 tmp;
+ tmp.x = dot(vertex->tex, majorAxis);
+ tmp.y = dot(vertex->tex, minorAxis);
+ tmp -= origin;
+ tmp *= scale;
+ if (tmp.x < 0 || tmp.y < 0) {
+ xaPrint("tmp: %f %f\n", tmp.x, tmp.y);
+ xaPrint("scale: %f\n", scale);
+ xaPrint("origin: %f %f\n", origin.x, origin.y);
+ xaPrint("majorAxis: %f %f\n", majorAxis.x, majorAxis.y);
+ xaPrint("minorAxis: %f %f\n", minorAxis.x, minorAxis.y);
+ xaDebugAssert(false);
+ }
+ //xaAssert(tmp.x >= 0 && tmp.y >= 0);
+ vertex->tex = tmp;
+ xaAssert(std::isfinite(vertex->tex.x) && std::isfinite(vertex->tex.y));
+ extents = max(extents, tmp);
+ }
+ xaDebugAssert(extents.x >= 0 && extents.y >= 0);
+ // Limit chart size.
+ if (extents.x > 1024 || extents.y > 1024) {
+ float limit = std::max(extents.x, extents.y);
+ scale = 1024 / (limit + 1);
+ for (uint32_t i = 0; i < vertexCount; i++) {
+ halfedge::Vertex *vertex = mesh->vertexAt(i);
+ vertex->tex *= scale;
+ }
+ extents *= scale;
+ xaDebugAssert(extents.x <= 1024 && extents.y <= 1024);
+ }
+ // Scale the charts to use the entire texel area available. So, if the width is 0.1 we could scale it to 1 without increasing the lightmap usage and making a better
+ // use of it. In many cases this also improves the look of the seams, since vertices on the chart boundaries have more chances of being aligned with the texel centers.
+ float scale_x = 1.0f;
+ float scale_y = 1.0f;
+ float divide_x = 1.0f;
+ float divide_y = 1.0f;
+ if (extents.x > 0) {
+ int cw = ftoi_ceil(extents.x);
+ if (options.blockAlign && chart->blockAligned) {
+ // Align all chart extents to 4x4 blocks, but taking padding into account.
+ if (options.conservative) {
+ cw = align(cw + 2, 4) - 2;
+ } else {
+ cw = align(cw + 1, 4) - 1;
+ }
+ }
+ scale_x = (float(cw) - NV_EPSILON);
+ divide_x = extents.x;
+ extents.x = float(cw);
+ }
+ if (extents.y > 0) {
+ int ch = ftoi_ceil(extents.y);
+ if (options.blockAlign && chart->blockAligned) {
+ // Align all chart extents to 4x4 blocks, but taking padding into account.
+ if (options.conservative) {
+ ch = align(ch + 2, 4) - 2;
+ } else {
+ ch = align(ch + 1, 4) - 1;
+ }
+ }
+ scale_y = (float(ch) - NV_EPSILON);
+ divide_y = extents.y;
+ extents.y = float(ch);
+ }
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ halfedge::Vertex *vertex = mesh->vertexAt(v);
+ vertex->tex.x /= divide_x;
+ vertex->tex.y /= divide_y;
+ vertex->tex.x *= scale_x;
+ vertex->tex.y *= scale_y;
+ xaAssert(std::isfinite(vertex->tex.x) && std::isfinite(vertex->tex.y));
+ }
+ }
+ chartExtents[c] = extents;
+ // Sort charts by perimeter.
+ chartOrderArray[c] = extents.x + extents.y;
+ }
+ // @@ We can try to improve compression of small charts by sorting them by proximity like we do with vertex samples.
+ // @@ How to do that? One idea: compute chart centroid, insert into grid, compute morton index of the cell, sort based on morton index.
+ // @@ We would sort by morton index, first, then quantize the chart sizes, so that all small charts have the same size, and sort by size preserving the morton order.
+ //xaPrint("Sorting charts.\n");
+ // Sort charts by area.
+ m_radix = RadixSort();
+ m_radix.sort(chartOrderArray);
+ const uint32_t *ranks = m_radix.ranks();
+ // First iteration - guess texelsPerUnit.
+ if (options.method != PackMethod::TexelArea && iteration == 0) {
+ // Estimate size of the map based on the mesh surface area and given texel scale.
+ const float texelCount = std::max(1.0f, meshArea * square(texelsPerUnit) / 0.75f); // Assume 75% utilization.
+ texelsPerUnit = sqrt((options.resolution * options.resolution) / texelCount);
+ resetUvs();
+ continue;
+ }
+ // Init bit map.
+ m_bitmap.clearAll();
+ m_bitmap.resize(options.resolution, options.resolution, false);
+ int w = 0;
+ int h = 0;
+ // Add sorted charts to bitmap.
+ for (uint32_t i = 0; i < chartCount; i++) {
+ uint32_t c = ranks[chartCount - i - 1]; // largest chart first
+ Chart *chart = m_atlas->chartAt(c);
+ if (!chart->isVertexMapped() && !chart->isDisk()) continue;
+ //float scale_x = 1;
+ //float scale_y = 1;
+ BitMap chart_bitmap;
+ if (chart->isVertexMapped()) {
+ chart->blockAligned = false;
+ // Init all bits to 1.
+ chart_bitmap.resize(ftoi_ceil(chartExtents[c].x), ftoi_ceil(chartExtents[c].y), /*initValue=*/true);
+ // @@ Another alternative would be to try to map each vertex to a different texel trying to fill all the available unused texels.
+ } else {
+ // @@ Add special cases for dot and line charts. @@ Lightmap rasterizer also needs to handle these special cases.
+ // @@ We could also have a special case for chart quads. If the quad surface <= 4 texels, align vertices with texel centers and do not add padding. May be very useful for foliage.
+ // @@ In general we could reduce the padding of all charts by one texel by using a rasterizer that takes into account the 2-texel footprint of the tent bilinear filter. For example,
+ // if we have a chart that is less than 1 texel wide currently we add one texel to the left and one texel to the right creating a 3-texel-wide bitmap. However, if we know that the
+ // chart is only 1 texel wide we could align it so that it only touches the footprint of two texels:
+ // | | <- Touches texels 0, 1 and 2.
+ // | | <- Only touches texels 0 and 1.
+ // \ \ / \ / /
+ // \ X X /
+ // \ / \ / \ /
+ // V V V
+ // 0 1 2
+ if (options.conservative) {
+ // Init all bits to 0.
+ chart_bitmap.resize(ftoi_ceil(chartExtents[c].x) + 1 + options.padding, ftoi_ceil(chartExtents[c].y) + 1 + options.padding, /*initValue=*/false); // + 2 to add padding on both sides.
+ // Rasterize chart and dilate.
+ drawChartBitmapDilate(chart, &chart_bitmap, options.padding);
+ } else {
+ // Init all bits to 0.
+ chart_bitmap.resize(ftoi_ceil(chartExtents[c].x) + 1, ftoi_ceil(chartExtents[c].y) + 1, /*initValue=*/false); // Add half a texels on each side.
+ // Rasterize chart and dilate.
+ drawChartBitmap(chart, &chart_bitmap, Vector2(1), Vector2(0.5));
+ }
+ }
+ int best_x, best_y;
+ int best_cw, best_ch; // Includes padding now.
+ int best_r;
+ findChartLocation(options.quality, &chart_bitmap, chartExtents[c], w, h, &best_x, &best_y, &best_cw, &best_ch, &best_r, chart->blockAligned);
+ /*if (w < best_x + best_cw || h < best_y + best_ch)
+ {
+ xaPrint("Resize extents to (%d, %d).\n", best_x + best_cw, best_y + best_ch);
+ }*/
+ // Update parametric extents.
+ w = std::max(w, best_x + best_cw);
+ h = std::max(h, best_y + best_ch);
+ w = align(w, 4);
+ h = align(h, 4);
+ // Resize bitmap if necessary.
+ if (uint32_t(w) > m_bitmap.width() || uint32_t(h) > m_bitmap.height()) {
+ //xaPrint("Resize bitmap (%d, %d).\n", nextPowerOfTwo(w), nextPowerOfTwo(h));
+ m_bitmap.resize(nextPowerOfTwo(uint32_t(w)), nextPowerOfTwo(uint32_t(h)), false);
+ }
+ //xaPrint("Add chart at (%d, %d).\n", best_x, best_y);
+ addChart(&chart_bitmap, w, h, best_x, best_y, best_r);
+ //float best_angle = 2 * PI * best_r;
+ // Translate and rotate chart texture coordinates.
+ halfedge::Mesh *mesh = chart->chartMesh();
+ const uint32_t vertexCount = mesh->vertexCount();
+ for (uint32_t v = 0; v < vertexCount; v++) {
+ halfedge::Vertex *vertex = mesh->vertexAt(v);
+ Vector2 t = vertex->tex;
+ if (best_r) std::swap(t.x, t.y);
+ //vertex->tex.x = best_x + t.x * cosf(best_angle) - t.y * sinf(best_angle);
+ //vertex->tex.y = best_y + t.x * sinf(best_angle) + t.y * cosf(best_angle);
+ vertex->tex.x = best_x + t.x + 0.5f;
+ vertex->tex.y = best_y + t.y + 0.5f;
+ xaAssert(vertex->tex.x >= 0 && vertex->tex.y >= 0);
+ xaAssert(std::isfinite(vertex->tex.x) && std::isfinite(vertex->tex.y));
+ }
+ }
+ //w -= padding - 1; // Leave one pixel border!
+ //h -= padding - 1;
+ m_width = std::max(0, w);
+ m_height = std::max(0, h);
+ xaAssert(isAligned(m_width, 4));
+ xaAssert(isAligned(m_height, 4));
+ if (options.method == PackMethod::ExactResolution) {
+ texelsPerUnit *= sqrt((options.resolution * options.resolution) / (float)(m_width * m_height));
+ if (iteration > 1 && m_width <= options.resolution && m_height <= options.resolution) {
+ m_width = m_height = options.resolution;
+ return;
+ }
+ resetUvs();
+ } else {
+ return;
+ }
+ }
+ }
+
+ float computeAtlasUtilization() const {
+ const uint32_t w = m_width;
+ const uint32_t h = m_height;
+ xaDebugAssert(w <= m_bitmap.width());
+ xaDebugAssert(h <= m_bitmap.height());
+ uint32_t count = 0;
+ for (uint32_t y = 0; y < h; y++) {
+ for (uint32_t x = 0; x < w; x++) {
+ count += m_bitmap.bitAt(x, y);
+ }
+ }
+ return float(count) / (w * h);
+ }
+
+private:
+ void resetUvs() {
+ for (uint32_t i = 0; i < m_atlas->chartCount(); i++) {
+ halfedge::Mesh *mesh = m_atlas->chartAt(i)->chartMesh();
+ for (uint32_t j = 0; j < mesh->vertexCount(); j++)
+ mesh->vertexAt(j)->tex = m_originalChartUvs[i][j];
+ }
+ }
+
+ // IC: Brute force is slow, and random may take too much time to converge. We start inserting large charts in a small atlas. Using brute force is lame, because most of the space
+ // is occupied at this point. At the end we have many small charts and a large atlas with sparse holes. Finding those holes randomly is slow. A better approach would be to
+ // start stacking large charts as if they were tetris pieces. Once charts get small try to place them randomly. It may be interesting to try a intermediate strategy, first try
+ // along one axis and then try exhaustively along that axis.
+ void findChartLocation(int quality, const BitMap *bitmap, Vector2::Arg extents, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned) {
+ int attempts = 256;
+ if (quality == 1) attempts = 4096;
+ if (quality == 2) attempts = 2048;
+ if (quality == 3) attempts = 1024;
+ if (quality == 4) attempts = 512;
+ if (quality == 0 || w * h < attempts) {
+ findChartLocation_bruteForce(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r, blockAligned);
+ } else {
+ findChartLocation_random(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r, attempts, blockAligned);
+ }
+ }
+
+ void findChartLocation_bruteForce(const BitMap *bitmap, Vector2::Arg /*extents*/, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned) {
+ const int BLOCK_SIZE = 4;
+ int best_metric = INT_MAX;
+ int step_size = blockAligned ? BLOCK_SIZE : 1;
+ // Try two different orientations.
+ for (int r = 0; r < 2; r++) {
+ int cw = bitmap->width();
+ int ch = bitmap->height();
+ if (r & 1) std::swap(cw, ch);
+ for (int y = 0; y <= h + 1; y += step_size) { // + 1 to extend atlas in case atlas full.
+ for (int x = 0; x <= w + 1; x += step_size) { // + 1 not really necessary here.
+ // Early out.
+ int area = std::max(w, x + cw) * std::max(h, y + ch);
+ //int perimeter = max(w, x+cw) + max(h, y+ch);
+ int extents = std::max(std::max(w, x + cw), std::max(h, y + ch));
+ int metric = extents * extents + area;
+ if (metric > best_metric) {
+ continue;
+ }
+ if (metric == best_metric && std::max(x, y) >= std::max(*best_x, *best_y)) {
+ // If metric is the same, pick the one closest to the origin.
+ continue;
+ }
+ if (canAddChart(bitmap, w, h, x, y, r)) {
+ best_metric = metric;
+ *best_x = x;
+ *best_y = y;
+ *best_w = cw;
+ *best_h = ch;
+ *best_r = r;
+ if (area == w * h) {
+ // Chart is completely inside, do not look at any other location.
+ goto done;
+ }
+ }
+ }
+ }
+ }
+ done:
+ xaDebugAssert(best_metric != INT_MAX);
+ }
+
+ void findChartLocation_random(const BitMap *bitmap, Vector2::Arg /*extents*/, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, int minTrialCount, bool blockAligned) {
+ const int BLOCK_SIZE = 4;
+ int best_metric = INT_MAX;
+ for (int i = 0; i < minTrialCount || best_metric == INT_MAX; i++) {
+ int r = m_rand.getRange(1);
+ int x = m_rand.getRange(w + 1); // + 1 to extend atlas in case atlas full. We may want to use a higher number to increase probability of extending atlas.
+ int y = m_rand.getRange(h + 1); // + 1 to extend atlas in case atlas full.
+ if (blockAligned) {
+ x = align(x, BLOCK_SIZE);
+ y = align(y, BLOCK_SIZE);
+ }
+ int cw = bitmap->width();
+ int ch = bitmap->height();
+ if (r & 1) std::swap(cw, ch);
+ // Early out.
+ int area = std::max(w, x + cw) * std::max(h, y + ch);
+ //int perimeter = max(w, x+cw) + max(h, y+ch);
+ int extents = std::max(std::max(w, x + cw), std::max(h, y + ch));
+ int metric = extents * extents + area;
+ if (metric > best_metric) {
+ continue;
+ }
+ if (metric == best_metric && std::min(x, y) > std::min(*best_x, *best_y)) {
+ // If metric is the same, pick the one closest to the origin.
+ continue;
+ }
+ if (canAddChart(bitmap, w, h, x, y, r)) {
+ best_metric = metric;
+ *best_x = x;
+ *best_y = y;
+ *best_w = cw;
+ *best_h = ch;
+ *best_r = r;
+ if (area == w * h) {
+ // Chart is completely inside, do not look at any other location.
+ break;
+ }
+ }
+ }
+ }
+
+ void drawChartBitmapDilate(const Chart *chart, BitMap *bitmap, int padding) {
+ const int w = bitmap->width();
+ const int h = bitmap->height();
+ const Vector2 extents = Vector2(float(w), float(h));
+ // Rasterize chart faces, check that all bits are not set.
+ const uint32_t faceCount = chart->faceCount();
+ for (uint32_t f = 0; f < faceCount; f++) {
+ const halfedge::Face *face = chart->chartMesh()->faceAt(f);
+ Vector2 vertices[4];
+ uint32_t edgeCount = 0;
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ if (edgeCount < 4) {
+ vertices[edgeCount] = it.vertex()->tex + Vector2(0.5) + Vector2(float(padding), float(padding));
+ }
+ edgeCount++;
+ }
+ if (edgeCount == 3) {
+ raster::drawTriangle(raster::Mode_Antialiased, extents, true, vertices, AtlasPacker::setBitsCallback, bitmap);
+ } else {
+ raster::drawQuad(raster::Mode_Antialiased, extents, true, vertices, AtlasPacker::setBitsCallback, bitmap);
+ }
+ }
+ // Expand chart by padding pixels. (dilation)
+ BitMap tmp(w, h);
+ for (int i = 0; i < padding; i++) {
+ tmp.clearAll();
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ bool b = bitmap->bitAt(x, y);
+ if (!b) {
+ if (x > 0) {
+ b |= bitmap->bitAt(x - 1, y);
+ if (y > 0) b |= bitmap->bitAt(x - 1, y - 1);
+ if (y < h - 1) b |= bitmap->bitAt(x - 1, y + 1);
+ }
+ if (y > 0) b |= bitmap->bitAt(x, y - 1);
+ if (y < h - 1) b |= bitmap->bitAt(x, y + 1);
+ if (x < w - 1) {
+ b |= bitmap->bitAt(x + 1, y);
+ if (y > 0) b |= bitmap->bitAt(x + 1, y - 1);
+ if (y < h - 1) b |= bitmap->bitAt(x + 1, y + 1);
+ }
+ }
+ if (b) tmp.setBitAt(x, y);
+ }
+ }
+ std::swap(tmp, *bitmap);
+ }
+ }
+
+ void drawChartBitmap(const Chart *chart, BitMap *bitmap, const Vector2 &scale, const Vector2 &offset) {
+ const int w = bitmap->width();
+ const int h = bitmap->height();
+ const Vector2 extents = Vector2(float(w), float(h));
+ static const Vector2 pad[4] = {
+ Vector2(-0.5, -0.5),
+ Vector2(0.5, -0.5),
+ Vector2(-0.5, 0.5),
+ Vector2(0.5, 0.5)
+ };
+ // Rasterize 4 times to add proper padding.
+ for (int i = 0; i < 4; i++) {
+ // Rasterize chart faces, check that all bits are not set.
+ const uint32_t faceCount = chart->chartMesh()->faceCount();
+ for (uint32_t f = 0; f < faceCount; f++) {
+ const halfedge::Face *face = chart->chartMesh()->faceAt(f);
+ Vector2 vertices[4];
+ uint32_t edgeCount = 0;
+ for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
+ if (edgeCount < 4) {
+ vertices[edgeCount] = it.vertex()->tex * scale + offset + pad[i];
+ xaAssert(ftoi_ceil(vertices[edgeCount].x) >= 0);
+ xaAssert(ftoi_ceil(vertices[edgeCount].y) >= 0);
+ xaAssert(ftoi_ceil(vertices[edgeCount].x) <= w);
+ xaAssert(ftoi_ceil(vertices[edgeCount].y) <= h);
+ }
+ edgeCount++;
+ }
+ if (edgeCount == 3) {
+ raster::drawTriangle(raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::setBitsCallback, bitmap);
+ } else {
+ raster::drawQuad(raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::setBitsCallback, bitmap);
+ }
+ }
+ }
+ // Expand chart by padding pixels. (dilation)
+ BitMap tmp(w, h);
+ tmp.clearAll();
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ bool b = bitmap->bitAt(x, y);
+ if (!b) {
+ if (x > 0) {
+ b |= bitmap->bitAt(x - 1, y);
+ if (y > 0) b |= bitmap->bitAt(x - 1, y - 1);
+ if (y < h - 1) b |= bitmap->bitAt(x - 1, y + 1);
+ }
+ if (y > 0) b |= bitmap->bitAt(x, y - 1);
+ if (y < h - 1) b |= bitmap->bitAt(x, y + 1);
+ if (x < w - 1) {
+ b |= bitmap->bitAt(x + 1, y);
+ if (y > 0) b |= bitmap->bitAt(x + 1, y - 1);
+ if (y < h - 1) b |= bitmap->bitAt(x + 1, y + 1);
+ }
+ }
+ if (b) tmp.setBitAt(x, y);
+ }
+ }
+ std::swap(tmp, *bitmap);
+ }
+
+ bool canAddChart(const BitMap *bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r) {
+ xaDebugAssert(r == 0 || r == 1);
+ // Check whether the two bitmaps overlap.
+ const int w = bitmap->width();
+ const int h = bitmap->height();
+ if (r == 0) {
+ for (int y = 0; y < h; y++) {
+ int yy = y + offset_y;
+ if (yy >= 0) {
+ for (int x = 0; x < w; x++) {
+ int xx = x + offset_x;
+ if (xx >= 0) {
+ if (bitmap->bitAt(x, y)) {
+ if (xx < atlas_w && yy < atlas_h) {
+ if (m_bitmap.bitAt(xx, yy)) return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ } else if (r == 1) {
+ for (int y = 0; y < h; y++) {
+ int xx = y + offset_x;
+ if (xx >= 0) {
+ for (int x = 0; x < w; x++) {
+ int yy = x + offset_y;
+ if (yy >= 0) {
+ if (bitmap->bitAt(x, y)) {
+ if (xx < atlas_w && yy < atlas_h) {
+ if (m_bitmap.bitAt(xx, yy)) return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ void addChart(const BitMap *bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r) {
+ xaDebugAssert(r == 0 || r == 1);
+ // Check whether the two bitmaps overlap.
+ const int w = bitmap->width();
+ const int h = bitmap->height();
+ if (r == 0) {
+ for (int y = 0; y < h; y++) {
+ int yy = y + offset_y;
+ if (yy >= 0) {
+ for (int x = 0; x < w; x++) {
+ int xx = x + offset_x;
+ if (xx >= 0) {
+ if (bitmap->bitAt(x, y)) {
+ if (xx < atlas_w && yy < atlas_h) {
+ xaDebugAssert(m_bitmap.bitAt(xx, yy) == false);
+ m_bitmap.setBitAt(xx, yy);
+ }
+ }
+ }
+ }
+ }
+ }
+ } else if (r == 1) {
+ for (int y = 0; y < h; y++) {
+ int xx = y + offset_x;
+ if (xx >= 0) {
+ for (int x = 0; x < w; x++) {
+ int yy = x + offset_y;
+ if (yy >= 0) {
+ if (bitmap->bitAt(x, y)) {
+ if (xx < atlas_w && yy < atlas_h) {
+ xaDebugAssert(m_bitmap.bitAt(xx, yy) == false);
+ m_bitmap.setBitAt(xx, yy);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ static bool setBitsCallback(void *param, int x, int y, Vector3::Arg, Vector3::Arg, Vector3::Arg, float area) {
+ BitMap *bitmap = (BitMap *)param;
+ if (area > 0.0) {
+ bitmap->setBitAt(x, y);
+ }
+ return true;
+ }
+
+ // Compute the convex hull using Graham Scan.
+ static void convexHull(const std::vector<Vector2> &input, std::vector<Vector2> &output, float epsilon) {
+ const uint32_t inputCount = input.size();
+ std::vector<float> coords(inputCount);
+ for (uint32_t i = 0; i < inputCount; i++) {
+ coords[i] = input[i].x;
+ }
+ RadixSort radix;
+ radix.sort(coords);
+ const uint32_t *ranks = radix.ranks();
+ std::vector<Vector2> top;
+ top.reserve(inputCount);
+ std::vector<Vector2> bottom;
+ bottom.reserve(inputCount);
+ Vector2 P = input[ranks[0]];
+ Vector2 Q = input[ranks[inputCount - 1]];
+ float topy = std::max(P.y, Q.y);
+ float boty = std::min(P.y, Q.y);
+ for (uint32_t i = 0; i < inputCount; i++) {
+ Vector2 p = input[ranks[i]];
+ if (p.y >= boty) top.push_back(p);
+ }
+ for (uint32_t i = 0; i < inputCount; i++) {
+ Vector2 p = input[ranks[inputCount - 1 - i]];
+ if (p.y <= topy) bottom.push_back(p);
+ }
+ // Filter top list.
+ output.clear();
+ output.push_back(top[0]);
+ output.push_back(top[1]);
+ for (uint32_t i = 2; i < top.size();) {
+ Vector2 a = output[output.size() - 2];
+ Vector2 b = output[output.size() - 1];
+ Vector2 c = top[i];
+ float area = triangleArea(a, b, c);
+ if (area >= -epsilon) {
+ output.pop_back();
+ }
+ if (area < -epsilon || output.size() == 1) {
+ output.push_back(c);
+ i++;
+ }
+ }
+ uint32_t top_count = output.size();
+ output.push_back(bottom[1]);
+ // Filter bottom list.
+ for (uint32_t i = 2; i < bottom.size();) {
+ Vector2 a = output[output.size() - 2];
+ Vector2 b = output[output.size() - 1];
+ Vector2 c = bottom[i];
+ float area = triangleArea(a, b, c);
+ if (area >= -epsilon) {
+ output.pop_back();
+ }
+ if (area < -epsilon || output.size() == top_count) {
+ output.push_back(c);
+ i++;
+ }
+ }
+ // Remove duplicate element.
+ xaDebugAssert(output.front() == output.back());
+ output.pop_back();
+ }
+
+ // This should compute convex hull and use rotating calipers to find the best box. Currently it uses a brute force method.
+ static void computeBoundingBox(Chart *chart, Vector2 *majorAxis, Vector2 *minorAxis, Vector2 *minCorner, Vector2 *maxCorner) {
+ // Compute list of boundary points.
+ std::vector<Vector2> points;
+ points.reserve(16);
+ halfedge::Mesh *mesh = chart->chartMesh();
+ const uint32_t vertexCount = mesh->vertexCount();
+ for (uint32_t i = 0; i < vertexCount; i++) {
+ halfedge::Vertex *vertex = mesh->vertexAt(i);
+ if (vertex->isBoundary()) {
+ points.push_back(vertex->tex);
+ }
+ }
+ xaDebugAssert(points.size() > 0);
+ std::vector<Vector2> hull;
+ convexHull(points, hull, 0.00001f);
+ // @@ Ideally I should use rotating calipers to find the best box. Using brute force for now.
+ float best_area = FLT_MAX;
+ Vector2 best_min;
+ Vector2 best_max;
+ Vector2 best_axis;
+ const uint32_t hullCount = hull.size();
+ for (uint32_t i = 0, j = hullCount - 1; i < hullCount; j = i, i++) {
+ if (equal(hull[i], hull[j])) {
+ continue;
+ }
+ Vector2 axis = normalize(hull[i] - hull[j], 0.0f);
+ xaDebugAssert(isFinite(axis));
+ // Compute bounding box.
+ Vector2 box_min(FLT_MAX, FLT_MAX);
+ Vector2 box_max(-FLT_MAX, -FLT_MAX);
+ for (uint32_t v = 0; v < hullCount; v++) {
+ Vector2 point = hull[v];
+ float x = dot(axis, point);
+ if (x < box_min.x) box_min.x = x;
+ if (x > box_max.x) box_max.x = x;
+ float y = dot(Vector2(-axis.y, axis.x), point);
+ if (y < box_min.y) box_min.y = y;
+ if (y > box_max.y) box_max.y = y;
+ }
+ // Compute box area.
+ float area = (box_max.x - box_min.x) * (box_max.y - box_min.y);
+ if (area < best_area) {
+ best_area = area;
+ best_min = box_min;
+ best_max = box_max;
+ best_axis = axis;
+ }
+ }
+ // Consider all points, not only boundary points, in case the input chart is malformed.
+ for (uint32_t i = 0; i < vertexCount; i++) {
+ halfedge::Vertex *vertex = mesh->vertexAt(i);
+ Vector2 point = vertex->tex;
+ float x = dot(best_axis, point);
+ if (x < best_min.x) best_min.x = x;
+ if (x > best_max.x) best_max.x = x;
+ float y = dot(Vector2(-best_axis.y, best_axis.x), point);
+ if (y < best_min.y) best_min.y = y;
+ if (y > best_max.y) best_max.y = y;
+ }
+ *majorAxis = best_axis;
+ *minorAxis = Vector2(-best_axis.y, best_axis.x);
+ *minCorner = best_min;
+ *maxCorner = best_max;
+ }
+
+ Atlas *m_atlas;
+ BitMap m_bitmap;
+ RadixSort m_radix;
+ uint32_t m_width;
+ uint32_t m_height;
+ MTRand m_rand;
+ std::vector<std::vector<Vector2> > m_originalChartUvs;
+};
+
+} // namespace param
+} // namespace internal
+
+struct Atlas {
+ internal::param::Atlas atlas;
+ std::vector<internal::halfedge::Mesh *> heMeshes;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ OutputMesh **outputMeshes = NULL;
+};
+
+void SetPrint(PrintFunc print) {
+ internal::s_print = print;
+}
+
+Atlas *Create() {
+ Atlas *atlas = new Atlas();
+ return atlas;
+}
+
+void Destroy(Atlas *atlas) {
+ xaAssert(atlas);
+ for (int i = 0; i < (int)atlas->heMeshes.size(); i++) {
+ delete atlas->heMeshes[i];
+ if (atlas->outputMeshes) {
+ OutputMesh *outputMesh = atlas->outputMeshes[i];
+ for (uint32_t j = 0; j < outputMesh->chartCount; j++)
+ delete[] outputMesh->chartArray[j].indexArray;
+ delete[] outputMesh->chartArray;
+ delete[] outputMesh->vertexArray;
+ delete[] outputMesh->indexArray;
+ delete outputMesh;
+ }
+ }
+ delete[] atlas->outputMeshes;
+ delete atlas;
+}
+
+static internal::Vector3 DecodePosition(const InputMesh &mesh, uint32_t index) {
+ xaAssert(mesh.vertexPositionData);
+ return *((const internal::Vector3 *)&((const uint8_t *)mesh.vertexPositionData)[mesh.vertexPositionStride * index]);
+}
+
+static internal::Vector3 DecodeNormal(const InputMesh &mesh, uint32_t index) {
+ xaAssert(mesh.vertexNormalData);
+ return *((const internal::Vector3 *)&((const uint8_t *)mesh.vertexNormalData)[mesh.vertexNormalStride * index]);
+}
+
+static internal::Vector2 DecodeUv(const InputMesh &mesh, uint32_t index) {
+ xaAssert(mesh.vertexUvData);
+ return *((const internal::Vector2 *)&((const uint8_t *)mesh.vertexUvData)[mesh.vertexUvStride * index]);
+}
+
+static uint32_t DecodeIndex(IndexFormat::Enum format, const void *indexData, uint32_t i) {
+ if (format == IndexFormat::HalfFloat)
+ return (uint32_t)((const uint16_t *)indexData)[i];
+ return ((const uint32_t *)indexData)[i];
+}
+
+static float EdgeLength(internal::Vector3 pos1, internal::Vector3 pos2) {
+ return internal::length(pos2 - pos1);
+}
+
+AddMeshError AddMesh(Atlas *atlas, const InputMesh &mesh, bool useColocalVertices) {
+ xaAssert(atlas);
+ AddMeshError error;
+ error.code = AddMeshErrorCode::Success;
+ error.face = error.index0 = error.index1 = UINT32_MAX;
+ // Expecting triangle faces.
+ if ((mesh.indexCount % 3) != 0) {
+ error.code = AddMeshErrorCode::InvalidIndexCount;
+ return error;
+ }
+ // Check if any index is out of range.
+ for (uint32_t j = 0; j < mesh.indexCount; j++) {
+ const uint32_t index = DecodeIndex(mesh.indexFormat, mesh.indexData, j);
+ if (index < 0 || index >= mesh.vertexCount) {
+ error.code = AddMeshErrorCode::IndexOutOfRange;
+ error.index0 = index;
+ return error;
+ }
+ }
+ // Build half edge mesh.
+ internal::halfedge::Mesh *heMesh = new internal::halfedge::Mesh;
+ std::vector<uint32_t> canonicalMap;
+ canonicalMap.reserve(mesh.vertexCount);
+ for (uint32_t i = 0; i < mesh.vertexCount; i++) {
+ internal::halfedge::Vertex *vertex = heMesh->addVertex(DecodePosition(mesh, i));
+ if (mesh.vertexNormalData)
+ vertex->nor = DecodeNormal(mesh, i);
+ if (mesh.vertexUvData)
+ vertex->tex = DecodeUv(mesh, i);
+ // Link colocals. You probably want to do this more efficiently! Sort by one axis or use a hash or grid.
+ uint32_t firstColocal = i;
+ if (useColocalVertices) {
+ for (uint32_t j = 0; j < i; j++) {
+ if (vertex->pos != DecodePosition(mesh, j))
+ continue;
+#if 0
+ if (mesh.vertexNormalData && vertex->nor != DecodeNormal(mesh, j))
+ continue;
+#endif
+ if (mesh.vertexUvData && vertex->tex != DecodeUv(mesh, j))
+ continue;
+ firstColocal = j;
+ break;
+ }
+ }
+ canonicalMap.push_back(firstColocal);
+ }
+ heMesh->linkColocalsWithCanonicalMap(canonicalMap);
+ for (uint32_t i = 0; i < mesh.indexCount / 3; i++) {
+ uint32_t tri[3];
+ for (int j = 0; j < 3; j++)
+ tri[j] = DecodeIndex(mesh.indexFormat, mesh.indexData, i * 3 + j);
+ // Check for zero length edges.
+ for (int j = 0; j < 3; j++) {
+ const uint32_t edges[6] = { 0, 1, 1, 2, 2, 0 };
+ const uint32_t index1 = tri[edges[j * 2 + 0]];
+ const uint32_t index2 = tri[edges[j * 2 + 1]];
+ const internal::Vector3 pos1 = DecodePosition(mesh, index1);
+ const internal::Vector3 pos2 = DecodePosition(mesh, index2);
+ if (EdgeLength(pos1, pos2) <= 0.0f) {
+ delete heMesh;
+ error.code = AddMeshErrorCode::ZeroLengthEdge;
+ error.face = i;
+ error.index0 = index1;
+ error.index1 = index2;
+ return error;
+ }
+ }
+ // Check for zero area faces.
+ {
+ const internal::Vector3 a = DecodePosition(mesh, tri[0]);
+ const internal::Vector3 b = DecodePosition(mesh, tri[1]);
+ const internal::Vector3 c = DecodePosition(mesh, tri[2]);
+ const float area = internal::length(internal::cross(b - a, c - a)) * 0.5f;
+ if (area <= 0.0f) {
+ delete heMesh;
+ error.code = AddMeshErrorCode::ZeroAreaFace;
+ error.face = i;
+ return error;
+ }
+ }
+ internal::halfedge::Face *face = heMesh->addFace(tri[0], tri[1], tri[2]);
+
+ if (!face && heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::AlreadyAddedEdge) {
+ //there is still hope for this, no reason to not add, at least add as separate
+ face = heMesh->addUniqueFace(tri[0], tri[1], tri[2]);
+ }
+
+ if (!face) {
+ //continue;
+
+ if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::AlreadyAddedEdge) {
+ error.code = AddMeshErrorCode::AlreadyAddedEdge;
+ } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DegenerateColocalEdge) {
+ error.code = AddMeshErrorCode::DegenerateColocalEdge;
+ } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DegenerateEdge) {
+ error.code = AddMeshErrorCode::DegenerateEdge;
+ } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DuplicateEdge) {
+ error.code = AddMeshErrorCode::DuplicateEdge;
+ }
+ error.face = i;
+ error.index0 = heMesh->errorIndex0;
+ error.index1 = heMesh->errorIndex1;
+ delete heMesh;
+ return error;
+ }
+ if (mesh.faceMaterialData)
+ face->material = mesh.faceMaterialData[i];
+ }
+ heMesh->linkBoundary();
+ atlas->heMeshes.push_back(heMesh);
+ return error;
+}
+
+void Generate(Atlas *atlas, CharterOptions charterOptions, PackerOptions packerOptions) {
+ xaAssert(atlas);
+ xaAssert(packerOptions.texelArea > 0);
+ // Chart meshes.
+ for (int i = 0; i < (int)atlas->heMeshes.size(); i++) {
+ std::vector<uint32_t> uncharted_materials;
+ atlas->atlas.computeCharts(atlas->heMeshes[i], charterOptions, uncharted_materials);
+ }
+ atlas->atlas.parameterizeCharts();
+ internal::param::AtlasPacker packer(&atlas->atlas);
+ packer.packCharts(packerOptions);
+ //float utilization = return packer.computeAtlasUtilization();
+ atlas->width = packer.getWidth();
+ atlas->height = packer.getHeight();
+ // Build output meshes.
+ atlas->outputMeshes = new OutputMesh *[atlas->heMeshes.size()];
+ for (int i = 0; i < (int)atlas->heMeshes.size(); i++) {
+ const internal::halfedge::Mesh *heMesh = atlas->heMeshes[i];
+ OutputMesh *outputMesh = atlas->outputMeshes[i] = new OutputMesh;
+ const internal::param::MeshCharts *charts = atlas->atlas.meshAt(i);
+ // Vertices.
+ outputMesh->vertexCount = charts->vertexCount();
+ outputMesh->vertexArray = new OutputVertex[outputMesh->vertexCount];
+ for (uint32_t i = 0; i < charts->chartCount(); i++) {
+ const internal::param::Chart *chart = charts->chartAt(i);
+ const uint32_t vertexOffset = charts->vertexCountBeforeChartAt(i);
+ for (uint32_t v = 0; v < chart->vertexCount(); v++) {
+ OutputVertex &output_vertex = outputMesh->vertexArray[vertexOffset + v];
+ output_vertex.xref = chart->mapChartVertexToOriginalVertex(v);
+ internal::Vector2 uv = chart->chartMesh()->vertexAt(v)->tex;
+ output_vertex.uv[0] = uv.x;
+ output_vertex.uv[1] = uv.y;
+ }
+ }
+ // Indices.
+ outputMesh->indexCount = heMesh->faceCount() * 3;
+ outputMesh->indexArray = new uint32_t[outputMesh->indexCount];
+ for (uint32_t f = 0; f < heMesh->faceCount(); f++) {
+ const uint32_t c = charts->faceChartAt(f);
+ const uint32_t i = charts->faceIndexWithinChartAt(f);
+ const uint32_t vertexOffset = charts->vertexCountBeforeChartAt(c);
+ const internal::param::Chart *chart = charts->chartAt(c);
+ xaDebugAssert(i < chart->chartMesh()->faceCount());
+ xaDebugAssert(chart->faceAt(i) == f);
+ const internal::halfedge::Face *face = chart->chartMesh()->faceAt(i);
+ const internal::halfedge::Edge *edge = face->edge;
+ outputMesh->indexArray[3 * f + 0] = vertexOffset + edge->vertex->id;
+ outputMesh->indexArray[3 * f + 1] = vertexOffset + edge->next->vertex->id;
+ outputMesh->indexArray[3 * f + 2] = vertexOffset + edge->next->next->vertex->id;
+ }
+ // Charts.
+ outputMesh->chartCount = charts->chartCount();
+ outputMesh->chartArray = new OutputChart[outputMesh->chartCount];
+ for (uint32_t i = 0; i < charts->chartCount(); i++) {
+ OutputChart *outputChart = &outputMesh->chartArray[i];
+ const internal::param::Chart *chart = charts->chartAt(i);
+ const uint32_t vertexOffset = charts->vertexCountBeforeChartAt(i);
+ const internal::halfedge::Mesh *mesh = chart->chartMesh();
+ outputChart->indexCount = mesh->faceCount() * 3;
+ outputChart->indexArray = new uint32_t[outputChart->indexCount];
+ for (uint32_t j = 0; j < mesh->faceCount(); j++) {
+ const internal::halfedge::Face *face = mesh->faceAt(j);
+ const internal::halfedge::Edge *edge = face->edge;
+ outputChart->indexArray[3 * j + 0] = vertexOffset + edge->vertex->id;
+ outputChart->indexArray[3 * j + 1] = vertexOffset + edge->next->vertex->id;
+ outputChart->indexArray[3 * j + 2] = vertexOffset + edge->next->next->vertex->id;
+ }
+ }
+ }
+}
+
+uint32_t GetWidth(const Atlas *atlas) {
+ xaAssert(atlas);
+ return atlas->width;
+}
+
+uint32_t GetHeight(const Atlas *atlas) {
+ xaAssert(atlas);
+ return atlas->height;
+}
+
+uint32_t GetNumCharts(const Atlas *atlas) {
+ xaAssert(atlas);
+ return atlas->atlas.chartCount();
+}
+
+const OutputMesh *const *GetOutputMeshes(const Atlas *atlas) {
+ xaAssert(atlas);
+ return atlas->outputMeshes;
+}
+
+const char *StringForEnum(AddMeshErrorCode::Enum error) {
+ if (error == AddMeshErrorCode::AlreadyAddedEdge)
+ return "already added edge";
+ if (error == AddMeshErrorCode::DegenerateColocalEdge)
+ return "degenerate colocal edge";
+ if (error == AddMeshErrorCode::DegenerateEdge)
+ return "degenerate edge";
+ if (error == AddMeshErrorCode::DuplicateEdge)
+ return "duplicate edge";
+ if (error == AddMeshErrorCode::IndexOutOfRange)
+ return "index out of range";
+ if (error == AddMeshErrorCode::InvalidIndexCount)
+ return "invalid index count";
+ if (error == AddMeshErrorCode::ZeroAreaFace)
+ return "zero area face";
+ if (error == AddMeshErrorCode::ZeroLengthEdge)
+ return "zero length edge";
+ return "success";
+}
+
+} // namespace xatlas
diff --git a/thirdparty/xatlas/xatlas.h b/thirdparty/xatlas/xatlas.h
new file mode 100644
index 0000000000..4140429fee
--- /dev/null
+++ b/thirdparty/xatlas/xatlas.h
@@ -0,0 +1,160 @@
+// This code is in the public domain -- castanyo@yahoo.es
+#pragma once
+#ifndef XATLAS_H
+#define XATLAS_H
+#include <float.h> // FLT_MAX
+#include <limits.h>
+#include <stdint.h>
+namespace xatlas {
+
+typedef void (*PrintFunc)(const char *, ...);
+
+struct Atlas;
+
+struct CharterOptions {
+ float proxyFitMetricWeight;
+ float roundnessMetricWeight;
+ float straightnessMetricWeight;
+ float normalSeamMetricWeight;
+ float textureSeamMetricWeight;
+ float maxChartArea;
+ float maxBoundaryLength;
+
+ CharterOptions() {
+ // These are the default values we use on The Witness.
+ proxyFitMetricWeight = 2.0f;
+ roundnessMetricWeight = 0.01f;
+ straightnessMetricWeight = 6.0f;
+ normalSeamMetricWeight = 4.0f;
+ textureSeamMetricWeight = 0.5f;
+ /*
+ proxyFitMetricWeight = 1.0f;
+ roundnessMetricWeight = 0.1f;
+ straightnessMetricWeight = 0.25f;
+ normalSeamMetricWeight = 1.0f;
+ textureSeamMetricWeight = 0.1f;
+ */
+ maxChartArea = FLT_MAX;
+ maxBoundaryLength = FLT_MAX;
+ }
+};
+
+struct PackMethod {
+ enum Enum {
+ TexelArea, // texel_area determines resolution
+ ApproximateResolution, // guess texel_area to approximately match desired resolution
+ ExactResolution // run the packer multiple times to exactly match the desired resolution (slow)
+ };
+};
+
+struct PackerOptions {
+ PackMethod::Enum method;
+
+ // 0 - brute force
+ // 1 - 4096 attempts
+ // 2 - 2048
+ // 3 - 1024
+ // 4 - 512
+ // other - 256
+ // Avoid brute force packing, since it can be unusably slow in some situations.
+ int quality;
+
+ float texelArea; // This is not really texel area, but 1 / texel width?
+ uint32_t resolution;
+ bool blockAlign; // Align charts to 4x4 blocks.
+ bool conservative; // Pack charts with extra padding.
+ int padding;
+
+ PackerOptions() {
+ method = PackMethod::ApproximateResolution;
+ quality = 1;
+ texelArea = 8;
+ resolution = 512;
+ blockAlign = false;
+ conservative = false;
+ padding = 0;
+ }
+};
+
+struct AddMeshErrorCode {
+ enum Enum {
+ Success,
+ AlreadyAddedEdge, // index0 and index1 are the edge indices
+ DegenerateColocalEdge, // index0 and index1 are the edge indices
+ DegenerateEdge, // index0 and index1 are the edge indices
+ DuplicateEdge, // index0 and index1 are the edge indices
+ IndexOutOfRange, // index0 is the index
+ InvalidIndexCount, // not evenly divisible by 3 - expecting triangles
+ ZeroAreaFace,
+ ZeroLengthEdge // index0 and index1 are the edge indices
+ };
+};
+
+struct AddMeshError {
+ AddMeshErrorCode::Enum code;
+ uint32_t face;
+ uint32_t index0, index1;
+};
+
+struct IndexFormat {
+ enum Enum {
+ HalfFloat,
+ Float
+ };
+};
+
+struct InputMesh {
+ uint32_t vertexCount;
+ const void *vertexPositionData;
+ uint32_t vertexPositionStride;
+ const void *vertexNormalData; // optional
+ uint32_t vertexNormalStride; // optional
+
+ // optional
+ // The input UVs are provided as a hint to the chart generator.
+ const void *vertexUvData;
+ uint32_t vertexUvStride;
+
+ uint32_t indexCount;
+ const void *indexData;
+ IndexFormat::Enum indexFormat;
+
+ // optional. indexCount / 3 in length.
+ // Charter also uses material boundaries as a hint to cut charts.
+ const uint16_t *faceMaterialData;
+};
+
+struct OutputChart {
+ uint32_t *indexArray;
+ uint32_t indexCount;
+};
+
+struct OutputVertex {
+ float uv[2];
+ uint32_t xref; // Index of input vertex from which this output vertex originated.
+};
+
+struct OutputMesh {
+ OutputChart *chartArray;
+ uint32_t chartCount;
+ uint32_t *indexArray;
+ uint32_t indexCount;
+ OutputVertex *vertexArray;
+ uint32_t vertexCount;
+};
+
+void SetPrint(PrintFunc print);
+Atlas *Create();
+void Destroy(Atlas *atlas);
+// useColocalVertices - generates fewer charts (good), but is more sensitive to bad geometry.
+AddMeshError AddMesh(Atlas *atlas, const InputMesh &mesh, bool useColocalVertices = true);
+void Generate(Atlas *atlas, CharterOptions charterOptions = CharterOptions(), PackerOptions packerOptions = PackerOptions());
+uint32_t GetWidth(const Atlas *atlas);
+uint32_t GetHeight(const Atlas *atlas);
+uint32_t GetNumCharts(const Atlas *atlas);
+const OutputMesh *const *GetOutputMeshes(const Atlas *atlas);
+const char *StringForEnum(AddMeshErrorCode::Enum error);
+
+} // namespace xatlas
+
+#endif // XATLAS_H
diff --git a/thirdparty/zstd/SCsub b/thirdparty/zstd/SCsub
deleted file mode 100644
index 899a18e1cf..0000000000
--- a/thirdparty/zstd/SCsub
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env python
-
-Import('env')
-
-thirdparty_zstd_dir = "#thirdparty/zstd/"
-thirdparty_zstd_sources = [
- "common/entropy_common.c",
- "common/error_private.c",
- "common/fse_decompress.c",
- "common/pool.c",
- "common/threading.c",
- "common/xxhash.c",
- "common/zstd_common.c",
- "compress/fse_compress.c",
- "compress/huf_compress.c",
- "compress/zstd_compress.c",
- "compress/zstd_double_fast.c",
- "compress/zstd_fast.c",
- "compress/zstd_lazy.c",
- "compress/zstd_ldm.c",
- "compress/zstdmt_compress.c",
- "compress/zstd_opt.c",
- "decompress/huf_decompress.c",
- "decompress/zstd_decompress.c",
-]
-thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources]
-env.add_source_files(env.core_sources, thirdparty_zstd_sources)
-env.Append(CPPPATH=["#thirdparty/zstd", "#thirdparty/zstd/common"])
-env.Append(CCFLAGS="-DZSTD_STATIC_LINKING_ONLY")