diff options
1537 files changed, 124040 insertions, 40256 deletions
diff --git a/AUTHORS.md b/AUTHORS.md index b7eca2d9b1..99482fb4df 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -45,6 +45,7 @@ name is available. BÅ‚ażej SzczygieÅ‚ (zaps166) Bojidar Marinov (bojidar-bg) bruvzg + Camille Mohr-Daurat (pouleyKetchoupp) Carl Olsson (not-surt) Carter Anderson (cart) Chris Bradfield (cbscribe) @@ -115,6 +116,7 @@ name is available. Pieter-Jan Briers (PJB3005) Poommetee Ketson (Noshyaar) PrzemysÅ‚aw Gołąb (n-pigeon) + RafaÅ‚ Mikrut (qarmin) Ralf Hölzemer (rollenrolm) Ramesh Ravone (RameshRavone) Ray Koopa (RayKoopa) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4c4a8a37a6..c27bee2b5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,7 +51,7 @@ understand that: and then figure out why it's failing. To speed up our work, please prepare for us **a simple project** that isolates -and reproduces the issue. This is always the **the best way for us to fix it**. +and reproduces the issue. This is always the **best way for us to fix it**. You can attach a zip file with the minimal project directly to the bug report, by drag and dropping the file in the GitHub edition field. diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 8b263a2d2c..3e2dc1538d 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -110,6 +110,11 @@ Copyright: 2007, Starbreeze Studios 2014-2019, Godot Engine contributors. License: Expat and Zlib +Files: ./thirdparty/assimp/ +Comment: Open Asset Import Library (assimp) +Copyright: 2006-2016, assimp team +License: BSD-3-clause + Files: ./thirdparty/b2d_convexdecomp/ Comment: Box2D (ConvexDecomp) Copyright: 2007, Eric Jordan @@ -369,17 +374,25 @@ Comment: libSquish Copyright: 2006, Simon Brown License: Expat -Files: ./thirdparty/thekla_atlas/ -Comment: Thekla Atlas -Copyright: 2013, Thekla, Inc -License: Expat - Files: ./thirdparty/tinyexr/ Comment: TinyEXR Copyright: 2014-2018, Syoyo Fujita 2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC License: BSD-3-clause +Files: ./thirdparty/vhacd/ +Comment: V-HACD +Copyright: 2011, Khaled Mamou + 2003-2009, Erwin Coumans +License: BSD-3-clause + +Files: ./thirdparty/xatlas/ +Comment: xatlas +Copyright: 2018, Jonathan Young + 2013, Thekla, Inc + 2006, NVIDIA Corporation, Ignacio Castano +License: Expat + Files: ./thirdparty/zlib/ Comment: zlib Copyright: 1995-2017, Jean-loup Gailly and Mark Adler @@ -18,22 +18,21 @@ generous deed immortalized in the next stable release of Godot Engine. Gamblify <https://www.gamblify.com> Image Campus <https://www.imagecampus.edu.ar> - Yakaz.com <https://yakaz.com> ## Mini sponsors Alan Beauchamp Anandarup Mallik Andrew Dunai - Arda Erol Brandon Lamb Christian Uldall Pedersen Christoph Woinke + Connor Hill Edward Flick GameDev.net GameDev.tv Hein-Pieter van Braam - Jamal Alyafei + Jacob McKenney Javary Games Jay Sistar Justin Arnold @@ -43,22 +42,18 @@ generous deed immortalized in the next stable release of Godot Engine. Matthieu Huvé Maxim Karsten Mike King - Nathan Warden Neal Gompa (Conan Kudo) Patrick Aarstad "Rainway " - Ruslan Mustakov Slobodan Milnovic StarFlare Software Stephan Lanfermann - Stoney Meyerhoeffer - thechris + TigerJ VilliHaukka Zashi ## Gold donors - Asdf Brandon Waite cheese65536 David Gehrig @@ -69,8 +64,10 @@ generous deed immortalized in the next stable release of Godot Engine. Nathanael Beisiegel Officine Pixel S.n.c. Retro Village + Sofox Zaven Muradyan + Alexander Trey Saunders Allen Schade Andreas Schüle Asher Glick @@ -80,7 +77,6 @@ generous deed immortalized in the next stable release of Godot Engine. David Giardi Edward E Florian Breisch - Gary Oberbrunner Gero Jay Horton Jon Smith @@ -88,6 +84,8 @@ generous deed immortalized in the next stable release of Godot Engine. Jorge Bernal Joshua Lesperance Justo Delgado Baudà + Karl Werf + Kommentgames Krzysztof Dluzniewski Moonwards Mored1984 @@ -95,6 +93,7 @@ generous deed immortalized in the next stable release of Godot Engine. paul gruenbacher Paul LaMotte Rob Messick + Ross Esmond Scott Wadden Sergey Svenne Krap @@ -103,19 +102,20 @@ generous deed immortalized in the next stable release of Godot Engine. Wyatt Goodin Xananax - BanjoNode2D - Beliar + Abel Oroz Vicente Chris Serino Christian Padilla Conrad Curry Craig Smith Daniel Egger Dean Harmon + Ian Richard Kunert Ivan Trombley Joan Fons Krzysztof Jankowski Lord Bloodhound Lucas Ferreira Franca + Michele Zilli Nathan Lundquist Pascal Grüter Petr Malac @@ -125,27 +125,34 @@ generous deed immortalized in the next stable release of Godot Engine. Ronnie Ashlock ScottMakesGames Thomas Bjarnelöf + Vincent Henderson Wojciech Chojnacki Xavier PATRICELLI Alessandra Pereyra Alexey Dyadchenko Andrew Bowen + Asdf Benjamin W Flint Chau Siu Hung Chris Goddard Chris Petrich Christian Leth Jeppesen + Christoph Schröder Cody Parker ComicSads D + Daniel + Daniel Eichler Deadly Lampshade + Eagle 3d E.G. Eric Eric Monson Ethan Bennis Eugenio Hugo Salgüero Jáñez flesk + Francisco Javier Moreno Carracedo gavlig GGGames.org Giles Montgomery @@ -153,21 +160,24 @@ generous deed immortalized in the next stable release of Godot Engine. Guilherme Felipe de C. G. da Silva Heath Hayes Hysteria - Jalal Chaabane - Jeppe Zapp Jose Malheiro + Joshua Flores Juan T Chen Juraj Móza Kasper Jeppesen Klavdij Voncina Leandro Voltolino - Lukáš Rendvanský + Maarten Elings + Malcolm Peralty Marius Kamm + Markus Fehr Markus Wiesner Martin Eigel Marvin Matt Eunson + Matthew Hillier Max R.R. Collada + MegaC Nick Nikitin Oliver Dick oziatek @@ -175,39 +185,45 @@ generous deed immortalized in the next stable release of Godot Engine. Paul Von Zimmerman Pete Goodwin Ranoller - ray-tracer - Ruben Soares Luis Samuel Judd - Sofox + Scott Pilet spilldata Stoned Xander + Thomas Krampl Tobias Bocanegra + Urho WytRabbit Xavier Fumado Beltran + yuanzhe zhou ## Silver donors 1D_Inc + Abraham Haskins Adam Brunnmeier Adam Carr Adam Nakonieczny Adam Smeltzer Adisibio + Aidan O'Flannagain Alder Stefano Alessandro Senese Alexander Gillberg Alexander Koppe + Alex Davies-Moore Alice Robinson + Andreas Evers Andreas Krampitz Anthony Bongiovanni - Arbor Interactive + Arda Erol Arthur S. Muszynski Aubrey Falconer Avencherus - Bailey Balázs Batári Bastian Böhm + Beliar Benedikt + Ben Phelan Ben Vercammen Bernd Jänichen Blair Allen @@ -221,14 +237,17 @@ generous deed immortalized in the next stable release of Godot Engine. Christian Baune Christian Winter Christoffer Sundbom + Christopher Fisher Chris Wilson + Clay Heaton Collin Shooltz - Connor Hill Daniel Johnson DanielMaximiano Daniel Reed + David Bullock David Cravens David May + Diliup Gabadamudalige Dominik Wetzel Duobix Edward Herbert @@ -238,20 +257,24 @@ generous deed immortalized in the next stable release of Godot Engine. Eric Martini Eric McCarthy Eric Williams - Fabian Becker + Fabian Lökes fengjiongmax Foomf G3Dev sà rl + Gary Hulst Gerrit Großkopf Grant Clarke Greg Olson + Greg Pennefather Guldoman - Haley Aycock Heribert Hirth Hiroshi Naruo + HMan Hunter Jones ialex32x Igor Buzatovic + Iiari + Isaac Morton Jaime Ruiz-Borau Vizárraga Jako Danar James A F Manley @@ -259,6 +282,7 @@ generous deed immortalized in the next stable release of Godot Engine. Jeremy Kahn Jesse Dubay Joel Fivat + Joel Setterberg Johannes Wuensch Jonas Rudlang Jonas Yamazaki @@ -267,14 +291,16 @@ generous deed immortalized in the next stable release of Godot Engine. Jonathon Jon Bonazza Jon Sully + Jose Aleman Josh 'Cheeseness' Bush Juanfran Juan Negrier Judd Julian Murgia + Julius Hackel Kajornthep Piyanun KC Chan - Kevin Boyer + kickmaniac Kiyohiro Kawamura (kyorohiro) Klagsam KR McGinley @@ -289,10 +315,13 @@ generous deed immortalized in the next stable release of Godot Engine. Major Haul Malcolm Markus Michael Egger + Martin Holas + Matthew Little Matthias Grandis Matt Welke Maxwell medecau + Menno Finlay-Smits Mertcan Mermerkaya mhilbrunner Michael Dürwald @@ -300,17 +329,19 @@ generous deed immortalized in the next stable release of Godot Engine. Michael Labbe Mikael Olsson Mikayla Hutchinson + Mike Cunningham MoM Moritz Laass Moritz Weissenberger + MuffinManKen Natrim nee Neil Blakey-Milner Nerdforge - Nick Pavlica Niclas Eriksen Nicolás Montaña Nicolas SAN AGUSTIN + Nithin Jino Oscar Norlander Pan Ip Patrick Nafarrete @@ -321,20 +352,29 @@ generous deed immortalized in the next stable release of Godot Engine. Pierre-Igor Berthet Pietro Vertechi Piotr Kaczmarski + Pitsanu Tongprasin + Poryg Prokhorenko Leonid Psyagnostic Rafael + Raphael Leroux Rémi Verschelde + Reneator Ricardo Alcantara Richman Stewart + Rob Crowle Robert Farr (Larington) + Robert Hernandez + Rodrigo Loli Roger Burgess Roger Smith Roland RzÄ…sa Roman Tinkov Ryan Cheung + Ryan Groom Ryan Hentz Sasori Olkof + Scott D. Yelich Sebastian Michailidis Shane Spoor Simon Wenner @@ -348,6 +388,7 @@ generous deed immortalized in the next stable release of Godot Engine. tiansheng li Tim Tim Drumheller + Tim Gudex Tom Larrow Torsten Crass Tryggve Sollid @@ -355,10 +396,13 @@ generous deed immortalized in the next stable release of Godot Engine. UltyX Vaiktorg Victor + Vigilant Watch Viktor Ferenczi waka nya Wayne Haak werner mendizabal + Wiley Thompson + Will William Gervasio William Hogben Wout Standaert @@ -26,7 +26,7 @@ Before being open sourced in February 2014, Godot had been developed by Juan Linietsky and Ariel Manzur (both still maintaining the project) for several years as an in-house engine, used to publish several work-for-hire titles. - + ### Getting the engine diff --git a/SConstruct b/SConstruct index b3875d6801..78d77d1b7a 100644 --- a/SConstruct +++ b/SConstruct @@ -5,7 +5,6 @@ EnsureSConsVersion(0, 98, 1) # System import glob import os -import string import sys # Local @@ -181,7 +180,6 @@ opts.Add(BoolVariable('builtin_opus', "Use the built-in Opus library", True)) 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)) @@ -342,17 +340,18 @@ if selected_platform in platform_list: shadow_local_warning = [] all_plus_warnings = ['-Wwrite-strings'] - if methods.use_gcc(env): + if methods.using_gcc(env): version = methods.get_compiler_version(env) if version != None and version[0] >= '7': shadow_local_warning = ['-Wshadow-local'] + if (env["warnings"] == 'extra'): - # FIXME: enable -Wimplicit-fallthrough once #26135 is fixed # FIXME: enable -Wclobbered once #26351 is fixed - env.Append(CCFLAGS=['-Wall', '-Wextra', '-Wno-implicit-fallthrough', '-Wno-unused-parameter'] + all_plus_warnings + shadow_local_warning) - if methods.use_gcc(env): + # Note: enable -Wimplicit-fallthrough for Clang (already part of -Wextra for GCC) + # once we switch to C++11 or later (necessary for our FALLTHROUGH macro). + env.Append(CCFLAGS=['-Wall', '-Wextra', '-Wno-unused-parameter'] + all_plus_warnings + shadow_local_warning) + if methods.using_gcc(env): env['CCFLAGS'] += ['-Wno-clobbered'] - elif (env["warnings"] == 'all'): env.Append(CCFLAGS=['-Wall'] + shadow_local_warning) elif (env["warnings"] == 'moderate'): diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 67de156fc3..ba595b9627 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -148,7 +148,7 @@ _ResourceLoader::_ResourceLoader() { singleton = this; } -Error _ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { +Error _ResourceSaver::save(const String &p_path, const RES &p_resource, SaverFlags p_flags) { ERR_FAIL_COND_V(p_resource.is_null(), ERR_INVALID_PARAMETER); return ResourceSaver::save(p_path, p_resource, p_flags); @@ -1579,7 +1579,7 @@ _Geometry::_Geometry() { ///////////////////////// FILE -Error _File::open_encrypted(const String &p_path, int p_mode_flags, const Vector<uint8_t> &p_key) { +Error _File::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) { Error err = open(p_path, p_mode_flags); if (err) @@ -1596,7 +1596,7 @@ Error _File::open_encrypted(const String &p_path, int p_mode_flags, const Vector return OK; } -Error _File::open_encrypted_pass(const String &p_path, int p_mode_flags, const String &p_pass) { +Error _File::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass) { Error err = open(p_path, p_mode_flags); if (err) @@ -1614,7 +1614,7 @@ Error _File::open_encrypted_pass(const String &p_path, int p_mode_flags, const S return OK; } -Error _File::open_compressed(const String &p_path, int p_mode_flags, int p_compress_mode) { +Error _File::open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode) { FileAccessCompressed *fac = memnew(FileAccessCompressed); @@ -1631,7 +1631,7 @@ Error _File::open_compressed(const String &p_path, int p_mode_flags, int p_compr return OK; } -Error _File::open(const String &p_path, int p_mode_flags) { +Error _File::open(const String &p_path, ModeFlags p_mode_flags) { close(); Error err; @@ -2445,7 +2445,8 @@ void _Thread::_start_func(void *ud) { reason = "Method Not Found"; } break; - default: {} + default: { + } } ERR_EXPLAIN("Could not call function '" + t->target_method.operator String() + "'' starting thread ID: " + t->get_id() + " Reason: " + reason); @@ -2453,12 +2454,12 @@ void _Thread::_start_func(void *ud) { } } -Error _Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, int p_priority) { +Error _Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) { ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(!p_instance, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_method == StringName(), ERR_INVALID_PARAMETER); - ERR_FAIL_INDEX_V(p_priority, 3, ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_priority, PRIORITY_MAX, ERR_INVALID_PARAMETER); ret = Variant(); target_method = p_method; diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 1c26d9b144..2906de4a4a 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -85,7 +85,7 @@ public: static _ResourceSaver *get_singleton() { return singleton; } - Error save(const String &p_path, const RES &p_resource, uint32_t p_flags); + Error save(const String &p_path, const RES &p_resource, SaverFlags p_flags); PoolVector<String> get_recognized_extensions(const RES &p_resource); _ResourceSaver(); @@ -436,11 +436,11 @@ public: COMPRESSION_GZIP = Compression::MODE_GZIP }; - Error open_encrypted(const String &p_path, int p_mode_flags, const Vector<uint8_t> &p_key); - Error open_encrypted_pass(const String &p_path, int p_mode_flags, const String &p_pass); - Error open_compressed(const String &p_path, int p_mode_flags, int p_compress_mode = 0); + Error open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key); + Error open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass); + Error open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ); - Error open(const String &p_path, int p_mode_flags); ///< open a file + Error open(const String &p_path, ModeFlags p_mode_flags); ///< open a file void close(); ///< close a file bool is_open() const; ///< true when file is open @@ -632,10 +632,11 @@ public: PRIORITY_LOW, PRIORITY_NORMAL, - PRIORITY_HIGH + PRIORITY_HIGH, + PRIORITY_MAX }; - Error start(Object *p_instance, const StringName &p_method, const Variant &p_userdata = Variant(), int p_priority = PRIORITY_NORMAL); + Error start(Object *p_instance, const StringName &p_method, const Variant &p_userdata = Variant(), Priority p_priority = PRIORITY_NORMAL); String get_id() const; bool is_active() const; Variant wait_to_finish(); diff --git a/core/class_db.cpp b/core/class_db.cpp index 219bdbddd8..0c844657a4 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -249,6 +249,11 @@ void ClassDB::set_current_api(APIType p_api) { current_api = p_api; } +ClassDB::APIType ClassDB::get_current_api() { + + return current_api; +} + HashMap<StringName, ClassDB::ClassInfo> ClassDB::classes; HashMap<StringName, StringName> ClassDB::resource_base_extensions; HashMap<StringName, StringName> ClassDB::compat_classes; @@ -307,6 +312,19 @@ void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringNa } } +void ClassDB::get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { + + OBJTYPE_RLOCK; + + const StringName *k = NULL; + + while ((k = classes.next(k))) { + + if (*k != p_class && get_parent_class(*k) == p_class) + p_classes->push_back(*k); + } +} + StringName ClassDB::get_parent_class_nocheck(const StringName &p_class) { OBJTYPE_RLOCK; diff --git a/core/class_db.h b/core/class_db.h index 321682d76b..efa1a46866 100644 --- a/core/class_db.h +++ b/core/class_db.h @@ -214,6 +214,7 @@ public: static void get_class_list(List<StringName> *p_classes); static void get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes); + static void get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes); static StringName get_parent_class_nocheck(const StringName &p_class); static StringName get_parent_class(const StringName &p_class); static bool class_exists(const StringName &p_class); @@ -371,6 +372,7 @@ public: static void init(); static void set_current_api(APIType p_api); + static APIType get_current_api(); static void cleanup(); }; diff --git a/core/image.cpp b/core/image.cpp index f547d7e973..99d5eab864 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -1266,7 +1266,8 @@ void Image::shrink_x2() { case FORMAT_RGBAH: _generate_po2_mipmap<uint16_t, 4, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r.ptr()), reinterpret_cast<uint16_t *>(w.ptr()), width, height); break; case FORMAT_RGBE9995: _generate_po2_mipmap<uint32_t, 1, false, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(reinterpret_cast<const uint32_t *>(r.ptr()), reinterpret_cast<uint32_t *>(w.ptr()), width, height); break; - default: {} + default: { + } } } @@ -1398,7 +1399,8 @@ Error Image::generate_mipmaps(bool p_renormalize) { _generate_po2_mipmap<uint32_t, 1, false, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(reinterpret_cast<const uint32_t *>(&wp[prev_ofs]), reinterpret_cast<uint32_t *>(&wp[ofs]), prev_w, prev_h); break; - default: {} + default: { + } } prev_ofs = ofs; @@ -1612,7 +1614,8 @@ void Image::create(const char **p_xpm) { if (y == (size_height - 1)) status = DONE; } break; - default: {} + default: { + } } line++; @@ -1685,7 +1688,8 @@ bool Image::is_invisible() const { case FORMAT_DXT5: { detected = true; } break; - default: {} + default: { + } } return !detected; @@ -1729,7 +1733,8 @@ Image::AlphaMode Image::detect_alpha() const { case FORMAT_DXT5: { detected = true; } break; - default: {} + default: { + } } if (detected) @@ -1871,7 +1876,7 @@ Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const P Rect2 Image::get_used_rect() const { - if (format != FORMAT_LA8 && format != FORMAT_RGBA8) + if (format != FORMAT_LA8 && format != FORMAT_RGBA8 && format != FORMAT_RGBAF && format != FORMAT_RGBAH && format != FORMAT_RGBA4444 && format != FORMAT_RGBA5551) return Rect2(Point2(), Size2(width, height)); int len = data.size(); @@ -1879,17 +1884,13 @@ Rect2 Image::get_used_rect() const { if (len == 0) return Rect2(); - //int data_size = len; - PoolVector<uint8_t>::Read r = data.read(); - const unsigned char *rptr = r.ptr(); - - int ps = format == FORMAT_LA8 ? 2 : 4; + const_cast<Image *>(this)->lock(); int minx = 0xFFFFFF, miny = 0xFFFFFFF; int maxx = -1, maxy = -1; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { - bool opaque = rptr[(j * width + i) * ps + (ps - 1)] > 2; + bool opaque = get_pixel(i, j).a > 0.99; if (!opaque) continue; if (i > maxx) @@ -1903,6 +1904,8 @@ Rect2 Image::get_used_rect() const { } } + const_cast<Image *>(this)->unlock(); + if (maxx == -1) return Rect2(); else diff --git a/core/io/compression.cpp b/core/io/compression.cpp index a113f3b61b..b51e50150e 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -175,7 +175,9 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p } break; case MODE_ZSTD: { ZSTD_DCtx *dctx = ZSTD_createDCtx(); - if (zstd_long_distance_matching) ZSTD_DCtx_setMaxWindowSize(dctx, (size_t)1 << zstd_window_log_size); + if (zstd_long_distance_matching) { + ZSTD_DCtx_setParameter(dctx, ZSTD_d_windowLogMax, zstd_window_log_size); + } int ret = ZSTD_decompressDCtx(dctx, p_dst, p_dst_max_size, p_src, p_src_size); ZSTD_freeDCtx(dctx); return ret; @@ -189,4 +191,4 @@ int Compression::zlib_level = Z_DEFAULT_COMPRESSION; int Compression::gzip_level = Z_DEFAULT_COMPRESSION; int Compression::zstd_level = 3; bool Compression::zstd_long_distance_matching = false; -int Compression::zstd_window_log_size = 27; +int Compression::zstd_window_log_size = 27; // ZSTD_WINDOWLOG_LIMIT_DEFAULT diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 363460311c..81b3829ffc 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -768,7 +768,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int r_variant = carray; } break; - default: { ERR_FAIL_V(ERR_BUG); } + default: { + ERR_FAIL_V(ERR_BUG); + } } return OK; @@ -823,7 +825,8 @@ 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 + default: { + } // nothing to do at this stage } if (buf) { @@ -1385,7 +1388,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 * 4 * len; } break; - default: { ERR_FAIL_V(ERR_BUG); } + default: { + ERR_FAIL_V(ERR_BUG); + } } return OK; diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 22530ad721..86382939f1 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -46,7 +46,8 @@ _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_mas case MultiplayerAPI::RPC_MODE_MASTERSYNC: { if (is_master) r_skip_rpc = true; // I am the master, so skip remote call. - } // Do not break, fall over to other sync. + FALLTHROUGH; + } case MultiplayerAPI::RPC_MODE_REMOTESYNC: case MultiplayerAPI::RPC_MODE_PUPPETSYNC: { // Call it, sync always results in a local call. @@ -64,7 +65,7 @@ _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_mas return false; } -_FORCE_INLINE_ bool _can_call_mode(MultiplayerAPI::RPCMode mode, int p_node_master, int p_remote_id) { +_FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) { switch (mode) { case MultiplayerAPI::RPC_MODE_DISABLED: { @@ -76,11 +77,11 @@ _FORCE_INLINE_ bool _can_call_mode(MultiplayerAPI::RPCMode mode, int p_node_mast } break; case MultiplayerAPI::RPC_MODE_MASTERSYNC: case MultiplayerAPI::RPC_MODE_MASTER: { - return p_node_master == NetworkedMultiplayerPeer::TARGET_PEER_SERVER; + return p_node->is_network_master(); } break; case MultiplayerAPI::RPC_MODE_PUPPETSYNC: case MultiplayerAPI::RPC_MODE_PUPPET: { - return p_node_master != NetworkedMultiplayerPeer::TARGET_PEER_SERVER && p_remote_id == p_node_master; + return !p_node->is_network_master() && p_remote_id == p_node->get_network_master(); } break; } @@ -282,9 +283,8 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name); } - int node_master_id = p_node->get_network_master(); - ERR_EXPLAIN("RPC '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(node_master_id) + "."); - ERR_FAIL_COND(!_can_call_mode(rpc_mode, p_from, node_master_id)); + ERR_EXPLAIN("RPC '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); + ERR_FAIL_COND(!_can_call_mode(p_node, rpc_mode, p_from)); int argc = p_packet[p_offset]; Vector<Variant> args; @@ -332,9 +332,8 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p rset_mode = p_node->get_script_instance()->get_rset_mode(p_name); } - int node_master_id = p_node->get_network_master(); - ERR_EXPLAIN("RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(node_master_id) + "."); - ERR_FAIL_COND(!_can_call_mode(rset_mode, p_from, node_master_id)); + ERR_EXPLAIN("RSET '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); + ERR_FAIL_COND(!_can_call_mode(p_node, rset_mode, p_from)); Variant value; Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed()); diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 42070cd132..f25abc4aab 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -1725,7 +1725,8 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant get_string_index(np.get_subname(i)); } break; - default: {} + default: { + } } } diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index b5fa412576..038a34ed51 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -94,6 +94,8 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy r_path_and_type.type = value; } else if (assign == "importer") { r_path_and_type.importer = value; + } else if (assign == "group_file") { + r_path_and_type.group_file = value; } else if (assign == "metadata") { r_path_and_type.metadata = value; } else if (assign == "valid") { @@ -294,6 +296,15 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat memdelete(f); } +String ResourceFormatImporter::get_import_group_file(const String &p_path) const { + + bool valid = true; + PathAndType pat; + _get_path_and_type(p_path, pat, &valid); + return valid?pat.group_file:String(); + +} + bool ResourceFormatImporter::is_import_valid(const String &p_path) const { bool valid = true; diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index 1c146c33d7..bdbdde6df6 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -43,6 +43,7 @@ class ResourceFormatImporter : public ResourceFormatLoader { String path; String type; String importer; + String group_file; Variant metadata; }; @@ -69,6 +70,7 @@ public: virtual bool is_import_valid(const String &p_path) const; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); virtual bool is_imported(const String &p_path) const { return recognize_path(p_path); } + virtual String get_import_group_file(const String &p_path) const; virtual bool exists(const String &p_path) const; virtual bool can_be_imported(const String &p_path) const; @@ -120,8 +122,11 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const = 0; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const = 0; + virtual String get_option_group_file() const { return String(); } virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL) = 0; + + virtual Error import_group_file(const String& p_group_file,const Map<String,Map<StringName, Variant> >&p_source_file_options, const Map<String,String>& p_base_paths) { return ERR_UNAVAILABLE; } virtual bool are_import_settings_valid(const String &p_path) const { return true; } virtual String get_import_settings_string() const { return String(); } }; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index e4b694b64f..56d3b8b133 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -608,6 +608,30 @@ int ResourceLoader::get_import_order(const String &p_path) { return 0; } +String ResourceLoader::get_import_group_file(const String &p_path) { + String path = _path_remap(p_path); + + String local_path; + if (path.is_rel_path()) + local_path = "res://" + path; + else + local_path = ProjectSettings::get_singleton()->localize_path(path); + + for (int i = 0; i < loader_count; i++) { + + if (!loader[i]->recognize_path(local_path)) + continue; + /* + if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) + continue; + */ + + return loader[i]->get_import_group_file(p_path); + } + + return String(); //not found +} + bool ResourceLoader::is_import_valid(const String &p_path) { String path = _path_remap(p_path); diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index ca7610a0d2..9e7020be7c 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -81,6 +81,7 @@ public: virtual bool is_import_valid(const String &p_path) const { return true; } virtual bool is_imported(const String &p_path) const { return false; } virtual int get_import_order(const String &p_path) const { return 0; } + virtual String get_import_group_file(const String &p_path) const { return ""; } //no group virtual ~ResourceFormatLoader() {} }; @@ -155,6 +156,7 @@ public: static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); static Error rename_dependencies(const String &p_path, const Map<String, String> &p_map); static bool is_import_valid(const String &p_path); + static String get_import_group_file(const String &p_path); static bool is_imported(const String &p_path); static int get_import_order(const String &p_path); diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 6c3b84d49a..e1388ad2ac 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -55,6 +55,7 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { pt->weight_scale = p_weight_scale; pt->prev_point = NULL; pt->last_pass = 0; + pt->enabled = true; points[p_id] = pt; } else { points[p_id]->pos = p_pos; @@ -242,6 +243,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { pass++; + if (!end_point->enabled) + return false; + SelfList<Point>::List open_list; bool found_route = false; @@ -249,6 +253,10 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { for (Set<Point *>::Element *E = begin_point->neighbours.front(); E; E = E->next()) { Point *n = E->get(); + + if (!n->enabled) + continue; + n->prev_point = begin_point; n->distance = _compute_cost(begin_point->id, n->id) * n->weight_scale; n->last_pass = pass; @@ -290,6 +298,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { Point *e = E->get(); + if (!e->enabled) + continue; + real_t distance = _compute_cost(p->id, e->id) * e->weight_scale + p->distance; if (e->last_pass == pass) { @@ -438,6 +449,14 @@ PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) { return path; } +void AStar::set_point_disabled(int p_id, bool p_disabled) { + points[p_id]->enabled = !p_disabled; +} + +bool AStar::is_point_disabled(int p_id) const { + return !points[p_id]->enabled; +} + void AStar::_bind_methods() { ClassDB::bind_method(D_METHOD("get_available_point_id"), &AStar::get_available_point_id); @@ -450,6 +469,9 @@ void AStar::_bind_methods() { ClassDB::bind_method(D_METHOD("has_point", "id"), &AStar::has_point); ClassDB::bind_method(D_METHOD("get_points"), &AStar::get_points); + ClassDB::bind_method(D_METHOD("set_point_disabled", "id", "disabled"), &AStar::set_point_disabled, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("is_point_disabled", "id"), &AStar::is_point_disabled); + ClassDB::bind_method(D_METHOD("get_point_connections", "id"), &AStar::get_point_connections); ClassDB::bind_method(D_METHOD("connect_points", "id", "to_id", "bidirectional"), &AStar::connect_points, DEFVAL(true)); diff --git a/core/math/a_star.h b/core/math/a_star.h index d094bc4863..c63e1aa4dc 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -54,6 +54,7 @@ class AStar : public Reference { Vector3 pos; real_t weight_scale; uint64_t last_pass; + bool enabled; Set<Point *> neighbours; @@ -114,6 +115,9 @@ public: PoolVector<int> get_point_connections(int p_id); Array get_points(); + void set_point_disabled(int p_id, bool p_disabled = true); + bool is_point_disabled(int p_id) const; + void connect_points(int p_id, int p_with_id, bool bidirectional = true); void disconnect_points(int p_id, int p_with_id); bool are_points_connected(int p_id, int p_with_id) const; diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h index f970c510e0..98e4e33021 100644 --- a/core/math/audio_frame.h +++ b/core/math/audio_frame.h @@ -31,6 +31,7 @@ #ifndef AUDIOFRAME_H #define AUDIOFRAME_H +#include "core/math/vector2.h" #include "core/typedefs.h" static inline float undenormalise(volatile float f) { @@ -122,6 +123,20 @@ struct AudioFrame { r = p_frame.r; } + _ALWAYS_INLINE_ AudioFrame operator=(const AudioFrame &p_frame) { + l = p_frame.l; + r = p_frame.r; + return *this; + } + + _ALWAYS_INLINE_ operator Vector2() const { + return Vector2(l, r); + } + + _ALWAYS_INLINE_ AudioFrame(const Vector2 &p_v2) { + l = p_v2.x; + r = p_v2.y; + } _ALWAYS_INLINE_ AudioFrame() {} }; diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 82b2f7006d..9fcecd1ba6 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -557,7 +557,7 @@ void Basis::set_euler_yxz(const Vector3 &p_euler) { *this = ymat * xmat * zmat; } -bool Basis::is_equal_approx(const Basis &a, const Basis &b,real_t p_epsilon) const { +bool Basis::is_equal_approx(const Basis &a, const Basis &b, real_t p_epsilon) const { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -569,7 +569,7 @@ bool Basis::is_equal_approx(const Basis &a, const Basis &b,real_t p_epsilon) con return true; } -bool Basis::is_equal_approx_ratio(const Basis &a, const Basis &b,real_t p_epsilon) const { +bool Basis::is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon) const { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { diff --git a/core/math/basis.h b/core/math/basis.h index aa0ddb280f..75037c2c52 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -133,8 +133,8 @@ public: return elements[0][2] * v[0] + elements[1][2] * v[1] + elements[2][2] * v[2]; } - bool is_equal_approx(const Basis &a, const Basis &b, real_t p_epsilon=CMP_EPSILON) const; - bool is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon=UNIT_EPSILON) const; + bool is_equal_approx(const Basis &a, const Basis &b, real_t p_epsilon = CMP_EPSILON) const; + bool is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon = UNIT_EPSILON) const; bool operator==(const Basis &p_matrix) const; bool operator!=(const Basis &p_matrix) const; diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 708054e4ab..133dcc7ab9 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -68,6 +68,7 @@ const char *Expression::func_name[Expression::FUNC_MAX] = { "lerp", "inverse_lerp", "range_lerp", + "smoothstep", "dectime", "randomize", "randi", @@ -185,6 +186,7 @@ int Expression::get_func_argument_count(BuiltinFunc p_func) { return 2; case MATH_LERP: case MATH_INVERSE_LERP: + case MATH_SMOOTHSTEP: case MATH_DECTIME: case MATH_WRAP: case MATH_WRAPF: @@ -392,6 +394,12 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant VALIDATE_ARG_NUM(4); *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); } break; + case MATH_SMOOTHSTEP: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::smoothstep((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; case MATH_DECTIME: { VALIDATE_ARG_NUM(0); @@ -752,7 +760,8 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return = String(color); } break; - default: {} + default: { + } } } @@ -1671,7 +1680,8 @@ Expression::ENode *Expression::_parse_expression() { case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; - default: {}; + default: { + }; } if (op == Variant::OP_MAX) { //stop appending stuff diff --git a/core/math/expression.h b/core/math/expression.h index fa0878c93c..f9075cb689 100644 --- a/core/math/expression.h +++ b/core/math/expression.h @@ -66,6 +66,7 @@ public: MATH_LERP, MATH_INVERSE_LERP, MATH_RANGE_LERP, + MATH_SMOOTHSTEP, MATH_DECTIME, MATH_RANDOMIZE, MATH_RAND, diff --git a/core/math/geometry.h b/core/math/geometry.h index 4b478b6b16..7347cb742a 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -702,9 +702,11 @@ public: /* if we can assume that the line segment starts outside the circle (e.g. for continuous time collision detection) then the following can be skipped and we can just return the equivalent of res1 */ sqrtterm = Math::sqrt(sqrtterm); real_t res1 = (-b - sqrtterm) / (2 * a); - //real_t res2 = ( -b + sqrtterm ) / (2 * a); + real_t res2 = (-b + sqrtterm) / (2 * a); - return (res1 >= 0 && res1 <= 1) ? res1 : -1; + if (res1 >= 0 && res1 <= 1) return res1; + if (res2 >= 0 && res2 <= 1) return res2; + return -1; } static inline Vector<Vector3> clip_polygon(const Vector<Vector3> &polygon, const Plane &p_plane) { diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 6ac6839827..0d209402dd 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -208,6 +208,17 @@ public: static _ALWAYS_INLINE_ double range_lerp(double p_value, double p_istart, double p_istop, double p_ostart, double p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); } static _ALWAYS_INLINE_ float range_lerp(float p_value, float p_istart, float p_istop, float p_ostart, float p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); } + static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_weight) { + if (is_equal_approx(p_from, p_to)) return p_from; + double x = CLAMP((p_weight - p_from) / (p_to - p_from), 0.0, 1.0); + return x * x * (3.0 - 2.0 * x); + } + static _ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_weight) { + if (is_equal_approx(p_from, p_to)) return p_from; + float x = CLAMP((p_weight - p_from) / (p_to - p_from), 0.0f, 1.0f); + return x * x * (3.0f - 2.0f * x); + } + static _ALWAYS_INLINE_ double linear2db(double p_linear) { return Math::log(p_linear) * 8.6858896380650365530225783783321; } static _ALWAYS_INLINE_ float linear2db(float p_linear) { return Math::log(p_linear) * 8.6858896380650365530225783783321; } diff --git a/core/math/quat.h b/core/math/quat.h index 7d71ec03e8..8ed2fa7cc2 100644 --- a/core/math/quat.h +++ b/core/math/quat.h @@ -131,6 +131,14 @@ public: w(q.w) { } + Quat operator=(const Quat &q) { + x = q.x; + y = q.y; + z = q.z; + w = q.w; + return *this; + } + Quat(const Vector3 &v0, const Vector3 &v1) // shortest arc { Vector3 c = v0.cross(v1); diff --git a/core/math/random_number_generator.cpp b/core/math/random_number_generator.cpp index fccc0f72fe..6add00c1d8 100644 --- a/core/math/random_number_generator.cpp +++ b/core/math/random_number_generator.cpp @@ -40,6 +40,7 @@ void RandomNumberGenerator::_bind_methods() { ClassDB::bind_method(D_METHOD("randi"), &RandomNumberGenerator::randi); ClassDB::bind_method(D_METHOD("randf"), &RandomNumberGenerator::randf); + ClassDB::bind_method(D_METHOD("randfn", "mean", "deviation"), &RandomNumberGenerator::randfn, DEFVAL(0.0), DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("randf_range", "from", "to"), &RandomNumberGenerator::randf_range); ClassDB::bind_method(D_METHOD("randi_range", "from", "to"), &RandomNumberGenerator::randi_range); ClassDB::bind_method(D_METHOD("randomize"), &RandomNumberGenerator::randomize); diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h index 66c77b8ccf..6b6bcdd2cd 100644 --- a/core/math/random_number_generator.h +++ b/core/math/random_number_generator.h @@ -55,6 +55,8 @@ public: _FORCE_INLINE_ real_t randf_range(real_t from, real_t to) { return randbase.random(from, to); } + _FORCE_INLINE_ real_t randfn(real_t mean = 0.0, real_t deviation = 1.0) { return randbase.randfn(mean, deviation); } + _FORCE_INLINE_ int randi_range(int from, int to) { unsigned int ret = randbase.rand(); return ret % (to - from + 1) + from; diff --git a/core/math/random_pcg.h b/core/math/random_pcg.h index cd721ef4d1..0d1b311c0d 100644 --- a/core/math/random_pcg.h +++ b/core/math/random_pcg.h @@ -31,6 +31,8 @@ #ifndef RANDOM_PCG_H #define RANDOM_PCG_H +#include <math.h> + #include "core/math/math_defs.h" #include "thirdparty/misc/pcg.h" @@ -61,6 +63,13 @@ public: _FORCE_INLINE_ double randd() { return (double)rand() / (double)RANDOM_MAX; } _FORCE_INLINE_ float randf() { return (float)rand() / (float)RANDOM_MAX; } + _FORCE_INLINE_ double randfn(double p_mean, double p_deviation) { + return p_mean + p_deviation * (cos(Math_TAU * randd()) * sqrt(-2.0 * log(randd()))); // Box-Muller transform + } + _FORCE_INLINE_ float randfn(float p_mean, float p_deviation) { + return p_mean + p_deviation * (cos(Math_TAU * randf()) * sqrt(-2.0 * log(randf()))); // Box-Muller transform + } + double random(double p_from, double p_to); float random(float p_from, float p_to); real_t random(int p_from, int p_to) { return (real_t)random((real_t)p_from, (real_t)p_to); } diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 7d00158f3d..1d0387bd45 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -106,7 +106,7 @@ Size2 Transform2D::get_scale() const { return Size2(elements[0].length(), det_sign * elements[1].length()); } -void Transform2D::set_scale(Size2 &p_scale) { +void Transform2D::set_scale(const Size2 &p_scale) { elements[0].normalize(); elements[1].normalize(); elements[0] *= p_scale.x; diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index b9e7a36fb3..c44678674a 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -81,7 +81,7 @@ struct Transform2D { real_t basis_determinant() const; Size2 get_scale() const; - void set_scale(Size2 &p_scale); + void set_scale(const Size2 &p_scale); _FORCE_INLINE_ const Vector2 &get_origin() const { return elements[2]; } _FORCE_INLINE_ void set_origin(const Vector2 &p_origin) { elements[2] = p_origin; } diff --git a/core/math/vector2.h b/core/math/vector2.h index a20326f667..9a214ef9b5 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -65,6 +65,7 @@ struct Vector2 { real_t distance_squared_to(const Vector2 &p_vector2) const; real_t angle_to(const Vector2 &p_vector2) const; real_t angle_to_point(const Vector2 &p_vector2) const; + _FORCE_INLINE_ Vector2 direction_to(const Vector2 &p_b) const; real_t dot(const Vector2 &p_other) const; real_t cross(const Vector2 &p_other) const; @@ -98,6 +99,7 @@ struct Vector2 { Vector2 operator/(const real_t &rvalue) const; void operator/=(const real_t &rvalue); + void operator/=(const Vector2 &rvalue) { *this = *this / rvalue; } Vector2 operator-() const; @@ -236,6 +238,12 @@ Vector2 Vector2::slerp(const Vector2 &p_b, real_t p_t) const { return rotated(theta * p_t); } +Vector2 Vector2::direction_to(const Vector2 &p_b) const { + Vector2 ret(p_b.x - x, p_b.y - y); + ret.normalize(); + return ret; +} + Vector2 Vector2::linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t) { Vector2 res = p_a; diff --git a/core/math/vector3.h b/core/math/vector3.h index b11838d16e..e9074c5bd4 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -112,6 +112,7 @@ struct Vector3 { _FORCE_INLINE_ Vector3 project(const Vector3 &p_b) const; _FORCE_INLINE_ real_t angle_to(const Vector3 &p_b) const; + _FORCE_INLINE_ Vector3 direction_to(const Vector3 &p_b) const; _FORCE_INLINE_ Vector3 slide(const Vector3 &p_normal) const; _FORCE_INLINE_ Vector3 bounce(const Vector3 &p_normal) const; @@ -244,6 +245,12 @@ real_t Vector3::angle_to(const Vector3 &p_b) const { return Math::atan2(cross(p_b).length(), dot(p_b)); } +Vector3 Vector3::direction_to(const Vector3 &p_b) const { + Vector3 ret(p_b.x - x, p_b.y - y, p_b.z - z); + ret.normalize(); + return ret; +} + /* Operators */ Vector3 &Vector3::operator+=(const Vector3 &p_v) { diff --git a/core/object.cpp b/core/object.cpp index 8b693f039c..03a11b8ca3 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -1637,7 +1637,8 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) { _clear_internal_resource_paths(d[E->get()]); } } break; - default: {} + default: { + } } } diff --git a/core/object.h b/core/object.h index e8a6178052..3730af1ad4 100644 --- a/core/object.h +++ b/core/object.h @@ -88,6 +88,7 @@ enum PropertyHint { PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send PROPERTY_HINT_NODE_PATH_VALID_TYPES, + PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog PROPERTY_HINT_MAX, // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit }; diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 39d9f45bd7..4be1364278 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -571,10 +571,16 @@ void FileAccess::store_buffer(const uint8_t *p_src, int p_length) { store_8(p_src[i]); } -Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path) { +Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_error) { - FileAccess *f = FileAccess::open(p_path, READ); - ERR_FAIL_COND_V(!f, Vector<uint8_t>()); + FileAccess *f = FileAccess::open(p_path, READ, r_error); + if (!f) { + if (r_error) { // if error requested, do not throw error + return Vector<uint8_t>(); + } else { + ERR_FAIL_COND_V(!f, Vector<uint8_t>()); + } + } Vector<uint8_t> data; data.resize(f->get_len()); f->get_buffer(data.ptrw(), data.size()); @@ -582,6 +588,26 @@ Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path) { return data; } +String FileAccess::get_file_as_string(const String &p_path, Error *r_error) { + + Error err; + Vector<uint8_t> array = get_file_as_array(p_path, &err); + if (r_error) { + *r_error = err; + } + if (err != OK) { + if (r_error) { + return String(); + } else { + ERR_FAIL_COND_V(err != OK, String()); + } + } + + String ret; + ret.parse_utf8((const char *)array.ptr(), array.size()); + return ret; +} + String FileAccess::get_md5(const String &p_file) { FileAccess *f = FileAccess::open(p_file, READ); diff --git a/core/os/file_access.h b/core/os/file_access.h index c65b75369c..9df2a5cade 100644 --- a/core/os/file_access.h +++ b/core/os/file_access.h @@ -164,7 +164,8 @@ public: static String get_sha256(const String &p_file); static String get_multiple_md5(const Vector<String> &p_file); - static Vector<uint8_t> get_file_as_array(const String &p_path); + static Vector<uint8_t> get_file_as_array(const String &p_path, Error *r_error = NULL); + static String get_file_as_string(const String &p_path, Error *r_error = NULL); template <class T> static void make_default(AccessType p_access) { diff --git a/core/os/input.cpp b/core/os/input.cpp index caa9fb1493..63bf1db499 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -89,6 +89,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("action_press", "action", "strength"), &Input::action_press, DEFVAL(1.f)); ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release); ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW)); + ClassDB::bind_method(D_METHOD("get_current_cursor_shape"), &Input::get_current_cursor_shape); ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event); ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input); diff --git a/core/os/input.h b/core/os/input.h index c8b80b28d0..de04f239e6 100644 --- a/core/os/input.h +++ b/core/os/input.h @@ -121,10 +121,10 @@ public: virtual bool is_emulating_touch_from_mouse() const = 0; virtual bool is_emulating_mouse_from_touch() const = 0; - virtual CursorShape get_default_cursor_shape() = 0; + virtual CursorShape get_default_cursor_shape() const = 0; virtual void set_default_cursor_shape(CursorShape p_shape) = 0; + virtual CursorShape get_current_cursor_shape() const = 0; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) = 0; - virtual void set_mouse_in_window(bool p_in_window) = 0; virtual String get_joy_button_string(int p_button) = 0; virtual String get_joy_axis_string(int p_axis) = 0; diff --git a/core/os/os.cpp b/core/os/os.cpp index 03e63f636e..ea378c9e83 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -220,6 +220,16 @@ int OS::get_virtual_keyboard_height() const { return 0; } +void OS::set_cursor_shape(CursorShape p_shape) { +} + +OS::CursorShape OS::get_cursor_shape() const { + return CURSOR_ARROW; +} + +void OS::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { +} + void OS::print_all_resources(String p_to_file) { ERR_FAIL_COND(p_to_file != "" && _OSPRF); diff --git a/core/os/os.h b/core/os/os.h index d02d5a2c84..12012fba80 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -377,8 +377,9 @@ public: // returns height of the currently shown virtual keyboard (0 if keyboard is hidden) virtual int get_virtual_keyboard_height() const; - virtual void set_cursor_shape(CursorShape p_shape) = 0; - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) = 0; + virtual void set_cursor_shape(CursorShape p_shape); + virtual CursorShape get_cursor_shape() const; + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); virtual bool get_swap_ok_cancel() { return false; } virtual void dump_memory_to_file(const char *p_file); diff --git a/core/packed_data_container.cpp b/core/packed_data_container.cpp index 99bed829c1..fa60be64a7 100644 --- a/core/packed_data_container.cpp +++ b/core/packed_data_container.cpp @@ -319,7 +319,8 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd } break; - default: {} + default: { + } } return OK; diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 8c9a3dbcd4..c86d1567dd 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -501,7 +501,7 @@ Error ProjectSettings::_load_settings_binary(const String p_path) { d.resize(vlen); f->get_buffer(d.ptrw(), vlen); Variant value; - err = decode_variant(value, d.ptr(), d.size(), NULL, false); + err = decode_variant(value, d.ptr(), d.size(), NULL, true); ERR_EXPLAIN("Error decoding property: " + key); ERR_CONTINUE(err != OK); set(key, value); @@ -694,7 +694,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str file->store_string(key); int len; - err = encode_variant(value, NULL, len, false); + err = encode_variant(value, NULL, len, true); if (err != OK) memdelete(file); ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); @@ -702,7 +702,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str Vector<uint8_t> buff; buff.resize(len); - err = encode_variant(value, buff.ptrw(), len, false); + err = encode_variant(value, buff.ptrw(), len, true); if (err != OK) memdelete(file); ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); @@ -1004,6 +1004,8 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("application/config/use_custom_user_dir", false); GLOBAL_DEF("application/config/custom_user_dir_name", ""); GLOBAL_DEF("application/config/project_settings_override", ""); + GLOBAL_DEF("audio/default_bus_layout", "res://default_bus_layout.tres"); + custom_prop_info["audio/default_bus_layout"] = PropertyInfo(Variant::STRING, "audio/default_bus_layout", PROPERTY_HINT_FILE, "*.tres"); action = Dictionary(); action["deadzone"] = Variant(0.5f); diff --git a/core/reference.h b/core/reference.h index f3fcc922c7..9105dbbd58 100644 --- a/core/reference.h +++ b/core/reference.h @@ -197,6 +197,19 @@ public: r.reference = NULL; } + template <class T_Other> + void reference_ptr(T_Other *p_ptr) { + if (reference == p_ptr) { + return; + } + unref(); + + T *r = Object::cast_to<T>(p_ptr); + if (r) { + ref_pointer(r); + } + } + Ref(const Ref &p_from) { reference = NULL; diff --git a/core/resource.cpp b/core/resource.cpp index 74c93cd790..74e2c1ed6b 100644 --- a/core/resource.cpp +++ b/core/resource.cpp @@ -363,6 +363,26 @@ bool Resource::is_translation_remapped() const { return remapped_list.in_list(); } +#ifdef TOOLS_ENABLED +//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored +void Resource::set_id_for_path(const String &p_path, int p_id) { + if (p_id == -1) { + id_for_path.erase(p_path); + } else { + id_for_path[p_path] = p_id; + } +} + +int Resource::get_id_for_path(const String &p_path) const { + + if (id_for_path.has(p_path)) { + return id_for_path[p_path]; + } else { + return -1; + } +} +#endif + void Resource::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path", "path"), &Resource::_set_path); diff --git a/core/resource.h b/core/resource.h index a4d9e998ac..853b2859c7 100644 --- a/core/resource.h +++ b/core/resource.h @@ -88,7 +88,9 @@ protected: void _set_path(const String &p_path); void _take_over_path(const String &p_path); - +#ifdef TOOLS_ENABLED + Map<String, int> id_for_path; +#endif public: static Node *(*_get_local_scene_func)(); //used by editor @@ -137,6 +139,12 @@ public: virtual RID get_rid() const; // some resources may offer conversion to RID +#ifdef TOOLS_ENABLED + //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored + void set_id_for_path(const String &p_path, int p_id); + int get_id_for_path(const String &p_path) const; +#endif + Resource(); ~Resource(); }; diff --git a/core/typedefs.h b/core/typedefs.h index a3d7314b1c..660139b90a 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -331,7 +331,28 @@ struct _GlobalLock { /** This is needed due to a strange OpenGL API that expects a pointer * type for an argument that is actually an offset. */ - #define CAST_INT_TO_UCHAR_PTR(ptr) ((uint8_t *)(uintptr_t)(ptr)) +/** Hint for compilers that this fallthrough in a switch is intentional. + * Can be replaced by [[fallthrough]] annotation if we move to C++17. + * Including conditional support for it for people who set -std=c++17 + * themselves. + * Requires a trailing semicolon when used. + */ +#if __cplusplus >= 201703L +#define FALLTHROUGH [[fallthrough]] +#elif defined(__GNUC__) && __GNUC__ >= 7 +#define FALLTHROUGH __attribute__((fallthrough)) +#elif defined(__llvm__) && __cplusplus >= 201103L && defined(__has_feature) +#if __has_feature(cxx_attributes) && defined(__has_warning) +#if __has_warning("-Wimplicit-fallthrough") +#define FALLTHROUGH [[clang::fallthrough]] +#endif +#endif +#endif + +#ifndef FALLTHROUGH +#define FALLTHROUGH +#endif + #endif // TYPEDEFS_H diff --git a/core/ucaps.h b/core/ucaps.h index 5dd81296dd..ae5729a53d 100644 --- a/core/ucaps.h +++ b/core/ucaps.h @@ -704,672 +704,672 @@ static const int caps_table[CAPS_LEN][2] = { }; static const int reverse_caps_table[CAPS_LEN - 1][2] = { - { 0x41, 0x61 }, - { 0x42, 0x62 }, - { 0x43, 0x63 }, - { 0x44, 0x64 }, - { 0x45, 0x65 }, - { 0x46, 0x66 }, - { 0x47, 0x67 }, - { 0x48, 0x68 }, - { 0x49, 0x69 }, - { 0x4a, 0x6a }, - { 0x4b, 0x6b }, - { 0x4c, 0x6c }, - { 0x4d, 0x6d }, - { 0x4e, 0x6e }, - { 0x4f, 0x6f }, - { 0x50, 0x70 }, - { 0x51, 0x71 }, - { 0x52, 0x72 }, - { 0x53, 0x73 }, - { 0x54, 0x74 }, - { 0x55, 0x75 }, - { 0x56, 0x76 }, - { 0x57, 0x77 }, - { 0x58, 0x78 }, - { 0x59, 0x79 }, - { 0x5a, 0x7a }, - { 0xc0, 0xe0 }, - { 0xc1, 0xe1 }, - { 0xc2, 0xe2 }, - { 0xc3, 0xe3 }, - { 0xc4, 0xe4 }, - { 0xc5, 0xe5 }, - { 0xc6, 0xe6 }, - { 0xc7, 0xe7 }, - { 0xc8, 0xe8 }, - { 0xc9, 0xe9 }, - { 0xca, 0xea }, - { 0xcb, 0xeb }, - { 0xcc, 0xec }, - { 0xcd, 0xed }, - { 0xce, 0xee }, - { 0xcf, 0xef }, - { 0xd0, 0xf0 }, - { 0xd1, 0xf1 }, - { 0xd2, 0xf2 }, - { 0xd3, 0xf3 }, - { 0xd4, 0xf4 }, - { 0xd5, 0xf5 }, - { 0xd6, 0xf6 }, - { 0xd8, 0xf8 }, - { 0xd9, 0xf9 }, - { 0xda, 0xfa }, - { 0xdb, 0xfb }, - { 0xdc, 0xfc }, - { 0xdd, 0xfd }, - { 0xde, 0xfe }, - { 0x178, 0xff }, - { 0x100, 0x101 }, - { 0x102, 0x103 }, - { 0x104, 0x105 }, - { 0x106, 0x107 }, - { 0x108, 0x109 }, - { 0x10a, 0x10b }, - { 0x10c, 0x10d }, - { 0x10e, 0x10f }, - { 0x110, 0x111 }, - { 0x112, 0x113 }, - { 0x114, 0x115 }, - { 0x116, 0x117 }, - { 0x118, 0x119 }, - { 0x11a, 0x11b }, - { 0x11c, 0x11d }, - { 0x11e, 0x11f }, - { 0x120, 0x121 }, - { 0x122, 0x123 }, - { 0x124, 0x125 }, - { 0x126, 0x127 }, - { 0x128, 0x129 }, - { 0x12a, 0x12b }, - { 0x12c, 0x12d }, - { 0x12e, 0x12f }, - //{0x49,0x131}, - { 0x132, 0x133 }, - { 0x134, 0x135 }, - { 0x136, 0x137 }, - { 0x139, 0x13a }, - { 0x13b, 0x13c }, - { 0x13d, 0x13e }, - { 0x13f, 0x140 }, - { 0x141, 0x142 }, - { 0x143, 0x144 }, - { 0x145, 0x146 }, - { 0x147, 0x148 }, - { 0x14a, 0x14b }, - { 0x14c, 0x14d }, - { 0x14e, 0x14f }, - { 0x150, 0x151 }, - { 0x152, 0x153 }, - { 0x154, 0x155 }, - { 0x156, 0x157 }, - { 0x158, 0x159 }, - { 0x15a, 0x15b }, - { 0x15c, 0x15d }, - { 0x15e, 0x15f }, - { 0x160, 0x161 }, - { 0x162, 0x163 }, - { 0x164, 0x165 }, - { 0x166, 0x167 }, - { 0x168, 0x169 }, - { 0x16a, 0x16b }, - { 0x16c, 0x16d }, - { 0x16e, 0x16f }, - { 0x170, 0x171 }, - { 0x172, 0x173 }, - { 0x174, 0x175 }, - { 0x176, 0x177 }, - { 0x179, 0x17a }, - { 0x17b, 0x17c }, - { 0x17d, 0x17e }, - { 0x182, 0x183 }, - { 0x184, 0x185 }, - { 0x187, 0x188 }, - { 0x18b, 0x18c }, - { 0x191, 0x192 }, - { 0x198, 0x199 }, - { 0x1a0, 0x1a1 }, - { 0x1a2, 0x1a3 }, - { 0x1a4, 0x1a5 }, - { 0x1a7, 0x1a8 }, - { 0x1ac, 0x1ad }, - { 0x1af, 0x1b0 }, - { 0x1b3, 0x1b4 }, - { 0x1b5, 0x1b6 }, - { 0x1b8, 0x1b9 }, - { 0x1bc, 0x1bd }, - { 0x1c4, 0x1c6 }, - { 0x1c7, 0x1c9 }, - { 0x1ca, 0x1cc }, - { 0x1cd, 0x1ce }, - { 0x1cf, 0x1d0 }, - { 0x1d1, 0x1d2 }, - { 0x1d3, 0x1d4 }, - { 0x1d5, 0x1d6 }, - { 0x1d7, 0x1d8 }, - { 0x1d9, 0x1da }, - { 0x1db, 0x1dc }, - { 0x1de, 0x1df }, - { 0x1e0, 0x1e1 }, - { 0x1e2, 0x1e3 }, - { 0x1e4, 0x1e5 }, - { 0x1e6, 0x1e7 }, - { 0x1e8, 0x1e9 }, - { 0x1ea, 0x1eb }, - { 0x1ec, 0x1ed }, - { 0x1ee, 0x1ef }, - { 0x1f1, 0x1f3 }, - { 0x1f4, 0x1f5 }, - { 0x1fa, 0x1fb }, - { 0x1fc, 0x1fd }, - { 0x1fe, 0x1ff }, - { 0x200, 0x201 }, - { 0x202, 0x203 }, - { 0x204, 0x205 }, - { 0x206, 0x207 }, - { 0x208, 0x209 }, - { 0x20a, 0x20b }, - { 0x20c, 0x20d }, - { 0x20e, 0x20f }, - { 0x210, 0x211 }, - { 0x212, 0x213 }, - { 0x214, 0x215 }, - { 0x216, 0x217 }, - { 0x181, 0x253 }, - { 0x186, 0x254 }, - { 0x18a, 0x257 }, - { 0x18e, 0x258 }, - { 0x18f, 0x259 }, - { 0x190, 0x25b }, - { 0x193, 0x260 }, - { 0x194, 0x263 }, - { 0x197, 0x268 }, - { 0x196, 0x269 }, - { 0x19c, 0x26f }, - { 0x19d, 0x272 }, - { 0x19f, 0x275 }, - { 0x1a9, 0x283 }, - { 0x1ae, 0x288 }, - { 0x1b1, 0x28a }, - { 0x1b2, 0x28b }, - { 0x1b7, 0x292 }, - { 0x386, 0x3ac }, - { 0x388, 0x3ad }, - { 0x389, 0x3ae }, - { 0x38a, 0x3af }, - { 0x391, 0x3b1 }, - { 0x392, 0x3b2 }, - { 0x393, 0x3b3 }, - { 0x394, 0x3b4 }, - { 0x395, 0x3b5 }, - { 0x396, 0x3b6 }, - { 0x397, 0x3b7 }, - { 0x398, 0x3b8 }, - { 0x399, 0x3b9 }, - { 0x39a, 0x3ba }, - { 0x39b, 0x3bb }, - { 0x39c, 0x3bc }, - { 0x39d, 0x3bd }, - { 0x39e, 0x3be }, - { 0x39f, 0x3bf }, - { 0x3a0, 0x3c0 }, - { 0x3a1, 0x3c1 }, - { 0x3a3, 0x3c3 }, - { 0x3a4, 0x3c4 }, - { 0x3a5, 0x3c5 }, - { 0x3a6, 0x3c6 }, - { 0x3a7, 0x3c7 }, - { 0x3a8, 0x3c8 }, - { 0x3a9, 0x3c9 }, - { 0x3aa, 0x3ca }, - { 0x3ab, 0x3cb }, - { 0x38c, 0x3cc }, - { 0x38e, 0x3cd }, - { 0x38f, 0x3ce }, - { 0x3e2, 0x3e3 }, - { 0x3e4, 0x3e5 }, - { 0x3e6, 0x3e7 }, - { 0x3e8, 0x3e9 }, - { 0x3ea, 0x3eb }, - { 0x3ec, 0x3ed }, - { 0x3ee, 0x3ef }, - { 0x410, 0x430 }, - { 0x411, 0x431 }, - { 0x412, 0x432 }, - { 0x413, 0x433 }, - { 0x414, 0x434 }, - { 0x415, 0x435 }, - { 0x416, 0x436 }, - { 0x417, 0x437 }, - { 0x418, 0x438 }, - { 0x419, 0x439 }, - { 0x41a, 0x43a }, - { 0x41b, 0x43b }, - { 0x41c, 0x43c }, - { 0x41d, 0x43d }, - { 0x41e, 0x43e }, - { 0x41f, 0x43f }, - { 0x420, 0x440 }, - { 0x421, 0x441 }, - { 0x422, 0x442 }, - { 0x423, 0x443 }, - { 0x424, 0x444 }, - { 0x425, 0x445 }, - { 0x426, 0x446 }, - { 0x427, 0x447 }, - { 0x428, 0x448 }, - { 0x429, 0x449 }, - { 0x42a, 0x44a }, - { 0x42b, 0x44b }, - { 0x42c, 0x44c }, - { 0x42d, 0x44d }, - { 0x42e, 0x44e }, - { 0x42f, 0x44f }, - { 0x401, 0x451 }, - { 0x402, 0x452 }, - { 0x403, 0x453 }, - { 0x404, 0x454 }, - { 0x405, 0x455 }, - { 0x406, 0x456 }, - { 0x407, 0x457 }, - { 0x408, 0x458 }, - { 0x409, 0x459 }, - { 0x40a, 0x45a }, - { 0x40b, 0x45b }, - { 0x40c, 0x45c }, - { 0x40e, 0x45e }, - { 0x40f, 0x45f }, - { 0x460, 0x461 }, - { 0x462, 0x463 }, - { 0x464, 0x465 }, - { 0x466, 0x467 }, - { 0x468, 0x469 }, - { 0x46a, 0x46b }, - { 0x46c, 0x46d }, - { 0x46e, 0x46f }, - { 0x470, 0x471 }, - { 0x472, 0x473 }, - { 0x474, 0x475 }, - { 0x476, 0x477 }, - { 0x478, 0x479 }, - { 0x47a, 0x47b }, - { 0x47c, 0x47d }, - { 0x47e, 0x47f }, - { 0x480, 0x481 }, - { 0x490, 0x491 }, - { 0x492, 0x493 }, - { 0x494, 0x495 }, - { 0x496, 0x497 }, - { 0x498, 0x499 }, - { 0x49a, 0x49b }, - { 0x49c, 0x49d }, - { 0x49e, 0x49f }, - { 0x4a0, 0x4a1 }, - { 0x4a2, 0x4a3 }, - { 0x4a4, 0x4a5 }, - { 0x4a6, 0x4a7 }, - { 0x4a8, 0x4a9 }, - { 0x4aa, 0x4ab }, - { 0x4ac, 0x4ad }, - { 0x4ae, 0x4af }, - { 0x4b0, 0x4b1 }, - { 0x4b2, 0x4b3 }, - { 0x4b4, 0x4b5 }, - { 0x4b6, 0x4b7 }, - { 0x4b8, 0x4b9 }, - { 0x4ba, 0x4bb }, - { 0x4bc, 0x4bd }, - { 0x4be, 0x4bf }, - { 0x4c1, 0x4c2 }, - { 0x4c3, 0x4c4 }, - { 0x4c7, 0x4c8 }, - { 0x4cb, 0x4cc }, - { 0x4d0, 0x4d1 }, - { 0x4d2, 0x4d3 }, - { 0x4d4, 0x4d5 }, - { 0x4d6, 0x4d7 }, - { 0x4d8, 0x4d9 }, - { 0x4da, 0x4db }, - { 0x4dc, 0x4dd }, - { 0x4de, 0x4df }, - { 0x4e0, 0x4e1 }, - { 0x4e2, 0x4e3 }, - { 0x4e4, 0x4e5 }, - { 0x4e6, 0x4e7 }, - { 0x4e8, 0x4e9 }, - { 0x4ea, 0x4eb }, - { 0x4ee, 0x4ef }, - { 0x4f0, 0x4f1 }, - { 0x4f2, 0x4f3 }, - { 0x4f4, 0x4f5 }, - { 0x4f8, 0x4f9 }, - { 0x531, 0x561 }, - { 0x532, 0x562 }, - { 0x533, 0x563 }, - { 0x534, 0x564 }, - { 0x535, 0x565 }, - { 0x536, 0x566 }, - { 0x537, 0x567 }, - { 0x538, 0x568 }, - { 0x539, 0x569 }, - { 0x53a, 0x56a }, - { 0x53b, 0x56b }, - { 0x53c, 0x56c }, - { 0x53d, 0x56d }, - { 0x53e, 0x56e }, - { 0x53f, 0x56f }, - { 0x540, 0x570 }, - { 0x541, 0x571 }, - { 0x542, 0x572 }, - { 0x543, 0x573 }, - { 0x544, 0x574 }, - { 0x545, 0x575 }, - { 0x546, 0x576 }, - { 0x547, 0x577 }, - { 0x548, 0x578 }, - { 0x549, 0x579 }, - { 0x54a, 0x57a }, - { 0x54b, 0x57b }, - { 0x54c, 0x57c }, - { 0x54d, 0x57d }, - { 0x54e, 0x57e }, - { 0x54f, 0x57f }, - { 0x550, 0x580 }, - { 0x551, 0x581 }, - { 0x552, 0x582 }, - { 0x553, 0x583 }, - { 0x554, 0x584 }, - { 0x555, 0x585 }, - { 0x556, 0x586 }, - { 0x10a0, 0x10d0 }, - { 0x10a1, 0x10d1 }, - { 0x10a2, 0x10d2 }, - { 0x10a3, 0x10d3 }, - { 0x10a4, 0x10d4 }, - { 0x10a5, 0x10d5 }, - { 0x10a6, 0x10d6 }, - { 0x10a7, 0x10d7 }, - { 0x10a8, 0x10d8 }, - { 0x10a9, 0x10d9 }, - { 0x10aa, 0x10da }, - { 0x10ab, 0x10db }, - { 0x10ac, 0x10dc }, - { 0x10ad, 0x10dd }, - { 0x10ae, 0x10de }, - { 0x10af, 0x10df }, - { 0x10b0, 0x10e0 }, - { 0x10b1, 0x10e1 }, - { 0x10b2, 0x10e2 }, - { 0x10b3, 0x10e3 }, - { 0x10b4, 0x10e4 }, - { 0x10b5, 0x10e5 }, - { 0x10b6, 0x10e6 }, - { 0x10b7, 0x10e7 }, - { 0x10b8, 0x10e8 }, - { 0x10b9, 0x10e9 }, - { 0x10ba, 0x10ea }, - { 0x10bb, 0x10eb }, - { 0x10bc, 0x10ec }, - { 0x10bd, 0x10ed }, - { 0x10be, 0x10ee }, - { 0x10bf, 0x10ef }, - { 0x10c0, 0x10f0 }, - { 0x10c1, 0x10f1 }, - { 0x10c2, 0x10f2 }, - { 0x10c3, 0x10f3 }, - { 0x10c4, 0x10f4 }, - { 0x10c5, 0x10f5 }, - { 0x1e00, 0x1e01 }, - { 0x1e02, 0x1e03 }, - { 0x1e04, 0x1e05 }, - { 0x1e06, 0x1e07 }, - { 0x1e08, 0x1e09 }, - { 0x1e0a, 0x1e0b }, - { 0x1e0c, 0x1e0d }, - { 0x1e0e, 0x1e0f }, - { 0x1e10, 0x1e11 }, - { 0x1e12, 0x1e13 }, - { 0x1e14, 0x1e15 }, - { 0x1e16, 0x1e17 }, - { 0x1e18, 0x1e19 }, - { 0x1e1a, 0x1e1b }, - { 0x1e1c, 0x1e1d }, - { 0x1e1e, 0x1e1f }, - { 0x1e20, 0x1e21 }, - { 0x1e22, 0x1e23 }, - { 0x1e24, 0x1e25 }, - { 0x1e26, 0x1e27 }, - { 0x1e28, 0x1e29 }, - { 0x1e2a, 0x1e2b }, - { 0x1e2c, 0x1e2d }, - { 0x1e2e, 0x1e2f }, - { 0x1e30, 0x1e31 }, - { 0x1e32, 0x1e33 }, - { 0x1e34, 0x1e35 }, - { 0x1e36, 0x1e37 }, - { 0x1e38, 0x1e39 }, - { 0x1e3a, 0x1e3b }, - { 0x1e3c, 0x1e3d }, - { 0x1e3e, 0x1e3f }, - { 0x1e40, 0x1e41 }, - { 0x1e42, 0x1e43 }, - { 0x1e44, 0x1e45 }, - { 0x1e46, 0x1e47 }, - { 0x1e48, 0x1e49 }, - { 0x1e4a, 0x1e4b }, - { 0x1e4c, 0x1e4d }, - { 0x1e4e, 0x1e4f }, - { 0x1e50, 0x1e51 }, - { 0x1e52, 0x1e53 }, - { 0x1e54, 0x1e55 }, - { 0x1e56, 0x1e57 }, - { 0x1e58, 0x1e59 }, - { 0x1e5a, 0x1e5b }, - { 0x1e5c, 0x1e5d }, - { 0x1e5e, 0x1e5f }, - { 0x1e60, 0x1e61 }, - { 0x1e62, 0x1e63 }, - { 0x1e64, 0x1e65 }, - { 0x1e66, 0x1e67 }, - { 0x1e68, 0x1e69 }, - { 0x1e6a, 0x1e6b }, - { 0x1e6c, 0x1e6d }, - { 0x1e6e, 0x1e6f }, - { 0x1e70, 0x1e71 }, - { 0x1e72, 0x1e73 }, - { 0x1e74, 0x1e75 }, - { 0x1e76, 0x1e77 }, - { 0x1e78, 0x1e79 }, - { 0x1e7a, 0x1e7b }, - { 0x1e7c, 0x1e7d }, - { 0x1e7e, 0x1e7f }, - { 0x1e80, 0x1e81 }, - { 0x1e82, 0x1e83 }, - { 0x1e84, 0x1e85 }, - { 0x1e86, 0x1e87 }, - { 0x1e88, 0x1e89 }, - { 0x1e8a, 0x1e8b }, - { 0x1e8c, 0x1e8d }, - { 0x1e8e, 0x1e8f }, - { 0x1e90, 0x1e91 }, - { 0x1e92, 0x1e93 }, - { 0x1e94, 0x1e95 }, - { 0x1ea0, 0x1ea1 }, - { 0x1ea2, 0x1ea3 }, - { 0x1ea4, 0x1ea5 }, - { 0x1ea6, 0x1ea7 }, - { 0x1ea8, 0x1ea9 }, - { 0x1eaa, 0x1eab }, - { 0x1eac, 0x1ead }, - { 0x1eae, 0x1eaf }, - { 0x1eb0, 0x1eb1 }, - { 0x1eb2, 0x1eb3 }, - { 0x1eb4, 0x1eb5 }, - { 0x1eb6, 0x1eb7 }, - { 0x1eb8, 0x1eb9 }, - { 0x1eba, 0x1ebb }, - { 0x1ebc, 0x1ebd }, - { 0x1ebe, 0x1ebf }, - { 0x1ec0, 0x1ec1 }, - { 0x1ec2, 0x1ec3 }, - { 0x1ec4, 0x1ec5 }, - { 0x1ec6, 0x1ec7 }, - { 0x1ec8, 0x1ec9 }, - { 0x1eca, 0x1ecb }, - { 0x1ecc, 0x1ecd }, - { 0x1ece, 0x1ecf }, - { 0x1ed0, 0x1ed1 }, - { 0x1ed2, 0x1ed3 }, - { 0x1ed4, 0x1ed5 }, - { 0x1ed6, 0x1ed7 }, - { 0x1ed8, 0x1ed9 }, - { 0x1eda, 0x1edb }, - { 0x1edc, 0x1edd }, - { 0x1ede, 0x1edf }, - { 0x1ee0, 0x1ee1 }, - { 0x1ee2, 0x1ee3 }, - { 0x1ee4, 0x1ee5 }, - { 0x1ee6, 0x1ee7 }, - { 0x1ee8, 0x1ee9 }, - { 0x1eea, 0x1eeb }, - { 0x1eec, 0x1eed }, - { 0x1eee, 0x1eef }, - { 0x1ef0, 0x1ef1 }, - { 0x1ef2, 0x1ef3 }, - { 0x1ef4, 0x1ef5 }, - { 0x1ef6, 0x1ef7 }, - { 0x1ef8, 0x1ef9 }, - { 0x1f08, 0x1f00 }, - { 0x1f09, 0x1f01 }, - { 0x1f0a, 0x1f02 }, - { 0x1f0b, 0x1f03 }, - { 0x1f0c, 0x1f04 }, - { 0x1f0d, 0x1f05 }, - { 0x1f0e, 0x1f06 }, - { 0x1f0f, 0x1f07 }, - { 0x1f18, 0x1f10 }, - { 0x1f19, 0x1f11 }, - { 0x1f1a, 0x1f12 }, - { 0x1f1b, 0x1f13 }, - { 0x1f1c, 0x1f14 }, - { 0x1f1d, 0x1f15 }, - { 0x1f28, 0x1f20 }, - { 0x1f29, 0x1f21 }, - { 0x1f2a, 0x1f22 }, - { 0x1f2b, 0x1f23 }, - { 0x1f2c, 0x1f24 }, - { 0x1f2d, 0x1f25 }, - { 0x1f2e, 0x1f26 }, - { 0x1f2f, 0x1f27 }, - { 0x1f38, 0x1f30 }, - { 0x1f39, 0x1f31 }, - { 0x1f3a, 0x1f32 }, - { 0x1f3b, 0x1f33 }, - { 0x1f3c, 0x1f34 }, - { 0x1f3d, 0x1f35 }, - { 0x1f3e, 0x1f36 }, - { 0x1f3f, 0x1f37 }, - { 0x1f48, 0x1f40 }, - { 0x1f49, 0x1f41 }, - { 0x1f4a, 0x1f42 }, - { 0x1f4b, 0x1f43 }, - { 0x1f4c, 0x1f44 }, - { 0x1f4d, 0x1f45 }, - { 0x1f59, 0x1f51 }, - { 0x1f5b, 0x1f53 }, - { 0x1f5d, 0x1f55 }, - { 0x1f5f, 0x1f57 }, - { 0x1f68, 0x1f60 }, - { 0x1f69, 0x1f61 }, - { 0x1f6a, 0x1f62 }, - { 0x1f6b, 0x1f63 }, - { 0x1f6c, 0x1f64 }, - { 0x1f6d, 0x1f65 }, - { 0x1f6e, 0x1f66 }, - { 0x1f6f, 0x1f67 }, - { 0x1f88, 0x1f80 }, - { 0x1f89, 0x1f81 }, - { 0x1f8a, 0x1f82 }, - { 0x1f8b, 0x1f83 }, - { 0x1f8c, 0x1f84 }, - { 0x1f8d, 0x1f85 }, - { 0x1f8e, 0x1f86 }, - { 0x1f8f, 0x1f87 }, - { 0x1f98, 0x1f90 }, - { 0x1f99, 0x1f91 }, - { 0x1f9a, 0x1f92 }, - { 0x1f9b, 0x1f93 }, - { 0x1f9c, 0x1f94 }, - { 0x1f9d, 0x1f95 }, - { 0x1f9e, 0x1f96 }, - { 0x1f9f, 0x1f97 }, - { 0x1fa8, 0x1fa0 }, - { 0x1fa9, 0x1fa1 }, - { 0x1faa, 0x1fa2 }, - { 0x1fab, 0x1fa3 }, - { 0x1fac, 0x1fa4 }, - { 0x1fad, 0x1fa5 }, - { 0x1fae, 0x1fa6 }, - { 0x1faf, 0x1fa7 }, - { 0x1fb8, 0x1fb0 }, - { 0x1fb9, 0x1fb1 }, - { 0x1fd8, 0x1fd0 }, - { 0x1fd9, 0x1fd1 }, - { 0x1fe8, 0x1fe0 }, - { 0x1fe9, 0x1fe1 }, - { 0x24b6, 0x24d0 }, - { 0x24b7, 0x24d1 }, - { 0x24b8, 0x24d2 }, - { 0x24b9, 0x24d3 }, - { 0x24ba, 0x24d4 }, - { 0x24bb, 0x24d5 }, - { 0x24bc, 0x24d6 }, - { 0x24bd, 0x24d7 }, - { 0x24be, 0x24d8 }, - { 0x24bf, 0x24d9 }, - { 0x24c0, 0x24da }, - { 0x24c1, 0x24db }, - { 0x24c2, 0x24dc }, - { 0x24c3, 0x24dd }, - { 0x24c4, 0x24de }, - { 0x24c5, 0x24df }, - { 0x24c6, 0x24e0 }, - { 0x24c7, 0x24e1 }, - { 0x24c8, 0x24e2 }, - { 0x24c9, 0x24e3 }, - { 0x24ca, 0x24e4 }, - { 0x24cb, 0x24e5 }, - { 0x24cc, 0x24e6 }, - { 0x24cd, 0x24e7 }, - { 0x24ce, 0x24e8 }, - { 0x24cf, 0x24e9 }, - { 0xff21, 0xff41 }, - { 0xff22, 0xff42 }, - { 0xff23, 0xff43 }, - { 0xff24, 0xff44 }, - { 0xff25, 0xff45 }, - { 0xff26, 0xff46 }, - { 0xff27, 0xff47 }, - { 0xff28, 0xff48 }, - { 0xff29, 0xff49 }, - { 0xff2a, 0xff4a }, - { 0xff2b, 0xff4b }, - { 0xff2c, 0xff4c }, - { 0xff2d, 0xff4d }, - { 0xff2e, 0xff4e }, - { 0xff2f, 0xff4f }, - { 0xff30, 0xff50 }, - { 0xff31, 0xff51 }, - { 0xff32, 0xff52 }, - { 0xff33, 0xff53 }, - { 0xff34, 0xff54 }, - { 0xff35, 0xff55 }, - { 0xff36, 0xff56 }, - { 0xff37, 0xff57 }, - { 0xff38, 0xff58 }, - { 0xff39, 0xff59 }, - { 0xff3a, 0xff5a } + { 0x0041, 0x0061 }, + { 0x0042, 0x0062 }, + { 0x0043, 0x0063 }, + { 0x0044, 0x0064 }, + { 0x0045, 0x0065 }, + { 0x0046, 0x0066 }, + { 0x0047, 0x0067 }, + { 0x0048, 0x0068 }, + { 0x0049, 0x0069 }, + // { 0x0049, 0x0131 }, // dotless I + { 0x004A, 0x006A }, + { 0x004B, 0x006B }, + { 0x004C, 0x006C }, + { 0x004D, 0x006D }, + { 0x004E, 0x006E }, + { 0x004F, 0x006F }, + { 0x0050, 0x0070 }, + { 0x0051, 0x0071 }, + { 0x0052, 0x0072 }, + { 0x0053, 0x0073 }, + { 0x0054, 0x0074 }, + { 0x0055, 0x0075 }, + { 0x0056, 0x0076 }, + { 0x0057, 0x0077 }, + { 0x0058, 0x0078 }, + { 0x0059, 0x0079 }, + { 0x005A, 0x007A }, + { 0x00C0, 0x00E0 }, + { 0x00C1, 0x00E1 }, + { 0x00C2, 0x00E2 }, + { 0x00C3, 0x00E3 }, + { 0x00C4, 0x00E4 }, + { 0x00C5, 0x00E5 }, + { 0x00C6, 0x00E6 }, + { 0x00C7, 0x00E7 }, + { 0x00C8, 0x00E8 }, + { 0x00C9, 0x00E9 }, + { 0x00CA, 0x00EA }, + { 0x00CB, 0x00EB }, + { 0x00CC, 0x00EC }, + { 0x00CD, 0x00ED }, + { 0x00CE, 0x00EE }, + { 0x00CF, 0x00EF }, + { 0x00D0, 0x00F0 }, + { 0x00D1, 0x00F1 }, + { 0x00D2, 0x00F2 }, + { 0x00D3, 0x00F3 }, + { 0x00D4, 0x00F4 }, + { 0x00D5, 0x00F5 }, + { 0x00D6, 0x00F6 }, + { 0x00D8, 0x00F8 }, + { 0x00D9, 0x00F9 }, + { 0x00DA, 0x00FA }, + { 0x00DB, 0x00FB }, + { 0x00DC, 0x00FC }, + { 0x00DD, 0x00FD }, + { 0x00DE, 0x00FE }, + { 0x0100, 0x0101 }, + { 0x0102, 0x0103 }, + { 0x0104, 0x0105 }, + { 0x0106, 0x0107 }, + { 0x0108, 0x0109 }, + { 0x010A, 0x010B }, + { 0x010C, 0x010D }, + { 0x010E, 0x010F }, + { 0x0110, 0x0111 }, + { 0x0112, 0x0113 }, + { 0x0114, 0x0115 }, + { 0x0116, 0x0117 }, + { 0x0118, 0x0119 }, + { 0x011A, 0x011B }, + { 0x011C, 0x011D }, + { 0x011E, 0x011F }, + { 0x0120, 0x0121 }, + { 0x0122, 0x0123 }, + { 0x0124, 0x0125 }, + { 0x0126, 0x0127 }, + { 0x0128, 0x0129 }, + { 0x012A, 0x012B }, + { 0x012C, 0x012D }, + { 0x012E, 0x012F }, + { 0x0132, 0x0133 }, + { 0x0134, 0x0135 }, + { 0x0136, 0x0137 }, + { 0x0139, 0x013A }, + { 0x013B, 0x013C }, + { 0x013D, 0x013E }, + { 0x013F, 0x0140 }, + { 0x0141, 0x0142 }, + { 0x0143, 0x0144 }, + { 0x0145, 0x0146 }, + { 0x0147, 0x0148 }, + { 0x014A, 0x014B }, + { 0x014C, 0x014D }, + { 0x014E, 0x014F }, + { 0x0150, 0x0151 }, + { 0x0152, 0x0153 }, + { 0x0154, 0x0155 }, + { 0x0156, 0x0157 }, + { 0x0158, 0x0159 }, + { 0x015A, 0x015B }, + { 0x015C, 0x015D }, + { 0x015E, 0x015F }, + { 0x0160, 0x0161 }, + { 0x0162, 0x0163 }, + { 0x0164, 0x0165 }, + { 0x0166, 0x0167 }, + { 0x0168, 0x0169 }, + { 0x016A, 0x016B }, + { 0x016C, 0x016D }, + { 0x016E, 0x016F }, + { 0x0170, 0x0171 }, + { 0x0172, 0x0173 }, + { 0x0174, 0x0175 }, + { 0x0176, 0x0177 }, + { 0x0178, 0x00FF }, + { 0x0179, 0x017A }, + { 0x017B, 0x017C }, + { 0x017D, 0x017E }, + { 0x0181, 0x0253 }, + { 0x0182, 0x0183 }, + { 0x0184, 0x0185 }, + { 0x0186, 0x0254 }, + { 0x0187, 0x0188 }, + { 0x018A, 0x0257 }, + { 0x018B, 0x018C }, + { 0x018E, 0x0258 }, + { 0x018F, 0x0259 }, + { 0x0190, 0x025B }, + { 0x0191, 0x0192 }, + { 0x0193, 0x0260 }, + { 0x0194, 0x0263 }, + { 0x0196, 0x0269 }, + { 0x0197, 0x0268 }, + { 0x0198, 0x0199 }, + { 0x019C, 0x026F }, + { 0x019D, 0x0272 }, + { 0x019F, 0x0275 }, + { 0x01A0, 0x01A1 }, + { 0x01A2, 0x01A3 }, + { 0x01A4, 0x01A5 }, + { 0x01A7, 0x01A8 }, + { 0x01A9, 0x0283 }, + { 0x01AC, 0x01AD }, + { 0x01AE, 0x0288 }, + { 0x01AF, 0x01B0 }, + { 0x01B1, 0x028A }, + { 0x01B2, 0x028B }, + { 0x01B3, 0x01B4 }, + { 0x01B5, 0x01B6 }, + { 0x01B7, 0x0292 }, + { 0x01B8, 0x01B9 }, + { 0x01BC, 0x01BD }, + { 0x01C4, 0x01C6 }, + { 0x01C7, 0x01C9 }, + { 0x01CA, 0x01CC }, + { 0x01CD, 0x01CE }, + { 0x01CF, 0x01D0 }, + { 0x01D1, 0x01D2 }, + { 0x01D3, 0x01D4 }, + { 0x01D5, 0x01D6 }, + { 0x01D7, 0x01D8 }, + { 0x01D9, 0x01DA }, + { 0x01DB, 0x01DC }, + { 0x01DE, 0x01DF }, + { 0x01E0, 0x01E1 }, + { 0x01E2, 0x01E3 }, + { 0x01E4, 0x01E5 }, + { 0x01E6, 0x01E7 }, + { 0x01E8, 0x01E9 }, + { 0x01EA, 0x01EB }, + { 0x01EC, 0x01ED }, + { 0x01EE, 0x01EF }, + { 0x01F1, 0x01F3 }, + { 0x01F4, 0x01F5 }, + { 0x01FA, 0x01FB }, + { 0x01FC, 0x01FD }, + { 0x01FE, 0x01FF }, + { 0x0200, 0x0201 }, + { 0x0202, 0x0203 }, + { 0x0204, 0x0205 }, + { 0x0206, 0x0207 }, + { 0x0208, 0x0209 }, + { 0x020A, 0x020B }, + { 0x020C, 0x020D }, + { 0x020E, 0x020F }, + { 0x0210, 0x0211 }, + { 0x0212, 0x0213 }, + { 0x0214, 0x0215 }, + { 0x0216, 0x0217 }, + { 0x0386, 0x03AC }, + { 0x0388, 0x03AD }, + { 0x0389, 0x03AE }, + { 0x038A, 0x03AF }, + { 0x038C, 0x03CC }, + { 0x038E, 0x03CD }, + { 0x038F, 0x03CE }, + { 0x0391, 0x03B1 }, + { 0x0392, 0x03B2 }, + { 0x0393, 0x03B3 }, + { 0x0394, 0x03B4 }, + { 0x0395, 0x03B5 }, + { 0x0396, 0x03B6 }, + { 0x0397, 0x03B7 }, + { 0x0398, 0x03B8 }, + { 0x0399, 0x03B9 }, + { 0x039A, 0x03BA }, + { 0x039B, 0x03BB }, + { 0x039C, 0x03BC }, + { 0x039D, 0x03BD }, + { 0x039E, 0x03BE }, + { 0x039F, 0x03BF }, + { 0x03A0, 0x03C0 }, + { 0x03A1, 0x03C1 }, + { 0x03A3, 0x03C3 }, + { 0x03A4, 0x03C4 }, + { 0x03A5, 0x03C5 }, + { 0x03A6, 0x03C6 }, + { 0x03A7, 0x03C7 }, + { 0x03A8, 0x03C8 }, + { 0x03A9, 0x03C9 }, + { 0x03AA, 0x03CA }, + { 0x03AB, 0x03CB }, + { 0x03E2, 0x03E3 }, + { 0x03E4, 0x03E5 }, + { 0x03E6, 0x03E7 }, + { 0x03E8, 0x03E9 }, + { 0x03EA, 0x03EB }, + { 0x03EC, 0x03ED }, + { 0x03EE, 0x03EF }, + { 0x0401, 0x0451 }, + { 0x0402, 0x0452 }, + { 0x0403, 0x0453 }, + { 0x0404, 0x0454 }, + { 0x0405, 0x0455 }, + { 0x0406, 0x0456 }, + { 0x0407, 0x0457 }, + { 0x0408, 0x0458 }, + { 0x0409, 0x0459 }, + { 0x040A, 0x045A }, + { 0x040B, 0x045B }, + { 0x040C, 0x045C }, + { 0x040E, 0x045E }, + { 0x040F, 0x045F }, + { 0x0410, 0x0430 }, + { 0x0411, 0x0431 }, + { 0x0412, 0x0432 }, + { 0x0413, 0x0433 }, + { 0x0414, 0x0434 }, + { 0x0415, 0x0435 }, + { 0x0416, 0x0436 }, + { 0x0417, 0x0437 }, + { 0x0418, 0x0438 }, + { 0x0419, 0x0439 }, + { 0x041A, 0x043A }, + { 0x041B, 0x043B }, + { 0x041C, 0x043C }, + { 0x041D, 0x043D }, + { 0x041E, 0x043E }, + { 0x041F, 0x043F }, + { 0x0420, 0x0440 }, + { 0x0421, 0x0441 }, + { 0x0422, 0x0442 }, + { 0x0423, 0x0443 }, + { 0x0424, 0x0444 }, + { 0x0425, 0x0445 }, + { 0x0426, 0x0446 }, + { 0x0427, 0x0447 }, + { 0x0428, 0x0448 }, + { 0x0429, 0x0449 }, + { 0x042A, 0x044A }, + { 0x042B, 0x044B }, + { 0x042C, 0x044C }, + { 0x042D, 0x044D }, + { 0x042E, 0x044E }, + { 0x042F, 0x044F }, + { 0x0460, 0x0461 }, + { 0x0462, 0x0463 }, + { 0x0464, 0x0465 }, + { 0x0466, 0x0467 }, + { 0x0468, 0x0469 }, + { 0x046A, 0x046B }, + { 0x046C, 0x046D }, + { 0x046E, 0x046F }, + { 0x0470, 0x0471 }, + { 0x0472, 0x0473 }, + { 0x0474, 0x0475 }, + { 0x0476, 0x0477 }, + { 0x0478, 0x0479 }, + { 0x047A, 0x047B }, + { 0x047C, 0x047D }, + { 0x047E, 0x047F }, + { 0x0480, 0x0481 }, + { 0x0490, 0x0491 }, + { 0x0492, 0x0493 }, + { 0x0494, 0x0495 }, + { 0x0496, 0x0497 }, + { 0x0498, 0x0499 }, + { 0x049A, 0x049B }, + { 0x049C, 0x049D }, + { 0x049E, 0x049F }, + { 0x04A0, 0x04A1 }, + { 0x04A2, 0x04A3 }, + { 0x04A4, 0x04A5 }, + { 0x04A6, 0x04A7 }, + { 0x04A8, 0x04A9 }, + { 0x04AA, 0x04AB }, + { 0x04AC, 0x04AD }, + { 0x04AE, 0x04AF }, + { 0x04B0, 0x04B1 }, + { 0x04B2, 0x04B3 }, + { 0x04B4, 0x04B5 }, + { 0x04B6, 0x04B7 }, + { 0x04B8, 0x04B9 }, + { 0x04BA, 0x04BB }, + { 0x04BC, 0x04BD }, + { 0x04BE, 0x04BF }, + { 0x04C1, 0x04C2 }, + { 0x04C3, 0x04C4 }, + { 0x04C7, 0x04C8 }, + { 0x04CB, 0x04CC }, + { 0x04D0, 0x04D1 }, + { 0x04D2, 0x04D3 }, + { 0x04D4, 0x04D5 }, + { 0x04D6, 0x04D7 }, + { 0x04D8, 0x04D9 }, + { 0x04DA, 0x04DB }, + { 0x04DC, 0x04DD }, + { 0x04DE, 0x04DF }, + { 0x04E0, 0x04E1 }, + { 0x04E2, 0x04E3 }, + { 0x04E4, 0x04E5 }, + { 0x04E6, 0x04E7 }, + { 0x04E8, 0x04E9 }, + { 0x04EA, 0x04EB }, + { 0x04EE, 0x04EF }, + { 0x04F0, 0x04F1 }, + { 0x04F2, 0x04F3 }, + { 0x04F4, 0x04F5 }, + { 0x04F8, 0x04F9 }, + { 0x0531, 0x0561 }, + { 0x0532, 0x0562 }, + { 0x0533, 0x0563 }, + { 0x0534, 0x0564 }, + { 0x0535, 0x0565 }, + { 0x0536, 0x0566 }, + { 0x0537, 0x0567 }, + { 0x0538, 0x0568 }, + { 0x0539, 0x0569 }, + { 0x053A, 0x056A }, + { 0x053B, 0x056B }, + { 0x053C, 0x056C }, + { 0x053D, 0x056D }, + { 0x053E, 0x056E }, + { 0x053F, 0x056F }, + { 0x0540, 0x0570 }, + { 0x0541, 0x0571 }, + { 0x0542, 0x0572 }, + { 0x0543, 0x0573 }, + { 0x0544, 0x0574 }, + { 0x0545, 0x0575 }, + { 0x0546, 0x0576 }, + { 0x0547, 0x0577 }, + { 0x0548, 0x0578 }, + { 0x0549, 0x0579 }, + { 0x054A, 0x057A }, + { 0x054B, 0x057B }, + { 0x054C, 0x057C }, + { 0x054D, 0x057D }, + { 0x054E, 0x057E }, + { 0x054F, 0x057F }, + { 0x0550, 0x0580 }, + { 0x0551, 0x0581 }, + { 0x0552, 0x0582 }, + { 0x0553, 0x0583 }, + { 0x0554, 0x0584 }, + { 0x0555, 0x0585 }, + { 0x0556, 0x0586 }, + { 0x10A0, 0x10D0 }, + { 0x10A1, 0x10D1 }, + { 0x10A2, 0x10D2 }, + { 0x10A3, 0x10D3 }, + { 0x10A4, 0x10D4 }, + { 0x10A5, 0x10D5 }, + { 0x10A6, 0x10D6 }, + { 0x10A7, 0x10D7 }, + { 0x10A8, 0x10D8 }, + { 0x10A9, 0x10D9 }, + { 0x10AA, 0x10DA }, + { 0x10AB, 0x10DB }, + { 0x10AC, 0x10DC }, + { 0x10AD, 0x10DD }, + { 0x10AE, 0x10DE }, + { 0x10AF, 0x10DF }, + { 0x10B0, 0x10E0 }, + { 0x10B1, 0x10E1 }, + { 0x10B2, 0x10E2 }, + { 0x10B3, 0x10E3 }, + { 0x10B4, 0x10E4 }, + { 0x10B5, 0x10E5 }, + { 0x10B6, 0x10E6 }, + { 0x10B7, 0x10E7 }, + { 0x10B8, 0x10E8 }, + { 0x10B9, 0x10E9 }, + { 0x10BA, 0x10EA }, + { 0x10BB, 0x10EB }, + { 0x10BC, 0x10EC }, + { 0x10BD, 0x10ED }, + { 0x10BE, 0x10EE }, + { 0x10BF, 0x10EF }, + { 0x10C0, 0x10F0 }, + { 0x10C1, 0x10F1 }, + { 0x10C2, 0x10F2 }, + { 0x10C3, 0x10F3 }, + { 0x10C4, 0x10F4 }, + { 0x10C5, 0x10F5 }, + { 0x1E00, 0x1E01 }, + { 0x1E02, 0x1E03 }, + { 0x1E04, 0x1E05 }, + { 0x1E06, 0x1E07 }, + { 0x1E08, 0x1E09 }, + { 0x1E0A, 0x1E0B }, + { 0x1E0C, 0x1E0D }, + { 0x1E0E, 0x1E0F }, + { 0x1E10, 0x1E11 }, + { 0x1E12, 0x1E13 }, + { 0x1E14, 0x1E15 }, + { 0x1E16, 0x1E17 }, + { 0x1E18, 0x1E19 }, + { 0x1E1A, 0x1E1B }, + { 0x1E1C, 0x1E1D }, + { 0x1E1E, 0x1E1F }, + { 0x1E20, 0x1E21 }, + { 0x1E22, 0x1E23 }, + { 0x1E24, 0x1E25 }, + { 0x1E26, 0x1E27 }, + { 0x1E28, 0x1E29 }, + { 0x1E2A, 0x1E2B }, + { 0x1E2C, 0x1E2D }, + { 0x1E2E, 0x1E2F }, + { 0x1E30, 0x1E31 }, + { 0x1E32, 0x1E33 }, + { 0x1E34, 0x1E35 }, + { 0x1E36, 0x1E37 }, + { 0x1E38, 0x1E39 }, + { 0x1E3A, 0x1E3B }, + { 0x1E3C, 0x1E3D }, + { 0x1E3E, 0x1E3F }, + { 0x1E40, 0x1E41 }, + { 0x1E42, 0x1E43 }, + { 0x1E44, 0x1E45 }, + { 0x1E46, 0x1E47 }, + { 0x1E48, 0x1E49 }, + { 0x1E4A, 0x1E4B }, + { 0x1E4C, 0x1E4D }, + { 0x1E4E, 0x1E4F }, + { 0x1E50, 0x1E51 }, + { 0x1E52, 0x1E53 }, + { 0x1E54, 0x1E55 }, + { 0x1E56, 0x1E57 }, + { 0x1E58, 0x1E59 }, + { 0x1E5A, 0x1E5B }, + { 0x1E5C, 0x1E5D }, + { 0x1E5E, 0x1E5F }, + { 0x1E60, 0x1E61 }, + { 0x1E62, 0x1E63 }, + { 0x1E64, 0x1E65 }, + { 0x1E66, 0x1E67 }, + { 0x1E68, 0x1E69 }, + { 0x1E6A, 0x1E6B }, + { 0x1E6C, 0x1E6D }, + { 0x1E6E, 0x1E6F }, + { 0x1E70, 0x1E71 }, + { 0x1E72, 0x1E73 }, + { 0x1E74, 0x1E75 }, + { 0x1E76, 0x1E77 }, + { 0x1E78, 0x1E79 }, + { 0x1E7A, 0x1E7B }, + { 0x1E7C, 0x1E7D }, + { 0x1E7E, 0x1E7F }, + { 0x1E80, 0x1E81 }, + { 0x1E82, 0x1E83 }, + { 0x1E84, 0x1E85 }, + { 0x1E86, 0x1E87 }, + { 0x1E88, 0x1E89 }, + { 0x1E8A, 0x1E8B }, + { 0x1E8C, 0x1E8D }, + { 0x1E8E, 0x1E8F }, + { 0x1E90, 0x1E91 }, + { 0x1E92, 0x1E93 }, + { 0x1E94, 0x1E95 }, + { 0x1EA0, 0x1EA1 }, + { 0x1EA2, 0x1EA3 }, + { 0x1EA4, 0x1EA5 }, + { 0x1EA6, 0x1EA7 }, + { 0x1EA8, 0x1EA9 }, + { 0x1EAA, 0x1EAB }, + { 0x1EAC, 0x1EAD }, + { 0x1EAE, 0x1EAF }, + { 0x1EB0, 0x1EB1 }, + { 0x1EB2, 0x1EB3 }, + { 0x1EB4, 0x1EB5 }, + { 0x1EB6, 0x1EB7 }, + { 0x1EB8, 0x1EB9 }, + { 0x1EBA, 0x1EBB }, + { 0x1EBC, 0x1EBD }, + { 0x1EBE, 0x1EBF }, + { 0x1EC0, 0x1EC1 }, + { 0x1EC2, 0x1EC3 }, + { 0x1EC4, 0x1EC5 }, + { 0x1EC6, 0x1EC7 }, + { 0x1EC8, 0x1EC9 }, + { 0x1ECA, 0x1ECB }, + { 0x1ECC, 0x1ECD }, + { 0x1ECE, 0x1ECF }, + { 0x1ED0, 0x1ED1 }, + { 0x1ED2, 0x1ED3 }, + { 0x1ED4, 0x1ED5 }, + { 0x1ED6, 0x1ED7 }, + { 0x1ED8, 0x1ED9 }, + { 0x1EDA, 0x1EDB }, + { 0x1EDC, 0x1EDD }, + { 0x1EDE, 0x1EDF }, + { 0x1EE0, 0x1EE1 }, + { 0x1EE2, 0x1EE3 }, + { 0x1EE4, 0x1EE5 }, + { 0x1EE6, 0x1EE7 }, + { 0x1EE8, 0x1EE9 }, + { 0x1EEA, 0x1EEB }, + { 0x1EEC, 0x1EED }, + { 0x1EEE, 0x1EEF }, + { 0x1EF0, 0x1EF1 }, + { 0x1EF2, 0x1EF3 }, + { 0x1EF4, 0x1EF5 }, + { 0x1EF6, 0x1EF7 }, + { 0x1EF8, 0x1EF9 }, + { 0x1F08, 0x1F00 }, + { 0x1F09, 0x1F01 }, + { 0x1F0A, 0x1F02 }, + { 0x1F0B, 0x1F03 }, + { 0x1F0C, 0x1F04 }, + { 0x1F0D, 0x1F05 }, + { 0x1F0E, 0x1F06 }, + { 0x1F0F, 0x1F07 }, + { 0x1F18, 0x1F10 }, + { 0x1F19, 0x1F11 }, + { 0x1F1A, 0x1F12 }, + { 0x1F1B, 0x1F13 }, + { 0x1F1C, 0x1F14 }, + { 0x1F1D, 0x1F15 }, + { 0x1F28, 0x1F20 }, + { 0x1F29, 0x1F21 }, + { 0x1F2A, 0x1F22 }, + { 0x1F2B, 0x1F23 }, + { 0x1F2C, 0x1F24 }, + { 0x1F2D, 0x1F25 }, + { 0x1F2E, 0x1F26 }, + { 0x1F2F, 0x1F27 }, + { 0x1F38, 0x1F30 }, + { 0x1F39, 0x1F31 }, + { 0x1F3A, 0x1F32 }, + { 0x1F3B, 0x1F33 }, + { 0x1F3C, 0x1F34 }, + { 0x1F3D, 0x1F35 }, + { 0x1F3E, 0x1F36 }, + { 0x1F3F, 0x1F37 }, + { 0x1F48, 0x1F40 }, + { 0x1F49, 0x1F41 }, + { 0x1F4A, 0x1F42 }, + { 0x1F4B, 0x1F43 }, + { 0x1F4C, 0x1F44 }, + { 0x1F4D, 0x1F45 }, + { 0x1F59, 0x1F51 }, + { 0x1F5B, 0x1F53 }, + { 0x1F5D, 0x1F55 }, + { 0x1F5F, 0x1F57 }, + { 0x1F68, 0x1F60 }, + { 0x1F69, 0x1F61 }, + { 0x1F6A, 0x1F62 }, + { 0x1F6B, 0x1F63 }, + { 0x1F6C, 0x1F64 }, + { 0x1F6D, 0x1F65 }, + { 0x1F6E, 0x1F66 }, + { 0x1F6F, 0x1F67 }, + { 0x1F88, 0x1F80 }, + { 0x1F89, 0x1F81 }, + { 0x1F8A, 0x1F82 }, + { 0x1F8B, 0x1F83 }, + { 0x1F8C, 0x1F84 }, + { 0x1F8D, 0x1F85 }, + { 0x1F8E, 0x1F86 }, + { 0x1F8F, 0x1F87 }, + { 0x1F98, 0x1F90 }, + { 0x1F99, 0x1F91 }, + { 0x1F9A, 0x1F92 }, + { 0x1F9B, 0x1F93 }, + { 0x1F9C, 0x1F94 }, + { 0x1F9D, 0x1F95 }, + { 0x1F9E, 0x1F96 }, + { 0x1F9F, 0x1F97 }, + { 0x1FA8, 0x1FA0 }, + { 0x1FA9, 0x1FA1 }, + { 0x1FAA, 0x1FA2 }, + { 0x1FAB, 0x1FA3 }, + { 0x1FAC, 0x1FA4 }, + { 0x1FAD, 0x1FA5 }, + { 0x1FAE, 0x1FA6 }, + { 0x1FAF, 0x1FA7 }, + { 0x1FB8, 0x1FB0 }, + { 0x1FB9, 0x1FB1 }, + { 0x1FD8, 0x1FD0 }, + { 0x1FD9, 0x1FD1 }, + { 0x1FE8, 0x1FE0 }, + { 0x1FE9, 0x1FE1 }, + { 0x24B6, 0x24D0 }, + { 0x24B7, 0x24D1 }, + { 0x24B8, 0x24D2 }, + { 0x24B9, 0x24D3 }, + { 0x24BA, 0x24D4 }, + { 0x24BB, 0x24D5 }, + { 0x24BC, 0x24D6 }, + { 0x24BD, 0x24D7 }, + { 0x24BE, 0x24D8 }, + { 0x24BF, 0x24D9 }, + { 0x24C0, 0x24DA }, + { 0x24C1, 0x24DB }, + { 0x24C2, 0x24DC }, + { 0x24C3, 0x24DD }, + { 0x24C4, 0x24DE }, + { 0x24C5, 0x24DF }, + { 0x24C6, 0x24E0 }, + { 0x24C7, 0x24E1 }, + { 0x24C8, 0x24E2 }, + { 0x24C9, 0x24E3 }, + { 0x24CA, 0x24E4 }, + { 0x24CB, 0x24E5 }, + { 0x24CC, 0x24E6 }, + { 0x24CD, 0x24E7 }, + { 0x24CE, 0x24E8 }, + { 0x24CF, 0x24E9 }, + { 0xFF21, 0xFF41 }, + { 0xFF22, 0xFF42 }, + { 0xFF23, 0xFF43 }, + { 0xFF24, 0xFF44 }, + { 0xFF25, 0xFF45 }, + { 0xFF26, 0xFF46 }, + { 0xFF27, 0xFF47 }, + { 0xFF28, 0xFF48 }, + { 0xFF29, 0xFF49 }, + { 0xFF2A, 0xFF4A }, + { 0xFF2B, 0xFF4B }, + { 0xFF2C, 0xFF4C }, + { 0xFF2D, 0xFF4D }, + { 0xFF2E, 0xFF4E }, + { 0xFF2F, 0xFF4F }, + { 0xFF30, 0xFF50 }, + { 0xFF31, 0xFF51 }, + { 0xFF32, 0xFF52 }, + { 0xFF33, 0xFF53 }, + { 0xFF34, 0xFF54 }, + { 0xFF35, 0xFF55 }, + { 0xFF36, 0xFF56 }, + { 0xFF37, 0xFF57 }, + { 0xFF38, 0xFF58 }, + { 0xFF39, 0xFF59 }, + { 0xFF3A, 0xFF5A }, }; static int _find_upper(int ch) { diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp index e13164d50f..69581e4115 100644 --- a/core/undo_redo.cpp +++ b/core/undo_redo.cpp @@ -90,7 +90,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { actions.write[actions.size() - 1].last_tick = ticks; merge_mode = p_mode; - + merging = true; } else { Action new_action; @@ -250,6 +250,11 @@ void UndoRedo::commit_action() { if (action_level > 0) return; //still nested + if (merging) { + version--; + merging = false; + } + commiting++; redo(); // perform action commiting--; @@ -396,6 +401,7 @@ UndoRedo::UndoRedo() { action_level = 0; current_action = -1; merge_mode = MERGE_DISABLE; + merging = false; callback = NULL; callback_ud = NULL; diff --git a/core/undo_redo.h b/core/undo_redo.h index b626149ce6..6293e77acc 100644 --- a/core/undo_redo.h +++ b/core/undo_redo.h @@ -80,6 +80,7 @@ private: int current_action; int action_level; MergeMode merge_mode; + bool merging; uint64_t version; void _pop_history_tail(); diff --git a/core/ustring.cpp b/core/ustring.cpp index ff8fcaaaaf..d60bd16921 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -3748,6 +3748,24 @@ bool String::is_valid_html_color() const { return Color::html_is_valid(*this); } +bool String::is_valid_filename() const { + + String stripped = strip_edges(); + if (*this != stripped) { + return false; + } + + if (stripped == String()) { + return false; + } + + if (find(":") != -1 || find("/") != -1 || find("\\") != -1 || find("?") != -1 || find("*") != -1 || find("\"") != -1 || find("|") != -1 || find("%") != -1 || find("<") != -1 || find(">") != -1) { + return false; + } else { + return true; + } +} + bool String::is_valid_ip_address() const { if (find(":") >= 0) { diff --git a/core/ustring.h b/core/ustring.h index cb3d87378a..85103057df 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -97,6 +97,10 @@ public: _FORCE_INLINE_ CharString() {} _FORCE_INLINE_ CharString(const CharString &p_str) { _cowdata._ref(p_str._cowdata); } + _FORCE_INLINE_ CharString operator=(const CharString &p_str) { + _cowdata._ref(p_str._cowdata); + return *this; + } bool operator<(const CharString &p_right) const; CharString &operator+=(char p_char); @@ -331,6 +335,7 @@ public: bool is_valid_hex_number(bool p_with_prefix) const; bool is_valid_html_color() const; bool is_valid_ip_address() const; + bool is_valid_filename() const; /** * The constructors must not depend on other overloads @@ -339,6 +344,10 @@ public: _FORCE_INLINE_ String() {} _FORCE_INLINE_ String(const String &p_str) { _cowdata._ref(p_str._cowdata); } + String operator=(const String &p_str) { + _cowdata._ref(p_str._cowdata); + return *this; + } String(const char *p_str); String(const CharType *p_str, int p_clip_to_len = -1); @@ -398,11 +407,18 @@ _FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) { //tool translate #ifdef TOOLS_ENABLED +//gets parsed String TTR(const String &); +//use for c strings +#define TTRC(m_value) m_value +//use to avoid parsing (for use later with C strings) +#define TTRGET(m_value) TTR(m_value) #else #define TTR(m_val) (String()) +#define TTRCDEF(m_value) (m_value) +#define TTRC(m_value) (m_value) #endif diff --git a/core/variant.cpp b/core/variant.cpp index 2ee2e8e293..6c54faf233 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -179,7 +179,8 @@ String Variant::get_type_name(Variant::Type p_type) { return "PoolColorArray"; } break; - default: {} + default: { + } } return ""; @@ -403,7 +404,8 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { valid_types = valid; } break; - default: {} + default: { + } } if (valid_types) { @@ -649,7 +651,8 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type valid_types = valid; } break; - default: {} + default: { + } } if (valid_types) { @@ -841,7 +844,8 @@ bool Variant::is_zero() const { return reinterpret_cast<const PoolVector<Color> *>(_data._mem)->size() == 0; } break; - default: {} + default: { + } } return false; @@ -896,7 +900,9 @@ bool Variant::is_one() const { } break; - default: { return !is_zero(); } + default: { + return !is_zero(); + } } return false; @@ -1039,7 +1045,8 @@ void Variant::reference(const Variant &p_variant) { memnew_placement(_data._mem, PoolVector<Color>(*reinterpret_cast<const PoolVector<Color> *>(p_variant._data._mem))); } break; - default: {} + default: { + } } } @@ -1143,7 +1150,8 @@ void Variant::clear() { reinterpret_cast<PoolVector<Color> *>(_data._mem)->~PoolVector<Color>(); } break; - default: {} /* not needed */ + default: { + } /* not needed */ } type = NIL; @@ -1823,7 +1831,9 @@ inline DA _convert_array_from_variant(const Variant &p_variant) { case Variant::POOL_COLOR_ARRAY: { return _convert_array<DA, PoolVector<Color> >(p_variant.operator PoolVector<Color>()); } - default: { return DA(); } + default: { + return DA(); + } } return DA(); @@ -2642,7 +2652,8 @@ void Variant::operator=(const Variant &p_variant) { *reinterpret_cast<PoolVector<Color> *>(_data._mem) = *reinterpret_cast<const PoolVector<Color> *>(p_variant._data._mem); } break; - default: {} + default: { + } } } @@ -2919,7 +2930,8 @@ uint32_t Variant::hash() const { return hash; } break; - default: {} + default: { + } } return 0; @@ -3167,7 +3179,8 @@ bool Variant::is_shared() const { case OBJECT: return true; case ARRAY: return true; case DICTIONARY: return true; - default: {} + default: { + } } return false; @@ -3204,7 +3217,8 @@ Variant Variant::call(const StringName &p_method, VARIANT_ARG_DECLARE) { String err = "Too many arguments for method '" + p_method + "'"; ERR_PRINT(err.utf8().get_data()); } break; - default: {} + default: { + } } return ret; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 3812592639..143b07418e 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -294,6 +294,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(String, is_valid_hex_number); VCALL_LOCALMEM0R(String, is_valid_html_color); VCALL_LOCALMEM0R(String, is_valid_ip_address); + VCALL_LOCALMEM0R(String, is_valid_filename); VCALL_LOCALMEM0R(String, to_int); VCALL_LOCALMEM0R(String, to_float); VCALL_LOCALMEM0R(String, hex_to_int); @@ -341,6 +342,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(Vector2, project); VCALL_LOCALMEM1R(Vector2, angle_to); VCALL_LOCALMEM1R(Vector2, angle_to_point); + VCALL_LOCALMEM1R(Vector2, direction_to); VCALL_LOCALMEM2R(Vector2, linear_interpolate); VCALL_LOCALMEM2R(Vector2, slerp); VCALL_LOCALMEM4R(Vector2, cubic_interpolate); @@ -397,6 +399,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(Vector3, distance_squared_to); VCALL_LOCALMEM1R(Vector3, project); VCALL_LOCALMEM1R(Vector3, angle_to); + VCALL_LOCALMEM1R(Vector3, direction_to); VCALL_LOCALMEM1R(Vector3, slide); VCALL_LOCALMEM1R(Vector3, bounce); VCALL_LOCALMEM1R(Vector3, reflect); @@ -1540,6 +1543,7 @@ void register_variant_methods() { ADDFUNC1R(STRING, BOOL, String, is_valid_hex_number, BOOL, "with_prefix", varray(false)); ADDFUNC0R(STRING, BOOL, String, is_valid_html_color, varray()); ADDFUNC0R(STRING, BOOL, String, is_valid_ip_address, varray()); + ADDFUNC0R(STRING, BOOL, String, is_valid_filename, varray()); ADDFUNC0R(STRING, INT, String, to_int, varray()); ADDFUNC0R(STRING, REAL, String, to_float, varray()); ADDFUNC0R(STRING, INT, String, hex_to_int, varray()); @@ -1556,6 +1560,7 @@ void register_variant_methods() { ADDFUNC0R(VECTOR2, REAL, Vector2, angle, varray()); ADDFUNC0R(VECTOR2, REAL, Vector2, length_squared, varray()); ADDFUNC0R(VECTOR2, BOOL, Vector2, is_normalized, varray()); + ADDFUNC1R(VECTOR2, VECTOR2, Vector2, direction_to, VECTOR2, "b", varray()); ADDFUNC1R(VECTOR2, REAL, Vector2, distance_to, VECTOR2, "to", varray()); ADDFUNC1R(VECTOR2, REAL, Vector2, distance_squared_to, VECTOR2, "to", varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, project, VECTOR2, "b", varray()); @@ -1604,6 +1609,7 @@ void register_variant_methods() { ADDFUNC2R(VECTOR3, VECTOR3, Vector3, linear_interpolate, VECTOR3, "b", REAL, "t", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, slerp, VECTOR3, "b", REAL, "t", varray()); ADDFUNC4R(VECTOR3, VECTOR3, Vector3, cubic_interpolate, VECTOR3, "b", VECTOR3, "pre_a", VECTOR3, "post_b", REAL, "t", varray()); + ADDFUNC1R(VECTOR3, VECTOR3, Vector3, direction_to, VECTOR3, "b", varray()); ADDFUNC1R(VECTOR3, REAL, Vector3, dot, VECTOR3, "b", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, cross, VECTOR3, "b", varray()); ADDFUNC1R(VECTOR3, BASIS, Vector3, outer, VECTOR3, "b", varray()); @@ -1927,9 +1933,9 @@ void register_variant_methods() { _VariantCall::add_variant_constant(Variant::TRANSFORM, "IDENTITY", identity_transform); transform_x.set(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_X", transform_x); - transform_x.set(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0); + transform_y.set(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0); _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Y", transform_y); - transform_x.set(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0); + transform_z.set(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0); _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Z", transform_z); _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_YZ", Plane(Vector3(1, 0, 0), 0)); diff --git a/core/variant_op.cpp b/core/variant_op.cpp index b40b6ce4a6..f3c9bcaa7e 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -2781,7 +2781,8 @@ bool Variant::in(const Variant &p_index, bool *r_valid) const { return false; } } break; - default: {} + default: { + } } if (r_valid) @@ -2912,7 +2913,8 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const { //nothing } break; - default: {} + default: { + } } } @@ -3251,7 +3253,8 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const { r_iter = idx; return true; } break; - default: {} + default: { + } } valid = false; @@ -3408,7 +3411,8 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const { #endif return arr->get(idx); } break; - default: {} + default: { + } } r_valid = false; diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp index 0056fc75b6..6377197282 100644 --- a/core/variant_parser.cpp +++ b/core/variant_parser.cpp @@ -1948,7 +1948,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, " )"); } break; - default: {} + default: { + } } return OK; diff --git a/core/vmap.h b/core/vmap.h index f46ed70a0f..fde9723d71 100644 --- a/core/vmap.h +++ b/core/vmap.h @@ -65,7 +65,7 @@ private: const Pair *a = _cowdata.ptr(); int middle = 0; -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (low > high) ERR_PRINT("low > high, this may be a bug"); #endif diff --git a/core/vset.h b/core/vset.h index 678ec507ba..5f087a5a03 100644 --- a/core/vset.h +++ b/core/vset.h @@ -50,7 +50,7 @@ class VSet { const T *a = &_data[0]; int middle = 0; -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (low > high) ERR_PRINT("low > high, this may be a bug"); #endif diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml index df52f8503a..e31d815063 100644 --- a/doc/classes/@GDScript.xml +++ b/doc/classes/@GDScript.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="Color8"> <return type="Color"> @@ -975,6 +973,24 @@ [/codeblock] </description> </method> + <method name="smoothstep"> + <return type="float"> + </return> + <argument index="0" name="from" type="float"> + </argument> + <argument index="1" name="to" type="float"> + </argument> + <argument index="2" name="weight" type="float"> + </argument> + <description> + Returns a number smoothly interpolated between the [code]from[/code] and [code]to[/code], based on the [code]weight[/code]. Similar to [method lerp], but interpolates faster at the beginning and slower at the end. + [codeblock] + smoothstep(0, 2, 0.5) # returns 0.15 + smoothstep(0, 2, 1.0) # returns 0.5 + smoothstep(0, 2, 2.0) # returns 1.0 + [/codeblock] + </description> + </method> <method name="sqrt"> <return type="float"> </return> diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 64ca2e54f6..d1e2a9c910 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/@NativeScript.xml b/doc/classes/@NativeScript.xml index 10174debe1..cb5de198ac 100644 --- a/doc/classes/@NativeScript.xml +++ b/doc/classes/@NativeScript.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/@VisualScript.xml b/doc/classes/@VisualScript.xml index c345bc5ee5..8d9408e6d4 100644 --- a/doc/classes/@VisualScript.xml +++ b/doc/classes/@VisualScript.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml index 2c0693934c..8ec07e4d5c 100644 --- a/doc/classes/AABB.xml +++ b/doc/classes/AABB.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="AABB"> <return type="AABB"> @@ -29,7 +27,7 @@ <argument index="0" name="with" type="AABB"> </argument> <description> - Returns [code]true[/code] if this [code]AABB[/code] completely encloses another one. + Returns [code]true[/code] if this [AABB] completely encloses another one. </description> </method> <method name="expand"> @@ -38,14 +36,14 @@ <argument index="0" name="to_point" type="Vector3"> </argument> <description> - Returns this [code]AABB[/code] expanded to include a given point. + Returns this [AABB] expanded to include a given point. </description> </method> <method name="get_area"> <return type="float"> </return> <description> - Gets the area of the [code]AABB[/code]. + Gets the area of the [AABB]. </description> </method> <method name="get_endpoint"> @@ -54,49 +52,49 @@ <argument index="0" name="idx" type="int"> </argument> <description> - Gets the position of the 8 endpoints of the [code]AABB[/code] in space. + Gets the position of the 8 endpoints of the [AABB] in space. </description> </method> <method name="get_longest_axis"> <return type="Vector3"> </return> <description> - Returns the normalized longest axis of the [code]AABB[/code]. + Returns the normalized longest axis of the [AABB]. </description> </method> <method name="get_longest_axis_index"> <return type="int"> </return> <description> - Returns the index of the longest axis of the [code]AABB[/code] (according to [Vector3]::AXIS* enum). + Returns the index of the longest axis of the [AABB] (according to [Vector3]::AXIS* enum). </description> </method> <method name="get_longest_axis_size"> <return type="float"> </return> <description> - Returns the scalar length of the longest axis of the [code]AABB[/code]. + Returns the scalar length of the longest axis of the [AABB]. </description> </method> <method name="get_shortest_axis"> <return type="Vector3"> </return> <description> - Returns the normalized shortest axis of the [code]AABB[/code]. + Returns the normalized shortest axis of the [AABB]. </description> </method> <method name="get_shortest_axis_index"> <return type="int"> </return> <description> - Returns the index of the shortest axis of the [code]AABB[/code] (according to [Vector3]::AXIS* enum). + Returns the index of the shortest axis of the [AABB] (according to [Vector3]::AXIS* enum). </description> </method> <method name="get_shortest_axis_size"> <return type="float"> </return> <description> - Returns the scalar length of the shortest axis of the [code]AABB[/code]. + Returns the scalar length of the shortest axis of the [AABB]. </description> </method> <method name="get_support"> @@ -114,21 +112,21 @@ <argument index="0" name="by" type="float"> </argument> <description> - Returns a copy of the [code]AABB[/code] grown a given amount of units towards all the sides. + Returns a copy of the [AABB] grown a given amount of units towards all the sides. </description> </method> <method name="has_no_area"> <return type="bool"> </return> <description> - Returns [code]true[/code] if the [code]AABB[/code] is flat or empty. + Returns [code]true[/code] if the [AABB] is flat or empty. </description> </method> <method name="has_no_surface"> <return type="bool"> </return> <description> - Returns [code]true[/code] if the [code]AABB[/code] is empty. + Returns [code]true[/code] if the [AABB] is empty. </description> </method> <method name="has_point"> @@ -137,7 +135,7 @@ <argument index="0" name="point" type="Vector3"> </argument> <description> - Returns [code]true[/code] if the [code]AABB[/code] contains a point. + Returns [code]true[/code] if the [AABB] contains a point. </description> </method> <method name="intersection"> @@ -146,7 +144,7 @@ <argument index="0" name="with" type="AABB"> </argument> <description> - Returns the intersection between two [code]AABB[/code]. An empty AABB (size 0,0,0) is returned on failure. + Returns the intersection between two [AABB]. An empty AABB (size 0,0,0) is returned on failure. </description> </method> <method name="intersects"> @@ -155,7 +153,7 @@ <argument index="0" name="with" type="AABB"> </argument> <description> - Returns [code]true[/code] if the [code]AABB[/code] overlaps with another. + Returns [code]true[/code] if the [AABB] overlaps with another. </description> </method> <method name="intersects_plane"> @@ -164,7 +162,7 @@ <argument index="0" name="plane" type="Plane"> </argument> <description> - Returns [code]true[/code] if the [code]AABB[/code] is on both sides of a plane. + Returns [code]true[/code] if the [AABB] is on both sides of a plane. </description> </method> <method name="intersects_segment"> @@ -175,7 +173,7 @@ <argument index="1" name="to" type="Vector3"> </argument> <description> - Returns [code]true[/code] if the [code]AABB[/code] intersects the line segment between [code]from[/code] and [code]to[/code]. + Returns [code]true[/code] if the [AABB] intersects the line segment between [code]from[/code] and [code]to[/code]. </description> </method> <method name="merge"> diff --git a/doc/classes/ARVRAnchor.xml b/doc/classes/ARVRAnchor.xml index 5197c1c651..092d5e2916 100644 --- a/doc/classes/ARVRAnchor.xml +++ b/doc/classes/ARVRAnchor.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_anchor_name" qualifiers="const"> <return type="String"> @@ -27,6 +25,13 @@ Returns true if the anchor is being tracked and false if no anchor with this id is currently known. </description> </method> + <method name="get_mesh" qualifiers="const"> + <return type="Mesh"> + </return> + <description> + If provided by the ARVR Interface this returns a mesh object for the anchor. For an anchor this can be a shape related to the object being tracked or it can be a mesh that provides topology related to the anchor and can be used to create shadows/reflections on surfaces or for generating collision shapes. + </description> + </method> <method name="get_plane" qualifiers="const"> <return type="Plane"> </return> @@ -47,6 +52,15 @@ The anchor's id. You can set this before the anchor itself exists. The first anchor gets an id of [code]1[/code], the second an id of [code]2[/code], etc. When anchors get removed, the engine can then assign the corresponding id to new anchors. The most common situation where anchors 'disappear' is when the AR server identifies that two anchors represent different parts of the same plane and merges them. </member> </members> + <signals> + <signal name="mesh_updated"> + <argument index="0" name="mesh" type="Mesh"> + </argument> + <description> + Emitted when the mesh associated with the anchor changes or when one becomes available. This is especially important for topology that is constantly being mesh_updated. + </description> + </signal> + </signals> <constants> </constants> </class> diff --git a/doc/classes/ARVRCamera.xml b/doc/classes/ARVRCamera.xml index 7e360df27d..1c468f193f 100644 --- a/doc/classes/ARVRCamera.xml +++ b/doc/classes/ARVRCamera.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ARVRController.xml b/doc/classes/ARVRController.xml index 1264f89f64..c2b7fb2c08 100644 --- a/doc/classes/ARVRController.xml +++ b/doc/classes/ARVRController.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_controller_name" qualifiers="const"> <return type="String"> @@ -50,6 +48,13 @@ Returns the ID of the joystick object bound to this. Every controller tracked by the ARVR Server that has buttons and axis will also be registered as a joystick within Godot. This means that all the normal joystick tracking and input mapping will work for buttons and axis found on the AR/VR controllers. This ID is purely offered as information so you can link up the controller with its joystick entry. </description> </method> + <method name="get_mesh" qualifiers="const"> + <return type="Mesh"> + </return> + <description> + If provided by the ARVR Interface this returns a mesh associated with the controller. This can be used to visualise the controller. + </description> + </method> <method name="is_button_pressed" qualifiers="const"> <return type="int"> </return> @@ -86,6 +91,13 @@ Emitted when a button on this controller is released. </description> </signal> + <signal name="mesh_updated"> + <argument index="0" name="mesh" type="Mesh"> + </argument> + <description> + Emitted when the mesh associated with the controller changes or when one becomes available. Generally speaking this will be a static mesh after becoming available. + </description> + </signal> </signals> <constants> </constants> diff --git a/doc/classes/ARVRInterface.xml b/doc/classes/ARVRInterface.xml index 76f7c352bb..bb03c50be9 100644 --- a/doc/classes/ARVRInterface.xml +++ b/doc/classes/ARVRInterface.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_capabilities" qualifiers="const"> <return type="int"> diff --git a/doc/classes/ARVROrigin.xml b/doc/classes/ARVROrigin.xml index 2d908ed500..758f00a9ca 100644 --- a/doc/classes/ARVROrigin.xml +++ b/doc/classes/ARVROrigin.xml @@ -11,8 +11,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ARVRPositionalTracker.xml b/doc/classes/ARVRPositionalTracker.xml index c1be8d2356..de8b6b0620 100644 --- a/doc/classes/ARVRPositionalTracker.xml +++ b/doc/classes/ARVRPositionalTracker.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_hand" qualifiers="const"> <return type="int" enum="ARVRPositionalTracker.TrackerHand"> @@ -27,6 +25,13 @@ If this is a controller that is being tracked the controller will also be represented by a joystick entry with this id. </description> </method> + <method name="get_mesh" qualifiers="const"> + <return type="Mesh"> + </return> + <description> + Returns the mesh related to a controller or anchor point if one is available. + </description> + </method> <method name="get_name" qualifiers="const"> <return type="String"> </return> diff --git a/doc/classes/ARVRServer.xml b/doc/classes/ARVRServer.xml index 608c031e14..b828d45cbe 100644 --- a/doc/classes/ARVRServer.xml +++ b/doc/classes/ARVRServer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="center_on_hmd"> <return type="void"> diff --git a/doc/classes/AStar.xml b/doc/classes/AStar.xml index 5a62c69a67..16ceb293ad 100644 --- a/doc/classes/AStar.xml +++ b/doc/classes/AStar.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_compute_cost" qualifiers="virtual"> <return type="float"> @@ -226,6 +224,15 @@ Returns whether a point associated with the given id exists. </description> </method> + <method name="is_point_disabled" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <description> + Returns whether a point is disabled or not for pathfinding. By default, all points are enabled. + </description> + </method> <method name="remove_point"> <return type="void"> </return> @@ -235,6 +242,17 @@ Removes the point associated with the given id from the points pool. </description> </method> + <method name="set_point_disabled"> + <return type="void"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <argument index="1" name="disabled" type="bool" default="true"> + </argument> + <description> + Disables or enables the specified point for pathfinding. Useful for making a temporary obstacle. + </description> + </method> <method name="set_point_position"> <return type="void"> </return> diff --git a/doc/classes/AcceptDialog.xml b/doc/classes/AcceptDialog.xml index 71b161ccb1..4adf515abc 100644 --- a/doc/classes/AcceptDialog.xml +++ b/doc/classes/AcceptDialog.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_button"> <return type="Button"> diff --git a/doc/classes/AnimatedSprite.xml b/doc/classes/AnimatedSprite.xml index ac88534f03..a5202dc612 100644 --- a/doc/classes/AnimatedSprite.xml +++ b/doc/classes/AnimatedSprite.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="is_playing" qualifiers="const"> <return type="bool"> @@ -23,8 +21,10 @@ </return> <argument index="0" name="anim" type="String" default=""""> </argument> + <argument index="1" name="backwards" type="bool" default="false"> + </argument> <description> - Play the animation set in parameter. If no parameter is provided, the current animation is played. + Play the animation set in parameter. If no parameter is provided, the current animation is played. Property [code]backwards[/code] plays the animation in reverse if set to [code]true[/code]. </description> </method> <method name="stop"> diff --git a/doc/classes/AnimatedSprite3D.xml b/doc/classes/AnimatedSprite3D.xml index d4ed5d5adc..1607a2e105 100644 --- a/doc/classes/AnimatedSprite3D.xml +++ b/doc/classes/AnimatedSprite3D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="is_playing" qualifiers="const"> <return type="bool"> diff --git a/doc/classes/AnimatedTexture.xml b/doc/classes/AnimatedTexture.xml index d1839f8e79..2009139474 100644 --- a/doc/classes/AnimatedTexture.xml +++ b/doc/classes/AnimatedTexture.xml @@ -4,14 +4,12 @@ Proxy texture for simple frame-based animations. </brief_description> <description> - [code]AnimatedTexture[/code] is a resource format for frame-based animations, where multiple textures can be chained automatically with a predefined delay for each frame. Unlike [AnimationPlayer] or [AnimatedSprite], it isn't a [Node], but has the advantage of being usable anywhere a [Texture] resource can be used, e.g. in a [TileSet]. + [AnimatedTexture] is a resource format for frame-based animations, where multiple textures can be chained automatically with a predefined delay for each frame. Unlike [AnimationPlayer] or [AnimatedSprite], it isn't a [Node], but has the advantage of being usable anywhere a [Texture] resource can be used, e.g. in a [TileSet]. The playback of the animation is controlled by the [member fps] property as well as each frame's optional delay (see [method set_frame_delay]). The animation loops, i.e. it will restart at frame 0 automatically after playing the last frame. - [code]AnimatedTexture[/code] currently requires all frame textures to have the same size, otherwise the bigger ones will be cropped to match the smallest one. + [AnimatedTexture] currently requires all frame textures to have the same size, otherwise the bigger ones will be cropped to match the smallest one. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_frame_delay" qualifiers="const"> <return type="float"> @@ -73,7 +71,7 @@ </members> <constants> <constant name="MAX_FRAMES" value="256"> - The maximum number of frames supported by [code]AnimatedTexture[/code]. If you need more frames in your animation, use [AnimationPlayer] or [AnimatedSprite]. + The maximum number of frames supported by [AnimatedTexture]. If you need more frames in your animation, use [AnimationPlayer] or [AnimatedSprite]. </constant> </constants> </class> diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index 04f142585e..23227c7404 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -19,8 +19,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/animation/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_track"> <return type="int"> diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml index f8478cbbf8..7ce4aae4fd 100644 --- a/doc/classes/AnimationNode.xml +++ b/doc/classes/AnimationNode.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_input"> <return type="void"> diff --git a/doc/classes/AnimationNodeAdd2.xml b/doc/classes/AnimationNodeAdd2.xml index b4ae87652b..ac2a28e396 100644 --- a/doc/classes/AnimationNodeAdd2.xml +++ b/doc/classes/AnimationNodeAdd2.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AnimationNodeAdd3.xml b/doc/classes/AnimationNodeAdd3.xml index 3170b8c28c..9cfeb47378 100644 --- a/doc/classes/AnimationNodeAdd3.xml +++ b/doc/classes/AnimationNodeAdd3.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AnimationNodeAnimation.xml b/doc/classes/AnimationNodeAnimation.xml index e096bb15cf..e7d53fc7b3 100644 --- a/doc/classes/AnimationNodeAnimation.xml +++ b/doc/classes/AnimationNodeAnimation.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AnimationNodeBlend2.xml b/doc/classes/AnimationNodeBlend2.xml index 9a8a9f4181..f15fa44f39 100644 --- a/doc/classes/AnimationNodeBlend2.xml +++ b/doc/classes/AnimationNodeBlend2.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AnimationNodeBlend3.xml b/doc/classes/AnimationNodeBlend3.xml index 1e97d0b418..2f82eea041 100644 --- a/doc/classes/AnimationNodeBlend3.xml +++ b/doc/classes/AnimationNodeBlend3.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AnimationNodeBlendSpace1D.xml b/doc/classes/AnimationNodeBlendSpace1D.xml index 09cd135374..b13aee277e 100644 --- a/doc/classes/AnimationNodeBlendSpace1D.xml +++ b/doc/classes/AnimationNodeBlendSpace1D.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_blend_point"> <return type="void"> diff --git a/doc/classes/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml index ad7d9dd380..2ec5977301 100644 --- a/doc/classes/AnimationNodeBlendSpace2D.xml +++ b/doc/classes/AnimationNodeBlendSpace2D.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_blend_point"> <return type="void"> @@ -136,8 +134,10 @@ <constant name="BLEND_MODE_INTERPOLATED" value="0" enum="BlendMode"> </constant> <constant name="BLEND_MODE_DISCRETE" value="1" enum="BlendMode"> + Useful for frame-by-frame 2D animations. </constant> <constant name="BLEND_MODE_DISCRETE_CARRY" value="2" enum="BlendMode"> + Keep the current play position when switching between discrete animations. </constant> </constants> </class> diff --git a/doc/classes/AnimationNodeBlendTree.xml b/doc/classes/AnimationNodeBlendTree.xml index b91c8a18fd..6454a26a04 100644 --- a/doc/classes/AnimationNodeBlendTree.xml +++ b/doc/classes/AnimationNodeBlendTree.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_node"> <return type="void"> diff --git a/doc/classes/AnimationNodeOneShot.xml b/doc/classes/AnimationNodeOneShot.xml index 09e1727527..b6a6459c9e 100644 --- a/doc/classes/AnimationNodeOneShot.xml +++ b/doc/classes/AnimationNodeOneShot.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_mix_mode" qualifiers="const"> <return type="int" enum="AnimationNodeOneShot.MixMode"> diff --git a/doc/classes/AnimationNodeOutput.xml b/doc/classes/AnimationNodeOutput.xml index 86cd77f9be..bfe28319c6 100644 --- a/doc/classes/AnimationNodeOutput.xml +++ b/doc/classes/AnimationNodeOutput.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AnimationNodeStateMachine.xml b/doc/classes/AnimationNodeStateMachine.xml index 6de544c9ac..b80069095b 100644 --- a/doc/classes/AnimationNodeStateMachine.xml +++ b/doc/classes/AnimationNodeStateMachine.xml @@ -3,11 +3,14 @@ <brief_description> </brief_description> <description> + Contains multiple root nodes as children in a graph. Each node is used as a state, and provides multiple functions to alternate between states. Retrieve the AnimationNodeStateMachinePlayback object from the [AnimationTree] node to control it programatically. + [codeblock] + var state_machine = anim_tree["parameters/StateMachine/playback"] + state_machine.travel("SomeState") + [codeblock] </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_node"> <return type="void"> @@ -170,7 +173,7 @@ <method name="set_graph_offset"> <return type="void"> </return> - <argument index="0" name="name" type="Vector2"> + <argument index="0" name="offset" type="Vector2"> </argument> <description> </description> diff --git a/doc/classes/AnimationNodeStateMachinePlayback.xml b/doc/classes/AnimationNodeStateMachinePlayback.xml index 01bf982281..fd38c28b16 100644 --- a/doc/classes/AnimationNodeStateMachinePlayback.xml +++ b/doc/classes/AnimationNodeStateMachinePlayback.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_current_node" qualifiers="const"> <return type="String"> @@ -47,6 +45,7 @@ <argument index="0" name="to_node" type="String"> </argument> <description> + Transition from the current state to another one, while visiting all the intermediate ones. This is done via the A* algorithm. </description> </method> </methods> diff --git a/doc/classes/AnimationNodeStateMachineTransition.xml b/doc/classes/AnimationNodeStateMachineTransition.xml index a1ee381882..a21ee50949 100644 --- a/doc/classes/AnimationNodeStateMachineTransition.xml +++ b/doc/classes/AnimationNodeStateMachineTransition.xml @@ -6,22 +6,26 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="advance_condition" type="String" setter="set_advance_condition" getter="get_advance_condition"> + Turn on auto advance when this condition is set. This is a custom text field that can be filled with a variable name. The variable can be modified from code. </member> <member name="auto_advance" type="bool" setter="set_auto_advance" getter="has_auto_advance"> + Turn on the transition automatically when this state is reached. This works best with [code]SWITCH_MODE_AT_END[/code]. </member> <member name="disabled" type="bool" setter="set_disabled" getter="is_disabled"> + Don't use this transition during [method AnimationNodeStateMachinePlayback.travel] or [member auto_advance]. </member> <member name="priority" type="int" setter="set_priority" getter="get_priority"> + Lower priority transitions are preferred when travelling through the tree via [method AnimationNodeStateMachinePlayback.travel] or [member auto_advance]. </member> <member name="switch_mode" type="int" setter="set_switch_mode" getter="get_switch_mode" enum="AnimationNodeStateMachineTransition.SwitchMode"> + The transition type. </member> <member name="xfade_time" type="float" setter="set_xfade_time" getter="get_xfade_time"> + The time to cross-fade between this state and the next. </member> </members> <signals> @@ -32,10 +36,13 @@ </signals> <constants> <constant name="SWITCH_MODE_IMMEDIATE" value="0" enum="SwitchMode"> + Switch to the next state immediately. The current state will end and blend into the beginning of the new one. </constant> <constant name="SWITCH_MODE_SYNC" value="1" enum="SwitchMode"> + Switch to the next state immediately, but will seek the new state to the playback position of the old state. </constant> <constant name="SWITCH_MODE_AT_END" value="2" enum="SwitchMode"> + Wait for the current state playback to end, then switch to the beginning of the next state animation. </constant> </constants> </class> diff --git a/doc/classes/AnimationNodeTimeScale.xml b/doc/classes/AnimationNodeTimeScale.xml index 6a7b29ae81..9ce773c4e2 100644 --- a/doc/classes/AnimationNodeTimeScale.xml +++ b/doc/classes/AnimationNodeTimeScale.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AnimationNodeTimeSeek.xml b/doc/classes/AnimationNodeTimeSeek.xml index 2d32f0658d..d593f5489c 100644 --- a/doc/classes/AnimationNodeTimeSeek.xml +++ b/doc/classes/AnimationNodeTimeSeek.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AnimationNodeTransition.xml b/doc/classes/AnimationNodeTransition.xml index fa408c1c08..311d0e8486 100644 --- a/doc/classes/AnimationNodeTransition.xml +++ b/doc/classes/AnimationNodeTransition.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml index 73db6f0441..509285cdb8 100644 --- a/doc/classes/AnimationPlayer.xml +++ b/doc/classes/AnimationPlayer.xml @@ -10,8 +10,6 @@ <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/animations.html</link> <link>https://docs.godotengine.org/en/latest/tutorials/animation/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_animation"> <return type="int" enum="Error"> @@ -57,7 +55,7 @@ <return type="void"> </return> <description> - [code]AnimationPlayer[/code] caches animated nodes. It may not notice if a node disappears, so clear_caches forces it to update the cache again. + [AnimationPlayer] caches animated nodes. It may not notice if a node disappears, so clear_caches forces it to update the cache again. </description> </method> <method name="clear_queue"> @@ -122,7 +120,7 @@ <argument index="0" name="name" type="String"> </argument> <description> - Returns [code]true[/code] if the [code]AnimationPlayer[/code] stores an [Animation] with key [code]name[/code]. + Returns [code]true[/code] if the [AnimationPlayer] stores an [Animation] with key [code]name[/code]. </description> </method> <method name="is_playing" qualifiers="const"> diff --git a/doc/classes/AnimationRootNode.xml b/doc/classes/AnimationRootNode.xml index 37bab88c76..735f3215e9 100644 --- a/doc/classes/AnimationRootNode.xml +++ b/doc/classes/AnimationRootNode.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AnimationTrackEditPlugin.xml b/doc/classes/AnimationTrackEditPlugin.xml index 346b10cc7c..2dcabb051f 100644 --- a/doc/classes/AnimationTrackEditPlugin.xml +++ b/doc/classes/AnimationTrackEditPlugin.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml index 1101c0ee62..f3dc1ecc1e 100644 --- a/doc/classes/AnimationTree.xml +++ b/doc/classes/AnimationTree.xml @@ -5,9 +5,9 @@ <description> </description> <tutorials> + <link>https://docs.godotengine.org/en/latest/tutorials/animation/animation_tree.html</link> + <link>https://github.com/godotengine/tps-demo</link> </tutorials> - <demos> - </demos> <methods> <method name="advance"> <return type="void"> diff --git a/doc/classes/AnimationTreePlayer.xml b/doc/classes/AnimationTreePlayer.xml index a6189f235a..6160b44076 100644 --- a/doc/classes/AnimationTreePlayer.xml +++ b/doc/classes/AnimationTreePlayer.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_node"> <return type="void"> @@ -38,7 +36,7 @@ <argument index="0" name="id" type="String"> </argument> <description> - Returns the [AnimationPlayer]'s [Animation] bound to the [code]AnimationTreePlayer[/code]'s animation node with name [code]id[/code]. + Returns the [AnimationPlayer]'s [Animation] bound to the [AnimationTreePlayer]'s animation node with name [code]id[/code]. </description> </method> <method name="animation_node_get_master_animation" qualifiers="const"> @@ -66,7 +64,7 @@ <argument index="1" name="animation" type="Animation"> </argument> <description> - Binds a new [Animation] from the [member master_player] to the [code]AnimationTreePlayer[/code]'s animation node with name [code]id[/code]. + Binds a new [Animation] from the [member master_player] to the [AnimationTreePlayer]'s animation node with name [code]id[/code]. </description> </method> <method name="animation_node_set_filter_path"> @@ -472,7 +470,7 @@ <return type="void"> </return> <description> - Resets this [code]AnimationTreePlayer[/code]. + Resets this [AnimationTreePlayer]. </description> </method> <method name="timescale_node_get_scale" qualifiers="const"> @@ -607,14 +605,14 @@ </methods> <members> <member name="active" type="bool" setter="set_active" getter="is_active"> - If [code]true[/code], the [code]AnimationTreePlayer[/code] is able to play animations. Default value: [code]false[/code]. + If [code]true[/code], the [AnimationTreePlayer] is able to play animations. Default value: [code]false[/code]. </member> <member name="base_path" type="NodePath" setter="set_base_path" getter="get_base_path"> The node from which to relatively access other nodes. Default value: [code]".."[/code]. It accesses the Bones, so it should point to the same Node the AnimationPlayer would point its Root Node at. </member> <member name="master_player" type="NodePath" setter="set_master_player" getter="get_master_player"> - The path to the [AnimationPlayer] from which this [code]AnimationTreePlayer[/code] binds animations to animation nodes. + The path to the [AnimationPlayer] from which this [AnimationTreePlayer] binds animations to animation nodes. Once set, Animation nodes can be added to the AnimationTreePlayer. </member> <member name="playback_process_mode" type="int" setter="set_animation_process_mode" getter="get_animation_process_mode" enum="AnimationTreePlayer.AnimationProcessMode"> diff --git a/doc/classes/Area.xml b/doc/classes/Area.xml index 7d8de23a56..c39c570be9 100644 --- a/doc/classes/Area.xml +++ b/doc/classes/Area.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_collision_layer_bit" qualifiers="const"> <return type="bool"> @@ -33,7 +31,7 @@ <return type="Array"> </return> <description> - Returns a list of intersecting [code]Area[/code]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead. + Returns a list of intersecting [Area]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead. </description> </method> <method name="get_overlapping_bodies" qualifiers="const"> @@ -69,7 +67,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> - Set/clear individual bits on the layer mask. This simplifies editing this [code]Area[/code]'s layers. + Set/clear individual bits on the layer mask. This simplifies editing this [Area]'s layers. </description> </method> <method name="set_collision_mask_bit"> @@ -80,7 +78,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> - Set/clear individual bits on the collision mask. This simplifies editing which [code]Area[/code] layers this [code]Area[/code] scans. + Set/clear individual bits on the collision mask. This simplifies editing which [Area] layers this [Area] scans. </description> </method> </methods> diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index 7a0381e413..2400efe0d3 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_collision_layer_bit" qualifiers="const"> <return type="bool"> @@ -33,7 +31,7 @@ <return type="Array"> </return> <description> - Returns a list of intersecting [code]Area2D[/code]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead. + Returns a list of intersecting [Area2D]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead. </description> </method> <method name="get_overlapping_bodies" qualifiers="const"> diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index fe8b36ff4b..73ca6ab5b3 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -17,8 +17,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="Array"> <return type="Array"> diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index 8113d99398..00bd72d4a5 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -3,7 +3,7 @@ <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 + The [ArrayMesh] 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)) @@ -23,8 +23,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_blend_shape"> <return type="void"> @@ -57,14 +55,14 @@ <return type="void"> </return> <description> - Remove all blend shapes from this [code]ArrayMesh[/code]. + Remove all blend shapes from this [ArrayMesh]. </description> </method> <method name="get_blend_shape_count" qualifiers="const"> <return type="int"> </return> <description> - Returns the number of blend shapes that the [code]ArrayMesh[/code] holds. + Returns the number of blend shapes that the [ArrayMesh] holds. </description> </method> <method name="get_blend_shape_name" qualifiers="const"> @@ -84,14 +82,14 @@ <argument index="1" name="texel_size" type="float"> </argument> <description> - Will perform a UV unwrap on the [code]ArrayMesh[/code] to prepare the mesh for lightmapping. + Will perform a UV unwrap on the [ArrayMesh] to prepare the mesh for lightmapping. </description> </method> <method name="regen_normalmaps"> <return type="void"> </return> <description> - Will regenerate normal maps for the [code]ArrayMesh[/code]. + Will regenerate normal maps for the [ArrayMesh]. </description> </method> <method name="surface_find_by_name" qualifiers="const"> @@ -100,7 +98,7 @@ <argument index="0" name="name" type="String"> </argument> <description> - Return the index of the first surface with this name held within this [code]ArrayMesh[/code]. If none are found -1 is returned. + Return the index of the first surface with this name held within this [ArrayMesh]. If none are found -1 is returned. </description> </method> <method name="surface_get_array_index_len" qualifiers="const"> diff --git a/doc/classes/AtlasTexture.xml b/doc/classes/AtlasTexture.xml index ca2d294796..e573512f5a 100644 --- a/doc/classes/AtlasTexture.xml +++ b/doc/classes/AtlasTexture.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioBusLayout.xml b/doc/classes/AudioBusLayout.xml index 59cfe96610..771e0390d3 100644 --- a/doc/classes/AudioBusLayout.xml +++ b/doc/classes/AudioBusLayout.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffect.xml b/doc/classes/AudioEffect.xml index 456bf56231..7c21d04b03 100644 --- a/doc/classes/AudioEffect.xml +++ b/doc/classes/AudioEffect.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffectAmplify.xml b/doc/classes/AudioEffectAmplify.xml index 0d99f280df..9fd3e73b12 100644 --- a/doc/classes/AudioEffectAmplify.xml +++ b/doc/classes/AudioEffectAmplify.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioEffectBandLimitFilter.xml b/doc/classes/AudioEffectBandLimitFilter.xml index bea0b106b2..1b511dfab0 100644 --- a/doc/classes/AudioEffectBandLimitFilter.xml +++ b/doc/classes/AudioEffectBandLimitFilter.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffectBandPassFilter.xml b/doc/classes/AudioEffectBandPassFilter.xml index 18fb0490a4..8df922da3a 100644 --- a/doc/classes/AudioEffectBandPassFilter.xml +++ b/doc/classes/AudioEffectBandPassFilter.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffectChorus.xml b/doc/classes/AudioEffectChorus.xml index dc82f1b047..f85755df08 100644 --- a/doc/classes/AudioEffectChorus.xml +++ b/doc/classes/AudioEffectChorus.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioEffectCompressor.xml b/doc/classes/AudioEffectCompressor.xml index d8ca33cccb..78b8047987 100644 --- a/doc/classes/AudioEffectCompressor.xml +++ b/doc/classes/AudioEffectCompressor.xml @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioEffectDelay.xml b/doc/classes/AudioEffectDelay.xml index 65536efc5d..0e19a4a89f 100644 --- a/doc/classes/AudioEffectDelay.xml +++ b/doc/classes/AudioEffectDelay.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioEffectDistortion.xml b/doc/classes/AudioEffectDistortion.xml index ac85b503f2..4393f942f0 100644 --- a/doc/classes/AudioEffectDistortion.xml +++ b/doc/classes/AudioEffectDistortion.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioEffectEQ.xml b/doc/classes/AudioEffectEQ.xml index eb2c68d129..fef3868d02 100644 --- a/doc/classes/AudioEffectEQ.xml +++ b/doc/classes/AudioEffectEQ.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_band_count" qualifiers="const"> <return type="int"> diff --git a/doc/classes/AudioEffectEQ10.xml b/doc/classes/AudioEffectEQ10.xml index e259c68668..e9fd0d72cb 100644 --- a/doc/classes/AudioEffectEQ10.xml +++ b/doc/classes/AudioEffectEQ10.xml @@ -20,8 +20,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffectEQ21.xml b/doc/classes/AudioEffectEQ21.xml index 74a1034667..d1b39d9824 100644 --- a/doc/classes/AudioEffectEQ21.xml +++ b/doc/classes/AudioEffectEQ21.xml @@ -31,8 +31,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffectEQ6.xml b/doc/classes/AudioEffectEQ6.xml index 06d8ebe07c..d40471dbd6 100644 --- a/doc/classes/AudioEffectEQ6.xml +++ b/doc/classes/AudioEffectEQ6.xml @@ -16,8 +16,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffectFilter.xml b/doc/classes/AudioEffectFilter.xml index d3669f8fb0..5ed4ae28db 100644 --- a/doc/classes/AudioEffectFilter.xml +++ b/doc/classes/AudioEffectFilter.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioEffectHighPassFilter.xml b/doc/classes/AudioEffectHighPassFilter.xml index ad3ef9f4f1..3a486acab8 100644 --- a/doc/classes/AudioEffectHighPassFilter.xml +++ b/doc/classes/AudioEffectHighPassFilter.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffectHighShelfFilter.xml b/doc/classes/AudioEffectHighShelfFilter.xml index 3c2e56b715..cccd18afcc 100644 --- a/doc/classes/AudioEffectHighShelfFilter.xml +++ b/doc/classes/AudioEffectHighShelfFilter.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffectInstance.xml b/doc/classes/AudioEffectInstance.xml new file mode 100644 index 0000000000..eb8bcfdf2a --- /dev/null +++ b/doc/classes/AudioEffectInstance.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AudioEffectInstance" inherits="Reference" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/AudioEffectLimiter.xml b/doc/classes/AudioEffectLimiter.xml index 17a568838c..c17816bb87 100644 --- a/doc/classes/AudioEffectLimiter.xml +++ b/doc/classes/AudioEffectLimiter.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioEffectLowPassFilter.xml b/doc/classes/AudioEffectLowPassFilter.xml index 3a28424091..9402045150 100644 --- a/doc/classes/AudioEffectLowPassFilter.xml +++ b/doc/classes/AudioEffectLowPassFilter.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffectLowShelfFilter.xml b/doc/classes/AudioEffectLowShelfFilter.xml index 32b4f0713a..1e60e637fc 100644 --- a/doc/classes/AudioEffectLowShelfFilter.xml +++ b/doc/classes/AudioEffectLowShelfFilter.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffectNotchFilter.xml b/doc/classes/AudioEffectNotchFilter.xml index b866148658..00e950b43a 100644 --- a/doc/classes/AudioEffectNotchFilter.xml +++ b/doc/classes/AudioEffectNotchFilter.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioEffectPanner.xml b/doc/classes/AudioEffectPanner.xml index da66b877ac..7d01a73b0b 100644 --- a/doc/classes/AudioEffectPanner.xml +++ b/doc/classes/AudioEffectPanner.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioEffectPhaser.xml b/doc/classes/AudioEffectPhaser.xml index e55e381690..c42b5f59ee 100644 --- a/doc/classes/AudioEffectPhaser.xml +++ b/doc/classes/AudioEffectPhaser.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioEffectPitchShift.xml b/doc/classes/AudioEffectPitchShift.xml index 2e270b43f7..7d6c5f2b20 100644 --- a/doc/classes/AudioEffectPitchShift.xml +++ b/doc/classes/AudioEffectPitchShift.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioEffectRecord.xml b/doc/classes/AudioEffectRecord.xml index 5f3d4e31e1..d6e678f276 100644 --- a/doc/classes/AudioEffectRecord.xml +++ b/doc/classes/AudioEffectRecord.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_recording" qualifiers="const"> <return type="AudioStreamSample"> diff --git a/doc/classes/AudioEffectReverb.xml b/doc/classes/AudioEffectReverb.xml index 435b9d5126..fde22c9238 100644 --- a/doc/classes/AudioEffectReverb.xml +++ b/doc/classes/AudioEffectReverb.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioEffectSpectrumAnalyzer.xml b/doc/classes/AudioEffectSpectrumAnalyzer.xml new file mode 100644 index 0000000000..8a71991437 --- /dev/null +++ b/doc/classes/AudioEffectSpectrumAnalyzer.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AudioEffectSpectrumAnalyzer" inherits="AudioEffect" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <members> + <member name="buffer_length" type="float" setter="set_buffer_length" getter="get_buffer_length"> + </member> + <member name="fft_size" type="int" setter="set_fft_size" getter="get_fft_size" enum="AudioEffectSpectrumAnalyzer.FFT_Size"> + </member> + <member name="tap_back_pos" type="float" setter="set_tap_back_pos" getter="get_tap_back_pos"> + </member> + </members> + <constants> + <constant name="FFT_SIZE_256" value="0" enum="FFT_Size"> + </constant> + <constant name="FFT_SIZE_512" value="1" enum="FFT_Size"> + </constant> + <constant name="FFT_SIZE_1024" value="2" enum="FFT_Size"> + </constant> + <constant name="FFT_SIZE_2048" value="3" enum="FFT_Size"> + </constant> + <constant name="FFT_SIZE_4096" value="4" enum="FFT_Size"> + </constant> + <constant name="FFT_SIZE_MAX" value="5" enum="FFT_Size"> + </constant> + </constants> +</class> diff --git a/doc/classes/AudioEffectSpectrumAnalyzerInstance.xml b/doc/classes/AudioEffectSpectrumAnalyzerInstance.xml new file mode 100644 index 0000000000..f4620f9905 --- /dev/null +++ b/doc/classes/AudioEffectSpectrumAnalyzerInstance.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AudioEffectSpectrumAnalyzerInstance" inherits="AudioEffectInstance" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="get_magnitude_for_frequency_range" qualifiers="const"> + <return type="Vector2"> + </return> + <argument index="0" name="from_hz" type="float"> + </argument> + <argument index="1" name="to_hz" type="float"> + </argument> + <argument index="2" name="mode" type="int" enum="AudioEffectSpectrumAnalyzerInstance.MagnitudeMode" default="1"> + </argument> + <description> + </description> + </method> + </methods> + <constants> + <constant name="MAGNITUDE_AVERAGE" value="0" enum="MagnitudeMode"> + </constant> + <constant name="MAGNITUDE_MAX" value="1" enum="MagnitudeMode"> + </constant> + </constants> +</class> diff --git a/doc/classes/AudioEffectStereoEnhance.xml b/doc/classes/AudioEffectStereoEnhance.xml index 9c5715c165..95bc6128f2 100644 --- a/doc/classes/AudioEffectStereoEnhance.xml +++ b/doc/classes/AudioEffectStereoEnhance.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml index 82e17e4957..83fbd28152 100644 --- a/doc/classes/AudioServer.xml +++ b/doc/classes/AudioServer.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_buses.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_bus"> <return type="void"> @@ -97,6 +95,18 @@ Returns the number of effects on the bus at [code]bus_idx[/code]. </description> </method> + <method name="get_bus_effect_instance"> + <return type="AudioEffectInstance"> + </return> + <argument index="0" name="bus_idx" type="int"> + </argument> + <argument index="1" name="effect_idx" type="int"> + </argument> + <argument index="2" name="channel" type="int" default="0"> + </argument> + <description> + </description> + </method> <method name="get_bus_index" qualifiers="const"> <return type="int"> </return> diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml index 860340e430..15662b7eff 100644 --- a/doc/classes/AudioStream.xml +++ b/doc/classes/AudioStream.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_length" qualifiers="const"> <return type="float"> diff --git a/doc/classes/AudioStreamGenerator.xml b/doc/classes/AudioStreamGenerator.xml new file mode 100644 index 0000000000..ab06ddd710 --- /dev/null +++ b/doc/classes/AudioStreamGenerator.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AudioStreamGenerator" inherits="AudioStream" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <members> + <member name="buffer_length" type="float" setter="set_buffer_length" getter="get_buffer_length"> + </member> + <member name="mix_rate" type="float" setter="set_mix_rate" getter="get_mix_rate"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/AudioStreamGeneratorPlayback.xml b/doc/classes/AudioStreamGeneratorPlayback.xml new file mode 100644 index 0000000000..310b58c4e5 --- /dev/null +++ b/doc/classes/AudioStreamGeneratorPlayback.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AudioStreamGeneratorPlayback" inherits="AudioStreamPlaybackResampled" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="can_push_buffer" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="amount" type="int"> + </argument> + <description> + </description> + </method> + <method name="clear_buffer"> + <return type="void"> + </return> + <description> + </description> + </method> + <method name="get_frames_available" qualifiers="const"> + <return type="int"> + </return> + <description> + </description> + </method> + <method name="get_skips" qualifiers="const"> + <return type="int"> + </return> + <description> + </description> + </method> + <method name="push_buffer"> + <return type="bool"> + </return> + <argument index="0" name="frames" type="PoolVector2Array"> + </argument> + <description> + </description> + </method> + <method name="push_frame"> + <return type="bool"> + </return> + <argument index="0" name="frame" type="Vector2"> + </argument> + <description> + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/AudioStreamMicrophone.xml b/doc/classes/AudioStreamMicrophone.xml index 2927bf5472..afa60655ea 100644 --- a/doc/classes/AudioStreamMicrophone.xml +++ b/doc/classes/AudioStreamMicrophone.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioStreamPlayback.xml b/doc/classes/AudioStreamPlayback.xml index 245cd9908d..773a396bc4 100644 --- a/doc/classes/AudioStreamPlayback.xml +++ b/doc/classes/AudioStreamPlayback.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/AudioStreamPlaybackResampled.xml b/doc/classes/AudioStreamPlaybackResampled.xml new file mode 100644 index 0000000000..07e7e70bf1 --- /dev/null +++ b/doc/classes/AudioStreamPlaybackResampled.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AudioStreamPlaybackResampled" inherits="AudioStreamPlayback" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml index d8b87a3c19..c6ec45f15e 100644 --- a/doc/classes/AudioStreamPlayer.xml +++ b/doc/classes/AudioStreamPlayer.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_playback_position"> <return type="float"> @@ -19,6 +17,12 @@ Returns the position in the [AudioStream] in seconds. </description> </method> + <method name="get_stream_playback"> + <return type="AudioStreamPlayback"> + </return> + <description> + </description> + </method> <method name="play"> <return type="void"> </return> diff --git a/doc/classes/AudioStreamPlayer2D.xml b/doc/classes/AudioStreamPlayer2D.xml index 1cd2bf7645..51bf5e7574 100644 --- a/doc/classes/AudioStreamPlayer2D.xml +++ b/doc/classes/AudioStreamPlayer2D.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_playback_position"> <return type="float"> @@ -19,6 +17,12 @@ Returns the position in the [AudioStream]. </description> </method> + <method name="get_stream_playback"> + <return type="AudioStreamPlayback"> + </return> + <description> + </description> + </method> <method name="play"> <return type="void"> </return> diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml index 8ff5223e4f..6083372b72 100644 --- a/doc/classes/AudioStreamPlayer3D.xml +++ b/doc/classes/AudioStreamPlayer3D.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_playback_position"> <return type="float"> @@ -19,6 +17,12 @@ Returns the position in the [AudioStream]. </description> </method> + <method name="get_stream_playback"> + <return type="AudioStreamPlayback"> + </return> + <description> + </description> + </method> <method name="play"> <return type="void"> </return> diff --git a/doc/classes/AudioStreamRandomPitch.xml b/doc/classes/AudioStreamRandomPitch.xml index 3201dcdfb0..49aa5a0192 100644 --- a/doc/classes/AudioStreamRandomPitch.xml +++ b/doc/classes/AudioStreamRandomPitch.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/AudioStreamSample.xml b/doc/classes/AudioStreamSample.xml index d9b07007de..e23080c9d3 100644 --- a/doc/classes/AudioStreamSample.xml +++ b/doc/classes/AudioStreamSample.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="save_to_wav"> <return type="int" enum="Error"> diff --git a/doc/classes/BackBufferCopy.xml b/doc/classes/BackBufferCopy.xml index 5bbd51f12a..65e8774c59 100644 --- a/doc/classes/BackBufferCopy.xml +++ b/doc/classes/BackBufferCopy.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/BakedLightmap.xml b/doc/classes/BakedLightmap.xml index 0eb0ec091c..735e55cd39 100644 --- a/doc/classes/BakedLightmap.xml +++ b/doc/classes/BakedLightmap.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/baked_lightmaps.html</link> </tutorials> - <demos> - </demos> <methods> <method name="bake"> <return type="int" enum="BakedLightmap.BakeError"> diff --git a/doc/classes/BakedLightmapData.xml b/doc/classes/BakedLightmapData.xml index 14d30a36d9..75f3187434 100644 --- a/doc/classes/BakedLightmapData.xml +++ b/doc/classes/BakedLightmapData.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_user"> <return type="void"> diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml index 993c36a8ad..3c8d4ea0d8 100644 --- a/doc/classes/BaseButton.xml +++ b/doc/classes/BaseButton.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_pressed" qualifiers="virtual"> <return type="void"> @@ -48,7 +46,7 @@ </member> <member name="button_mask" type="int" setter="set_button_mask" getter="get_button_mask"> Binary mask to choose which mouse buttons this button will respond to. - To allow both left-click and right-click, set this to 3, because it's BUTTON_MASK_LEFT | BUTTON_MASK_RIGHT. + To allow both left-click and right-click, use [code]BUTTON_MASK_LEFT | BUTTON_MASK_RIGHT[/code]. </member> <member name="disabled" type="bool" setter="set_disabled" getter="is_disabled"> If [code]true[/code], the button is in disabled state and can't be clicked or toggled. diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index 7b43d58c71..cee3035eab 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -10,8 +10,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link> </tutorials> - <demos> - </demos> <methods> <method name="Basis"> <return type="Basis"> @@ -76,6 +74,12 @@ This function considers a discretization of rotations into 24 points on unit sphere, lying along the vectors (x,y,z) with each component being either -1,0 or 1, and returns the index of the point best representing the orientation of the object. It is mainly used by the grid map editor. For further details, refer to Godot source code. </description> </method> + <method name="get_rotation_quat"> + <return type="Quat"> + </return> + <description> + </description> + </method> <method name="get_scale"> <return type="Vector3"> </return> @@ -90,6 +94,16 @@ Return the inverse of the matrix. </description> </method> + <method name="is_equal_approx"> + <return type="bool"> + </return> + <argument index="0" name="b" type="Basis"> + </argument> + <argument index="1" name="epsilon" type="float" default="0.00001"> + </argument> + <description> + </description> + </method> <method name="orthonormalized"> <return type="Basis"> </return> diff --git a/doc/classes/BitMap.xml b/doc/classes/BitMap.xml index 2a666befaf..2e4aaed099 100644 --- a/doc/classes/BitMap.xml +++ b/doc/classes/BitMap.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="create"> <return type="void"> diff --git a/doc/classes/BitmapFont.xml b/doc/classes/BitmapFont.xml index 6c033e5504..149d92e870 100644 --- a/doc/classes/BitmapFont.xml +++ b/doc/classes/BitmapFont.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_char"> <return type="void"> @@ -38,7 +36,7 @@ <argument index="2" name="kerning" type="int"> </argument> <description> - Adds a kerning pair to the [code]BitmapFont[/code] as a difference. Kerning pairs are special cases where a typeface advance is determined by the next character. + Adds a kerning pair to the [BitmapFont] as a difference. Kerning pairs are special cases where a typeface advance is determined by the next character. </description> </method> <method name="add_texture"> @@ -47,7 +45,7 @@ <argument index="0" name="texture" type="Texture"> </argument> <description> - Adds a texture to the [code]BitmapFont[/code]. + Adds a texture to the [BitmapFont]. </description> </method> <method name="clear"> diff --git a/doc/classes/Bone2D.xml b/doc/classes/Bone2D.xml index 064506b539..59f7bec889 100644 --- a/doc/classes/Bone2D.xml +++ b/doc/classes/Bone2D.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="apply_rest"> <return type="void"> diff --git a/doc/classes/BoneAttachment.xml b/doc/classes/BoneAttachment.xml index a109efd897..109cac4fa3 100644 --- a/doc/classes/BoneAttachment.xml +++ b/doc/classes/BoneAttachment.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/BoxContainer.xml b/doc/classes/BoxContainer.xml index cab115ea01..9aa69e4164 100644 --- a/doc/classes/BoxContainer.xml +++ b/doc/classes/BoxContainer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_spacer"> <return type="void"> diff --git a/doc/classes/BoxShape.xml b/doc/classes/BoxShape.xml index 211eed6200..3678c0e393 100644 --- a/doc/classes/BoxShape.xml +++ b/doc/classes/BoxShape.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index 26c8f27f33..5d3027e347 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ButtonGroup.xml b/doc/classes/ButtonGroup.xml index d7c5d17026..cd2a8d7307 100644 --- a/doc/classes/ButtonGroup.xml +++ b/doc/classes/ButtonGroup.xml @@ -9,14 +9,12 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_buttons"> <return type="Array"> </return> <description> - Returns an [Array] of [Button]s who have this as their [code]ButtonGroup[/code] (see [member BaseButton.group]). + Returns an [Array] of [Button]s who have this as their [ButtonGroup] (see [member BaseButton.group]). </description> </method> <method name="get_pressed_button"> diff --git a/doc/classes/CPUParticles.xml b/doc/classes/CPUParticles.xml index 7843add097..599c067328 100644 --- a/doc/classes/CPUParticles.xml +++ b/doc/classes/CPUParticles.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="convert_from_particles"> <return type="void"> diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml index 8bcdd96253..e1f71e3600 100644 --- a/doc/classes/CPUParticles2D.xml +++ b/doc/classes/CPUParticles2D.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="convert_from_particles"> <return type="void"> diff --git a/doc/classes/Camera.xml b/doc/classes/Camera.xml index 16b59c725f..2716bee0f3 100644 --- a/doc/classes/Camera.xml +++ b/doc/classes/Camera.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear_current"> <return type="void"> diff --git a/doc/classes/Camera2D.xml b/doc/classes/Camera2D.xml index c24bcde7b3..e5d1649c29 100644 --- a/doc/classes/Camera2D.xml +++ b/doc/classes/Camera2D.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="align"> <return type="void"> @@ -23,7 +21,7 @@ <return type="void"> </return> <description> - Removes any [code]Camera2D[/code] from the ancestor [Viewport]'s internal currently-assigned camera. + Removes any [Camera2D] from the ancestor [Viewport]'s internal currently-assigned camera. </description> </method> <method name="force_update_scroll"> @@ -44,7 +42,7 @@ <return type="Vector2"> </return> <description> - Returns the location of the [code]Camera2D[/code]'s screen-center, relative to the origin. + Returns the location of the [Camera2D]'s screen-center, relative to the origin. </description> </method> <method name="make_current"> @@ -71,7 +69,7 @@ If [code]true[/code], the camera is the active camera for the current scene. Only one camera can be current, so setting a different camera [code]current[/code] will disable this one. </member> <member name="custom_viewport" type="Node" setter="set_custom_viewport" getter="get_custom_viewport"> - The custom [Viewport] node attached to the [code]Camera2D[/code]. If null or not a [Viewport], uses the default viewport instead. + The custom [Viewport] node attached to the [Camera2D]. If null or not a [Viewport], uses the default viewport instead. </member> <member name="drag_margin_bottom" type="float" setter="set_drag_margin" getter="get_drag_margin"> Bottom margin needed to drag the camera. A value of [code]1[/code] makes the camera move only when reaching the edge of the screen. diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index a3c3756a78..f3e598c17d 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -14,8 +14,6 @@ <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link> <link>https://docs.godotengine.org/en/latest/tutorials/2d/custom_drawing_in_2d.html</link> </tutorials> - <demos> - </demos> <methods> <method name="_draw" qualifiers="virtual"> <return type="void"> @@ -510,16 +508,16 @@ </methods> <members> <member name="light_mask" type="int" setter="set_light_mask" getter="get_light_mask"> - The rendering layers in which this [code]CanvasItem[/code] responds to [Light2D] nodes. Default value: [code]1[/code]. + The rendering layers in which this [CanvasItem] responds to [Light2D] nodes. Default value: [code]1[/code]. </member> <member name="material" type="Material" setter="set_material" getter="get_material"> - The material applied to textures on this [code]CanvasItem[/code]. Default value: [code]null[/code]. + The material applied to textures on this [CanvasItem]. Default value: [code]null[/code]. </member> <member name="modulate" type="Color" setter="set_modulate" getter="get_modulate"> - The color applied to textures on this [code]CanvasItem[/code]. Default value: [code]Color(1, 1, 1, 1)[/code] (opaque "white"). + The color applied to textures on this [CanvasItem]. Default value: [code]Color(1, 1, 1, 1)[/code] (opaque "white"). </member> <member name="self_modulate" type="Color" setter="set_self_modulate" getter="get_self_modulate"> - The color applied to textures on this [code]CanvasItem[/code]. This is not inherited by children [code]CanvasItem[/code]s. Default value: [code]Color(1, 1, 1, 1)[/code] (opaque "white").. + The color applied to textures on this [CanvasItem]. This is not inherited by children [CanvasItem]s. Default value: [code]Color(1, 1, 1, 1)[/code] (opaque "white").. </member> <member name="show_behind_parent" type="bool" setter="set_draw_behind_parent" getter="is_draw_behind_parent_enabled"> If [code]true[/code], the object draws behind its parent. Default value: [code]false[/code]. @@ -528,10 +526,10 @@ If [code]true[/code], the object draws on top of its parent. Default value: [code]true[/code]. </member> <member name="use_parent_material" type="bool" setter="set_use_parent_material" getter="get_use_parent_material"> - If [code]true[/code], the parent [code]CanvasItem[/code]'s [member material] property is used as this one's material. Default value: [code]false[/code]. + If [code]true[/code], the parent [CanvasItem]'s [member material] property is used as this one's material. Default value: [code]false[/code]. </member> <member name="visible" type="bool" setter="set_visible" getter="is_visible"> - If [code]true[/code], this [code]CanvasItem[/code] is drawn. Default value: [code]true[/code]. For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple popup*() functions instead. + If [code]true[/code], this [CanvasItem] is drawn. Default value: [code]true[/code]. For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple popup*() functions instead. </member> </members> <signals> @@ -575,7 +573,7 @@ <constant name="BLEND_MODE_DISABLED" value="5" enum="BlendMode"> Disable blending mode. Colors including alpha are written as is. Only applicable for render targets with a transparent background. No lighting will be applied. </constant> - <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="29"> + <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="2000"> Canvas item transform has changed. Notification is only received if enabled by [method set_notify_transform] or [method set_notify_local_transform]. </constant> <constant name="NOTIFICATION_DRAW" value="30"> diff --git a/doc/classes/CanvasItemMaterial.xml b/doc/classes/CanvasItemMaterial.xml index 3cd71e6cb3..f125c2be0d 100644 --- a/doc/classes/CanvasItemMaterial.xml +++ b/doc/classes/CanvasItemMaterial.xml @@ -4,12 +4,10 @@ A material for [CanvasItem]s. </brief_description> <description> - [code]CanvasItemMaterial[/code]s provide a means of modifying the textures associated with a CanvasItem. They specialize in describing blend and lighting behaviors for textures. Use a [ShaderMaterial] to more fully customize a material's interactions with a [CanvasItem]. + [CanvasItemMaterial]s provide a means of modifying the textures associated with a CanvasItem. They specialize in describing blend and lighting behaviors for textures. Use a [ShaderMaterial] to more fully customize a material's interactions with a [CanvasItem]. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml index 6d4b4fdb94..d152442b85 100644 --- a/doc/classes/CanvasLayer.xml +++ b/doc/classes/CanvasLayer.xml @@ -4,14 +4,12 @@ Canvas drawing layer. </brief_description> <description> - Canvas drawing layer. [CanvasItem] nodes that are direct or indirect children of a [code]CanvasLayer[/code] will be drawn in that layer. The layer is a numeric index that defines the draw order. The default 2D scene renders with index 0, so a [code]CanvasLayer[/code] with index -1 will be drawn below, and one with index 1 will be drawn above. This is very useful for HUDs (in layer 1+ or above), or backgrounds (in layer -1 or below). + Canvas drawing layer. [CanvasItem] nodes that are direct or indirect children of a [CanvasLayer] will be drawn in that layer. The layer is a numeric index that defines the draw order. The default 2D scene renders with index 0, so a [CanvasLayer] with index -1 will be drawn below, and one with index 1 will be drawn above. This is very useful for HUDs (in layer 1+ or above), or backgrounds (in layer -1 or below). </description> <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link> <link>https://docs.godotengine.org/en/latest/tutorials/2d/canvas_layers.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_canvas" qualifiers="const"> <return type="RID"> @@ -23,7 +21,11 @@ </methods> <members> <member name="custom_viewport" type="Node" setter="set_custom_viewport" getter="get_custom_viewport"> - The custom [Viewport] node assigned to the [code]CanvasLayer[/code]. If null, uses the default viewport instead. + The custom [Viewport] node assigned to the [CanvasLayer]. If null, uses the default viewport instead. + </member> + <member name="follow_viewport_enable" type="bool" setter="set_follow_viewport" getter="is_following_viewport"> + </member> + <member name="follow_viewport_scale" type="float" setter="set_follow_viewport_scale" getter="get_follow_viewport_scale"> </member> <member name="layer" type="int" setter="set_layer" getter="get_layer"> Layer index for draw order. Lower values are drawn first. Default value: [code]1[/code]. diff --git a/doc/classes/CanvasModulate.xml b/doc/classes/CanvasModulate.xml index ecf8efdf5c..c409b690e7 100644 --- a/doc/classes/CanvasModulate.xml +++ b/doc/classes/CanvasModulate.xml @@ -4,12 +4,10 @@ Tint the entire canvas. </brief_description> <description> - [code]CanvasModulate[/code] tints the canvas elements using its assigned [code]color[/code]. + [CanvasModulate] tints the canvas elements using its assigned [code]color[/code]. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/CapsuleMesh.xml b/doc/classes/CapsuleMesh.xml index c1806bba36..eba440a84e 100644 --- a/doc/classes/CapsuleMesh.xml +++ b/doc/classes/CapsuleMesh.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/CapsuleShape.xml b/doc/classes/CapsuleShape.xml index 2b60eb6cd3..c867dcf326 100644 --- a/doc/classes/CapsuleShape.xml +++ b/doc/classes/CapsuleShape.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/CapsuleShape2D.xml b/doc/classes/CapsuleShape2D.xml index 0fa21ad618..c7724314d6 100644 --- a/doc/classes/CapsuleShape2D.xml +++ b/doc/classes/CapsuleShape2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/CenterContainer.xml b/doc/classes/CenterContainer.xml index 74c5668ee7..5460831ce3 100644 --- a/doc/classes/CenterContainer.xml +++ b/doc/classes/CenterContainer.xml @@ -8,13 +8,11 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="use_top_left" type="bool" setter="set_use_top_left" getter="is_using_top_left"> - If [code]true[/code], centers children relative to the [code]CenterContainer[/code]'s top left corner. Default value: [code]false[/code]. + If [code]true[/code], centers children relative to the [CenterContainer]'s top left corner. Default value: [code]false[/code]. </member> </members> <constants> diff --git a/doc/classes/CheckBox.xml b/doc/classes/CheckBox.xml index 097e32b997..d486b5fad2 100644 --- a/doc/classes/CheckBox.xml +++ b/doc/classes/CheckBox.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/CheckButton.xml b/doc/classes/CheckButton.xml index fe1fcbbecd..94e4eececd 100644 --- a/doc/classes/CheckButton.xml +++ b/doc/classes/CheckButton.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/CircleShape2D.xml b/doc/classes/CircleShape2D.xml index 2248553eb5..7c8bf923f4 100644 --- a/doc/classes/CircleShape2D.xml +++ b/doc/classes/CircleShape2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ClassDB.xml b/doc/classes/ClassDB.xml index 6e5381769a..a37dd28d72 100644 --- a/doc/classes/ClassDB.xml +++ b/doc/classes/ClassDB.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="can_instance" qualifiers="const"> <return type="bool"> diff --git a/doc/classes/ClippedCamera.xml b/doc/classes/ClippedCamera.xml index 6898b9b719..b7f158dd65 100644 --- a/doc/classes/ClippedCamera.xml +++ b/doc/classes/ClippedCamera.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_exception"> <return type="void"> diff --git a/doc/classes/CollisionObject.xml b/doc/classes/CollisionObject.xml index fc7df904e2..cb10530006 100644 --- a/doc/classes/CollisionObject.xml +++ b/doc/classes/CollisionObject.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_input_event" qualifiers="virtual"> <return type="void"> @@ -183,7 +181,7 @@ </methods> <members> <member name="input_capture_on_drag" type="bool" setter="set_capture_input_on_drag" getter="get_capture_input_on_drag"> - If [code]true[/code], the [code]CollisionObject[/code] will continue to receive input events as the mouse is dragged across its shapes. Default value: [code]false[/code]. + If [code]true[/code], the [CollisionObject] will continue to receive input events as the mouse is dragged across its shapes. Default value: [code]false[/code]. </member> <member name="input_ray_pickable" type="bool" setter="set_ray_pickable" getter="is_ray_pickable"> If [code]true[/code], the [CollisionObject]'s shapes will respond to [RayCast]s. Default value: [code]true[/code]. diff --git a/doc/classes/CollisionObject2D.xml b/doc/classes/CollisionObject2D.xml index b363906447..1467812e4d 100644 --- a/doc/classes/CollisionObject2D.xml +++ b/doc/classes/CollisionObject2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_input_event" qualifiers="virtual"> <return type="void"> @@ -70,7 +68,7 @@ <argument index="0" name="owner_id" type="int"> </argument> <description> - Returns [code]true[/code] if collisions for the shape owner originating from this [code]CollisionObject2D[/code] will not be reported to collided with [code]CollisionObject2D[/code]s. + Returns [code]true[/code] if collisions for the shape owner originating from this [CollisionObject2D] will not be reported to collided with [CollisionObject2D]s. </description> </method> <method name="remove_shape_owner"> @@ -190,7 +188,7 @@ <argument index="1" name="enable" type="bool"> </argument> <description> - If [code]enable[/code] is [code]true[/code], collisions for the shape owner originating from this [code]CollisionObject2D[/code] will not be reported to collided with [code]CollisionObject2D[/code]s. + If [code]enable[/code] is [code]true[/code], collisions for the shape owner originating from this [CollisionObject2D] will not be reported to collided with [CollisionObject2D]s. </description> </method> <method name="shape_owner_set_one_way_collision_margin"> diff --git a/doc/classes/CollisionPolygon.xml b/doc/classes/CollisionPolygon.xml index 9a9a3ca673..c40c8a82c3 100644 --- a/doc/classes/CollisionPolygon.xml +++ b/doc/classes/CollisionPolygon.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/CollisionPolygon2D.xml b/doc/classes/CollisionPolygon2D.xml index 8ac72013e3..3d609fa965 100644 --- a/doc/classes/CollisionPolygon2D.xml +++ b/doc/classes/CollisionPolygon2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/CollisionShape.xml b/doc/classes/CollisionShape.xml index ab14f18bfe..3f2e550e83 100644 --- a/doc/classes/CollisionShape.xml +++ b/doc/classes/CollisionShape.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> - <demos> - </demos> <methods> <method name="make_convex_from_brothers"> <return type="void"> diff --git a/doc/classes/CollisionShape2D.xml b/doc/classes/CollisionShape2D.xml index 0f8cafe896..97dc230f22 100644 --- a/doc/classes/CollisionShape2D.xml +++ b/doc/classes/CollisionShape2D.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index 9aa30c6755..9f48f6e018 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="Color"> <return type="Color"> diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index ca1390feb3..d0a81ad8ae 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_preset"> <return type="void"> diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml index ec1662f99d..3e4911cac3 100644 --- a/doc/classes/ColorPickerButton.xml +++ b/doc/classes/ColorPickerButton.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_picker"> <return type="ColorPicker"> diff --git a/doc/classes/ColorRect.xml b/doc/classes/ColorRect.xml index 1bc522a187..d659ebdfcd 100644 --- a/doc/classes/ColorRect.xml +++ b/doc/classes/ColorRect.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ConcavePolygonShape.xml b/doc/classes/ConcavePolygonShape.xml index bc3fdc04af..afc6ce74a5 100644 --- a/doc/classes/ConcavePolygonShape.xml +++ b/doc/classes/ConcavePolygonShape.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_faces" qualifiers="const"> <return type="PoolVector3Array"> diff --git a/doc/classes/ConcavePolygonShape2D.xml b/doc/classes/ConcavePolygonShape2D.xml index b68f441631..5414fe42c5 100644 --- a/doc/classes/ConcavePolygonShape2D.xml +++ b/doc/classes/ConcavePolygonShape2D.xml @@ -5,17 +5,15 @@ </brief_description> <description> Concave polygon 2D shape resource for physics. It is made out of segments and is very optimal for complex polygonal concave collisions. It is really not advised to use for [RigidBody2D] nodes. A CollisionPolygon2D in convex decomposition mode (solids) or several convex objects are advised for that instead. Otherwise, a concave polygon 2D shape is better for static collisions. - The main difference between a [ConvexPolygonShape2D] and a [code]ConcavePolygonShape2D[/code] is that a concave polygon assumes it is concave and uses a more complex method of collision detection, and a convex one forces itself to be convex in order to speed up collision detection. + The main difference between a [ConvexPolygonShape2D] and a [ConcavePolygonShape2D] is that a concave polygon assumes it is concave and uses a more complex method of collision detection, and a convex one forces itself to be convex in order to speed up collision detection. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="segments" type="PoolVector2Array" setter="set_segments" getter="get_segments"> - The array of points that make up the [code]ConcavePolygonShape2D[/code]'s line segments. + The array of points that make up the [ConcavePolygonShape2D]'s line segments. </member> </members> <constants> diff --git a/doc/classes/ConeTwistJoint.xml b/doc/classes/ConeTwistJoint.xml index 2bdc1b59ae..cd2289d715 100644 --- a/doc/classes/ConeTwistJoint.xml +++ b/doc/classes/ConeTwistJoint.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -28,7 +26,7 @@ <member name="swing_span" type="float" setter="_set_swing_span" getter="_get_swing_span"> Swing is rotation from side to side, around the axis perpendicular to the twist axis. The swing span defines, how much rotation will not get corrected allong the swing axis. - Could be defined as looseness in the [code]ConeTwistJoint[/code]. + Could be defined as looseness in the [ConeTwistJoint]. If below 0.05, this behaviour is locked. Default value: [code]PI/4[/code]. </member> <member name="twist_span" type="float" setter="_set_twist_span" getter="_get_twist_span"> @@ -40,7 +38,7 @@ <constant name="PARAM_SWING_SPAN" value="0" enum="Param"> Swing is rotation from side to side, around the axis perpendicular to the twist axis. The swing span defines, how much rotation will not get corrected allong the swing axis. - Could be defined as looseness in the [code]ConeTwistJoint[/code]. + Could be defined as looseness in the [ConeTwistJoint]. If below 0.05, this behaviour is locked. Default value: [code]PI/4[/code]. </constant> <constant name="PARAM_TWIST_SPAN" value="1" enum="Param"> diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml index 5958a6d079..68831a886a 100644 --- a/doc/classes/ConfigFile.xml +++ b/doc/classes/ConfigFile.xml @@ -29,8 +29,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="erase_section"> <return type="void"> diff --git a/doc/classes/ConfirmationDialog.xml b/doc/classes/ConfirmationDialog.xml index d8d8c038ba..f39915f4b7 100644 --- a/doc/classes/ConfirmationDialog.xml +++ b/doc/classes/ConfirmationDialog.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_cancel"> <return type="Button"> diff --git a/doc/classes/Container.xml b/doc/classes/Container.xml index 09f6c08bd0..8a3aac88b5 100644 --- a/doc/classes/Container.xml +++ b/doc/classes/Container.xml @@ -4,13 +4,11 @@ Base node for containers. </brief_description> <description> - Base node for containers. A [code]Container[/code] contains other controls and automatically arranges them in a certain way. + Base node for containers. A [Container] contains other controls and automatically arranges them in a certain way. A Control can inherit this to create custom container classes. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="fit_child_in_rect"> <return type="void"> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index de982f029b..224efeb3f5 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -4,20 +4,18 @@ All User Interface nodes inherit from Control. A control's anchors and margins adapt its position and size relative to its parent. </brief_description> <description> - Base class for all User Interface or [i]UI[/i] related nodes. [code]Control[/code] features a bounding rectangle that defines its extents, an anchor position relative to its parent and margins that represent an offset to the anchor. The margins update automatically when the node, any of its parents, or the screen size change. - For more information on Godot's UI system, anchors, margins, and containers, see the related tutorials in the manual. To build flexible UIs, you'll need a mix of UI elements that inherit from [code]Control[/code] and [Container] nodes. + Base class for all User Interface or [i]UI[/i] related nodes. [Control] features a bounding rectangle that defines its extents, an anchor position relative to its parent and margins that represent an offset to the anchor. The margins update automatically when the node, any of its parents, or the screen size change. + For more information on Godot's UI system, anchors, margins, and containers, see the related tutorials in the manual. To build flexible UIs, you'll need a mix of UI elements that inherit from [Control] and [Container] nodes. [b]User Interface nodes and input[/b] Godot sends input events to the scene's root node first, by calling [method Node._input]. [method Node._input] forwards the event down the node tree to the nodes under the mouse cursor, or on keyboard focus. To do so, it calls [method MainLoop._input_event]. Call [method accept_event] so no other node receives the event. Once you accepted an input, it becomes handled so [method Node._unhandled_input] will not process it. - Only one [code]Control[/code] node can be in keyboard focus. Only the node in focus will receive keyboard events. To get the focus, call [method grab_focus]. [code]Control[/code] nodes lose focus when another node grabs it, or if you hide the node in focus. - Set [member mouse_filter] to [constant MOUSE_FILTER_IGNORE] to tell a [code]Control[/code] node to ignore mouse or touch events. You'll need it if you place an icon on top of a button. - [Theme] resources change the Control's appearance. If you change the [Theme] on a [code]Control[/code] node, it affects all of its children. To override some of the theme's parameters, call one of the [code]add_*_override[/code] methods, like [method add_font_override]. You can override the theme with the inspector. + Only one [Control] node can be in keyboard focus. Only the node in focus will receive keyboard events. To get the focus, call [method grab_focus]. [Control] nodes lose focus when another node grabs it, or if you hide the node in focus. + Set [member mouse_filter] to [constant MOUSE_FILTER_IGNORE] to tell a [Control] node to ignore mouse or touch events. You'll need it if you place an icon on top of a button. + [Theme] resources change the Control's appearance. If you change the [Theme] on a [Control] node, it affects all of its children. To override some of the theme's parameters, call one of the [code]add_*_override[/code] methods, like [method add_font_override]. You can override the theme with the inspector. </description> <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/gui/index.html</link> <link>https://docs.godotengine.org/en/latest/tutorials/2d/custom_drawing_in_2d.html</link> </tutorials> - <demos> - </demos> <methods> <method name="_clips_input" qualifiers="virtual"> <return type="bool"> @@ -632,16 +630,16 @@ The focus access mode for the control (None, Click or All). Only one Control can be focused at the same time, and it will receive keyboard signals. </member> <member name="focus_neighbour_bottom" type="NodePath" setter="set_focus_neighbour" getter="get_focus_neighbour"> - Tells Godot which node it should give keyboard focus to if the user presses the down arrow on the keyboard or down on a gamepad by default. You can change the key by editing the [code]ui_down[/code] input action. The node must be a [code]Control[/code]. If this property is not set, Godot will give focus to the closest [code]Control[/code] to the bottom of this one. + Tells Godot which node it should give keyboard focus to if the user presses the down arrow on the keyboard or down on a gamepad by default. You can change the key by editing the [code]ui_down[/code] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the bottom of this one. </member> <member name="focus_neighbour_left" type="NodePath" setter="set_focus_neighbour" getter="get_focus_neighbour"> - Tells Godot which node it should give keyboard focus to if the user presses the left arrow on the keyboard or left on a gamepad by default. You can change the key by editing the [code]ui_left[/code] input action. The node must be a [code]Control[/code]. If this property is not set, Godot will give focus to the closest [code]Control[/code] to the left of this one. + Tells Godot which node it should give keyboard focus to if the user presses the left arrow on the keyboard or left on a gamepad by default. You can change the key by editing the [code]ui_left[/code] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the left of this one. </member> <member name="focus_neighbour_right" type="NodePath" setter="set_focus_neighbour" getter="get_focus_neighbour"> - Tells Godot which node it should give keyboard focus to if the user presses the right arrow on the keyboard or right on a gamepad by default. You can change the key by editing the [code]ui_right[/code] input action. The node must be a [code]Control[/code]. If this property is not set, Godot will give focus to the closest [code]Control[/code] to the bottom of this one. + Tells Godot which node it should give keyboard focus to if the user presses the right arrow on the keyboard or right on a gamepad by default. You can change the key by editing the [code]ui_right[/code] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the bottom of this one. </member> <member name="focus_neighbour_top" type="NodePath" setter="set_focus_neighbour" getter="get_focus_neighbour"> - Tells Godot which node it should give keyboard focus to if the user presses the top arrow on the keyboard or top on a gamepad by default. You can change the key by editing the [code]ui_top[/code] input action. The node must be a [code]Control[/code]. If this property is not set, Godot will give focus to the closest [code]Control[/code] to the bottom of this one. + Tells Godot which node it should give keyboard focus to if the user presses the top arrow on the keyboard or top on a gamepad by default. You can change the key by editing the [code]ui_top[/code] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the bottom of this one. </member> <member name="focus_next" type="NodePath" setter="set_focus_next" getter="get_focus_next"> Tells Godot which node it should give keyboard focus to if the user presses Tab on a keyboard by default. You can change the key by editing the [code]ui_focus_next[/code] input action. @@ -717,7 +715,7 @@ Tells the parent [Container] nodes how they should resize and place the node on the Y axis. Use one of the [code]SIZE_*[/code] constants to change the flags. See the constants to learn what each does. </member> <member name="theme" type="Theme" setter="set_theme" getter="get_theme"> - Changing this property replaces the current [Theme] resource this node and all its [code]Control[/code] children use. + Changing this property replaces the current [Theme] resource this node and all its [Control] children use. </member> </members> <signals> @@ -745,7 +743,7 @@ </signal> <signal name="modal_closed"> <description> - Emitted when a modal [code]Control[/code] is closed. See [method show_modal]. + Emitted when a modal [Control] is closed. See [method show_modal]. </description> </signal> <signal name="mouse_entered"> @@ -903,7 +901,7 @@ Snap all 4 anchors to a horizontal line that cuts the parent control in half. Use with [method set_anchors_preset]. </constant> <constant name="PRESET_WIDE" value="15" enum="LayoutPreset"> - Snap all 4 anchors to the respective corners of the parent control. Set all 4 margins to 0 after you applied this preset and the [code]Control[/code] will fit its parent control. This is equivalent to to the "Full Rect" layout option in the editor. Use with [method set_anchors_preset]. + Snap all 4 anchors to the respective corners of the parent control. Set all 4 margins to 0 after you applied this preset and the [Control] will fit its parent control. This is equivalent to to the "Full Rect" layout option in the editor. Use with [method set_anchors_preset]. </constant> <constant name="PRESET_MODE_MINSIZE" value="0" enum="LayoutPresetMode"> </constant> diff --git a/doc/classes/ConvexPolygonShape.xml b/doc/classes/ConvexPolygonShape.xml index 874088a6f1..31b4d08213 100644 --- a/doc/classes/ConvexPolygonShape.xml +++ b/doc/classes/ConvexPolygonShape.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ConvexPolygonShape2D.xml b/doc/classes/ConvexPolygonShape2D.xml index c30258a116..7add252481 100644 --- a/doc/classes/ConvexPolygonShape2D.xml +++ b/doc/classes/ConvexPolygonShape2D.xml @@ -5,12 +5,10 @@ </brief_description> <description> Convex Polygon Shape for 2D physics. A convex polygon, whatever its shape, is internally decomposed into as many convex polygons as needed to ensure all collision checks against it are always done on convex polygons (which are faster to check). - The main difference between a [code]ConvexPolygonShape2D[/code] and a [ConcavePolygonShape2D] is that a concave polygon assumes it is concave and uses a more complex method of collision detection, and a convex one forces itself to be convex in order to speed up collision detection. + The main difference between a [ConvexPolygonShape2D] and a [ConcavePolygonShape2D] is that a concave polygon assumes it is concave and uses a more complex method of collision detection, and a convex one forces itself to be convex in order to speed up collision detection. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="set_point_cloud"> <return type="void"> diff --git a/doc/classes/CubeMap.xml b/doc/classes/CubeMap.xml index bdf3826164..f5d2823115 100644 --- a/doc/classes/CubeMap.xml +++ b/doc/classes/CubeMap.xml @@ -8,14 +8,12 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_height" qualifiers="const"> <return type="int"> </return> <description> - Returns the [code]CubeMap[/code]'s height. + Returns the [CubeMap]'s height. </description> </method> <method name="get_side" qualifiers="const"> @@ -24,14 +22,14 @@ <argument index="0" name="side" type="int" enum="CubeMap.Side"> </argument> <description> - Returns an [Image] for a side of the [code]CubeMap[/code] using one of the [code]SIDE_*[/code] constants or an integer 0-5. + Returns an [Image] for a side of the [CubeMap] using one of the [code]SIDE_*[/code] constants or an integer 0-5. </description> </method> <method name="get_width" qualifiers="const"> <return type="int"> </return> <description> - Returns the [code]CubeMap[/code]'s width. + Returns the [CubeMap]'s width. </description> </method> <method name="set_side"> @@ -42,48 +40,48 @@ <argument index="1" name="image" type="Image"> </argument> <description> - Sets an [Image] for a side of the [code]CubeMap[/code] using one of the [code]SIDE_*[/code] constants or an integer 0-5. + Sets an [Image] for a side of the [CubeMap] using one of the [code]SIDE_*[/code] constants or an integer 0-5. </description> </method> </methods> <members> <member name="flags" type="int" setter="set_flags" getter="get_flags"> - The render flags for the [code]CubeMap[/code]. See the [code]FLAG_*[/code] constants for details. + The render flags for the [CubeMap]. See the [code]FLAG_*[/code] constants for details. </member> <member name="lossy_storage_quality" type="float" setter="set_lossy_storage_quality" getter="get_lossy_storage_quality"> - The lossy storage quality of the [code]CubeMap[/code] if the storage mode is set to STORAGE_COMPRESS_LOSSY. + The lossy storage quality of the [CubeMap] if the storage mode is set to STORAGE_COMPRESS_LOSSY. </member> <member name="storage_mode" type="int" setter="set_storage" getter="get_storage" enum="CubeMap.Storage"> - The [code]CubeMap[/code]'s storage mode. See [code]STORAGE_*[/code] constants. + The [CubeMap]'s storage mode. See [code]STORAGE_*[/code] constants. </member> </members> <constants> <constant name="STORAGE_RAW" value="0" enum="Storage"> - Store the [code]CubeMap[/code] without any compression. + Store the [CubeMap] without any compression. </constant> <constant name="STORAGE_COMPRESS_LOSSY" value="1" enum="Storage"> - Store the [code]CubeMap[/code] with strong compression that reduces image quality. + Store the [CubeMap] with strong compression that reduces image quality. </constant> <constant name="STORAGE_COMPRESS_LOSSLESS" value="2" enum="Storage"> - Store the [code]CubeMap[/code] with moderate compression that doesn't reduce image quality. + Store the [CubeMap] with moderate compression that doesn't reduce image quality. </constant> <constant name="SIDE_LEFT" value="0" enum="Side"> - Identifier for the left face of the [code]CubeMap[/code]. + Identifier for the left face of the [CubeMap]. </constant> <constant name="SIDE_RIGHT" value="1" enum="Side"> - Identifier for the right face of the [code]CubeMap[/code]. + Identifier for the right face of the [CubeMap]. </constant> <constant name="SIDE_BOTTOM" value="2" enum="Side"> - Identifier for the bottom face of the [code]CubeMap[/code]. + Identifier for the bottom face of the [CubeMap]. </constant> <constant name="SIDE_TOP" value="3" enum="Side"> - Identifier for the top face of the [code]CubeMap[/code]. + Identifier for the top face of the [CubeMap]. </constant> <constant name="SIDE_FRONT" value="4" enum="Side"> - Identifier for the front face of the [code]CubeMap[/code]. + Identifier for the front face of the [CubeMap]. </constant> <constant name="SIDE_BACK" value="5" enum="Side"> - Identifier for the back face of the [code]CubeMap[/code]. + Identifier for the back face of the [CubeMap]. </constant> <constant name="FLAG_MIPMAPS" value="1" enum="Flags"> Generate mipmaps, to enable smooth zooming out of the texture. diff --git a/doc/classes/CubeMesh.xml b/doc/classes/CubeMesh.xml index 811c350014..814c2f21c1 100644 --- a/doc/classes/CubeMesh.xml +++ b/doc/classes/CubeMesh.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml index 327dc42142..20afb03048 100644 --- a/doc/classes/Curve.xml +++ b/doc/classes/Curve.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_point"> <return type="int"> diff --git a/doc/classes/Curve2D.xml b/doc/classes/Curve2D.xml index 7fe05181ab..d4aaa24b8c 100644 --- a/doc/classes/Curve2D.xml +++ b/doc/classes/Curve2D.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_point"> <return type="void"> diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml index 41957af506..1d9761407d 100644 --- a/doc/classes/Curve3D.xml +++ b/doc/classes/Curve3D.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_point"> <return type="void"> diff --git a/doc/classes/CurveTexture.xml b/doc/classes/CurveTexture.xml index e0de5d2581..0184e1e6b6 100644 --- a/doc/classes/CurveTexture.xml +++ b/doc/classes/CurveTexture.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/CylinderMesh.xml b/doc/classes/CylinderMesh.xml index a5e39d4972..fa0a3bf412 100644 --- a/doc/classes/CylinderMesh.xml +++ b/doc/classes/CylinderMesh.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/CylinderShape.xml b/doc/classes/CylinderShape.xml index bd6849a8c4..847265ef25 100644 --- a/doc/classes/CylinderShape.xml +++ b/doc/classes/CylinderShape.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/DampedSpringJoint2D.xml b/doc/classes/DampedSpringJoint2D.xml index bd43e20140..d6973848e2 100644 --- a/doc/classes/DampedSpringJoint2D.xml +++ b/doc/classes/DampedSpringJoint2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 65cfc6abd3..b0a3b1ac75 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -19,8 +19,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/gdscript_basics.html#dictionary</link> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <description> @@ -60,7 +58,7 @@ <argument index="1" name="default" type="Variant" default="Null"> </argument> <description> - Returns the current value for the specified key in the [code]Dictionary[/code]. If the key does not exist, the method returns the value of the optional default argument, or Null if it is omitted. + Returns the current value for the specified key in the [Dictionary]. If the key does not exist, the method returns the value of the optional default argument, or Null if it is omitted. </description> </method> <method name="has"> @@ -92,7 +90,7 @@ <return type="Array"> </return> <description> - Return the list of keys in the [code]Dictionary[/code]. + Return the list of keys in the [Dictionary]. </description> </method> <method name="size"> @@ -106,7 +104,7 @@ <return type="Array"> </return> <description> - Return the list of values in the [code]Dictionary[/code]. + Return the list of values in the [Dictionary]. </description> </method> </methods> diff --git a/doc/classes/DirectionalLight.xml b/doc/classes/DirectionalLight.xml index cb509b8bb3..c6650a1641 100644 --- a/doc/classes/DirectionalLight.xml +++ b/doc/classes/DirectionalLight.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Directory.xml b/doc/classes/Directory.xml index 18e819784a..8a81695198 100644 --- a/doc/classes/Directory.xml +++ b/doc/classes/Directory.xml @@ -25,8 +25,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/filesystem.html</link> </tutorials> - <demos> - </demos> <methods> <method name="change_dir"> <return type="int" enum="Error"> diff --git a/doc/classes/DynamicFont.xml b/doc/classes/DynamicFont.xml index 500da1b30f..99ba5143e3 100644 --- a/doc/classes/DynamicFont.xml +++ b/doc/classes/DynamicFont.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_fallback"> <return type="void"> diff --git a/doc/classes/DynamicFontData.xml b/doc/classes/DynamicFontData.xml index d9fd6bfcb0..8eff5fb993 100644 --- a/doc/classes/DynamicFontData.xml +++ b/doc/classes/DynamicFontData.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml index e25c76b3f5..998b05d495 100644 --- a/doc/classes/EditorExportPlugin.xml +++ b/doc/classes/EditorExportPlugin.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_export_begin" qualifiers="virtual"> <return type="void"> diff --git a/doc/classes/EditorFeatureProfile.xml b/doc/classes/EditorFeatureProfile.xml new file mode 100644 index 0000000000..410b71a43e --- /dev/null +++ b/doc/classes/EditorFeatureProfile.xml @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="EditorFeatureProfile" inherits="Reference" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="get_feature_name"> + <return type="String"> + </return> + <argument index="0" name="feature" type="int" enum="EditorFeatureProfile.Feature"> + </argument> + <description> + </description> + </method> + <method name="is_class_disabled" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="class_name" type="String"> + </argument> + <description> + </description> + </method> + <method name="is_class_editor_disabled" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="class_name" type="String"> + </argument> + <description> + </description> + </method> + <method name="is_class_property_disabled" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="class_name" type="String"> + </argument> + <argument index="1" name="arg1" type="String"> + </argument> + <description> + </description> + </method> + <method name="is_feature_disabled" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="feature" type="int" enum="EditorFeatureProfile.Feature"> + </argument> + <description> + </description> + </method> + <method name="load_from_file"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <description> + </description> + </method> + <method name="save_to_file"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <description> + </description> + </method> + <method name="set_disable_class"> + <return type="void"> + </return> + <argument index="0" name="class_name" type="String"> + </argument> + <argument index="1" name="disable" type="bool"> + </argument> + <description> + </description> + </method> + <method name="set_disable_class_editor"> + <return type="void"> + </return> + <argument index="0" name="class_name" type="String"> + </argument> + <argument index="1" name="disable" type="bool"> + </argument> + <description> + </description> + </method> + <method name="set_disable_class_property"> + <return type="void"> + </return> + <argument index="0" name="class_name" type="String"> + </argument> + <argument index="1" name="property" type="String"> + </argument> + <argument index="2" name="arg2" type="bool"> + </argument> + <description> + </description> + </method> + <method name="set_disable_feature"> + <return type="void"> + </return> + <argument index="0" name="feature" type="int" enum="EditorFeatureProfile.Feature"> + </argument> + <argument index="1" name="disable" type="bool"> + </argument> + <description> + </description> + </method> + </methods> + <constants> + <constant name="FEATURE_3D" value="0" enum="Feature"> + </constant> + <constant name="FEATURE_SCRIPT" value="1" enum="Feature"> + </constant> + <constant name="FEATURE_ASSET_LIB" value="2" enum="Feature"> + </constant> + <constant name="FEATURE_SCENE_TREE" value="3" enum="Feature"> + </constant> + <constant name="FEATURE_IMPORT_DOCK" value="4" enum="Feature"> + </constant> + <constant name="FEATURE_NODE_DOCK" value="5" enum="Feature"> + </constant> + <constant name="FEATURE_FILESYSTEM_DOCK" value="6" enum="Feature"> + </constant> + <constant name="FEATURE_MAX" value="7" enum="Feature"> + </constant> + </constants> +</class> diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml index 5f088c33e2..15271b8050 100644 --- a/doc/classes/EditorFileDialog.xml +++ b/doc/classes/EditorFileDialog.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_filter"> <return type="void"> @@ -15,7 +13,7 @@ <argument index="0" name="filter" type="String"> </argument> <description> - Adds a comma-delimited file extension filter option to the [code]EditorFileDialog[/code] with an optional semi-colon-delimited label. + Adds a comma-delimited file extension filter option to the [EditorFileDialog] with an optional semi-colon-delimited label. Example: "*.tscn, *.scn; Scenes", results in filter text "Scenes (*.tscn, *.scn)". </description> </method> @@ -37,7 +35,7 @@ <return type="void"> </return> <description> - Notify the [code]EditorFileDialog[/code] that its view of the data is no longer accurate. Updates the view contents on next view update. + Notify the [EditorFileDialog] that its view of the data is no longer accurate. Updates the view contents on next view update. </description> </method> </methods> @@ -55,16 +53,16 @@ The file system path in the address bar. </member> <member name="disable_overwrite_warning" type="bool" setter="set_disable_overwrite_warning" getter="is_overwrite_warning_disabled"> - If [code]true[/code], the [code]EditorFileDialog[/code] will not warn the user before overwriting files. + If [code]true[/code], the [EditorFileDialog] will not warn the user before overwriting files. </member> <member name="display_mode" type="int" setter="set_display_mode" getter="get_display_mode" enum="EditorFileDialog.DisplayMode"> - The view format in which the [code]EditorFileDialog[/code] displays resources to the user. + The view format in which the [EditorFileDialog] displays resources to the user. </member> <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="EditorFileDialog.Mode"> - The purpose of the [code]EditorFileDialog[/code]. Changes allowed behaviors. + The purpose of the [EditorFileDialog]. Changes allowed behaviors. </member> <member name="show_hidden_files" type="bool" setter="set_show_hidden_files" getter="is_showing_hidden_files"> - If [code]true[/code], hidden files and directories will be visible in the [code]EditorFileDialog[/code]. + If [code]true[/code], hidden files and directories will be visible in the [EditorFileDialog]. </member> </members> <signals> @@ -92,34 +90,34 @@ </signals> <constants> <constant name="MODE_OPEN_FILE" value="0" enum="Mode"> - The [code]EditorFileDialog[/code] can select only one file. Accepting the window will open the file. + The [EditorFileDialog] can select only one file. Accepting the window will open the file. </constant> <constant name="MODE_OPEN_FILES" value="1" enum="Mode"> - The [code]EditorFileDialog[/code] can select multiple files. Accepting the window will open all files. + The [EditorFileDialog] can select multiple files. Accepting the window will open all files. </constant> <constant name="MODE_OPEN_DIR" value="2" enum="Mode"> - The [code]EditorFileDialog[/code] can select only one directory. Accepting the window will open the directory. + The [EditorFileDialog] can select only one directory. Accepting the window will open the directory. </constant> <constant name="MODE_OPEN_ANY" value="3" enum="Mode"> - The [code]EditorFileDialog[/code] can select a file or directory. Accepting the window will open it. + The [EditorFileDialog] can select a file or directory. Accepting the window will open it. </constant> <constant name="MODE_SAVE_FILE" value="4" enum="Mode"> - The [code]EditorFileDialog[/code] can select only one file. Accepting the window will save the file. + The [EditorFileDialog] can select only one file. Accepting the window will save the file. </constant> <constant name="ACCESS_RESOURCES" value="0" enum="Access"> - The [code]EditorFileDialog[/code] can only view [code]res://[/code] directory contents. + The [EditorFileDialog] can only view [code]res://[/code] directory contents. </constant> <constant name="ACCESS_USERDATA" value="1" enum="Access"> - The [code]EditorFileDialog[/code] can only view [code]user://[/code] directory contents. + The [EditorFileDialog] can only view [code]user://[/code] directory contents. </constant> <constant name="ACCESS_FILESYSTEM" value="2" enum="Access"> - The [code]EditorFileDialog[/code] can view the entire local file system. + The [EditorFileDialog] can view the entire local file system. </constant> <constant name="DISPLAY_THUMBNAILS" value="0" enum="DisplayMode"> - The [code]EditorFileDialog[/code] displays resources as thumbnails. + The [EditorFileDialog] displays resources as thumbnails. </constant> <constant name="DISPLAY_LIST" value="1" enum="DisplayMode"> - The [code]EditorFileDialog[/code] displays resources as a list of filenames. + The [EditorFileDialog] displays resources as a list of filenames. </constant> </constants> </class> diff --git a/doc/classes/EditorFileSystem.xml b/doc/classes/EditorFileSystem.xml index 9e4380e6a0..f110183081 100644 --- a/doc/classes/EditorFileSystem.xml +++ b/doc/classes/EditorFileSystem.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_file_type" qualifiers="const"> <return type="String"> diff --git a/doc/classes/EditorFileSystemDirectory.xml b/doc/classes/EditorFileSystemDirectory.xml index bf6a9a1dc1..c7920c6ed6 100644 --- a/doc/classes/EditorFileSystemDirectory.xml +++ b/doc/classes/EditorFileSystemDirectory.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="find_dir_index" qualifiers="const"> <return type="int"> diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml index c1eead174d..c3f38e9e20 100644 --- a/doc/classes/EditorImportPlugin.xml +++ b/doc/classes/EditorImportPlugin.xml @@ -51,8 +51,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/plugins/editor/import_plugins.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_import_options" qualifiers="virtual"> <return type="Array"> diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml index e7e9ea495d..cf14183099 100644 --- a/doc/classes/EditorInspector.xml +++ b/doc/classes/EditorInspector.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="refresh"> <return type="void"> diff --git a/doc/classes/EditorInspectorPlugin.xml b/doc/classes/EditorInspectorPlugin.xml index e5e886057f..49efee49f4 100644 --- a/doc/classes/EditorInspectorPlugin.xml +++ b/doc/classes/EditorInspectorPlugin.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_custom_control"> <return type="void"> diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index 30ff609a81..ecbb5da10c 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="edit_resource"> <return type="void"> @@ -48,6 +46,12 @@ Returns the editor [Viewport]. </description> </method> + <method name="get_inspector" qualifiers="const"> + <return type="EditorInspector"> + </return> + <description> + </description> + </method> <method name="get_open_scenes" qualifiers="const"> <return type="Array"> </return> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 9d0f0d8035..829563c8ac 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/plugins/editor/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_autoload_singleton"> <return type="void"> diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml index b3e1a613b0..2fd7f18461 100644 --- a/doc/classes/EditorProperty.xml +++ b/doc/classes/EditorProperty.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="emit_changed"> <return type="void"> diff --git a/doc/classes/EditorResourceConversionPlugin.xml b/doc/classes/EditorResourceConversionPlugin.xml index 9f8412becd..2638836c68 100644 --- a/doc/classes/EditorResourceConversionPlugin.xml +++ b/doc/classes/EditorResourceConversionPlugin.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_convert" qualifiers="virtual"> <return type="Resource"> diff --git a/doc/classes/EditorResourcePreview.xml b/doc/classes/EditorResourcePreview.xml index d5f7a8ac89..8de905e710 100644 --- a/doc/classes/EditorResourcePreview.xml +++ b/doc/classes/EditorResourcePreview.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_preview_generator"> <return type="void"> diff --git a/doc/classes/EditorResourcePreviewGenerator.xml b/doc/classes/EditorResourcePreviewGenerator.xml index d607b66c05..6592ffd1a8 100644 --- a/doc/classes/EditorResourcePreviewGenerator.xml +++ b/doc/classes/EditorResourcePreviewGenerator.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="generate" qualifiers="virtual"> <return type="Texture"> diff --git a/doc/classes/EditorSceneImporter.xml b/doc/classes/EditorSceneImporter.xml index aee8712e74..4707543c91 100644 --- a/doc/classes/EditorSceneImporter.xml +++ b/doc/classes/EditorSceneImporter.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_get_extensions" qualifiers="virtual"> <return type="Array"> diff --git a/doc/classes/EditorSceneImporterAssimp.xml b/doc/classes/EditorSceneImporterAssimp.xml new file mode 100644 index 0000000000..b1f397e2b9 --- /dev/null +++ b/doc/classes/EditorSceneImporterAssimp.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="EditorSceneImporterAssimp" inherits="EditorSceneImporter" category="Core" version="3.2"> + <brief_description> + This is a multi-format 3d asset importer. + </brief_description> + <description> + This is a multi-format 3d asset importer. + Use these FBX export settings from Autodesk Maya. + [codeblock] + * Smoothing Groups + * Smooth Mesh + * Triangluate (For mesh with blendshapes) + * Bake Animation + * Resample All + * Deformed Models + * Skins + * Blend Shapes + * Curve Filters + * Constant Key Reducer + * Auto Tangents Only + * DO NOT CHECK Constraints (Will Break File) + * Can check Embed Media (Embeds textures into FBX file to import) + -- Note: When importing embed media, texture and mesh will be a un-alterable file. + -- Reimport of fbx with updated texture is need if texture is updated. + * Units: Centimeters + * Up Axis: Y + * Binary format in FBX 2017 + [/codeblock] + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/EditorScenePostImport.xml b/doc/classes/EditorScenePostImport.xml index 788574d7d2..3b0590f78c 100644 --- a/doc/classes/EditorScenePostImport.xml +++ b/doc/classes/EditorScenePostImport.xml @@ -28,8 +28,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/workflow/assets/importing_scenes.html#custom-script</link> </tutorials> - <demos> - </demos> <methods> <method name="get_source_file" qualifiers="const"> <return type="String"> diff --git a/doc/classes/EditorScript.xml b/doc/classes/EditorScript.xml index 117817d311..52d7ce3f17 100644 --- a/doc/classes/EditorScript.xml +++ b/doc/classes/EditorScript.xml @@ -17,8 +17,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_run" qualifiers="virtual"> <return type="void"> diff --git a/doc/classes/EditorSelection.xml b/doc/classes/EditorSelection.xml index d1975d786e..336390b2b1 100644 --- a/doc/classes/EditorSelection.xml +++ b/doc/classes/EditorSelection.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_node"> <return type="void"> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 0743a4bd6b..7d38031a4c 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_property_info"> <return type="void"> diff --git a/doc/classes/EditorSpatialGizmo.xml b/doc/classes/EditorSpatialGizmo.xml index 0eff0bc0f8..58d04e2bbf 100644 --- a/doc/classes/EditorSpatialGizmo.xml +++ b/doc/classes/EditorSpatialGizmo.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_collision_segments"> <return type="void"> diff --git a/doc/classes/EditorSpatialGizmoPlugin.xml b/doc/classes/EditorSpatialGizmoPlugin.xml index bebf166e37..8700c1cff9 100644 --- a/doc/classes/EditorSpatialGizmoPlugin.xml +++ b/doc/classes/EditorSpatialGizmoPlugin.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/plugins/editor/spatial_gizmos.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_material"> <return type="void"> diff --git a/doc/classes/EncodedObjectAsID.xml b/doc/classes/EncodedObjectAsID.xml index f7052de46e..1822f6c199 100644 --- a/doc/classes/EncodedObjectAsID.xml +++ b/doc/classes/EncodedObjectAsID.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_object_id" qualifiers="const"> <return type="int"> diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml index 9253895e84..ac46d34198 100644 --- a/doc/classes/Engine.xml +++ b/doc/classes/Engine.xml @@ -4,12 +4,10 @@ Access to basic engine properties. </brief_description> <description> - The [code]Engine[/code] class allows you to query and modify the game's run-time parameters, such as frames per second, time scale, and others. + The [Engine] class allows you to query and modify the game's run-time parameters, such as frames per second, time scale, and others. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_author_info" qualifiers="const"> <return type="Dictionary"> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index d75baa30cf..17fdc59c6f 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -15,8 +15,6 @@ <link>https://docs.godotengine.org/en/latest/tutorials/3d/environment_and_post_processing.html</link> <link>https://docs.godotengine.org/en/latest/tutorials/3d/high_dynamic_range.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Expression.xml b/doc/classes/Expression.xml index a983ab0cf3..464ed44ecd 100644 --- a/doc/classes/Expression.xml +++ b/doc/classes/Expression.xml @@ -25,8 +25,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="execute"> <return type="Variant"> diff --git a/doc/classes/File.xml b/doc/classes/File.xml index 533a6d6399..30da143f88 100644 --- a/doc/classes/File.xml +++ b/doc/classes/File.xml @@ -24,8 +24,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/filesystem.html</link> </tutorials> - <demos> - </demos> <methods> <method name="close"> <return type="void"> @@ -84,6 +82,7 @@ </return> <description> Returns the whole file as a [String]. + Text is interpreted as being UTF-8 encoded. </description> </method> <method name="get_buffer" qualifiers="const"> @@ -102,6 +101,7 @@ </argument> <description> Returns the next value of the file in CSV (Comma Separated Values) format. You can pass a different delimiter to use other than the default "," (comma), it should be one character long. + Text is interpreted as being UTF-8 encoded. </description> </method> <method name="get_double" qualifiers="const"> @@ -137,6 +137,7 @@ </return> <description> Returns the next line of the file as a [String]. + Text is interpreted as being UTF-8 encoded. </description> </method> <method name="get_md5" qualifiers="const"> @@ -162,6 +163,7 @@ </return> <description> Returns a [String] saved in Pascal format from the file. + Text is interpreted as being UTF-8 encoded. </description> </method> <method name="get_path" qualifiers="const"> @@ -223,7 +225,7 @@ </return> <argument index="0" name="path" type="String"> </argument> - <argument index="1" name="flags" type="int"> + <argument index="1" name="flags" type="int" enum="File.ModeFlags"> </argument> <description> Opens the file for writing or reading, depending on the flags. @@ -234,9 +236,9 @@ </return> <argument index="0" name="path" type="String"> </argument> - <argument index="1" name="mode_flags" type="int"> + <argument index="1" name="mode_flags" type="int" enum="File.ModeFlags"> </argument> - <argument index="2" name="compression_mode" type="int" default="0"> + <argument index="2" name="compression_mode" type="int" enum="File.CompressionMode" default="0"> </argument> <description> Opens a compressed file for reading or writing. Use COMPRESSION_* constants to set [code]compression_mode[/code]. @@ -247,7 +249,7 @@ </return> <argument index="0" name="path" type="String"> </argument> - <argument index="1" name="mode_flags" type="int"> + <argument index="1" name="mode_flags" type="int" enum="File.ModeFlags"> </argument> <argument index="2" name="key" type="PoolByteArray"> </argument> @@ -260,7 +262,7 @@ </return> <argument index="0" name="path" type="String"> </argument> - <argument index="1" name="mode_flags" type="int"> + <argument index="1" name="mode_flags" type="int" enum="File.ModeFlags"> </argument> <argument index="2" name="pass" type="String"> </argument> @@ -340,6 +342,7 @@ </argument> <description> Store the given [PoolStringArray] in the file as a line formatted in the CSV (Comma Separated Values) format. You can pass a different delimiter to use other than the default "," (comma), it should be one character long. + Text will be encoded as UTF-8. </description> </method> <method name="store_double"> @@ -367,6 +370,7 @@ </argument> <description> Stores the given [String] as a line in the file. + Text will be encoded as UTF-8. </description> </method> <method name="store_pascal_string"> @@ -376,6 +380,7 @@ </argument> <description> Stores the given [String] as a line in the file in Pascal format (i.e. also store the length of the string). + Text will be encoded as UTF-8. </description> </method> <method name="store_real"> @@ -394,6 +399,7 @@ </argument> <description> Stores the given [String] in the file. + Text will be encoded as UTF-8. </description> </method> <method name="store_var"> diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml index 3ebe37e5ba..66e6fa5314 100644 --- a/doc/classes/FileDialog.xml +++ b/doc/classes/FileDialog.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_filter"> <return type="void"> diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml index 3e2347a4f8..bd189928b0 100644 --- a/doc/classes/Font.xml +++ b/doc/classes/Font.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="draw" qualifiers="const"> <return type="void"> diff --git a/doc/classes/FuncRef.xml b/doc/classes/FuncRef.xml index 523acb8983..3f84316503 100644 --- a/doc/classes/FuncRef.xml +++ b/doc/classes/FuncRef.xml @@ -5,12 +5,10 @@ </brief_description> <description> In GDScript, functions are not [i]first-class objects[/i]. This means it is impossible to store them directly as variables, return them from another function, or pass them as arguments. - However, by creating a [code]FuncRef[/code] using the [method @GDScript.funcref] function, a reference to a function in a given object can be created, passed around and called. + However, by creating a [FuncRef] using the [method @GDScript.funcref] function, a reference to a function in a given object can be created, passed around and called. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="call_func" qualifiers="vararg"> <return type="Variant"> diff --git a/doc/classes/GDNativeLibraryResourceLoader.xml b/doc/classes/GDNativeLibraryResourceLoader.xml index 17ee34c41b..1b98fa3889 100644 --- a/doc/classes/GDNativeLibraryResourceLoader.xml +++ b/doc/classes/GDNativeLibraryResourceLoader.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/GDNativeLibraryResourceSaver.xml b/doc/classes/GDNativeLibraryResourceSaver.xml index f7d4b538ae..346d0d87d4 100644 --- a/doc/classes/GDNativeLibraryResourceSaver.xml +++ b/doc/classes/GDNativeLibraryResourceSaver.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/GIProbe.xml b/doc/classes/GIProbe.xml index c32fbc651e..e42d3ed2c4 100644 --- a/doc/classes/GIProbe.xml +++ b/doc/classes/GIProbe.xml @@ -7,8 +7,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/gi_probes.html</link> </tutorials> - <demos> - </demos> <methods> <method name="bake"> <return type="void"> diff --git a/doc/classes/GIProbeData.xml b/doc/classes/GIProbeData.xml index fcb9e6c14c..94b86dee74 100644 --- a/doc/classes/GIProbeData.xml +++ b/doc/classes/GIProbeData.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Generic6DOFJoint.xml b/doc/classes/Generic6DOFJoint.xml index 0a3b7b9027..53cece21fa 100644 --- a/doc/classes/Generic6DOFJoint.xml +++ b/doc/classes/Generic6DOFJoint.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Geometry.xml b/doc/classes/Geometry.xml index fe431d55a9..68539d7ecb 100644 --- a/doc/classes/Geometry.xml +++ b/doc/classes/Geometry.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="build_box_planes"> <return type="Array"> diff --git a/doc/classes/GeometryInstance.xml b/doc/classes/GeometryInstance.xml index 023b782586..6a89d81aa0 100644 --- a/doc/classes/GeometryInstance.xml +++ b/doc/classes/GeometryInstance.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Gradient.xml b/doc/classes/Gradient.xml index 321d630b08..d80fed204e 100644 --- a/doc/classes/Gradient.xml +++ b/doc/classes/Gradient.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_point"> <return type="void"> diff --git a/doc/classes/GradientTexture.xml b/doc/classes/GradientTexture.xml index f343fb9b34..fc952346ad 100644 --- a/doc/classes/GradientTexture.xml +++ b/doc/classes/GradientTexture.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 68ab1a40c6..86504b2c98 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_valid_connection_type"> <return type="void"> diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml index a26310e912..845fc35a7a 100644 --- a/doc/classes/GraphNode.xml +++ b/doc/classes/GraphNode.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear_all_slots"> <return type="void"> diff --git a/doc/classes/GridContainer.xml b/doc/classes/GridContainer.xml index 6103264dc7..94d805e3f6 100644 --- a/doc/classes/GridContainer.xml +++ b/doc/classes/GridContainer.xml @@ -8,13 +8,11 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="columns" type="int" setter="set_columns" getter="get_columns"> - The number of columns in the [code]GridContainer[/code]. If modified, [code]GridContainer[/code] reorders its children to accommodate the new layout. + The number of columns in the [GridContainer]. If modified, [GridContainer] reorders its children to accommodate the new layout. </member> </members> <constants> diff --git a/doc/classes/GrooveJoint2D.xml b/doc/classes/GrooveJoint2D.xml index 7c951eca83..dd758d0bd3 100644 --- a/doc/classes/GrooveJoint2D.xml +++ b/doc/classes/GrooveJoint2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/HBoxContainer.xml b/doc/classes/HBoxContainer.xml index 3adaf56cbf..fce030a260 100644 --- a/doc/classes/HBoxContainer.xml +++ b/doc/classes/HBoxContainer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/HScrollBar.xml b/doc/classes/HScrollBar.xml index fd6fa148cb..14ace8bc76 100644 --- a/doc/classes/HScrollBar.xml +++ b/doc/classes/HScrollBar.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/HSeparator.xml b/doc/classes/HSeparator.xml index f5d9f23993..c2c22d3bcd 100644 --- a/doc/classes/HSeparator.xml +++ b/doc/classes/HSeparator.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/HSlider.xml b/doc/classes/HSlider.xml index fe34404572..2fef4669d2 100644 --- a/doc/classes/HSlider.xml +++ b/doc/classes/HSlider.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/HSplitContainer.xml b/doc/classes/HSplitContainer.xml index 88527cf4ea..accc09d190 100644 --- a/doc/classes/HSplitContainer.xml +++ b/doc/classes/HSplitContainer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml index ddc3c12445..13e018f88f 100644 --- a/doc/classes/HTTPClient.xml +++ b/doc/classes/HTTPClient.xml @@ -6,21 +6,19 @@ <description> Hyper-text transfer protocol client (sometimes called "User Agent"). Used to make HTTP requests to download web content, upload files and other data or to communicate with various services, among other use cases. Note that this client only needs to connect to a host once (see [method connect_to_host]) to send multiple requests. Because of this, methods that take URLs usually take just the part after the host instead of the full URL, as the client is already connected to a host. See [method request] for a full example and to get started. - A [code]HTTPClient[/code] should be reused between multiple requests or to connect to different hosts instead of creating one client per request. Supports SSL and SSL server certificate verification. HTTP status codes in the 2xx range indicate success, 3xx redirection (i.e. "try again, but over here"), 4xx something was wrong with the request, and 5xx something went wrong on the server's side. + A [HTTPClient] should be reused between multiple requests or to connect to different hosts instead of creating one client per request. Supports SSL and SSL server certificate verification. HTTP status codes in the 2xx range indicate success, 3xx redirection (i.e. "try again, but over here"), 4xx something was wrong with the request, and 5xx something went wrong on the server's side. For more information on HTTP, see https://developer.mozilla.org/en-US/docs/Web/HTTP (or read RFC 2616 to get it straight from the source: https://tools.ietf.org/html/rfc2616). </description> <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/networking/http_client_class.html</link> <link>https://docs.godotengine.org/en/latest/tutorials/networking/ssl_certificates.html</link> </tutorials> - <demos> - </demos> <methods> <method name="close"> <return type="void"> </return> <description> - Closes the current connection, allowing reuse of this [code]HTTPClient[/code]. + Closes the current connection, allowing reuse of this [HTTPClient]. </description> </method> <method name="connect_to_host"> @@ -82,14 +80,14 @@ <return type="bool"> </return> <description> - If [code]true[/code], this [code]HTTPClient[/code] has a response available. + If [code]true[/code], this [HTTPClient] has a response available. </description> </method> <method name="is_response_chunked" qualifiers="const"> <return type="bool"> </return> <description> - If [code]true[/code], this [code]HTTPClient[/code] has a response that is chunked. + If [code]true[/code], this [HTTPClient] has a response that is chunked. </description> </method> <method name="poll"> diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index 7444503060..3dccc8e741 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -10,8 +10,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/networking/ssl_certificates.html</link> </tutorials> - <demos> - </demos> <methods> <method name="cancel_request"> <return type="void"> diff --git a/doc/classes/HeightMapShape.xml b/doc/classes/HeightMapShape.xml new file mode 100644 index 0000000000..5ffeda1c10 --- /dev/null +++ b/doc/classes/HeightMapShape.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="HeightMapShape" inherits="Shape" category="Core" version="3.2"> + <brief_description> + Height map shape for 3D physics (bullet only) + </brief_description> + <description> + Height map shape resource, which can be added to a [PhysicsBody] or [Area]. + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <members> + <member name="map_data" type="PoolRealArray" setter="set_map_data" getter="get_map_data"> + Height map data, pool array must be of [member map_width] * [member map_depth] size. + </member> + <member name="map_depth" type="int" setter="set_map_depth" getter="get_map_depth"> + Depth of the height map data. Changing this will resize the [member map_data]. + </member> + <member name="map_width" type="int" setter="set_map_width" getter="get_map_width"> + Width of the height map data. Changing this will resize the [member map_data]. + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/HingeJoint.xml b/doc/classes/HingeJoint.xml index cb99cca6bf..79df64f226 100644 --- a/doc/classes/HingeJoint.xml +++ b/doc/classes/HingeJoint.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/IP.xml b/doc/classes/IP.xml index 4c7365b0f6..3616a9ec50 100644 --- a/doc/classes/IP.xml +++ b/doc/classes/IP.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear_cache"> <return type="void"> diff --git a/doc/classes/IP_Unix.xml b/doc/classes/IP_Unix.xml index 68392c9bdf..4e09bf5dc0 100644 --- a/doc/classes/IP_Unix.xml +++ b/doc/classes/IP_Unix.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 8512d82c3d..2abc3af8c5 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -4,12 +4,10 @@ Image datatype. </brief_description> <description> - Native image datatype. Contains image data, which can be converted to a [Texture], and several functions to interact with it. The maximum width and height for an [code]Image[/code] are [constant MAX_WIDTH] and [constant MAX_HEIGHT]. + Native image datatype. Contains image data, which can be converted to a [Texture], and several functions to interact with it. The maximum width and height for an [Image] are [constant MAX_WIDTH] and [constant MAX_HEIGHT]. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="blend_rect"> <return type="void"> @@ -477,10 +475,10 @@ </members> <constants> <constant name="MAX_WIDTH" value="16384"> - The maximal width allowed for [code]Image[/code] resources. + The maximal width allowed for [Image] resources. </constant> <constant name="MAX_HEIGHT" value="16384"> - The maximal height allowed for [code]Image[/code] resources. + The maximal height allowed for [Image] resources. </constant> <constant name="FORMAT_L8" value="0" enum="Format"> </constant> diff --git a/doc/classes/ImageTexture.xml b/doc/classes/ImageTexture.xml index a335c1faf3..15ecd4ee6d 100644 --- a/doc/classes/ImageTexture.xml +++ b/doc/classes/ImageTexture.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="create"> <return type="void"> @@ -23,7 +21,7 @@ <argument index="3" name="flags" type="int" default="7"> </argument> <description> - Create a new [code]ImageTexture[/code] with [code]width[/code] and [code]height[/code]. + Create a new [ImageTexture] with [code]width[/code] and [code]height[/code]. [code]format[/code] is a value from [enum Image.Format], [code]flags[/code] is any combination of [enum Texture.Flags]. </description> </method> @@ -35,14 +33,14 @@ <argument index="1" name="flags" type="int" default="7"> </argument> <description> - Create a new [code]ImageTexture[/code] from an [Image] with [code]flags[/code] from [enum Texture.Flags]. An sRGB to linear color space conversion can take place, according to [enum Image.Format]. + Create a new [ImageTexture] from an [Image] with [code]flags[/code] from [enum Texture.Flags]. An sRGB to linear color space conversion can take place, according to [enum Image.Format]. </description> </method> <method name="get_format" qualifiers="const"> <return type="int" enum="Image.Format"> </return> <description> - Return the format of the [code]ImageTexture[/code], one of [enum Image.Format]. + Return the format of the [ImageTexture], one of [enum Image.Format]. </description> </method> <method name="load"> @@ -51,7 +49,7 @@ <argument index="0" name="path" type="String"> </argument> <description> - Load an [code]ImageTexture[/code] from a file path. + Load an [ImageTexture] from a file path. </description> </method> <method name="set_data"> @@ -60,7 +58,7 @@ <argument index="0" name="image" type="Image"> </argument> <description> - Set the [Image] of this [code]ImageTexture[/code]. + Set the [Image] of this [ImageTexture]. </description> </method> <method name="set_size_override"> @@ -69,7 +67,7 @@ <argument index="0" name="size" type="Vector2"> </argument> <description> - Resizes the [code]ImageTexture[/code] to the specified dimensions. + Resizes the [ImageTexture] to the specified dimensions. </description> </method> </methods> diff --git a/doc/classes/ImmediateGeometry.xml b/doc/classes/ImmediateGeometry.xml index 8ea3f41812..ca45fc5f9e 100644 --- a/doc/classes/ImmediateGeometry.xml +++ b/doc/classes/ImmediateGeometry.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_sphere"> <return type="void"> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 9af81eebf9..4aff5dd62b 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="action_press"> <return type="void"> @@ -68,6 +66,12 @@ Returns an [Array] containing the device IDs of all currently connected joypads. </description> </method> + <method name="get_current_cursor_shape" qualifiers="const"> + <return type="int" enum="Input.CursorShape"> + </return> + <description> + </description> + </method> <method name="get_gravity" qualifiers="const"> <return type="Vector3"> </return> @@ -90,7 +94,7 @@ <argument index="1" name="axis" type="int"> </argument> <description> - Returns the current value of the joypad axis at given index (see [code]JOY_*[/code] constants in [@GlobalScope]) + Returns the current value of the joypad axis at given index (see [enum JoystickList]). </description> </method> <method name="get_joy_axis_index_from_string"> @@ -108,7 +112,7 @@ <argument index="0" name="axis_index" type="int"> </argument> <description> - Receives a [code]JOY_AXIS_*[/code] Enum and returns its equivalent name as a string. + Receives a [enum JoystickList] axis and returns its equivalent name as a string. </description> </method> <method name="get_joy_button_index_from_string"> @@ -126,7 +130,7 @@ <argument index="0" name="button_index" type="int"> </argument> <description> - Receives a [code]JOY_BUTTON_*[/code] Enum and returns its equivalent name as a string. + Receives a joy button from [enum JoystickList] and returns its equivalent name as a string. </description> </method> <method name="get_joy_guid" qualifiers="const"> @@ -229,7 +233,7 @@ <argument index="1" name="button" type="int"> </argument> <description> - Returns [code]true[/code] if you are pressing the joypad button. (see [code]JOY_*[/code] constants in [@GlobalScope]) + Returns [code]true[/code] if you are pressing the joypad button (see [enum JoystickList]). </description> </method> <method name="is_joy_known"> @@ -238,7 +242,7 @@ <argument index="0" name="device" type="int"> </argument> <description> - Returns [code]true[/code] if the system knows the specified device. This means that it sets all button and axis indices exactly as defined in the [code]JOY_*[/code] constants (see [@GlobalScope]). Unknown joypads are not expected to match these constants, but you can still retrieve events from them. + Returns [code]true[/code] if the system knows the specified device. This means that it sets all button and axis indices exactly as defined in [enum JoystickList]. Unknown joypads are not expected to match these constants, but you can still retrieve events from them. </description> </method> <method name="is_key_pressed" qualifiers="const"> @@ -247,7 +251,7 @@ <argument index="0" name="scancode" type="int"> </argument> <description> - Returns [code]true[/code] if you are pressing the key. You can pass [code]KEY_*[/code], which are pre-defined constants listed in [@GlobalScope]. + Returns [code]true[/code] if you are pressing the key. You can pass a [enum KeyList] constant. </description> </method> <method name="is_mouse_button_pressed" qualifiers="const"> @@ -256,7 +260,7 @@ <argument index="0" name="button" type="int"> </argument> <description> - Returns [code]true[/code] if you are pressing the mouse button. You can pass [code]BUTTON_*[/code], which are pre-defined constants listed in [@GlobalScope]. + Returns [code]true[/code] if you are pressing the mouse button specified with [enum ButtonList]. </description> </method> <method name="joy_connection_changed"> diff --git a/doc/classes/InputDefault.xml b/doc/classes/InputDefault.xml index 471a43ff7e..e96b885d10 100644 --- a/doc/classes/InputDefault.xml +++ b/doc/classes/InputDefault.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/InputEvent.xml b/doc/classes/InputEvent.xml index 5c838a6b4c..934597d0c6 100644 --- a/doc/classes/InputEvent.xml +++ b/doc/classes/InputEvent.xml @@ -10,8 +10,6 @@ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link> </tutorials> - <demos> - </demos> <methods> <method name="accumulate"> <return type="bool"> @@ -67,7 +65,7 @@ <return type="bool"> </return> <description> - Returns [code]true[/code] if this input event's type is one of the [code]InputEvent[/code] constants. + Returns [code]true[/code] if this input event's type is one of the [InputEvent] constants. </description> </method> <method name="is_echo" qualifiers="const"> diff --git a/doc/classes/InputEventAction.xml b/doc/classes/InputEventAction.xml index a3eeea8756..25e9073ec7 100644 --- a/doc/classes/InputEventAction.xml +++ b/doc/classes/InputEventAction.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html#actions</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/InputEventGesture.xml b/doc/classes/InputEventGesture.xml index 9d6b44f140..b1abf04b51 100644 --- a/doc/classes/InputEventGesture.xml +++ b/doc/classes/InputEventGesture.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/InputEventJoypadButton.xml b/doc/classes/InputEventJoypadButton.xml index 9e66c0dc33..78cd55b79a 100644 --- a/doc/classes/InputEventJoypadButton.xml +++ b/doc/classes/InputEventJoypadButton.xml @@ -9,13 +9,11 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="button_index" type="int" setter="set_button_index" getter="get_button_index"> - Button identifier. One of the [code]JOY_BUTTON_*[/code] constants from [@GlobalScope]. + Button identifier. One of the [enum JoystickList] button constants. </member> <member name="pressed" type="bool" setter="set_pressed" getter="is_pressed"> If [code]true[/code], the button's state is pressed. If [code]false[/code], the button's state is released. diff --git a/doc/classes/InputEventJoypadMotion.xml b/doc/classes/InputEventJoypadMotion.xml index e6b1ab9b82..5330b2a6e0 100644 --- a/doc/classes/InputEventJoypadMotion.xml +++ b/doc/classes/InputEventJoypadMotion.xml @@ -4,18 +4,16 @@ Input event type for gamepad joysticks and other motions. For buttons see [code]InputEventJoypadButton[/code]. </brief_description> <description> - Stores information about joystick motions. One [code]InputEventJoypadMotion[/code] represents one axis at a time. + Stores information about joystick motions. One [InputEventJoypadMotion] represents one axis at a time. </description> <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="axis" type="int" setter="set_axis" getter="get_axis"> - Axis identifier. Use one of the [code]JOY_AXIS_*[/code] constants in [@GlobalScope]. + Axis identifier. Use one of the [enum JoystickList] axis constants. </member> <member name="axis_value" type="float" setter="set_axis_value" getter="get_axis_value"> Current position of the joystick on the given axis. The value ranges from [code]-1.0[/code] to [code]1.0[/code]. A value of [code]0[/code] means the axis is in its resting position. diff --git a/doc/classes/InputEventKey.xml b/doc/classes/InputEventKey.xml index c991c6a8cc..86a1362230 100644 --- a/doc/classes/InputEventKey.xml +++ b/doc/classes/InputEventKey.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_scancode_with_modifiers" qualifiers="const"> <return type="int"> @@ -28,7 +26,7 @@ If [code]true[/code], the key's state is pressed. If [code]false[/code], the key's state is released. </member> <member name="scancode" type="int" setter="set_scancode" getter="get_scancode"> - Key scancode, one of the [code]KEY_*[/code] constants in [@GlobalScope]. + Key scancode, one of the [enum KeyList] constants. </member> <member name="unicode" type="int" setter="set_unicode" getter="get_unicode"> Key unicode identifier when relevant. diff --git a/doc/classes/InputEventMIDI.xml b/doc/classes/InputEventMIDI.xml index 3f7cf0cf7c..5ded362bc7 100644 --- a/doc/classes/InputEventMIDI.xml +++ b/doc/classes/InputEventMIDI.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/InputEventMagnifyGesture.xml b/doc/classes/InputEventMagnifyGesture.xml index c4f66c4d45..8f01f6f486 100644 --- a/doc/classes/InputEventMagnifyGesture.xml +++ b/doc/classes/InputEventMagnifyGesture.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/InputEventMouse.xml b/doc/classes/InputEventMouse.xml index 7010b81e80..8ba272d470 100644 --- a/doc/classes/InputEventMouse.xml +++ b/doc/classes/InputEventMouse.xml @@ -9,13 +9,11 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="button_mask" type="int" setter="set_button_mask" getter="get_button_mask"> - Mouse button mask identifier, one of or a bitwise combination of the BUTTON_MASK_* constants in [@GlobalScope]. + Mouse button mask identifier, one of or a bitwise combination of the [enum ButtonList] button masks. </member> <member name="global_position" type="Vector2" setter="set_global_position" getter="get_global_position"> Mouse position relative to the current [Viewport] when used in [method Control._gui_input], otherwise is at 0,0. diff --git a/doc/classes/InputEventMouseButton.xml b/doc/classes/InputEventMouseButton.xml index e9ca396fc0..99e25d2c6f 100644 --- a/doc/classes/InputEventMouseButton.xml +++ b/doc/classes/InputEventMouseButton.xml @@ -9,13 +9,11 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/mouse_and_input_coordinates.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="button_index" type="int" setter="set_button_index" getter="get_button_index"> - Mouse button identifier, one of the BUTTON_* or BUTTON_WHEEL_* constants in [@GlobalScope]. + Mouse button identifier, one of the [enum ButtonList] button or button wheel constants. </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. diff --git a/doc/classes/InputEventMouseMotion.xml b/doc/classes/InputEventMouseMotion.xml index 0accf02aaf..3df47177dc 100644 --- a/doc/classes/InputEventMouseMotion.xml +++ b/doc/classes/InputEventMouseMotion.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/mouse_and_input_coordinates.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/InputEventPanGesture.xml b/doc/classes/InputEventPanGesture.xml index be5867e7b9..8a2fa74597 100644 --- a/doc/classes/InputEventPanGesture.xml +++ b/doc/classes/InputEventPanGesture.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/InputEventScreenDrag.xml b/doc/classes/InputEventScreenDrag.xml index 9d9e4b33ba..36e47ee51a 100644 --- a/doc/classes/InputEventScreenDrag.xml +++ b/doc/classes/InputEventScreenDrag.xml @@ -10,8 +10,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/InputEventScreenTouch.xml b/doc/classes/InputEventScreenTouch.xml index 6c5d37330f..8f537f12bc 100644 --- a/doc/classes/InputEventScreenTouch.xml +++ b/doc/classes/InputEventScreenTouch.xml @@ -10,8 +10,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/InputEventWithModifiers.xml b/doc/classes/InputEventWithModifiers.xml index 620927af08..de120446fb 100644 --- a/doc/classes/InputEventWithModifiers.xml +++ b/doc/classes/InputEventWithModifiers.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/InputMap.xml b/doc/classes/InputMap.xml index 3b6fc2b49b..bb29a09d22 100644 --- a/doc/classes/InputMap.xml +++ b/doc/classes/InputMap.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html#inputmap</link> </tutorials> - <demos> - </demos> <methods> <method name="action_add_event"> <return type="void"> @@ -72,7 +70,7 @@ <argument index="1" name="deadzone" type="float" default="0.5"> </argument> <description> - Adds an empty action to the [code]InputMap[/code] with a configurable [code]deadzone[/code]. + Adds an empty action to the [InputMap] with a configurable [code]deadzone[/code]. An [InputEvent] can then be added to this action with [method action_add_event]. </description> </method> @@ -82,7 +80,7 @@ <argument index="0" name="action" type="String"> </argument> <description> - Removes an action from the [code]InputMap[/code]. + Removes an action from the [InputMap]. </description> </method> <method name="event_is_action" qualifiers="const"> @@ -109,7 +107,7 @@ <return type="Array"> </return> <description> - Returns an array of all actions in the [code]InputMap[/code]. + Returns an array of all actions in the [InputMap]. </description> </method> <method name="has_action" qualifiers="const"> @@ -118,14 +116,14 @@ <argument index="0" name="action" type="String"> </argument> <description> - Returns [code]true[/code] if the [code]InputMap[/code] has a registered action with the given name. + Returns [code]true[/code] if the [InputMap] has a registered action with the given name. </description> </method> <method name="load_from_globals"> <return type="void"> </return> <description> - Clears all [InputEventAction] in the [code]InputMap[/code] and load it anew from [ProjectSettings]. + Clears all [InputEventAction] in the [InputMap] and load it anew from [ProjectSettings]. </description> </method> </methods> diff --git a/doc/classes/InstancePlaceholder.xml b/doc/classes/InstancePlaceholder.xml index 7dc5b4031a..c4752c735d 100644 --- a/doc/classes/InstancePlaceholder.xml +++ b/doc/classes/InstancePlaceholder.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="create_instance"> <return type="Node"> diff --git a/doc/classes/InterpolatedCamera.xml b/doc/classes/InterpolatedCamera.xml index 38b97be962..d124ea707a 100644 --- a/doc/classes/InterpolatedCamera.xml +++ b/doc/classes/InterpolatedCamera.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="set_target"> <return type="void"> diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml index b16a80a537..1cfd9e03f7 100644 --- a/doc/classes/ItemList.xml +++ b/doc/classes/ItemList.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_icon_item"> <return type="void"> diff --git a/doc/classes/JSON.xml b/doc/classes/JSON.xml index ee02733615..cb62a1e88e 100644 --- a/doc/classes/JSON.xml +++ b/doc/classes/JSON.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="parse"> <return type="JSONParseResult"> diff --git a/doc/classes/JSONParseResult.xml b/doc/classes/JSONParseResult.xml index b07150ee9e..c9caa5c5d4 100644 --- a/doc/classes/JSONParseResult.xml +++ b/doc/classes/JSONParseResult.xml @@ -4,12 +4,10 @@ Data class wrapper for decoded JSON. </brief_description> <description> - Returned by [method JSON.parse], [code]JSONParseResult[/code] contains decoded JSON or error information if JSON source not successfully parsed. You can check if JSON source was successfully parsed with [code]if json_result.error == OK[/code]. + Returned by [method JSON.parse], [JSONParseResult] contains decoded JSON or error information if JSON source not successfully parsed. You can check if JSON source was successfully parsed with [code]if json_result.error == OK[/code]. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/JavaScript.xml b/doc/classes/JavaScript.xml index 524de0c915..68b6831103 100644 --- a/doc/classes/JavaScript.xml +++ b/doc/classes/JavaScript.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/workflow/export/exporting_for_web.html#calling-javascript-from-script</link> </tutorials> - <demos> - </demos> <methods> <method name="eval"> <return type="Variant"> diff --git a/doc/classes/Joint.xml b/doc/classes/Joint.xml index 8ebe597d80..5cd59852df 100644 --- a/doc/classes/Joint.xml +++ b/doc/classes/Joint.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Joint2D.xml b/doc/classes/Joint2D.xml index 61df0ee698..b700bbd991 100644 --- a/doc/classes/Joint2D.xml +++ b/doc/classes/Joint2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/KinematicBody.xml b/doc/classes/KinematicBody.xml index 299d54ed7c..dae549125f 100644 --- a/doc/classes/KinematicBody.xml +++ b/doc/classes/KinematicBody.xml @@ -11,8 +11,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/kinematic_character_2d.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_floor_velocity" qualifiers="const"> <return type="Vector3"> @@ -90,7 +88,7 @@ <argument index="5" name="infinite_inertia" type="bool" default="true"> </argument> <description> - Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [code]KinematicBody[/code] or [RigidBody], it will also be affected by the motion of the other body. You can use this to make moving or rotating platforms, or to make nodes push other nodes. + Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [KinematicBody] or [RigidBody], it will also be affected by the motion of the other body. You can use this to make moving or rotating platforms, or to make nodes push other nodes. [code]linear_velocity[/code] is the velocity vector (typically meters per second). Unlike in [method move_and_collide], you should [i]not[/i] multiply it by [code]delta[/code] — the physics engine handles applying the velocity. [code]floor_normal[/code] is the up direction, used to determine what is a wall and what is a floor or a ceiling. If set to the default value of [code]Vector3(0, 0, 0)[/code], everything is considered a wall. This is useful for topdown games. If [code]stop_on_slope[/code] is true, body will not slide on slopes if you include gravity in [code]linear_velocity[/code]. diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml index ab3cd136f3..b41b5412d7 100644 --- a/doc/classes/KinematicBody2D.xml +++ b/doc/classes/KinematicBody2D.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_floor_velocity" qualifiers="const"> <return type="Vector2"> @@ -94,7 +92,7 @@ <argument index="5" name="infinite_inertia" type="bool" default="true"> </argument> <description> - Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [code]KinematicBody2D[/code] or [RigidBody2D], it will also be affected by the motion of the other body. You can use this to make moving or rotating platforms, or to make nodes push other nodes. + Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [KinematicBody2D] or [RigidBody2D], it will also be affected by the motion of the other body. You can use this to make moving or rotating platforms, or to make nodes push other nodes. [code]linear_velocity[/code] is the velocity vector in pixels per second. Unlike in [method move_and_collide], you should [i]not[/i] multiply it by [code]delta[/code] — the physics engine handles applying the velocity. [code]floor_normal[/code] is the up direction, used to determine what is a wall and what is a floor or a ceiling. If set to the default value of [code]Vector2(0, 0)[/code], everything is considered a wall. This is useful for topdown games. If [code]stop_on_slope[/code] is true, body will not slide on slopes when you include gravity in [code]linear_velocity[/code] and the body is standing still. diff --git a/doc/classes/KinematicCollision.xml b/doc/classes/KinematicCollision.xml index b2ae9445bb..4ce74df767 100644 --- a/doc/classes/KinematicCollision.xml +++ b/doc/classes/KinematicCollision.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/KinematicCollision2D.xml b/doc/classes/KinematicCollision2D.xml index 8a57dc0188..5a0303b7ba 100644 --- a/doc/classes/KinematicCollision2D.xml +++ b/doc/classes/KinematicCollision2D.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml index 0a640ac320..955fe6f822 100644 --- a/doc/classes/Label.xml +++ b/doc/classes/Label.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_line_count" qualifiers="const"> <return type="int"> @@ -37,7 +35,7 @@ <return type="int"> </return> <description> - Returns the number of lines shown. Useful if the [code]Label[/code] 's height cannot currently display all lines. + Returns the number of lines shown. Useful if the [Label] 's height cannot currently display all lines. </description> </method> </methods> diff --git a/doc/classes/LargeTexture.xml b/doc/classes/LargeTexture.xml index 263a622e2a..4611bc228e 100644 --- a/doc/classes/LargeTexture.xml +++ b/doc/classes/LargeTexture.xml @@ -5,12 +5,10 @@ </brief_description> <description> A Texture capable of storing many smaller Textures with offsets. - You can dynamically add pieces([Texture]) to this [code]LargeTexture[/code] using different offsets. + You can dynamically add pieces([Texture]) to this [LargeTexture] using different offsets. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_piece"> <return type="int"> @@ -20,21 +18,21 @@ <argument index="1" name="texture" type="Texture"> </argument> <description> - Add another [Texture] to this [code]LargeTexture[/code], starting on offset "ofs". + Add another [Texture] to this [LargeTexture], starting on offset "ofs". </description> </method> <method name="clear"> <return type="void"> </return> <description> - Clears the [code]LargeTexture[/code]. + Clears the [LargeTexture]. </description> </method> <method name="get_piece_count" qualifiers="const"> <return type="int"> </return> <description> - Returns the number of pieces currently in this [code]LargeTexture[/code]. + Returns the number of pieces currently in this [LargeTexture]. </description> </method> <method name="get_piece_offset" qualifiers="const"> @@ -83,7 +81,7 @@ <argument index="0" name="size" type="Vector2"> </argument> <description> - Sets the size of this [code]LargeTexture[/code]. + Sets the size of this [LargeTexture]. </description> </method> </methods> diff --git a/doc/classes/Light.xml b/doc/classes/Light.xml index 54f0e4336b..ecbc07ba72 100644 --- a/doc/classes/Light.xml +++ b/doc/classes/Light.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Light2D.xml b/doc/classes/Light2D.xml index 2626382a48..a487fdf2fe 100644 --- a/doc/classes/Light2D.xml +++ b/doc/classes/Light2D.xml @@ -9,8 +9,6 @@ <tutorials> <link>http://docs.godotengine.org/en/latest/tutorials/2d/2d_lights_and_shadows.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/LightOccluder2D.xml b/doc/classes/LightOccluder2D.xml index a5df0a5d11..a621937eb3 100644 --- a/doc/classes/LightOccluder2D.xml +++ b/doc/classes/LightOccluder2D.xml @@ -9,8 +9,6 @@ <tutorials> <link>http://docs.godotengine.org/en/latest/tutorials/2d/2d_lights_and_shadows.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Line2D.xml b/doc/classes/Line2D.xml index 66d54b7d9e..623b23807f 100644 --- a/doc/classes/Line2D.xml +++ b/doc/classes/Line2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_point"> <return type="void"> @@ -20,6 +18,13 @@ Add a point at the [code]position[/code]. Appends the point at the end of the line. </description> </method> + <method name="clear_points"> + <return type="void"> + </return> + <description> + Removes all points from the line. + </description> + </method> <method name="get_point_count" qualifiers="const"> <return type="int"> </return> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index 3d94a465dc..6ea097ec2f 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="append_at_cursor"> <return type="void"> @@ -24,7 +22,7 @@ <return type="void"> </return> <description> - Erases the [code]LineEdit[/code] text. + Erases the [LineEdit] text. </description> </method> <method name="deselect"> @@ -38,7 +36,7 @@ <return type="PopupMenu"> </return> <description> - Returns the [PopupMenu] of this [code]LineEdit[/code]. By default, this menu is displayed when right-clicking on the [code]LineEdit[/code]. + Returns the [PopupMenu] of this [LineEdit]. By default, this menu is displayed when right-clicking on the [LineEdit]. </description> </method> <method name="menu_option"> @@ -58,7 +56,7 @@ <argument index="1" name="to" type="int" default="-1"> </argument> <description> - Selects characters inside [code]LineEdit[/code] between [code]from[/code] and [code]to[/code]. By default [code]from[/code] is at the beginning and [code]to[/code] at the end. + Selects characters inside [LineEdit] between [code]from[/code] and [code]to[/code]. By default [code]from[/code] is at the beginning and [code]to[/code] at the end. [codeblock] text = "Welcome" select() # Welcome @@ -86,10 +84,10 @@ Duration (in seconds) of a caret's blinking cycle. </member> <member name="caret_position" type="int" setter="set_cursor_position" getter="get_cursor_position"> - The cursor's position inside the [code]LineEdit[/code]. When set, the text may scroll to accommodate it. + The cursor's position inside the [LineEdit]. When set, the text may scroll to accommodate it. </member> <member name="clear_button_enabled" type="bool" setter="set_clear_button_enabled" getter="is_clear_button_enabled"> - If [code]true[/code], the [code]LineEdit[/code] will show a clear button if [code]text[/code] is not empty. + If [code]true[/code], the [LineEdit] will show a clear button if [code]text[/code] is not empty. </member> <member name="context_menu_enabled" type="bool" setter="set_context_menu_enabled" getter="is_context_menu_enabled"> If [code]true[/code], the context menu will appear when right clicked. @@ -98,19 +96,19 @@ If [code]false[/code], existing text cannot be modified and new text cannot be added. </member> <member name="expand_to_text_length" type="bool" setter="set_expand_to_text_length" getter="get_expand_to_text_length"> - If [code]true[/code], the [code]LineEdit[/code] width will increase to stay longer than the [member text]. It will [b]not[/b] compress if the [member text] is shortened. + If [code]true[/code], the [LineEdit] width will increase to stay longer than the [member text]. It will [b]not[/b] compress if the [member text] is shortened. </member> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" enum="Control.FocusMode"> - Defines how the [code]LineEdit[/code] can grab focus (Keyboard and mouse, only keyboard, or none). See [code]enum FocusMode[/code] in [Control] for details. + Defines how the [LineEdit] can grab focus (Keyboard and mouse, only keyboard, or none). See [enum Control.FocusMode] in [Control] for details. </member> <member name="max_length" type="int" setter="set_max_length" getter="get_max_length"> - Maximum amount of characters that can be entered inside the [code]LineEdit[/code]. If [code]0[/code], there is no limit. + Maximum amount of characters that can be entered inside the [LineEdit]. If [code]0[/code], there is no limit. </member> <member name="placeholder_alpha" type="float" setter="set_placeholder_alpha" getter="get_placeholder_alpha"> Opacity of the [member placeholder_text]. From [code]0[/code] to [code]1[/code]. </member> <member name="placeholder_text" type="String" setter="set_placeholder" getter="get_placeholder"> - Text shown when the [code]LineEdit[/code] is empty. It is [b]not[/b] the [code]LineEdit[/code]'s default value (see [member text]). + Text shown when the [LineEdit] is empty. It is [b]not[/b] the [LineEdit]'s default value (see [member text]). </member> <member name="secret" type="bool" setter="set_secret" getter="is_secret"> If [code]true[/code], every character is replaced with the secret character (see [member secret_character]). @@ -119,7 +117,7 @@ The character to use to mask secret input (defaults to "*"). Only a single character can be used as the secret character. </member> <member name="text" type="String" setter="set_text" getter="get_text"> - String value of the [code]LineEdit[/code]. + String value of the [LineEdit]. </member> </members> <signals> @@ -134,22 +132,22 @@ <argument index="0" name="new_text" type="String"> </argument> <description> - Emitted when the user presses [code]KEY_ENTER[/code] on the [code]LineEdit[/code]. + Emitted when the user presses [constant KEY_ENTER] on the [LineEdit]. </description> </signal> </signals> <constants> <constant name="ALIGN_LEFT" value="0" enum="Align"> - Aligns the text on the left hand side of the [code]LineEdit[/code]. + Aligns the text on the left hand side of the [LineEdit]. </constant> <constant name="ALIGN_CENTER" value="1" enum="Align"> - Centers the text in the middle of the [code]LineEdit[/code]. + Centers the text in the middle of the [LineEdit]. </constant> <constant name="ALIGN_RIGHT" value="2" enum="Align"> - Aligns the text on the right hand side of the [code]LineEdit[/code]. + Aligns the text on the right hand side of the [LineEdit]. </constant> <constant name="ALIGN_FILL" value="3" enum="Align"> - Stretches whitespaces to fit the [code]LineEdit[/code]'s width. + Stretches whitespaces to fit the [LineEdit]'s width. </constant> <constant name="MENU_CUT" value="0" enum="MenuItems"> Cuts (copies and clears) the selected text. @@ -161,10 +159,10 @@ Pastes the clipboard text over the selected text (or at the cursor's position). </constant> <constant name="MENU_CLEAR" value="3" enum="MenuItems"> - Erases the whole [code]LineEdit[/code] text. + Erases the whole [LineEdit] text. </constant> <constant name="MENU_SELECT_ALL" value="4" enum="MenuItems"> - Selects the whole [code]LineEdit[/code] text. + Selects the whole [LineEdit] text. </constant> <constant name="MENU_UNDO" value="5" enum="MenuItems"> Undoes the previous action. diff --git a/doc/classes/LineShape2D.xml b/doc/classes/LineShape2D.xml index fb5925277e..94409cbd83 100644 --- a/doc/classes/LineShape2D.xml +++ b/doc/classes/LineShape2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/LinkButton.xml b/doc/classes/LinkButton.xml index 80729877cf..ef9f785f0f 100644 --- a/doc/classes/LinkButton.xml +++ b/doc/classes/LinkButton.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Listener.xml b/doc/classes/Listener.xml index 41daf99c78..130263a069 100644 --- a/doc/classes/Listener.xml +++ b/doc/classes/Listener.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear_current"> <return type="void"> diff --git a/doc/classes/MainLoop.xml b/doc/classes/MainLoop.xml index ce0aabe67d..324ee72b8e 100644 --- a/doc/classes/MainLoop.xml +++ b/doc/classes/MainLoop.xml @@ -4,12 +4,10 @@ Main loop is the abstract main loop base class. </brief_description> <description> - Main loop is the abstract main loop base class. All other main loop classes are derived from it. Upon application start, a [code]MainLoop[/code] has to be provided to OS, else the application will exit. This happens automatically (and a [SceneTree] is created), unless a main [Script] is supplied, which may or not create and return a [code]MainLoop[/code]. + Main loop is the abstract main loop base class. All other main loop classes are derived from it. Upon application start, a [MainLoop] has to be provided to OS, else the application will exit. This happens automatically (and a [SceneTree] is created), unless a main [Script] is supplied, which may or not create and return a [MainLoop]. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_drop_files" qualifiers="virtual"> <return type="void"> @@ -114,29 +112,29 @@ </method> </methods> <constants> - <constant name="NOTIFICATION_WM_MOUSE_ENTER" value="2"> + <constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002"> </constant> - <constant name="NOTIFICATION_WM_MOUSE_EXIT" value="3"> + <constant name="NOTIFICATION_WM_MOUSE_EXIT" value="1003"> </constant> - <constant name="NOTIFICATION_WM_FOCUS_IN" value="4"> + <constant name="NOTIFICATION_WM_FOCUS_IN" value="1004"> </constant> - <constant name="NOTIFICATION_WM_FOCUS_OUT" value="5"> + <constant name="NOTIFICATION_WM_FOCUS_OUT" value="1005"> </constant> - <constant name="NOTIFICATION_WM_QUIT_REQUEST" value="6"> + <constant name="NOTIFICATION_WM_QUIT_REQUEST" value="1006"> </constant> - <constant name="NOTIFICATION_WM_GO_BACK_REQUEST" value="7"> + <constant name="NOTIFICATION_WM_GO_BACK_REQUEST" value="1007"> </constant> - <constant name="NOTIFICATION_WM_UNFOCUS_REQUEST" value="8"> + <constant name="NOTIFICATION_WM_UNFOCUS_REQUEST" value="1008"> </constant> - <constant name="NOTIFICATION_OS_MEMORY_WARNING" value="9"> + <constant name="NOTIFICATION_OS_MEMORY_WARNING" value="1009"> </constant> - <constant name="NOTIFICATION_TRANSLATION_CHANGED" value="90"> + <constant name="NOTIFICATION_TRANSLATION_CHANGED" value="1010"> </constant> - <constant name="NOTIFICATION_WM_ABOUT" value="91"> + <constant name="NOTIFICATION_WM_ABOUT" value="1011"> </constant> - <constant name="NOTIFICATION_CRASH" value="92"> + <constant name="NOTIFICATION_CRASH" value="1012"> </constant> - <constant name="NOTIFICATION_OS_IME_UPDATE" value="93"> + <constant name="NOTIFICATION_OS_IME_UPDATE" value="1013"> </constant> </constants> </class> diff --git a/doc/classes/MarginContainer.xml b/doc/classes/MarginContainer.xml index 2224c83271..0993ed88ca 100644 --- a/doc/classes/MarginContainer.xml +++ b/doc/classes/MarginContainer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Marshalls.xml b/doc/classes/Marshalls.xml index 9210d0b0f5..0d07d8b649 100644 --- a/doc/classes/Marshalls.xml +++ b/doc/classes/Marshalls.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="base64_to_raw"> <return type="PoolByteArray"> diff --git a/doc/classes/Material.xml b/doc/classes/Material.xml index 976adfd331..848a43e6c4 100644 --- a/doc/classes/Material.xml +++ b/doc/classes/Material.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/MenuButton.xml b/doc/classes/MenuButton.xml index aa10946afe..dadc31b794 100644 --- a/doc/classes/MenuButton.xml +++ b/doc/classes/MenuButton.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_popup" qualifiers="const"> <return type="PopupMenu"> diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index 7752575312..ae035341f6 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="create_convex_shape" qualifiers="const"> <return type="Shape"> @@ -52,7 +50,7 @@ <return type="int"> </return> <description> - Return the amount of surfaces that the [code]Mesh[/code] holds. + Return the amount of surfaces that the [Mesh] holds. </description> </method> <method name="surface_get_arrays" qualifiers="const"> diff --git a/doc/classes/MeshDataTool.xml b/doc/classes/MeshDataTool.xml index cfe565b538..eeb251c8c7 100644 --- a/doc/classes/MeshDataTool.xml +++ b/doc/classes/MeshDataTool.xml @@ -20,8 +20,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> diff --git a/doc/classes/MeshInstance.xml b/doc/classes/MeshInstance.xml index 3a5df32563..c5c15e0ddc 100644 --- a/doc/classes/MeshInstance.xml +++ b/doc/classes/MeshInstance.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="create_convex_collision"> <return type="void"> diff --git a/doc/classes/MeshInstance2D.xml b/doc/classes/MeshInstance2D.xml index a39782acbf..d18ba96a95 100644 --- a/doc/classes/MeshInstance2D.xml +++ b/doc/classes/MeshInstance2D.xml @@ -7,8 +7,6 @@ <tutorials> <link>http://docs.godotengine.org/en/latest/tutorials/2d/2d_meshes.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/MeshLibrary.xml b/doc/classes/MeshLibrary.xml index 8fb10696f2..de4f6cdf72 100644 --- a/doc/classes/MeshLibrary.xml +++ b/doc/classes/MeshLibrary.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> @@ -68,6 +66,14 @@ <description> </description> </method> + <method name="get_item_navmesh_transform" qualifiers="const"> + <return type="Transform"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <description> + </description> + </method> <method name="get_item_preview" qualifiers="const"> <return type="Texture"> </return> @@ -132,6 +138,16 @@ <description> </description> </method> + <method name="set_item_navmesh_transform"> + <return type="void"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <argument index="1" name="navmesh" type="Transform"> + </argument> + <description> + </description> + </method> <method name="set_item_preview"> <return type="void"> </return> diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml index 536ad5e638..723ba7ea2b 100644 --- a/doc/classes/MultiMesh.xml +++ b/doc/classes/MultiMesh.xml @@ -12,8 +12,6 @@ <tutorials> <link>http://docs.godotengine.org/en/latest/tutorials/3d/vertex_animation/animating_thousands_of_fish.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_aabb" qualifiers="const"> <return type="AABB"> @@ -66,7 +64,7 @@ </argument> <description> Set the color of a specific instance. - For the color to take effect, ensure that [member color_format] is non-[code]null[/code] on the [code]MultiMesh[/code] and [member SpatialMaterial.vertex_color_use_as_albedo] is [code]true[/code] on the material. + For the color to take effect, ensure that [member color_format] is non-[code]null[/code] on the [MultiMesh] and [member SpatialMaterial.vertex_color_use_as_albedo] is [code]true[/code] on the material. </description> </method> <method name="set_instance_custom_data"> diff --git a/doc/classes/MultiMeshInstance.xml b/doc/classes/MultiMeshInstance.xml index 504394d868..70834d6085 100644 --- a/doc/classes/MultiMeshInstance.xml +++ b/doc/classes/MultiMeshInstance.xml @@ -4,20 +4,18 @@ Node that instances a [MultiMesh]. </brief_description> <description> - [code]MultiMeshInstance[/code] is a specialized node to instance [GeometryInstance]s based on a [MultiMesh] resource. + [MultiMeshInstance] is a specialized node to instance [GeometryInstance]s based on a [MultiMesh] resource. This is useful to optimize the rendering of a high amount of instances of a given mesh (for example tree in a forest or grass strands). </description> <tutorials> <link>http://docs.godotengine.org/en/latest/tutorials/3d/vertex_animation/animating_thousands_of_fish.html</link> <link>http://docs.godotengine.org/en/latest/tutorials/3d/using_multi_mesh_instance.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="multimesh" type="MultiMesh" setter="set_multimesh" getter="get_multimesh"> - The [MultiMesh] resource that will be used and shared among all instances of the [code]MultiMeshInstance[/code]. + The [MultiMesh] resource that will be used and shared among all instances of the [MultiMeshInstance]. </member> </members> <constants> diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml index 4e7f6c5b17..7b5794bbfc 100644 --- a/doc/classes/MultiplayerAPI.xml +++ b/doc/classes/MultiplayerAPI.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> diff --git a/doc/classes/Mutex.xml b/doc/classes/Mutex.xml index effcc53b6e..73c0a53f59 100644 --- a/doc/classes/Mutex.xml +++ b/doc/classes/Mutex.xml @@ -8,28 +8,26 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="lock"> <return type="void"> </return> <description> - Lock this [code]Mutex[/code], blocks until it is unlocked by the current owner. + Lock this [Mutex], blocks until it is unlocked by the current owner. </description> </method> <method name="try_lock"> <return type="int" enum="Error"> </return> <description> - Try locking this [code]Mutex[/code], does not block. Returns [code]OK[/code] on success, [code]ERR_BUSY[/code] otherwise. + Try locking this [Mutex], does not block. Returns [code]OK[/code] on success, [code]ERR_BUSY[/code] otherwise. </description> </method> <method name="unlock"> <return type="void"> </return> <description> - Unlock this [code]Mutex[/code], leaving it to other threads. + Unlock this [Mutex], leaving it to other threads. </description> </method> </methods> diff --git a/doc/classes/Navigation.xml b/doc/classes/Navigation.xml index baa3ca57fa..3690c1d682 100644 --- a/doc/classes/Navigation.xml +++ b/doc/classes/Navigation.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_closest_point"> <return type="Vector3"> diff --git a/doc/classes/Navigation2D.xml b/doc/classes/Navigation2D.xml index 6dd26ccf71..67b5d72182 100644 --- a/doc/classes/Navigation2D.xml +++ b/doc/classes/Navigation2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_closest_point"> <return type="Vector2"> diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml index 0e093992ba..2788eb053a 100644 --- a/doc/classes/NavigationMesh.xml +++ b/doc/classes/NavigationMesh.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_polygon"> <return type="void"> diff --git a/doc/classes/NavigationMeshInstance.xml b/doc/classes/NavigationMeshInstance.xml index 1e94292c48..3b11005e38 100644 --- a/doc/classes/NavigationMeshInstance.xml +++ b/doc/classes/NavigationMeshInstance.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml index 099ffbc429..2630ae1377 100644 --- a/doc/classes/NavigationPolygon.xml +++ b/doc/classes/NavigationPolygon.xml @@ -25,8 +25,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_outline"> <return type="void"> diff --git a/doc/classes/NavigationPolygonInstance.xml b/doc/classes/NavigationPolygonInstance.xml index ae4db14a37..e566e25336 100644 --- a/doc/classes/NavigationPolygonInstance.xml +++ b/doc/classes/NavigationPolygonInstance.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/NetworkedMultiplayerPeer.xml b/doc/classes/NetworkedMultiplayerPeer.xml index b335c9bb08..e5cadb6ffe 100644 --- a/doc/classes/NetworkedMultiplayerPeer.xml +++ b/doc/classes/NetworkedMultiplayerPeer.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_connection_status" qualifiers="const"> <return type="int" enum="NetworkedMultiplayerPeer.ConnectionStatus"> @@ -23,14 +21,14 @@ <return type="int"> </return> <description> - Returns the ID of the [code]NetworkedMultiplayerPeer[/code] who sent the most recent packet. + Returns the ID of the [NetworkedMultiplayerPeer] who sent the most recent packet. </description> </method> <method name="get_unique_id" qualifiers="const"> <return type="int"> </return> <description> - Returns the ID of this [code]NetworkedMultiplayerPeer[/code]. + Returns the ID of this [NetworkedMultiplayerPeer]. </description> </method> <method name="poll"> @@ -53,7 +51,7 @@ </methods> <members> <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections"> - If [code]true[/code], this [code]NetworkedMultiplayerPeer[/code] refuses new connections. Default value: [code]false[/code]. + If [code]true[/code], this [NetworkedMultiplayerPeer] refuses new connections. Default value: [code]false[/code]. </member> <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" enum="NetworkedMultiplayerPeer.TransferMode"> The manner in which to send packets to the [code]target_peer[/code]. See [enum TransferMode]. diff --git a/doc/classes/Nil.xml b/doc/classes/Nil.xml index 1e29da047b..a5c1ade6ed 100644 --- a/doc/classes/Nil.xml +++ b/doc/classes/Nil.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="Nil"> <argument index="0" name="from" type="PoolColorArray"> diff --git a/doc/classes/NinePatchRect.xml b/doc/classes/NinePatchRect.xml index c8574d9c3a..3095da14be 100644 --- a/doc/classes/NinePatchRect.xml +++ b/doc/classes/NinePatchRect.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index e515c0f39a..1fb2e7350f 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -19,8 +19,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/scenes_and_nodes.html</link> </tutorials> - <demos> - </demos> <methods> <method name="_enter_tree" qualifiers="virtual"> <return type="void"> @@ -42,6 +40,8 @@ <return type="String"> </return> <description> + The string returned from this method is displayed as a warning in the "Scene Dock" if the script that overrides it is a [code]tool[/code] script. + Returning an empty string produces no warning. </description> </method> <method name="_input" qualifiers="virtual"> @@ -146,6 +146,7 @@ </argument> <description> Adds the node to a group. Groups are helpers to name and organize a subset of nodes, for example "enemies" or "collectables". A node can be in any number of groups. Nodes can be assigned a group at any time, but will not be added until they are inside the scene tree (see [method is_inside_tree]). See notes in the description, and the group methods in [SceneTree]. + [code]persistent[/code] option is used when packing node to [PackedScene] and saving to file. Non-persistent groups aren't stored. </description> </method> <method name="can_process" qualifiers="const"> @@ -275,14 +276,14 @@ <argument index="0" name="path" type="NodePath"> </argument> <description> - Similar to [method get_node], but does not raise an error when [code]path[/code] does not point to a valid [code]Node[/code]. + Similar to [method get_node], but does not raise an error when [code]path[/code] does not point to a valid [Node]. </description> </method> <method name="get_parent" qualifiers="const"> <return type="Node"> </return> <description> - Returns the parent node of the current node, or an empty [code]Node[/code] if the node lacks a parent. + Returns the parent node of the current node, or an empty [Node] if the node lacks a parent. </description> </method> <method name="get_path" qualifiers="const"> @@ -885,15 +886,37 @@ <constant name="NOTIFICATION_PATH_CHANGED" value="23"> Notification received when the node's [NodePath] changed. </constant> - <constant name="NOTIFICATION_TRANSLATION_CHANGED" value="24"> - Notification received when translations may have changed. Can be triggered by the user changing the locale. Can be used to respond to language changes, for example to change the UI strings on the fly. Useful when working with the built-in translation support, like [method Object.tr]. - </constant> <constant name="NOTIFICATION_INTERNAL_PROCESS" value="25"> Notification received every frame when the internal process flag is set (see [method set_process_internal]). </constant> <constant name="NOTIFICATION_INTERNAL_PHYSICS_PROCESS" value="26"> Notification received every frame when the internal physics process flag is set (see [method set_physics_process_internal]). </constant> + <constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002"> + </constant> + <constant name="NOTIFICATION_WM_MOUSE_EXIT" value="1003"> + </constant> + <constant name="NOTIFICATION_WM_FOCUS_IN" value="1004"> + </constant> + <constant name="NOTIFICATION_WM_FOCUS_OUT" value="1005"> + </constant> + <constant name="NOTIFICATION_WM_QUIT_REQUEST" value="1006"> + </constant> + <constant name="NOTIFICATION_WM_GO_BACK_REQUEST" value="1007"> + </constant> + <constant name="NOTIFICATION_WM_UNFOCUS_REQUEST" value="1008"> + </constant> + <constant name="NOTIFICATION_OS_MEMORY_WARNING" value="1009"> + </constant> + <constant name="NOTIFICATION_TRANSLATION_CHANGED" value="1010"> + Notification received when translations may have changed. Can be triggered by the user changing the locale. Can be used to respond to language changes, for example to change the UI strings on the fly. Useful when working with the built-in translation support, like [method Object.tr]. + </constant> + <constant name="NOTIFICATION_WM_ABOUT" value="1011"> + </constant> + <constant name="NOTIFICATION_CRASH" value="1012"> + </constant> + <constant name="NOTIFICATION_OS_IME_UPDATE" value="1013"> + </constant> <constant name="PAUSE_MODE_INHERIT" value="0" enum="PauseMode"> Inherits pause mode from the node's parent. For the root node, it is equivalent to PAUSE_MODE_STOP. Default. </constant> diff --git a/doc/classes/Node2D.xml b/doc/classes/Node2D.xml index e6bb35cf0f..14299cdcba 100644 --- a/doc/classes/Node2D.xml +++ b/doc/classes/Node2D.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/2d/custom_drawing_in_2d.html</link> </tutorials> - <demos> - </demos> <methods> <method name="apply_scale"> <return type="void"> diff --git a/doc/classes/NodePath.xml b/doc/classes/NodePath.xml index 41f0dee7f6..4c682f1266 100644 --- a/doc/classes/NodePath.xml +++ b/doc/classes/NodePath.xml @@ -5,13 +5,11 @@ </brief_description> <description> A pre-parsed relative or absolute path in a scene tree, for use with [method Node.get_node] and similar functions. It can reference a node, a resource within a node, or a property of a node or resource. For instance, [code]"Path2D/PathFollow2D/Sprite:texture:size"[/code] would refer to the size property of the texture resource on the node named "Sprite" which is a child of the other named nodes in the path. Note that if you want to get a resource, you must end the path with a colon, otherwise the last element will be used as a property name. - You will usually just pass a string to [method Node.get_node] and it will be automatically converted, but you may occasionally want to parse a path ahead of time with [code]NodePath[/code] or the literal syntax [code]@"path"[/code]. Exporting a [code]NodePath[/code] variable will give you a node selection widget in the properties panel of the editor, which can often be useful. - A [code]NodePath[/code] is made up of a list of node names, a list of "subnode" (resource) names, and the name of a property in the final node or resource. + You will usually just pass a string to [method Node.get_node] and it will be automatically converted, but you may occasionally want to parse a path ahead of time with [NodePath] or the literal syntax [code]@"path"[/code]. Exporting a [NodePath] variable will give you a node selection widget in the properties panel of the editor, which can often be useful. + A [NodePath] is made up of a list of node names, a list of "subnode" (resource) names, and the name of a property in the final node or resource. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="NodePath"> <return type="NodePath"> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 0cd2e2b708..c9c83bc0e0 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="alert"> <return type="void"> diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index f76038fd69..4724cb9270 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -13,8 +13,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_get" qualifiers="virtual"> <return type="Variant"> diff --git a/doc/classes/OccluderPolygon2D.xml b/doc/classes/OccluderPolygon2D.xml index 6d8d9d9a70..f1d7d55cb9 100644 --- a/doc/classes/OccluderPolygon2D.xml +++ b/doc/classes/OccluderPolygon2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/OmniLight.xml b/doc/classes/OmniLight.xml index 649c4c08f0..408018fdcb 100644 --- a/doc/classes/OmniLight.xml +++ b/doc/classes/OmniLight.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml index 4ca69e9802..51d2cbc867 100644 --- a/doc/classes/OptionButton.xml +++ b/doc/classes/OptionButton.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_icon_item"> <return type="void"> @@ -46,7 +44,7 @@ <return type="void"> </return> <description> - Clear all the items in the [code]OptionButton[/code]. + Clear all the items in the [OptionButton]. </description> </method> <method name="get_item_count" qualifiers="const"> diff --git a/doc/classes/PCKPacker.xml b/doc/classes/PCKPacker.xml index 83610c82f0..f3091a3e3f 100644 --- a/doc/classes/PCKPacker.xml +++ b/doc/classes/PCKPacker.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_file"> <return type="int" enum="Error"> diff --git a/doc/classes/PHashTranslation.xml b/doc/classes/PHashTranslation.xml index 2dcbdc80e1..7c771b6b04 100644 --- a/doc/classes/PHashTranslation.xml +++ b/doc/classes/PHashTranslation.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="generate"> <return type="void"> diff --git a/doc/classes/PackedDataContainer.xml b/doc/classes/PackedDataContainer.xml index 087674a64a..f230953aac 100644 --- a/doc/classes/PackedDataContainer.xml +++ b/doc/classes/PackedDataContainer.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="pack"> <return type="int" enum="Error"> diff --git a/doc/classes/PackedDataContainerRef.xml b/doc/classes/PackedDataContainerRef.xml index a9545d40e7..8623ff7843 100644 --- a/doc/classes/PackedDataContainerRef.xml +++ b/doc/classes/PackedDataContainerRef.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="size" qualifiers="const"> <return type="int"> diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml index 531fd4006c..777e5221ed 100644 --- a/doc/classes/PackedScene.xml +++ b/doc/classes/PackedScene.xml @@ -29,8 +29,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="can_instance" qualifiers="const"> <return type="bool"> diff --git a/doc/classes/PacketPeer.xml b/doc/classes/PacketPeer.xml index 3cc7ed1f84..5a54c7ba1c 100644 --- a/doc/classes/PacketPeer.xml +++ b/doc/classes/PacketPeer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_available_packet_count" qualifiers="const"> <return type="int"> diff --git a/doc/classes/PacketPeerStream.xml b/doc/classes/PacketPeerStream.xml index 5fb5aea46e..20e5e570ac 100644 --- a/doc/classes/PacketPeerStream.xml +++ b/doc/classes/PacketPeerStream.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/PacketPeerUDP.xml b/doc/classes/PacketPeerUDP.xml index 90e6367014..5892eb17b7 100644 --- a/doc/classes/PacketPeerUDP.xml +++ b/doc/classes/PacketPeerUDP.xml @@ -8,14 +8,12 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="close"> <return type="void"> </return> <description> - Close the UDP socket the [code]PacketPeerUDP[/code] is currently listening on. + Close the UDP socket the [PacketPeerUDP] is currently listening on. </description> </method> <method name="get_packet_ip" qualifiers="const"> @@ -36,7 +34,7 @@ <return type="bool"> </return> <description> - Return whether this [code]PacketPeerUDP[/code] is listening. + Return whether this [PacketPeerUDP] is listening. </description> </method> <method name="listen"> @@ -49,7 +47,7 @@ <argument index="2" name="recv_buf_size" type="int" default="65536"> </argument> <description> - Make this [code]PacketPeerUDP[/code] listen on the "port" binding to "bind_address" with a buffer size "recv_buf_size". + Make this [PacketPeerUDP] listen on the "port" binding to "bind_address" with a buffer size "recv_buf_size". If "bind_address" is set as "*" (default), the peer will listen on all available addresses (both IPv4 and IPv6). If "bind_address" is set as "0.0.0.0" (for IPv4) or "::" (for IPv6), the peer will listen on all available addresses matching that IP type. If "bind_address" is set to any valid address (e.g. "192.168.1.101", "::1", etc), the peer will only listen on the interface with that addresses (or fail if no interface with the given address exists). diff --git a/doc/classes/Panel.xml b/doc/classes/Panel.xml index 483f31dbc2..1cee2b5386 100644 --- a/doc/classes/Panel.xml +++ b/doc/classes/Panel.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/PanelContainer.xml b/doc/classes/PanelContainer.xml index 681777119c..64fae6d95c 100644 --- a/doc/classes/PanelContainer.xml +++ b/doc/classes/PanelContainer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/PanoramaSky.xml b/doc/classes/PanoramaSky.xml index 664b867c13..30d15c030a 100644 --- a/doc/classes/PanoramaSky.xml +++ b/doc/classes/PanoramaSky.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ParallaxBackground.xml b/doc/classes/ParallaxBackground.xml index 911d6377d5..6c9d5de945 100644 --- a/doc/classes/ParallaxBackground.xml +++ b/doc/classes/ParallaxBackground.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ParallaxLayer.xml b/doc/classes/ParallaxLayer.xml index 5ca3c9b4ae..993f38c31a 100644 --- a/doc/classes/ParallaxLayer.xml +++ b/doc/classes/ParallaxLayer.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Particles.xml b/doc/classes/Particles.xml index 1c5326c02b..0023fce97d 100644 --- a/doc/classes/Particles.xml +++ b/doc/classes/Particles.xml @@ -4,14 +4,12 @@ 3D particle emitter. </brief_description> <description> - 3D particle node used to create a variety of particle systems and effects. [code]Particles[/code] features an emitter that generates some number of particles at a given rate. + 3D particle node used to create a variety of particle systems and effects. [Particles] features an emitter that generates some number of particles at a given rate. Use the [code]process_material[/code] property to add a [ParticlesMaterial] to configure particle appearance and behavior. Alternatively, you can add a [ShaderMaterial] which will be applied to all particles. </description> <tutorials> <link>http://docs.godotengine.org/en/latest/tutorials/3d/vertex_animation/controlling_thousands_of_fish.html</link> </tutorials> - <demos> - </demos> <methods> <method name="capture_aabb" qualifiers="const"> <return type="AABB"> diff --git a/doc/classes/Particles2D.xml b/doc/classes/Particles2D.xml index c7b99b1363..de4877b639 100644 --- a/doc/classes/Particles2D.xml +++ b/doc/classes/Particles2D.xml @@ -4,14 +4,12 @@ 2D particle emitter. </brief_description> <description> - 2D particle node used to create a variety of particle systems and effects. [code]Particles2D[/code] features an emitter that generates some number of particles at a given rate. + 2D particle node used to create a variety of particle systems and effects. [Particles2D] features an emitter that generates some number of particles at a given rate. Use the [code]process_material[/code] property to add a [ParticlesMaterial] to configure particle appearance and behavior. Alternatively, you can add a [ShaderMaterial] which will be applied to all particles. </description> <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/2d/particle_systems_2d.html</link> </tutorials> - <demos> - </demos> <methods> <method name="capture_rect" qualifiers="const"> <return type="Rect2"> diff --git a/doc/classes/ParticlesMaterial.xml b/doc/classes/ParticlesMaterial.xml index ab6eb5e656..6ae5afeac5 100644 --- a/doc/classes/ParticlesMaterial.xml +++ b/doc/classes/ParticlesMaterial.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Path.xml b/doc/classes/Path.xml index f490b2c36e..1b553dbc02 100644 --- a/doc/classes/Path.xml +++ b/doc/classes/Path.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Path2D.xml b/doc/classes/Path2D.xml index 4f36fbf275..c77e00a28e 100644 --- a/doc/classes/Path2D.xml +++ b/doc/classes/Path2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/PathFollow.xml b/doc/classes/PathFollow.xml index 651b2c1ab2..ba27ca04eb 100644 --- a/doc/classes/PathFollow.xml +++ b/doc/classes/PathFollow.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/PathFollow2D.xml b/doc/classes/PathFollow2D.xml index dc89a03610..6f38b9f08d 100644 --- a/doc/classes/PathFollow2D.xml +++ b/doc/classes/PathFollow2D.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml index 4ee43d5760..881e4a9d82 100644 --- a/doc/classes/Performance.xml +++ b/doc/classes/Performance.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_monitor" qualifiers="const"> <return type="float"> diff --git a/doc/classes/PhysicalBone.xml b/doc/classes/PhysicalBone.xml index 29e6991a21..bf379afab8 100644 --- a/doc/classes/PhysicalBone.xml +++ b/doc/classes/PhysicalBone.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_bone_id" qualifiers="const"> <return type="int"> diff --git a/doc/classes/Physics2DDirectBodyState.xml b/doc/classes/Physics2DDirectBodyState.xml index e2d5886d87..1fd578ecd8 100644 --- a/doc/classes/Physics2DDirectBodyState.xml +++ b/doc/classes/Physics2DDirectBodyState.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_central_force"> <return type="void"> diff --git a/doc/classes/Physics2DDirectBodyStateSW.xml b/doc/classes/Physics2DDirectBodyStateSW.xml index 6f388443e9..8aeea560c3 100644 --- a/doc/classes/Physics2DDirectBodyStateSW.xml +++ b/doc/classes/Physics2DDirectBodyStateSW.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Physics2DDirectSpaceState.xml b/doc/classes/Physics2DDirectSpaceState.xml index 6aa14bd087..040d7d05c3 100644 --- a/doc/classes/Physics2DDirectSpaceState.xml +++ b/doc/classes/Physics2DDirectSpaceState.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> - <demos> - </demos> <methods> <method name="cast_motion"> <return type="Array"> diff --git a/doc/classes/Physics2DServer.xml b/doc/classes/Physics2DServer.xml index bea2c88a3e..641e1898ff 100644 --- a/doc/classes/Physics2DServer.xml +++ b/doc/classes/Physics2DServer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="area_add_shape"> <return type="void"> diff --git a/doc/classes/Physics2DServerSW.xml b/doc/classes/Physics2DServerSW.xml index 23e708af61..e478a33e2c 100644 --- a/doc/classes/Physics2DServerSW.xml +++ b/doc/classes/Physics2DServerSW.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Physics2DShapeQueryParameters.xml b/doc/classes/Physics2DShapeQueryParameters.xml index 9375bdbbf8..43d9b71c94 100644 --- a/doc/classes/Physics2DShapeQueryParameters.xml +++ b/doc/classes/Physics2DShapeQueryParameters.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="set_shape"> <return type="void"> diff --git a/doc/classes/Physics2DShapeQueryResult.xml b/doc/classes/Physics2DShapeQueryResult.xml index 89f45437ab..dd38b488f5 100644 --- a/doc/classes/Physics2DShapeQueryResult.xml +++ b/doc/classes/Physics2DShapeQueryResult.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_result_count" qualifiers="const"> <return type="int"> diff --git a/doc/classes/Physics2DTestMotionResult.xml b/doc/classes/Physics2DTestMotionResult.xml index 9fe3564e70..a0f8a1ca4c 100644 --- a/doc/classes/Physics2DTestMotionResult.xml +++ b/doc/classes/Physics2DTestMotionResult.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/PhysicsBody.xml b/doc/classes/PhysicsBody.xml index 827a3e01dc..4d94c57ad1 100644 --- a/doc/classes/PhysicsBody.xml +++ b/doc/classes/PhysicsBody.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_collision_exception_with"> <return type="void"> diff --git a/doc/classes/PhysicsBody2D.xml b/doc/classes/PhysicsBody2D.xml index 1735e572fb..faa992ef7a 100644 --- a/doc/classes/PhysicsBody2D.xml +++ b/doc/classes/PhysicsBody2D.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_collision_exception_with"> <return type="void"> diff --git a/doc/classes/PhysicsDirectBodyState.xml b/doc/classes/PhysicsDirectBodyState.xml index 89fb785659..e556e8037a 100644 --- a/doc/classes/PhysicsDirectBodyState.xml +++ b/doc/classes/PhysicsDirectBodyState.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_central_force"> <return type="void"> diff --git a/doc/classes/PhysicsDirectSpaceState.xml b/doc/classes/PhysicsDirectSpaceState.xml index a029b7d37b..1a3324bf0e 100644 --- a/doc/classes/PhysicsDirectSpaceState.xml +++ b/doc/classes/PhysicsDirectSpaceState.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> - <demos> - </demos> <methods> <method name="cast_motion"> <return type="Array"> diff --git a/doc/classes/PhysicsMaterial.xml b/doc/classes/PhysicsMaterial.xml index 80ae115418..b507b4f9f5 100644 --- a/doc/classes/PhysicsMaterial.xml +++ b/doc/classes/PhysicsMaterial.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml index d8238a7af7..25869a853c 100644 --- a/doc/classes/PhysicsServer.xml +++ b/doc/classes/PhysicsServer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="area_add_shape"> <return type="void"> diff --git a/doc/classes/PhysicsShapeQueryParameters.xml b/doc/classes/PhysicsShapeQueryParameters.xml index 75e0cdfb73..c1f8b48d73 100644 --- a/doc/classes/PhysicsShapeQueryParameters.xml +++ b/doc/classes/PhysicsShapeQueryParameters.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="set_shape"> <return type="void"> diff --git a/doc/classes/PhysicsShapeQueryResult.xml b/doc/classes/PhysicsShapeQueryResult.xml index 41c04970d7..92a4393979 100644 --- a/doc/classes/PhysicsShapeQueryResult.xml +++ b/doc/classes/PhysicsShapeQueryResult.xml @@ -7,8 +7,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_result_count" qualifiers="const"> <return type="int"> diff --git a/doc/classes/PinJoint.xml b/doc/classes/PinJoint.xml index 3c705a9e01..4546377171 100644 --- a/doc/classes/PinJoint.xml +++ b/doc/classes/PinJoint.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/PinJoint2D.xml b/doc/classes/PinJoint2D.xml index e7a89654d9..4a11ce30e1 100644 --- a/doc/classes/PinJoint2D.xml +++ b/doc/classes/PinJoint2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Plane.xml b/doc/classes/Plane.xml index 4a7a099800..df76b45f9f 100644 --- a/doc/classes/Plane.xml +++ b/doc/classes/Plane.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="Plane"> <return type="Plane"> diff --git a/doc/classes/PlaneMesh.xml b/doc/classes/PlaneMesh.xml index a5ad82f777..a8927f8479 100644 --- a/doc/classes/PlaneMesh.xml +++ b/doc/classes/PlaneMesh.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/PlaneShape.xml b/doc/classes/PlaneShape.xml index 6468c86bbf..83d8b09280 100644 --- a/doc/classes/PlaneShape.xml +++ b/doc/classes/PlaneShape.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Polygon2D.xml b/doc/classes/Polygon2D.xml index 515a0fc017..7098f5f071 100644 --- a/doc/classes/Polygon2D.xml +++ b/doc/classes/Polygon2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_bone"> <return type="void"> diff --git a/doc/classes/PolygonPathFinder.xml b/doc/classes/PolygonPathFinder.xml index 504acf2125..7286857b51 100644 --- a/doc/classes/PolygonPathFinder.xml +++ b/doc/classes/PolygonPathFinder.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="find_path"> <return type="PoolVector2Array"> diff --git a/doc/classes/PoolByteArray.xml b/doc/classes/PoolByteArray.xml index 26acb76b77..67419377e8 100644 --- a/doc/classes/PoolByteArray.xml +++ b/doc/classes/PoolByteArray.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="PoolByteArray"> <return type="PoolByteArray"> @@ -17,7 +15,7 @@ <argument index="0" name="from" type="Array"> </argument> <description> - Construct a new [code]PoolByteArray[/code]. Optionally, you can pass in a generic [Array] that will be converted. + Construct a new [PoolByteArray]. Optionally, you can pass in a generic [Array] that will be converted. </description> </method> <method name="append"> @@ -31,7 +29,7 @@ <argument index="0" name="array" type="PoolByteArray"> </argument> <description> - Append a [code]PoolByteArray[/code] at the end of this array. + Append a [PoolByteArray] at the end of this array. </description> </method> <method name="compress"> @@ -40,7 +38,7 @@ <argument index="0" name="compression_mode" type="int" default="0"> </argument> <description> - Returns a new [code]PoolByteArray[/code] with the data compressed. Set the compression mode using one of [enum File.CompressionMode]'s constants. + Returns a new [PoolByteArray] with the data compressed. Set the compression mode using one of [enum File.CompressionMode]'s constants. </description> </method> <method name="decompress"> @@ -51,7 +49,7 @@ <argument index="1" name="compression_mode" type="int" default="0"> </argument> <description> - Returns a new [code]PoolByteArray[/code] with the data decompressed. Set [code]buffer_size[/code] to the size of the uncompressed data. Set the compression mode using one of [enum File.CompressionMode]'s constants. + Returns a new [PoolByteArray] with the data decompressed. Set [code]buffer_size[/code] to the size of the uncompressed data. Set the compression mode using one of [enum File.CompressionMode]'s constants. </description> </method> <method name="get_string_from_ascii"> @@ -129,7 +127,7 @@ <argument index="1" name="to" type="int"> </argument> <description> - Returns the slice of the [code]PoolByteArray[/code] between indices (inclusive) as a new [code]PoolByteArray[/code]. Any negative index is considered to be from the end of the array. + Returns the slice of the [PoolByteArray] between indices (inclusive) as a new [PoolByteArray]. Any negative index is considered to be from the end of the array. </description> </method> </methods> diff --git a/doc/classes/PoolColorArray.xml b/doc/classes/PoolColorArray.xml index 890aef4f41..d16029a8d7 100644 --- a/doc/classes/PoolColorArray.xml +++ b/doc/classes/PoolColorArray.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="PoolColorArray"> <return type="PoolColorArray"> @@ -17,7 +15,7 @@ <argument index="0" name="from" type="Array"> </argument> <description> - Construct a new [code]PoolColorArray[/code]. Optionally, you can pass in a generic [Array] that will be converted. + Construct a new [PoolColorArray]. Optionally, you can pass in a generic [Array] that will be converted. </description> </method> <method name="append"> @@ -31,7 +29,7 @@ <argument index="0" name="array" type="PoolColorArray"> </argument> <description> - Append a [code]PoolColorArray[/code] at the end of this array. + Append a [PoolColorArray] at the end of this array. </description> </method> <method name="insert"> diff --git a/doc/classes/PoolIntArray.xml b/doc/classes/PoolIntArray.xml index c88fc1bac5..83d73c8a24 100644 --- a/doc/classes/PoolIntArray.xml +++ b/doc/classes/PoolIntArray.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="PoolIntArray"> <return type="PoolIntArray"> @@ -17,7 +15,7 @@ <argument index="0" name="from" type="Array"> </argument> <description> - Construct a new [code]PoolIntArray[/code]. Optionally, you can pass in a generic [Array] that will be converted. + Construct a new [PoolIntArray]. Optionally, you can pass in a generic [Array] that will be converted. </description> </method> <method name="append"> @@ -31,7 +29,7 @@ <argument index="0" name="array" type="PoolIntArray"> </argument> <description> - Append a [code]PoolIntArray[/code] at the end of this array. + Append a [PoolIntArray] at the end of this array. </description> </method> <method name="insert"> diff --git a/doc/classes/PoolRealArray.xml b/doc/classes/PoolRealArray.xml index ba790164f5..e1da99519c 100644 --- a/doc/classes/PoolRealArray.xml +++ b/doc/classes/PoolRealArray.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="PoolRealArray"> <return type="PoolRealArray"> @@ -17,7 +15,7 @@ <argument index="0" name="from" type="Array"> </argument> <description> - Construct a new [code]PoolRealArray[/code]. Optionally, you can pass in a generic [Array] that will be converted. + Construct a new [PoolRealArray]. Optionally, you can pass in a generic [Array] that will be converted. </description> </method> <method name="append"> @@ -31,7 +29,7 @@ <argument index="0" name="array" type="PoolRealArray"> </argument> <description> - Append a [code]PoolRealArray[/code] at the end of this array. + Append a [PoolRealArray] at the end of this array. </description> </method> <method name="insert"> diff --git a/doc/classes/PoolStringArray.xml b/doc/classes/PoolStringArray.xml index 764d139f29..6ef4222996 100644 --- a/doc/classes/PoolStringArray.xml +++ b/doc/classes/PoolStringArray.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="PoolStringArray"> <return type="PoolStringArray"> @@ -17,7 +15,7 @@ <argument index="0" name="from" type="Array"> </argument> <description> - Construct a new [code]PoolStringArray[/code]. Optionally, you can pass in a generic [Array] that will be converted. + Construct a new [PoolStringArray]. Optionally, you can pass in a generic [Array] that will be converted. </description> </method> <method name="append"> @@ -31,7 +29,7 @@ <argument index="0" name="array" type="PoolStringArray"> </argument> <description> - Append a [code]PoolStringArray[/code] at the end of this array. + Append a [PoolStringArray] at the end of this array. </description> </method> <method name="insert"> diff --git a/doc/classes/PoolVector2Array.xml b/doc/classes/PoolVector2Array.xml index d51d9032ef..50ded06b90 100644 --- a/doc/classes/PoolVector2Array.xml +++ b/doc/classes/PoolVector2Array.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="PoolVector2Array"> <return type="PoolVector2Array"> @@ -17,7 +15,7 @@ <argument index="0" name="from" type="Array"> </argument> <description> - Construct a new [code]PoolVector2Array[/code]. Optionally, you can pass in a generic [Array] that will be converted. + Construct a new [PoolVector2Array]. Optionally, you can pass in a generic [Array] that will be converted. </description> </method> <method name="append"> @@ -31,7 +29,7 @@ <argument index="0" name="array" type="PoolVector2Array"> </argument> <description> - Append a [code]PoolVector2Array[/code] at the end of this array. + Append a [PoolVector2Array] at the end of this array. </description> </method> <method name="insert"> diff --git a/doc/classes/PoolVector3Array.xml b/doc/classes/PoolVector3Array.xml index ed0efdfc62..69b4e54692 100644 --- a/doc/classes/PoolVector3Array.xml +++ b/doc/classes/PoolVector3Array.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="PoolVector3Array"> <return type="PoolVector3Array"> @@ -17,7 +15,7 @@ <argument index="0" name="from" type="Array"> </argument> <description> - Construct a new [code]PoolVector3Array[/code]. Optionally, you can pass in a generic [Array] that will be converted. + Construct a new [PoolVector3Array]. Optionally, you can pass in a generic [Array] that will be converted. </description> </method> <method name="append"> @@ -31,7 +29,7 @@ <argument index="0" name="array" type="PoolVector3Array"> </argument> <description> - Append a [code]PoolVector3Array[/code] at the end of this array. + Append a [PoolVector3Array] at the end of this array. </description> </method> <method name="insert"> diff --git a/doc/classes/Popup.xml b/doc/classes/Popup.xml index 095993a56e..c3256f2f5b 100644 --- a/doc/classes/Popup.xml +++ b/doc/classes/Popup.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="popup"> <return type="void"> diff --git a/doc/classes/PopupDialog.xml b/doc/classes/PopupDialog.xml index 0aebfb7712..b510208ae2 100644 --- a/doc/classes/PopupDialog.xml +++ b/doc/classes/PopupDialog.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index 5b0ea5ef59..4924175b6e 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_check_item"> <return type="void"> diff --git a/doc/classes/PopupPanel.xml b/doc/classes/PopupPanel.xml index 58b9f3fcf4..bed4d97112 100644 --- a/doc/classes/PopupPanel.xml +++ b/doc/classes/PopupPanel.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Position2D.xml b/doc/classes/Position2D.xml index 9bbfbdbc15..034e35d9d7 100644 --- a/doc/classes/Position2D.xml +++ b/doc/classes/Position2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Position3D.xml b/doc/classes/Position3D.xml index d563898d93..247f1fb6a3 100644 --- a/doc/classes/Position3D.xml +++ b/doc/classes/Position3D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/PrimitiveMesh.xml b/doc/classes/PrimitiveMesh.xml index 9003a3377b..c9c3643edb 100644 --- a/doc/classes/PrimitiveMesh.xml +++ b/doc/classes/PrimitiveMesh.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_mesh_arrays" qualifiers="const"> <return type="Array"> diff --git a/doc/classes/PrismMesh.xml b/doc/classes/PrismMesh.xml index 4bbbc959d1..0d0f330647 100644 --- a/doc/classes/PrismMesh.xml +++ b/doc/classes/PrismMesh.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ProceduralSky.xml b/doc/classes/ProceduralSky.xml index 16c5bb423d..97ad1efa93 100644 --- a/doc/classes/ProceduralSky.xml +++ b/doc/classes/ProceduralSky.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ProgressBar.xml b/doc/classes/ProgressBar.xml index 848140bd34..98d7e5c128 100644 --- a/doc/classes/ProgressBar.xml +++ b/doc/classes/ProgressBar.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index e67e3c9a03..a1558c186b 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_property_info"> <return type="void"> @@ -215,6 +213,8 @@ <member name="audio/channel_disable_time" type="float" setter="" getter=""> Audio buses will disable automatically when sound goes below a given DB threshold for a given time. This saves CPU as effects assigned to that bus will no longer do any processing. </member> + <member name="audio/default_bus_layout" type="String" setter="" getter=""> + </member> <member name="audio/driver" type="String" setter="" getter=""> </member> <member name="audio/enable_audio_input" type="bool" setter="" getter=""> @@ -268,6 +268,8 @@ </member> <member name="debug/gdscript/warnings/return_value_discarded" type="bool" setter="" getter=""> </member> + <member name="debug/gdscript/warnings/shadowed_variable" type="bool" setter="" getter=""> + </member> <member name="debug/gdscript/warnings/standalone_expression" type="bool" setter="" getter=""> </member> <member name="debug/gdscript/warnings/treat_warnings_as_errors" type="bool" setter="" getter=""> diff --git a/doc/classes/ProximityGroup.xml b/doc/classes/ProximityGroup.xml index bd7a7bc904..ad4cb682cb 100644 --- a/doc/classes/ProximityGroup.xml +++ b/doc/classes/ProximityGroup.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="broadcast"> <return type="void"> diff --git a/doc/classes/ProxyTexture.xml b/doc/classes/ProxyTexture.xml index 1350462142..a36f670c42 100644 --- a/doc/classes/ProxyTexture.xml +++ b/doc/classes/ProxyTexture.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/QuadMesh.xml b/doc/classes/QuadMesh.xml index fbc2c454d5..1b33f62e0f 100644 --- a/doc/classes/QuadMesh.xml +++ b/doc/classes/QuadMesh.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Quat.xml b/doc/classes/Quat.xml index 3867fca24f..f7134e580d 100644 --- a/doc/classes/Quat.xml +++ b/doc/classes/Quat.xml @@ -11,8 +11,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions</link> </tutorials> - <demos> - </demos> <methods> <method name="Quat"> <return type="Quat"> diff --git a/doc/classes/RID.xml b/doc/classes/RID.xml index e8fa652b17..89778e1c8b 100644 --- a/doc/classes/RID.xml +++ b/doc/classes/RID.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="RID"> <return type="RID"> diff --git a/doc/classes/RandomNumberGenerator.xml b/doc/classes/RandomNumberGenerator.xml index 6fa2d44fd0..be7bcc9c35 100644 --- a/doc/classes/RandomNumberGenerator.xml +++ b/doc/classes/RandomNumberGenerator.xml @@ -1,20 +1,19 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="RandomNumberGenerator" inherits="Reference" category="Core" version="3.2"> <brief_description> - A class for generation pseudo-random numbers. + A class for generating pseudo-random numbers. </brief_description> <description> + RandomNumberGenerator is a class for generating pseudo-random numbers. It currently uses PCG32. The underlying algorithm is an implementation detail. As a result, it should not be depended upon for reproducible random streams across Godot versions. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="randf"> <return type="float"> </return> <description> - Generates pseudo-random float between '0.0' and '1.0'. + Generates pseudo-random float between '0.0' and '1.0', inclusive. </description> </method> <method name="randf_range"> @@ -25,14 +24,25 @@ <argument index="1" name="to" type="float"> </argument> <description> - Generates pseudo-random float between [code]from[/code] and [code]to[/code]. + Generates pseudo-random float between [code]from[/code] and [code]to[/code], inclusive. + </description> + </method> + <method name="randfn"> + <return type="float"> + </return> + <argument index="0" name="mean" type="float" default="0.0"> + </argument> + <argument index="1" name="deviation" type="float" default="1.0"> + </argument> + <description> + Generates normally(gaussian) distributed pseudo-random number, using Box-Muller transform with the specified [code]mean[/code] and a standard [code]deviation[/code]. </description> </method> <method name="randi"> <return type="int"> </return> <description> - Generates pseudo-random 32-bit unsigned integer between '0' and '4294967295'. + Generates pseudo-random 32-bit unsigned integer between '0' and '4294967295', inclusive. </description> </method> <method name="randi_range"> @@ -57,6 +67,7 @@ <members> <member name="seed" type="int" setter="set_seed" getter="get_seed"> The seed used by the random number generator. A given seed will give a reproducible sequence of pseudo-random numbers. + [b]Note:[/b] The RNG does not have an avalanche effect, and can output similar random streams given similar seeds. Consider using a hash function to improve your seed quality if they're sourced externally. </member> </members> <constants> diff --git a/doc/classes/Range.xml b/doc/classes/Range.xml index ea749f0537..3c7317bf85 100644 --- a/doc/classes/Range.xml +++ b/doc/classes/Range.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="share"> <return type="void"> diff --git a/doc/classes/RayCast.xml b/doc/classes/RayCast.xml index 3b3a207bc3..1d2999a1ed 100644 --- a/doc/classes/RayCast.xml +++ b/doc/classes/RayCast.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_exception"> <return type="void"> diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml index 00345bff6b..7a16e9e5e0 100644 --- a/doc/classes/RayCast2D.xml +++ b/doc/classes/RayCast2D.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_exception"> <return type="void"> diff --git a/doc/classes/RayShape.xml b/doc/classes/RayShape.xml index a14618a423..69b159fa97 100644 --- a/doc/classes/RayShape.xml +++ b/doc/classes/RayShape.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/RayShape2D.xml b/doc/classes/RayShape2D.xml index 4c3d0de34e..e0ef01bd2a 100644 --- a/doc/classes/RayShape2D.xml +++ b/doc/classes/RayShape2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml index 9c70138503..c4f7844a03 100644 --- a/doc/classes/Rect2.xml +++ b/doc/classes/Rect2.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="Rect2"> <return type="Rect2"> @@ -20,7 +18,7 @@ <argument index="1" name="size" type="Vector2"> </argument> <description> - Constructs a [code]Rect2[/code] by position and size. + Constructs a [Rect2] by position and size. </description> </method> <method name="Rect2"> @@ -35,14 +33,14 @@ <argument index="3" name="height" type="float"> </argument> <description> - Constructs a [code]Rect2[/code] by x, y, width, and height. + Constructs a [Rect2] by x, y, width, and height. </description> </method> <method name="abs"> <return type="Rect2"> </return> <description> - Returns a [code]Rect2[/code] with equivalent position and area, modified so that the top-left corner is the origin and [code]width[/code] and [code]height[/code] are positive. + Returns a [Rect2] with equivalent position and area, modified so that the top-left corner is the origin and [code]width[/code] and [code]height[/code] are positive. </description> </method> <method name="clip"> @@ -51,7 +49,7 @@ <argument index="0" name="b" type="Rect2"> </argument> <description> - Returns the intersection of this [code]Rect2[/code] and b. + Returns the intersection of this [Rect2] and b. </description> </method> <method name="encloses"> @@ -60,7 +58,7 @@ <argument index="0" name="b" type="Rect2"> </argument> <description> - Returns [code]true[/code] if this [code]Rect2[/code] completely encloses another one. + Returns [code]true[/code] if this [Rect2] completely encloses another one. </description> </method> <method name="expand"> @@ -69,14 +67,14 @@ <argument index="0" name="to" type="Vector2"> </argument> <description> - Returns this [code]Rect2[/code] expanded to include a given point. + Returns this [Rect2] expanded to include a given point. </description> </method> <method name="get_area"> <return type="float"> </return> <description> - Returns the area of the [code]Rect2[/code]. + Returns the area of the [Rect2]. </description> </method> <method name="grow"> @@ -85,7 +83,7 @@ <argument index="0" name="by" type="float"> </argument> <description> - Returns a copy of the [code]Rect2[/code] grown a given amount of units towards all the sides. + Returns a copy of the [Rect2] grown a given amount of units towards all the sides. </description> </method> <method name="grow_individual"> @@ -100,7 +98,7 @@ <argument index="3" name=" bottom" type="float"> </argument> <description> - Returns a copy of the [code]Rect2[/code] grown a given amount of units towards each direction individually. + Returns a copy of the [Rect2] grown a given amount of units towards each direction individually. </description> </method> <method name="grow_margin"> @@ -111,14 +109,14 @@ <argument index="1" name="by" type="float"> </argument> <description> - Returns a copy of the [code]Rect2[/code] grown a given amount of units towards the [enum Margin] direction. + Returns a copy of the [Rect2] grown a given amount of units towards the [enum Margin] direction. </description> </method> <method name="has_no_area"> <return type="bool"> </return> <description> - Returns [code]true[/code] if the [code]Rect2[/code] is flat or empty. + Returns [code]true[/code] if the [Rect2] is flat or empty. </description> </method> <method name="has_point"> @@ -127,7 +125,7 @@ <argument index="0" name="point" type="Vector2"> </argument> <description> - Returns [code]true[/code] if the [code]Rect2[/code] contains a point. + Returns [code]true[/code] if the [Rect2] contains a point. </description> </method> <method name="intersects"> @@ -136,7 +134,7 @@ <argument index="0" name="b" type="Rect2"> </argument> <description> - Returns [code]true[/code] if the [code]Rect2[/code] overlaps with another. + Returns [code]true[/code] if the [Rect2] overlaps with another. </description> </method> <method name="merge"> diff --git a/doc/classes/RectangleShape2D.xml b/doc/classes/RectangleShape2D.xml index 6d8411e127..62ead5598b 100644 --- a/doc/classes/RectangleShape2D.xml +++ b/doc/classes/RectangleShape2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Reference.xml b/doc/classes/Reference.xml index b2e63befe4..bc24483367 100644 --- a/doc/classes/Reference.xml +++ b/doc/classes/Reference.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="init_ref"> <return type="bool"> diff --git a/doc/classes/ReferenceRect.xml b/doc/classes/ReferenceRect.xml index b07b9bf80e..0e19e35e58 100644 --- a/doc/classes/ReferenceRect.xml +++ b/doc/classes/ReferenceRect.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml index 58bd254e29..3268e9f8e5 100644 --- a/doc/classes/ReflectionProbe.xml +++ b/doc/classes/ReflectionProbe.xml @@ -7,8 +7,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/reflection_probes.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/RemoteTransform.xml b/doc/classes/RemoteTransform.xml index 36260853f0..17ee7bf123 100644 --- a/doc/classes/RemoteTransform.xml +++ b/doc/classes/RemoteTransform.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/RemoteTransform2D.xml b/doc/classes/RemoteTransform2D.xml index e56cff2a76..b8eb7c6f13 100644 --- a/doc/classes/RemoteTransform2D.xml +++ b/doc/classes/RemoteTransform2D.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml index 0c75bb12a0..8b6fb549aa 100644 --- a/doc/classes/Resource.xml +++ b/doc/classes/Resource.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/stable/getting_started/step_by_step/resources.html</link> </tutorials> - <demos> - </demos> <methods> <method name="_setup_local_to_scene" qualifiers="virtual"> <return type="void"> @@ -52,7 +50,7 @@ <argument index="0" name="path" type="String"> </argument> <description> - Sets the path of the resource. Differs from [code]set_path()[/code], if another [code]Resource[/code] exists with "path" it over-takes it, instead of failing. + Sets the path of the resource. Differs from [code]set_path()[/code], if another [Resource] exists with "path" it over-takes it, instead of failing. </description> </method> </methods> diff --git a/doc/classes/ResourceFormatDDS.xml b/doc/classes/ResourceFormatDDS.xml index 1a99773c35..9717052a5d 100644 --- a/doc/classes/ResourceFormatDDS.xml +++ b/doc/classes/ResourceFormatDDS.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatImporter.xml b/doc/classes/ResourceFormatImporter.xml index efe5dfe0b3..e5c84e6db4 100644 --- a/doc/classes/ResourceFormatImporter.xml +++ b/doc/classes/ResourceFormatImporter.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoader.xml b/doc/classes/ResourceFormatLoader.xml index f1899d3ddc..925706b50c 100644 --- a/doc/classes/ResourceFormatLoader.xml +++ b/doc/classes/ResourceFormatLoader.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_dependencies" qualifiers="virtual"> <return type="void"> diff --git a/doc/classes/ResourceFormatLoaderBMFont.xml b/doc/classes/ResourceFormatLoaderBMFont.xml index e90fbc8834..f5b26a6ff7 100644 --- a/doc/classes/ResourceFormatLoaderBMFont.xml +++ b/doc/classes/ResourceFormatLoaderBMFont.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoaderBinary.xml b/doc/classes/ResourceFormatLoaderBinary.xml index 5f172da9fb..cf4a2373dc 100644 --- a/doc/classes/ResourceFormatLoaderBinary.xml +++ b/doc/classes/ResourceFormatLoaderBinary.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoaderDynamicFont.xml b/doc/classes/ResourceFormatLoaderDynamicFont.xml index 7cbae5ba94..af06ea0d8d 100644 --- a/doc/classes/ResourceFormatLoaderDynamicFont.xml +++ b/doc/classes/ResourceFormatLoaderDynamicFont.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoaderGDScript.xml b/doc/classes/ResourceFormatLoaderGDScript.xml index 6a6e0458f6..5bb9127978 100644 --- a/doc/classes/ResourceFormatLoaderGDScript.xml +++ b/doc/classes/ResourceFormatLoaderGDScript.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoaderImage.xml b/doc/classes/ResourceFormatLoaderImage.xml index 1cd1687053..de53d2c076 100644 --- a/doc/classes/ResourceFormatLoaderImage.xml +++ b/doc/classes/ResourceFormatLoaderImage.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoaderNativeScript.xml b/doc/classes/ResourceFormatLoaderNativeScript.xml index 05dd3ed9d0..7355f702b9 100644 --- a/doc/classes/ResourceFormatLoaderNativeScript.xml +++ b/doc/classes/ResourceFormatLoaderNativeScript.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoaderShader.xml b/doc/classes/ResourceFormatLoaderShader.xml index befd0a61db..1bef7485fb 100644 --- a/doc/classes/ResourceFormatLoaderShader.xml +++ b/doc/classes/ResourceFormatLoaderShader.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoaderStreamTexture.xml b/doc/classes/ResourceFormatLoaderStreamTexture.xml index 5fdf863d82..4bdc74acbc 100644 --- a/doc/classes/ResourceFormatLoaderStreamTexture.xml +++ b/doc/classes/ResourceFormatLoaderStreamTexture.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoaderText.xml b/doc/classes/ResourceFormatLoaderText.xml index 7d77658fcb..92ad3179bf 100644 --- a/doc/classes/ResourceFormatLoaderText.xml +++ b/doc/classes/ResourceFormatLoaderText.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoaderTextureLayered.xml b/doc/classes/ResourceFormatLoaderTextureLayered.xml index b726139897..69c3eedf63 100644 --- a/doc/classes/ResourceFormatLoaderTextureLayered.xml +++ b/doc/classes/ResourceFormatLoaderTextureLayered.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoaderTheora.xml b/doc/classes/ResourceFormatLoaderTheora.xml index fd3caa9a69..1b5e30f09c 100644 --- a/doc/classes/ResourceFormatLoaderTheora.xml +++ b/doc/classes/ResourceFormatLoaderTheora.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatLoaderWebm.xml b/doc/classes/ResourceFormatLoaderWebm.xml index 2a9e93af91..a45e5d488d 100644 --- a/doc/classes/ResourceFormatLoaderWebm.xml +++ b/doc/classes/ResourceFormatLoaderWebm.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatPKM.xml b/doc/classes/ResourceFormatPKM.xml index 16a085c83d..6c442cd934 100644 --- a/doc/classes/ResourceFormatPKM.xml +++ b/doc/classes/ResourceFormatPKM.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatPVR.xml b/doc/classes/ResourceFormatPVR.xml index e330ab0712..d82f48f207 100644 --- a/doc/classes/ResourceFormatPVR.xml +++ b/doc/classes/ResourceFormatPVR.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatSaver.xml b/doc/classes/ResourceFormatSaver.xml index 74577d7468..79a813bcb5 100644 --- a/doc/classes/ResourceFormatSaver.xml +++ b/doc/classes/ResourceFormatSaver.xml @@ -4,13 +4,11 @@ Saves a specific resource type to a file. </brief_description> <description> - The engine can save resources when you do it from the editor, or when you call [method ResourceSaver.save]. This is accomplished with multiple [code]ResourceFormatSaver[/code]s, each handling its own format. + The engine can save resources when you do it from the editor, or when you call [method ResourceSaver.save]. This is accomplished with multiple [ResourceFormatSaver]s, each handling its own format. By default, Godot saves resources as [code].tres[/code], [code].res[/code] or another built-in format, but you can choose to create your own format by extending this class. You should give it a global class name with [code]class_name[/code] for it to be registered. You may as well implement a [ResourceFormatLoader]. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_recognized_extensions" qualifiers="virtual"> <return type="PoolStringArray"> diff --git a/doc/classes/ResourceFormatSaverBinary.xml b/doc/classes/ResourceFormatSaverBinary.xml index adb4db35ad..32c286d0d9 100644 --- a/doc/classes/ResourceFormatSaverBinary.xml +++ b/doc/classes/ResourceFormatSaverBinary.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatSaverGDScript.xml b/doc/classes/ResourceFormatSaverGDScript.xml index 5018122329..0fe65e28b0 100644 --- a/doc/classes/ResourceFormatSaverGDScript.xml +++ b/doc/classes/ResourceFormatSaverGDScript.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatSaverNativeScript.xml b/doc/classes/ResourceFormatSaverNativeScript.xml index 3c69915e18..7a699a2cca 100644 --- a/doc/classes/ResourceFormatSaverNativeScript.xml +++ b/doc/classes/ResourceFormatSaverNativeScript.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatSaverShader.xml b/doc/classes/ResourceFormatSaverShader.xml index 783cc00396..65ddee42fd 100644 --- a/doc/classes/ResourceFormatSaverShader.xml +++ b/doc/classes/ResourceFormatSaverShader.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceFormatSaverText.xml b/doc/classes/ResourceFormatSaverText.xml index b02ca40d7a..921de81031 100644 --- a/doc/classes/ResourceFormatSaverText.xml +++ b/doc/classes/ResourceFormatSaverText.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceImporter.xml b/doc/classes/ResourceImporter.xml index 96f9152ac0..e0c0aa9a47 100644 --- a/doc/classes/ResourceImporter.xml +++ b/doc/classes/ResourceImporter.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/ResourceInteractiveLoader.xml b/doc/classes/ResourceInteractiveLoader.xml index f1e9678499..6b37cb25fb 100644 --- a/doc/classes/ResourceInteractiveLoader.xml +++ b/doc/classes/ResourceInteractiveLoader.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_resource"> <return type="Resource"> diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml index 41fdbd05ef..70deb4509f 100644 --- a/doc/classes/ResourceLoader.xml +++ b/doc/classes/ResourceLoader.xml @@ -4,12 +4,10 @@ Resource Loader. </brief_description> <description> - Resource Loader. This is a static object accessible as [code]ResourceLoader[/code]. GDScript has a simplified load() function, though. + Resource Loader. This is a static object accessible as [ResourceLoader]. GDScript has a simplified load() function, though. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="exists"> <return type="bool"> diff --git a/doc/classes/ResourcePreloader.xml b/doc/classes/ResourcePreloader.xml index 9dc9469c4e..d9e2466182 100644 --- a/doc/classes/ResourcePreloader.xml +++ b/doc/classes/ResourcePreloader.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_resource"> <return type="void"> diff --git a/doc/classes/ResourceSaver.xml b/doc/classes/ResourceSaver.xml index 165ab0dc95..eac022f564 100644 --- a/doc/classes/ResourceSaver.xml +++ b/doc/classes/ResourceSaver.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_recognized_extensions"> <return type="PoolStringArray"> @@ -27,7 +25,7 @@ </argument> <argument index="1" name="resource" type="Resource"> </argument> - <argument index="2" name="flags" type="int" default="0"> + <argument index="2" name="flags" type="int" enum="ResourceSaver.SaverFlags" default="0"> </argument> <description> Saves a resource to disk. diff --git a/doc/classes/ResourceSaverPNG.xml b/doc/classes/ResourceSaverPNG.xml index 3931c39c98..5ed4829f25 100644 --- a/doc/classes/ResourceSaverPNG.xml +++ b/doc/classes/ResourceSaverPNG.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 4ecde6260b..c8dd7821a3 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -10,8 +10,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/gui/bbcode_in_richtextlabel.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_image"> <return type="void"> diff --git a/doc/classes/RigidBody.xml b/doc/classes/RigidBody.xml index d55e56578a..25b896ec3a 100644 --- a/doc/classes/RigidBody.xml +++ b/doc/classes/RigidBody.xml @@ -12,8 +12,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> - <demos> - </demos> <methods> <method name="_integrate_forces" qualifiers="virtual"> <return type="void"> diff --git a/doc/classes/RigidBody2D.xml b/doc/classes/RigidBody2D.xml index f7fc408bca..16ee1a444c 100644 --- a/doc/classes/RigidBody2D.xml +++ b/doc/classes/RigidBody2D.xml @@ -11,8 +11,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_integrate_forces" qualifiers="virtual"> <return type="void"> diff --git a/doc/classes/RootMotionView.xml b/doc/classes/RootMotionView.xml index c1b72b6205..bb939e82f3 100644 --- a/doc/classes/RootMotionView.xml +++ b/doc/classes/RootMotionView.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/SceneState.xml b/doc/classes/SceneState.xml index 711071cd68..34a28b5d1c 100644 --- a/doc/classes/SceneState.xml +++ b/doc/classes/SceneState.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_connection_binds" qualifiers="const"> <return type="Array"> diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index 9fc849e3f1..b9199194cd 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -4,14 +4,12 @@ SceneTree manages a hierarchy of nodes. </brief_description> <description> - As one of the most important classes, the [code]SceneTree[/code] manages the hierarchy of nodes in a scene as well as scenes themselves. Nodes can be added, retrieved and removed. The whole scene tree (and thus the current scene) can be paused. Scenes can be loaded, switched and reloaded. You can also use the SceneTree to organize your nodes into groups: every node can be assigned as many groups as you want to create, e.g. a "enemy" group. You can then iterate these groups or even call methods and set properties on all the group's members at once. + As one of the most important classes, the [SceneTree] manages the hierarchy of nodes in a scene as well as scenes themselves. Nodes can be added, retrieved and removed. The whole scene tree (and thus the current scene) can be paused. Scenes can be loaded, switched and reloaded. You can also use the SceneTree to organize your nodes into groups: every node can be assigned as many groups as you want to create, e.g. a "enemy" group. You can then iterate these groups or even call methods and set properties on all the group's members at once. </description> <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/scene_tree.html</link> <link>https://docs.godotengine.org/en/latest/tutorials/viewports/multiple_resolutions.html</link> </tutorials> - <demos> - </demos> <methods> <method name="call_group" qualifiers="vararg"> <return type="Variant"> @@ -77,6 +75,7 @@ <return type="int"> </return> <description> + Returns the current frame, i.e. number of frames since the application started. </description> </method> <method name="get_network_connected_peers" qualifiers="const"> diff --git a/doc/classes/SceneTreeTimer.xml b/doc/classes/SceneTreeTimer.xml index 52a5101986..a33f875be2 100644 --- a/doc/classes/SceneTreeTimer.xml +++ b/doc/classes/SceneTreeTimer.xml @@ -15,8 +15,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml index f9316a8dfc..fca73e3ea7 100644 --- a/doc/classes/Script.xml +++ b/doc/classes/Script.xml @@ -10,8 +10,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/scripting.html</link> </tutorials> - <demos> - </demos> <methods> <method name="can_instance" qualifiers="const"> <return type="bool"> diff --git a/doc/classes/ScriptCreateDialog.xml b/doc/classes/ScriptCreateDialog.xml index 402c11677a..b2c2d0f060 100644 --- a/doc/classes/ScriptCreateDialog.xml +++ b/doc/classes/ScriptCreateDialog.xml @@ -4,7 +4,7 @@ The Editor's popup dialog for creating new [Script] files. </brief_description> <description> - The [code]ScriptCreateDialog[/code] creates script files according to a given template for a given scripting language. The standard use is to configure its fields prior to calling one of the [method Popup.popup] methods. + The [ScriptCreateDialog] creates script files according to a given template for a given scripting language. The standard use is to configure its fields prior to calling one of the [method Popup.popup] methods. [codeblock] func _ready(): dialog.config("Node", "res://new_node.gd") # for in-engine types @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="config"> <return type="void"> diff --git a/doc/classes/ScriptEditor.xml b/doc/classes/ScriptEditor.xml index 8f1f393da4..666ab15802 100644 --- a/doc/classes/ScriptEditor.xml +++ b/doc/classes/ScriptEditor.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="can_drop_data_fw" qualifiers="const"> <return type="bool"> diff --git a/doc/classes/ScrollBar.xml b/doc/classes/ScrollBar.xml index 3175853912..8b8314d2c8 100644 --- a/doc/classes/ScrollBar.xml +++ b/doc/classes/ScrollBar.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml index 0c44f757d7..7d7dfe167a 100644 --- a/doc/classes/ScrollContainer.xml +++ b/doc/classes/ScrollContainer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_h_scrollbar"> <return type="HScrollBar"> diff --git a/doc/classes/SegmentShape2D.xml b/doc/classes/SegmentShape2D.xml index 227af45d62..af2392787c 100644 --- a/doc/classes/SegmentShape2D.xml +++ b/doc/classes/SegmentShape2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Semaphore.xml b/doc/classes/Semaphore.xml index 8263e932e3..dadb445d4b 100644 --- a/doc/classes/Semaphore.xml +++ b/doc/classes/Semaphore.xml @@ -8,21 +8,19 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="post"> <return type="int" enum="Error"> </return> <description> - Lowers the [code]Semaphore[/code], allowing one more thread in. Returns [code]OK[/code] on success, [code]ERR_BUSY[/code] otherwise. + Lowers the [Semaphore], allowing one more thread in. Returns [code]OK[/code] on success, [code]ERR_BUSY[/code] otherwise. </description> </method> <method name="wait"> <return type="int" enum="Error"> </return> <description> - Tries to wait for the [code]Semaphore[/code], if its value is zero, blocks until non-zero. Returns [code]OK[/code] on success, [code]ERR_BUSY[/code] otherwise. + Tries to wait for the [Semaphore], if its value is zero, blocks until non-zero. Returns [code]OK[/code] on success, [code]ERR_BUSY[/code] otherwise. </description> </method> </methods> diff --git a/doc/classes/Separator.xml b/doc/classes/Separator.xml index 882d82a93e..11f6f1b22f 100644 --- a/doc/classes/Separator.xml +++ b/doc/classes/Separator.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Shader.xml b/doc/classes/Shader.xml index b42c39b03c..668fb6bafa 100644 --- a/doc/classes/Shader.xml +++ b/doc/classes/Shader.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/shading/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_default_texture_param" qualifiers="const"> <return type="Texture"> diff --git a/doc/classes/ShaderMaterial.xml b/doc/classes/ShaderMaterial.xml index 6ccd255fc4..ef355c4417 100644 --- a/doc/classes/ShaderMaterial.xml +++ b/doc/classes/ShaderMaterial.xml @@ -9,8 +9,6 @@ <tutorials> <link>http://docs.godotengine.org/en/latest/tutorials/shading/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_shader_param" qualifiers="const"> <return type="Variant"> diff --git a/doc/classes/Shape.xml b/doc/classes/Shape.xml index 75b3cc162b..d622ff1cf2 100644 --- a/doc/classes/Shape.xml +++ b/doc/classes/Shape.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Shape2D.xml b/doc/classes/Shape2D.xml index 4daac404c2..dec3c2b43c 100644 --- a/doc/classes/Shape2D.xml +++ b/doc/classes/Shape2D.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> - <demos> - </demos> <methods> <method name="collide"> <return type="bool"> diff --git a/doc/classes/ShortCut.xml b/doc/classes/ShortCut.xml index c02f1765c5..4c5dc0e77b 100644 --- a/doc/classes/ShortCut.xml +++ b/doc/classes/ShortCut.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_as_text" qualifiers="const"> <return type="String"> diff --git a/doc/classes/Skeleton.xml b/doc/classes/Skeleton.xml index 003bd6a79b..4377cf52c3 100644 --- a/doc/classes/Skeleton.xml +++ b/doc/classes/Skeleton.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_bone"> <return type="void"> diff --git a/doc/classes/Skeleton2D.xml b/doc/classes/Skeleton2D.xml index 6725de8116..064a7266bd 100644 --- a/doc/classes/Skeleton2D.xml +++ b/doc/classes/Skeleton2D.xml @@ -7,8 +7,6 @@ <tutorials> <link>http://docs.godotengine.org/en/latest/tutorials/animation/2d_skeletons.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_bone"> <return type="Bone2D"> diff --git a/doc/classes/SkeletonIK.xml b/doc/classes/SkeletonIK.xml index 12127c81ea..42c7759c1f 100644 --- a/doc/classes/SkeletonIK.xml +++ b/doc/classes/SkeletonIK.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_parent_skeleton" qualifiers="const"> <return type="Skeleton"> diff --git a/doc/classes/Sky.xml b/doc/classes/Sky.xml index 113bc1abb1..ffd8c3fdee 100644 --- a/doc/classes/Sky.xml +++ b/doc/classes/Sky.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/Slider.xml b/doc/classes/Slider.xml index 1e665f0236..8fdf2bc848 100644 --- a/doc/classes/Slider.xml +++ b/doc/classes/Slider.xml @@ -8,20 +8,22 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="editable" type="bool" setter="set_editable" getter="is_editable"> + If [code]true[/code], the slider can be interacted with. If [code]false[/code], the value can be changed only by code. </member> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" enum="Control.FocusMode"> </member> <member name="scrollable" type="bool" setter="set_scrollable" getter="is_scrollable"> + If [code]true[/code], the value can be changed using the mouse wheel. </member> <member name="tick_count" type="int" setter="set_ticks" getter="get_ticks"> + Number of ticks displayed on the slider, including border ticks. Ticks are uniformly-distributed value markers. </member> <member name="ticks_on_borders" type="bool" setter="set_ticks_on_borders" getter="get_ticks_on_borders"> + If [code]true[/code], the slider will display ticks for minimum and maximum values. </member> </members> <constants> diff --git a/doc/classes/SliderJoint.xml b/doc/classes/SliderJoint.xml index a7702efd55..997930eaa3 100644 --- a/doc/classes/SliderJoint.xml +++ b/doc/classes/SliderJoint.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/SoftBody.xml b/doc/classes/SoftBody.xml index 29e359b548..b235a26ad7 100644 --- a/doc/classes/SoftBody.xml +++ b/doc/classes/SoftBody.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_collision_exception_with"> <return type="void"> diff --git a/doc/classes/Spatial.xml b/doc/classes/Spatial.xml index 1d67abe7af..9d1761ba9f 100644 --- a/doc/classes/Spatial.xml +++ b/doc/classes/Spatial.xml @@ -4,14 +4,12 @@ Most basic 3D game object, parent of all 3D related nodes. </brief_description> <description> - Most basic 3D game object, with a 3D [Transform] and visibility settings. All other 3D game objects inherit from Spatial. Use [code]Spatial[/code] as a parent node to move, scale, rotate and show/hide children in a 3D project. - Affine operations (rotate, scale, translate) happen in parent's local coordinate system, unless the [code]Spatial[/code] object is set as top level. Affine operations in this coordinate system correspond to direct affine operations on the [code]Spatial[/code]'s transform. The word local below refers to this coordinate system. The coordinate system that is attached to the [code]Spatial[/code] object itself is referred to as object-local coordinate system. + Most basic 3D game object, with a 3D [Transform] and visibility settings. All other 3D game objects inherit from Spatial. Use [Spatial] as a parent node to move, scale, rotate and show/hide children in a 3D project. + Affine operations (rotate, scale, translate) happen in parent's local coordinate system, unless the [Spatial] object is set as top level. Affine operations in this coordinate system correspond to direct affine operations on the [Spatial]'s transform. The word local below refers to this coordinate system. The coordinate system that is attached to the [Spatial] object itself is referred to as object-local coordinate system. </description> <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/introduction_to_3d.html</link> </tutorials> - <demos> - </demos> <methods> <method name="force_update_transform"> <return type="void"> @@ -23,14 +21,14 @@ <return type="Spatial"> </return> <description> - Returns the parent [code]Spatial[/code], or an empty [Object] if no parent exists or parent is not of type [code]Spatial[/code]. + Returns the parent [Spatial], or an empty [Object] if no parent exists or parent is not of type [Spatial]. </description> </method> <method name="get_world" qualifiers="const"> <return type="World"> </return> <description> - Returns the current [World] resource this [code]Spatial[/code] node is registered to. + Returns the current [World] resource this [Spatial] node is registered to. </description> </method> <method name="global_rotate"> @@ -72,7 +70,7 @@ <return type="bool"> </return> <description> - Returns whether node notifies about its local transformation changes. [code]Spatial[/code] will not propagate this by default. + Returns whether node notifies about its local transformation changes. [Spatial] will not propagate this by default. </description> </method> <method name="is_scale_disabled" qualifiers="const"> @@ -92,7 +90,7 @@ <return type="bool"> </return> <description> - Returns whether the node notifies about its global and local transformation changes. [code]Spatial[/code] will not propagate this by default. + Returns whether the node notifies about its global and local transformation changes. [Spatial] will not propagate this by default. </description> </method> <method name="is_visible_in_tree" qualifiers="const"> @@ -232,7 +230,7 @@ <argument index="0" name="enable" type="bool"> </argument> <description> - Set whether the node notifies about its local transformation changes. [code]Spatial[/code] will not propagate this by default. + Set whether the node notifies about its local transformation changes. [Spatial] will not propagate this by default. </description> </method> <method name="set_notify_transform"> @@ -241,7 +239,7 @@ <argument index="0" name="enable" type="bool"> </argument> <description> - Set whether the node notifies about its global and local transformation changes. [code]Spatial[/code] will not propagate this by default. + Set whether the node notifies about its global and local transformation changes. [Spatial] will not propagate this by default. </description> </method> <method name="show"> @@ -329,7 +327,7 @@ </signal> </signals> <constants> - <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="29"> + <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="2000"> Spatial nodes receives this notification when their global transform changes. This means that either the current or a parent node changed its transform. In order for [code]NOTIFICATION_TRANSFORM_CHANGED[/code] to work, users first need to ask for it, with [method set_notify_transform]. </constant> diff --git a/doc/classes/SpatialGizmo.xml b/doc/classes/SpatialGizmo.xml index aa6734ea40..5cca1883b3 100644 --- a/doc/classes/SpatialGizmo.xml +++ b/doc/classes/SpatialGizmo.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/SpatialMaterial.xml b/doc/classes/SpatialMaterial.xml index c56e4b7feb..8e93160af9 100644 --- a/doc/classes/SpatialMaterial.xml +++ b/doc/classes/SpatialMaterial.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/spatial_material.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/SpatialVelocityTracker.xml b/doc/classes/SpatialVelocityTracker.xml index caa483f34d..45dd06e926 100644 --- a/doc/classes/SpatialVelocityTracker.xml +++ b/doc/classes/SpatialVelocityTracker.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_tracked_linear_velocity" qualifiers="const"> <return type="Vector3"> diff --git a/doc/classes/SphereMesh.xml b/doc/classes/SphereMesh.xml index b386ee4c19..01f6ee027e 100644 --- a/doc/classes/SphereMesh.xml +++ b/doc/classes/SphereMesh.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/SphereShape.xml b/doc/classes/SphereShape.xml index 54553d4cff..21309ae83e 100644 --- a/doc/classes/SphereShape.xml +++ b/doc/classes/SphereShape.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/SpinBox.xml b/doc/classes/SpinBox.xml index cb32999332..8e2e2f1baa 100644 --- a/doc/classes/SpinBox.xml +++ b/doc/classes/SpinBox.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_line_edit"> <return type="LineEdit"> diff --git a/doc/classes/SplitContainer.xml b/doc/classes/SplitContainer.xml index a1b071572c..5d9a233624 100644 --- a/doc/classes/SplitContainer.xml +++ b/doc/classes/SplitContainer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clamp_split_offset"> <return type="void"> diff --git a/doc/classes/SpotLight.xml b/doc/classes/SpotLight.xml index c84e364e83..7369189c72 100644 --- a/doc/classes/SpotLight.xml +++ b/doc/classes/SpotLight.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/SpringArm.xml b/doc/classes/SpringArm.xml index c4eb5fabd1..50c6fef148 100644 --- a/doc/classes/SpringArm.xml +++ b/doc/classes/SpringArm.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_excluded_object"> <return type="void"> diff --git a/doc/classes/Sprite.xml b/doc/classes/Sprite.xml index 279d73b369..cc5fe93567 100644 --- a/doc/classes/Sprite.xml +++ b/doc/classes/Sprite.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_rect" qualifiers="const"> <return type="Rect2"> diff --git a/doc/classes/Sprite3D.xml b/doc/classes/Sprite3D.xml index 8b7af5449b..81fe182782 100644 --- a/doc/classes/Sprite3D.xml +++ b/doc/classes/Sprite3D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/SpriteBase3D.xml b/doc/classes/SpriteBase3D.xml index 5173e130f7..50e00fe333 100644 --- a/doc/classes/SpriteBase3D.xml +++ b/doc/classes/SpriteBase3D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="generate_triangle_mesh" qualifiers="const"> <return type="TriangleMesh"> diff --git a/doc/classes/SpriteFrames.xml b/doc/classes/SpriteFrames.xml index 3e94b196fc..f28192519e 100644 --- a/doc/classes/SpriteFrames.xml +++ b/doc/classes/SpriteFrames.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_animation"> <return type="void"> diff --git a/doc/classes/StaticBody.xml b/doc/classes/StaticBody.xml index cd1b166924..d2b7abebb1 100644 --- a/doc/classes/StaticBody.xml +++ b/doc/classes/StaticBody.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/StaticBody2D.xml b/doc/classes/StaticBody2D.xml index 52c59762d2..98b38e9e26 100644 --- a/doc/classes/StaticBody2D.xml +++ b/doc/classes/StaticBody2D.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml index 02f5cbeecd..af8e43944c 100644 --- a/doc/classes/StreamPeer.xml +++ b/doc/classes/StreamPeer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_16"> <return type="int"> @@ -43,7 +41,7 @@ <return type="int"> </return> <description> - Return the amount of bytes this [code]StreamPeer[/code] has available. + Return the amount of bytes this [StreamPeer] has available. </description> </method> <method name="get_data"> @@ -274,7 +272,7 @@ </methods> <members> <member name="big_endian" type="bool" setter="set_big_endian" getter="is_big_endian_enabled"> - If [code]true[/code], this [code]StreamPeer[/code] will using big-endian format for encoding and decoding. + If [code]true[/code], this [StreamPeer] will using big-endian format for encoding and decoding. </member> </members> <constants> diff --git a/doc/classes/StreamPeerBuffer.xml b/doc/classes/StreamPeerBuffer.xml index d44c53abfe..de4603b7c0 100644 --- a/doc/classes/StreamPeerBuffer.xml +++ b/doc/classes/StreamPeerBuffer.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> diff --git a/doc/classes/StreamPeerSSL.xml b/doc/classes/StreamPeerSSL.xml index 099a7824bb..07a21a0b57 100644 --- a/doc/classes/StreamPeerSSL.xml +++ b/doc/classes/StreamPeerSSL.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/networking/ssl_certificates.html</link> </tutorials> - <demos> - </demos> <methods> <method name="accept_stream"> <return type="int" enum="Error"> @@ -30,7 +28,7 @@ <argument index="2" name="for_hostname" type="String" default=""""> </argument> <description> - Connect to a peer using an underlying [StreamPeer] "stream", when "validate_certs" is true, [code]StreamPeerSSL[/code] will validate that the certificate presented by the peer matches the "for_hostname". + Connect to a peer using an underlying [StreamPeer] "stream", when "validate_certs" is true, [StreamPeerSSL] will validate that the certificate presented by the peer matches the "for_hostname". </description> </method> <method name="disconnect_from_stream"> @@ -61,12 +59,12 @@ </members> <constants> <constant name="STATUS_DISCONNECTED" value="0" enum="Status"> - A status representing a [code]StreamPeerSSL[/code] that is disconnected. + A status representing a [StreamPeerSSL] that is disconnected. </constant> <constant name="STATUS_HANDSHAKING" value="1" enum="Status"> </constant> <constant name="STATUS_CONNECTED" value="2" enum="Status"> - A status representing a [code]StreamPeerSSL[/code] that is connected to a host. + A status representing a [StreamPeerSSL] that is connected to a host. </constant> <constant name="STATUS_ERROR" value="3" enum="Status"> </constant> diff --git a/doc/classes/StreamPeerTCP.xml b/doc/classes/StreamPeerTCP.xml index 027489ee16..16bf69110d 100644 --- a/doc/classes/StreamPeerTCP.xml +++ b/doc/classes/StreamPeerTCP.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="connect_to_host"> <return type="int" enum="Error"> @@ -70,16 +68,16 @@ </methods> <constants> <constant name="STATUS_NONE" value="0" enum="Status"> - The initial status of the [code]StreamPeerTCP[/code], also the status after a disconnect. + The initial status of the [StreamPeerTCP], also the status after a disconnect. </constant> <constant name="STATUS_CONNECTING" value="1" enum="Status"> - A status representing a [code]StreamPeerTCP[/code] that is connecting to a host. + A status representing a [StreamPeerTCP] that is connecting to a host. </constant> <constant name="STATUS_CONNECTED" value="2" enum="Status"> - A status representing a [code]StreamPeerTCP[/code] that is connected to a host. + A status representing a [StreamPeerTCP] that is connected to a host. </constant> <constant name="STATUS_ERROR" value="3" enum="Status"> - A status representing a [code]StreamPeerTCP[/code] in error state. + A status representing a [StreamPeerTCP] in error state. </constant> </constants> </class> diff --git a/doc/classes/StreamTexture.xml b/doc/classes/StreamTexture.xml index 5f26ba0fab..1ff2500d1a 100644 --- a/doc/classes/StreamTexture.xml +++ b/doc/classes/StreamTexture.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index c3c4b6f938..526a9427dc 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="String"> <return type="String"> @@ -432,6 +430,14 @@ Returns [code]true[/code] if this string is a subsequence of the given string, without considering case. </description> </method> + <method name="is_valid_filename"> + <return type="bool"> + </return> + <description> + Returns [code]true[/code] if this string is free from characters that aren't allowed in file names, those being: + [code]: / \ ? * " | % < >[/code] + </description> + </method> <method name="is_valid_float"> <return type="bool"> </return> diff --git a/doc/classes/StyleBox.xml b/doc/classes/StyleBox.xml index d80a5c0bc6..40cb2632b6 100644 --- a/doc/classes/StyleBox.xml +++ b/doc/classes/StyleBox.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="draw" qualifiers="const"> <return type="void"> diff --git a/doc/classes/StyleBoxEmpty.xml b/doc/classes/StyleBoxEmpty.xml index 5833fc9c05..9dc8569b75 100644 --- a/doc/classes/StyleBoxEmpty.xml +++ b/doc/classes/StyleBoxEmpty.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/StyleBoxFlat.xml b/doc/classes/StyleBoxFlat.xml index 448b635886..884b52459b 100644 --- a/doc/classes/StyleBoxFlat.xml +++ b/doc/classes/StyleBoxFlat.xml @@ -23,8 +23,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_border_width_min" qualifiers="const"> <return type="int"> @@ -148,6 +146,9 @@ <member name="shadow_color" type="Color" setter="set_shadow_color" getter="get_shadow_color"> The color of the shadow. (This has no effect when shadow_size < 1) </member> + <member name="shadow_offset" type="Vector2" setter="set_shadow_offset" getter="get_shadow_offset"> + The shadow offset in pixels. Adjusts the position of the shadow relatively to the stylebox. + </member> <member name="shadow_size" type="int" setter="set_shadow_size" getter="get_shadow_size"> The shadow size in pixels. </member> diff --git a/doc/classes/StyleBoxLine.xml b/doc/classes/StyleBoxLine.xml index 9cbeb2d675..aa306291c8 100644 --- a/doc/classes/StyleBoxLine.xml +++ b/doc/classes/StyleBoxLine.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/StyleBoxTexture.xml b/doc/classes/StyleBoxTexture.xml index f6c29f0b62..8e55795053 100644 --- a/doc/classes/StyleBoxTexture.xml +++ b/doc/classes/StyleBoxTexture.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="set_expand_margin_all"> <return type="void"> diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index 0786aece34..25188a80db 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -4,7 +4,7 @@ Helper tool to create geometry. </brief_description> <description> - The [code]SurfaceTool[/code] is used to construct a [Mesh] by specifying vertex attributes individually. It can be used to construct a [Mesh] from script. All properties except index need to be added before a call to [method add_vertex]. For example adding vertex colors and UVs looks like + The [SurfaceTool] is used to construct a [Mesh] by specifying vertex attributes individually. It can be used to construct a [Mesh] from script. All properties except index need to be added before a call to [method add_vertex]. For example adding vertex colors and UVs looks like [codeblock] var st = SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) @@ -12,14 +12,12 @@ st.add_uv(Vector2(0, 0)) st.add_vertex(Vector3(0, 0, 0)) [/codeblock] - The [code]SurfaceTool[/code] now contains one vertex of a triangle which has a UV coordinate and a specified [Color]. If another vertex were added without calls to [method add_uv] or [method add_color] then the last values would be used. + The [SurfaceTool] now contains one vertex of a triangle which has a UV coordinate and a specified [Color]. If another vertex were added without calls to [method add_uv] or [method add_color] then the last values would be used. It is very important that vertex attributes are passed [b]before[/b] the call to [method add_vertex], failure to do this will result in an error when committing the vertex information to a mesh. Additionally, the attributes used before the first vertex is added determine the format of the mesh. For example if you only add UVs to the first vertex, you cannot add color to any of the subsequent vertices. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_bones"> <return type="void"> diff --git a/doc/classes/TCP_Server.xml b/doc/classes/TCP_Server.xml index 41e70ec96f..2780e39067 100644 --- a/doc/classes/TCP_Server.xml +++ b/doc/classes/TCP_Server.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="is_connection_available" qualifiers="const"> <return type="bool"> diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index e6422b8462..e1260ba44e 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -7,12 +7,10 @@ Sets the active tab's [code]visible[/code] property to the value [code]true[/code]. Sets all other children's to [code]false[/code]. Ignores non-[Control] children. Individual tabs are always visible unless you use [method set_tab_disabled] and [method set_tab_title] to hide it. - To hide only a tab's content, nest the content inside a child [Control], so it receives the [code]TabContainer[/code]'s visibility setting instead. + To hide only a tab's content, nest the content inside a child [Control], so it receives the [TabContainer]'s visibility setting instead. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_current_tab_control" qualifiers="const"> <return type="Control"> @@ -82,7 +80,7 @@ <return type="int"> </return> <description> - Returns the [code]TabContainer[/code] rearrange group id. + Returns the [TabContainer] rearrange group id. </description> </method> <method name="set_popup"> @@ -91,7 +89,7 @@ <argument index="0" name="popup" type="Node"> </argument> <description> - If set on a [Popup] node instance, a popup menu icon appears in the top-right corner of the [code]TabContainer[/code]. Clicking it will expand the [Popup] node. + If set on a [Popup] node instance, a popup menu icon appears in the top-right corner of the [TabContainer]. Clicking it will expand the [Popup] node. </description> </method> <method name="set_tab_disabled"> @@ -133,7 +131,7 @@ <argument index="0" name="group_id" type="int"> </argument> <description> - Defines rearrange group id, choose for each [code]TabContainer[/code] the same value to enable tab drag between [code]TabContainer[/code]. Enable drag with [code]set_drag_to_rearrange_enabled(true)[/code]. + Defines rearrange group id, choose for each [TabContainer] the same value to enable tab drag between [TabContainer]. Enable drag with [code]set_drag_to_rearrange_enabled(true)[/code]. </description> </method> </methods> @@ -154,7 +152,7 @@ <signals> <signal name="pre_popup_pressed"> <description> - Emitted when the [code]TabContainer[/code]'s [Popup] button is clicked. See [method set_popup] for details. + Emitted when the [TabContainer]'s [Popup] button is clicked. See [method set_popup] for details. </description> </signal> <signal name="tab_changed"> diff --git a/doc/classes/Tabs.xml b/doc/classes/Tabs.xml index f50c0810f5..6e61f62f43 100644 --- a/doc/classes/Tabs.xml +++ b/doc/classes/Tabs.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_tab"> <return type="void"> @@ -97,7 +95,7 @@ <return type="int"> </return> <description> - Returns the [code]Tabs[/code] rearrange group id. + Returns the [Tabs] rearrange group id. </description> </method> <method name="move_tab"> @@ -168,7 +166,7 @@ <argument index="0" name="group_id" type="int"> </argument> <description> - Defines rearrange group id, choose for each [code]Tabs[/code] the same value to enable tab drag between [code]Tabs[/code]. Enable drag with [code]set_drag_to_rearrange_enabled(true)[/code]. + Defines rearrange group id, choose for each [Tabs] the same value to enable tab drag between [Tabs]. Enable drag with [code]set_drag_to_rearrange_enabled(true)[/code]. </description> </method> </methods> diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 0d52245538..c55d4d6960 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_color_region"> <return type="void"> @@ -401,9 +399,10 @@ String value of the [TextEdit]. </member> <member name="v_scroll_speed" type="float" setter="set_v_scroll_speed" getter="get_v_scroll_speed"> - If [code]true[/code], enables text wrapping when it goes beyond he edge of what is visible. + Vertical scroll sensitivity. </member> <member name="wrap_enabled" type="bool" setter="set_wrap_enabled" getter="is_wrap_enabled"> + If [code]true[/code], enables text wrapping when it goes beyond the edge of what is visible. </member> </members> <signals> diff --git a/doc/classes/TextFile.xml b/doc/classes/TextFile.xml index be64f07396..163900a482 100644 --- a/doc/classes/TextFile.xml +++ b/doc/classes/TextFile.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Texture.xml b/doc/classes/Texture.xml index 17894d663d..39f3ee447c 100644 --- a/doc/classes/Texture.xml +++ b/doc/classes/Texture.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="draw" qualifiers="const"> <return type="void"> diff --git a/doc/classes/Texture3D.xml b/doc/classes/Texture3D.xml index 895ba26c9c..30724eed50 100644 --- a/doc/classes/Texture3D.xml +++ b/doc/classes/Texture3D.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/TextureArray.xml b/doc/classes/TextureArray.xml index 7f851ae68e..0a9ac1fb2e 100644 --- a/doc/classes/TextureArray.xml +++ b/doc/classes/TextureArray.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/TextureButton.xml b/doc/classes/TextureButton.xml index 29cc80e448..7b436fcf2b 100644 --- a/doc/classes/TextureButton.xml +++ b/doc/classes/TextureButton.xml @@ -4,13 +4,11 @@ Texture-based button. Supports Pressed, Hover, Disabled and Focused states. </brief_description> <description> - [code]TextureButton[/code] has the same functionality as [Button], except it uses sprites instead of Godot's [Theme] resource. It is faster to create, but it doesn't support localization like more complex Controls. + [TextureButton] has the same functionality as [Button], except it uses sprites instead of Godot's [Theme] resource. It is faster to create, but it doesn't support localization like more complex Controls. The Normal state's texture is required. Others are optional. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/TextureLayered.xml b/doc/classes/TextureLayered.xml index b4cfc19414..7c77bd43a3 100644 --- a/doc/classes/TextureLayered.xml +++ b/doc/classes/TextureLayered.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="create"> <return type="void"> diff --git a/doc/classes/TextureProgress.xml b/doc/classes/TextureProgress.xml index 2230f472f1..ae306c6862 100644 --- a/doc/classes/TextureProgress.xml +++ b/doc/classes/TextureProgress.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/TextureRect.xml b/doc/classes/TextureRect.xml index 13fd762908..829105d561 100644 --- a/doc/classes/TextureRect.xml +++ b/doc/classes/TextureRect.xml @@ -8,14 +8,18 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="expand" type="bool" setter="set_expand" getter="has_expand"> If [code]true[/code], the texture scales to fit its bounding rectangle. Default value: [code]false[/code]. </member> + <member name="flip_h" type="bool" setter="set_flip_h" getter="is_flipped_h"> + If [code]true[/code], texture is flipped horizontally. Default value: [code]false[/code]. + </member> + <member name="flip_v" type="bool" setter="set_flip_v" getter="is_flipped_v"> + If [code]true[/code], texture is flipped vertically. Default value: [code]false[/code]. + </member> <member name="stretch_mode" type="int" setter="set_stretch_mode" getter="get_stretch_mode" enum="TextureRect.StretchMode"> Controls the texture's behavior when resizing the node's bounding rectangle. See [enum StretchMode]. </member> diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml index 9ebb9710d7..bc44ec161a 100644 --- a/doc/classes/Theme.xml +++ b/doc/classes/Theme.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> diff --git a/doc/classes/Thread.xml b/doc/classes/Thread.xml index 683424d512..d1d19d91d7 100644 --- a/doc/classes/Thread.xml +++ b/doc/classes/Thread.xml @@ -8,21 +8,19 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_id" qualifiers="const"> <return type="String"> </return> <description> - Returns the current [code]Thread[/code]s id, uniquely identifying it among all threads. + Returns the current [Thread]s id, uniquely identifying it among all threads. </description> </method> <method name="is_active" qualifiers="const"> <return type="bool"> </return> <description> - Returns true if this [code]Thread[/code] is currently active. An active [code]Thread[/code] cannot start work on a new method but can be joined with [method wait_to_finish]. + Returns true if this [Thread] is currently active. An active [Thread] cannot start work on a new method but can be joined with [method wait_to_finish]. </description> </method> <method name="start"> @@ -34,10 +32,10 @@ </argument> <argument index="2" name="userdata" type="Variant" default="null"> </argument> - <argument index="3" name="priority" type="int" default="1"> + <argument index="3" name="priority" type="int" enum="Thread.Priority" default="1"> </argument> <description> - Starts a new [code]Thread[/code] that runs "method" on object "instance" with "userdata" passed as an argument. The "priority" of the [code]Thread[/code] can be changed by passing a PRIORITY_* enum. + Starts a new [Thread] that runs "method" on object "instance" with "userdata" passed as an argument. The "priority" of the [Thread] can be changed by passing a PRIORITY_* enum. Returns OK on success, or ERR_CANT_CREATE on failure. </description> </method> @@ -45,7 +43,7 @@ <return type="Variant"> </return> <description> - Joins the [code]Thread[/code] and waits for it to finish. Returns what the method called returned. + Joins the [Thread] and waits for it to finish. Returns what the method called returned. </description> </method> </methods> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index a9f57cb8fa..ed4f914136 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/2d/using_tilemaps.html</link> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> @@ -165,6 +163,13 @@ Optionally, the tile can also be flipped, transposed, or given autotile coordinates. Note that data such as navigation polygons and collision shapes are not immediately updated for performance reasons. If you need these to be immediately updated, you can call [method update_dirty_quadrants]. + Overriding this method also overrides it internally, allowing custom logic to be implemented when tiles are placed/removed: + [codeblock] + func set_cell(x, y, tile, flip_x, flip_y, transpose, autotile_coord) + # Write your custom logic here. + # To call the default method: + .set_cell(x, y, tile, flip_x, flip_y, transpose, autotile_coord) + [/codeblock] </description> </method> <method name="set_cellv"> @@ -323,6 +328,12 @@ <constant name="HALF_OFFSET_DISABLED" value="2" enum="HalfOffset"> Half offset disabled. </constant> + <constant name="HALF_OFFSET_NEGATIVE_X" value="3" enum="HalfOffset"> + Half offset on the X coordinate (negative). + </constant> + <constant name="HALF_OFFSET_NEGATIVE_Y" value="4" enum="HalfOffset"> + Half offset on the Y coordinate (negative). + </constant> <constant name="TILE_ORIGIN_TOP_LEFT" value="0" enum="TileOrigin"> Tile origin at its top-left corner. </constant> diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 5445be518d..0bf602dcf7 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_forward_subtile_selection" qualifiers="virtual"> <return type="Vector2"> diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index aaffbc7360..74bd86b34b 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="is_stopped" qualifiers="const"> <return type="bool"> diff --git a/doc/classes/ToolButton.xml b/doc/classes/ToolButton.xml index f5d5e1bb04..b48ab75fd0 100644 --- a/doc/classes/ToolButton.xml +++ b/doc/classes/ToolButton.xml @@ -4,7 +4,7 @@ Flat button helper class. </brief_description> <description> - This is a helper class to generate a flat [Button] (see [member Button.flat]), creating a [code]ToolButton[/code] is equivalent to: + This is a helper class to generate a flat [Button] (see [member Button.flat]), creating a [ToolButton] is equivalent to: [codeblock] var btn = Button.new() btn.flat = true @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/TouchScreenButton.xml b/doc/classes/TouchScreenButton.xml index 008a3ac8e1..4b5a832b05 100644 --- a/doc/classes/TouchScreenButton.xml +++ b/doc/classes/TouchScreenButton.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="is_pressed" qualifiers="const"> <return type="bool"> diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml index e110fa7b40..dfbb93acfc 100644 --- a/doc/classes/Transform.xml +++ b/doc/classes/Transform.xml @@ -10,8 +10,6 @@ <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link> </tutorials> - <demos> - </demos> <methods> <method name="Transform"> <return type="Transform"> @@ -172,9 +170,9 @@ </constant> <constant name="FLIP_X" value="Transform( -1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )"> </constant> - <constant name="FLIP_Y" value="Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )"> + <constant name="FLIP_Y" value="Transform( 1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0 )"> </constant> - <constant name="FLIP_Z" value="Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )"> + <constant name="FLIP_Z" value="Transform( 1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0 )"> </constant> </constants> </class> diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index e2d83e0ea6..bc003284ad 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="Transform2D"> <return type="Transform2D"> diff --git a/doc/classes/Translation.xml b/doc/classes/Translation.xml index 4dc6196b2b..5d51f7ee09 100644 --- a/doc/classes/Translation.xml +++ b/doc/classes/Translation.xml @@ -10,8 +10,6 @@ <link>https://docs.godotengine.org/en/stable/tutorials/i18n/internationalizing_games.html</link> <link>https://docs.godotengine.org/en/stable/tutorials/i18n/locales.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_message"> <return type="void"> diff --git a/doc/classes/TranslationLoaderPO.xml b/doc/classes/TranslationLoaderPO.xml index 0af0ee8cca..d9896e1807 100644 --- a/doc/classes/TranslationLoaderPO.xml +++ b/doc/classes/TranslationLoaderPO.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/TranslationServer.xml b/doc/classes/TranslationServer.xml index c07a61b522..a3acc2c659 100644 --- a/doc/classes/TranslationServer.xml +++ b/doc/classes/TranslationServer.xml @@ -10,8 +10,6 @@ <link>https://docs.godotengine.org/en/stable/tutorials/i18n/internationalizing_games.html</link> <link>https://docs.godotengine.org/en/stable/tutorials/i18n/locales.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_translation"> <return type="void"> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index 4659fe61b7..b60673bb5b 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -19,8 +19,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="are_column_titles_visible" qualifiers="const"> <return type="bool"> diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index 7ece531514..9524e048bb 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -4,12 +4,10 @@ Control for a single item inside a [Tree]. </brief_description> <description> - Control for a single item inside a [Tree]. May have child [code]TreeItem[/code]s and be styled as well as contain buttons. + Control for a single item inside a [Tree]. May have child [TreeItem]s and be styled as well as contain buttons. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_button"> <return type="void"> diff --git a/doc/classes/TriangleMesh.xml b/doc/classes/TriangleMesh.xml index 5dd77e2599..9600a1d196 100644 --- a/doc/classes/TriangleMesh.xml +++ b/doc/classes/TriangleMesh.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index 0ed10f15c1..d121fdc125 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -18,8 +18,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="follow_method"> <return type="bool"> @@ -350,6 +348,11 @@ </member> </members> <signals> + <signal name="tween_all_completed"> + <description> + Emitted when all processes in a tween end. + </description> + </signal> <signal name="tween_completed"> <argument index="0" name="object" type="Object"> </argument> diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml index 42f89f01d6..c0b73cd8e3 100644 --- a/doc/classes/UndoRedo.xml +++ b/doc/classes/UndoRedo.xml @@ -30,8 +30,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_do_method" qualifiers="vararg"> <return type="Variant"> diff --git a/doc/classes/VBoxContainer.xml b/doc/classes/VBoxContainer.xml index 4de182ce67..1b4a6a6b62 100644 --- a/doc/classes/VBoxContainer.xml +++ b/doc/classes/VBoxContainer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VScrollBar.xml b/doc/classes/VScrollBar.xml index be58663210..0f46654bc2 100644 --- a/doc/classes/VScrollBar.xml +++ b/doc/classes/VScrollBar.xml @@ -7,8 +7,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VSeparator.xml b/doc/classes/VSeparator.xml index ab8de9131d..ab33c24bb6 100644 --- a/doc/classes/VSeparator.xml +++ b/doc/classes/VSeparator.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VSlider.xml b/doc/classes/VSlider.xml index 477c618064..550bd16074 100644 --- a/doc/classes/VSlider.xml +++ b/doc/classes/VSlider.xml @@ -4,12 +4,10 @@ Vertical slider. </brief_description> <description> - Vertical slider. See [Slider]. This one goes from left (min) to right (max). + Vertical slider. See [Slider]. This one goes from bottom (min) to top (max). </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VSplitContainer.xml b/doc/classes/VSplitContainer.xml index 83b2dca23c..594b662fea 100644 --- a/doc/classes/VSplitContainer.xml +++ b/doc/classes/VSplitContainer.xml @@ -4,12 +4,10 @@ Vertical split container. </brief_description> <description> - Vertical split container. See [SplitContainer]. This goes from left to right. + Vertical split container. See [SplitContainer]. This goes from top to bottom. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Variant.xml b/doc/classes/Variant.xml index 84aba3dabb..ee598c39f8 100644 --- a/doc/classes/Variant.xml +++ b/doc/classes/Variant.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index f1fd0173fe..e6fb32a032 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="Vector2"> <return type="Vector2"> @@ -112,6 +110,15 @@ Cubicly interpolates between this vector and [code]b[/code] using [code]pre_a[/code] and [code]post_b[/code] as handles, and returns the result at position [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], representing the amount of interpolation. </description> </method> + <method name="direction_to"> + <return type="Vector2"> + </return> + <argument index="0" name="b" type="Vector2"> + </argument> + <description> + Returns the normalized vector pointing from this vector to [code]b[/code]. + </description> + </method> <method name="distance_squared_to"> <return type="float"> </return> diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index 7b0d1c5a97..bcd745945e 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="Vector3"> <return type="Vector3"> @@ -81,6 +79,15 @@ Performs a cubic interpolation between vectors [code]pre_a[/code], [code]a[/code], [code]b[/code], [code]post_b[/code] ([code]a[/code] is current), by the given amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], representing the amount of interpolation. </description> </method> + <method name="direction_to"> + <return type="Vector3"> + </return> + <argument index="0" name="b" type="Vector3"> + </argument> + <description> + Returns the normalized vector pointing from this vector to [code]b[/code]. + </description> + </method> <method name="distance_squared_to"> <return type="float"> </return> diff --git a/doc/classes/VehicleBody.xml b/doc/classes/VehicleBody.xml index 184b8bd23e..81d853090c 100644 --- a/doc/classes/VehicleBody.xml +++ b/doc/classes/VehicleBody.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VehicleWheel.xml b/doc/classes/VehicleWheel.xml index bf2df957ba..6a507d630c 100644 --- a/doc/classes/VehicleWheel.xml +++ b/doc/classes/VehicleWheel.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_skidinfo" qualifiers="const"> <return type="float"> diff --git a/doc/classes/VideoPlayer.xml b/doc/classes/VideoPlayer.xml index 07c753835c..15d1dfcf19 100644 --- a/doc/classes/VideoPlayer.xml +++ b/doc/classes/VideoPlayer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_stream_name" qualifiers="const"> <return type="String"> diff --git a/doc/classes/VideoStream.xml b/doc/classes/VideoStream.xml index 8906763c8d..7772c61e85 100644 --- a/doc/classes/VideoStream.xml +++ b/doc/classes/VideoStream.xml @@ -7,8 +7,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 5ceeb80e52..b48dc9de35 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -15,8 +15,6 @@ <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link> <link>https://docs.godotengine.org/en/latest/tutorials/viewports/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="find_world" qualifiers="const"> <return type="World"> diff --git a/doc/classes/ViewportContainer.xml b/doc/classes/ViewportContainer.xml index 8a26de5ed7..1642f7e85f 100644 --- a/doc/classes/ViewportContainer.xml +++ b/doc/classes/ViewportContainer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/ViewportTexture.xml b/doc/classes/ViewportTexture.xml index 01832b18bf..ab3c2696aa 100644 --- a/doc/classes/ViewportTexture.xml +++ b/doc/classes/ViewportTexture.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisibilityEnabler.xml b/doc/classes/VisibilityEnabler.xml index a507c0be9a..910ca033d1 100644 --- a/doc/classes/VisibilityEnabler.xml +++ b/doc/classes/VisibilityEnabler.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisibilityEnabler2D.xml b/doc/classes/VisibilityEnabler2D.xml index 177c70246e..3943eca7f5 100644 --- a/doc/classes/VisibilityEnabler2D.xml +++ b/doc/classes/VisibilityEnabler2D.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisibilityNotifier.xml b/doc/classes/VisibilityNotifier.xml index e30078d82f..986d6b8b13 100644 --- a/doc/classes/VisibilityNotifier.xml +++ b/doc/classes/VisibilityNotifier.xml @@ -8,14 +8,13 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="is_on_screen" qualifiers="const"> <return type="bool"> </return> <description> If [code]true[/code], the bounding box is on the screen. + Note: It takes one frame for the node's visibility to be assessed once added to the scene tree, so this method will return [code]false[/code] right after it is instantiated, even if it will be on screen in the draw pass. </description> </method> </methods> diff --git a/doc/classes/VisibilityNotifier2D.xml b/doc/classes/VisibilityNotifier2D.xml index 5689ced4bf..787a33d752 100644 --- a/doc/classes/VisibilityNotifier2D.xml +++ b/doc/classes/VisibilityNotifier2D.xml @@ -8,14 +8,13 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="is_on_screen" qualifiers="const"> <return type="bool"> </return> <description> If [code]true[/code], the bounding rectangle is on the screen. + Note: It takes one frame for the node's visibility to be assessed once added to the scene tree, so this method will return [code]false[/code] right after it is instantiated, even if it will be on screen in the draw pass. </description> </method> </methods> diff --git a/doc/classes/VisualInstance.xml b/doc/classes/VisualInstance.xml index 486c05eff7..381679f8bb 100644 --- a/doc/classes/VisualInstance.xml +++ b/doc/classes/VisualInstance.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_aabb" qualifiers="const"> <return type="AABB"> diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml index 94a7786295..0030795a6b 100644 --- a/doc/classes/VisualServer.xml +++ b/doc/classes/VisualServer.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="black_bars_set_images"> <return type="void"> diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml index 7240d7a4d0..d399f69c96 100644 --- a/doc/classes/VisualShader.xml +++ b/doc/classes/VisualShader.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_node"> <return type="void"> diff --git a/doc/classes/VisualShaderNode.xml b/doc/classes/VisualShaderNode.xml index 4175cb088a..4fa80d8c82 100644 --- a/doc/classes/VisualShaderNode.xml +++ b/doc/classes/VisualShaderNode.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_input_port_default_value" qualifiers="const"> <return type="Variant"> diff --git a/doc/classes/VisualShaderNodeBooleanConstant.xml b/doc/classes/VisualShaderNodeBooleanConstant.xml new file mode 100644 index 0000000000..fd406db053 --- /dev/null +++ b/doc/classes/VisualShaderNodeBooleanConstant.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeBooleanConstant" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <members> + <member name="constant" type="bool" setter="set_constant" getter="get_constant"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeBooleanUniform.xml b/doc/classes/VisualShaderNodeBooleanUniform.xml new file mode 100644 index 0000000000..518c7ba3b8 --- /dev/null +++ b/doc/classes/VisualShaderNodeBooleanUniform.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeBooleanUniform" inherits="VisualShaderNodeUniform" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeColorConstant.xml b/doc/classes/VisualShaderNodeColorConstant.xml index 5f8005561c..d9a927e75d 100644 --- a/doc/classes/VisualShaderNodeColorConstant.xml +++ b/doc/classes/VisualShaderNodeColorConstant.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisualShaderNodeColorFunc.xml b/doc/classes/VisualShaderNodeColorFunc.xml new file mode 100644 index 0000000000..23223587a5 --- /dev/null +++ b/doc/classes/VisualShaderNodeColorFunc.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeColorFunc" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <members> + <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeColorFunc.Function"> + </member> + </members> + <constants> + <constant name="FUNC_GRAYSCALE" value="0" enum="Function"> + </constant> + <constant name="FUNC_SEPIA" value="1" enum="Function"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeColorOp.xml b/doc/classes/VisualShaderNodeColorOp.xml index 9b01bced8b..b78efa2adc 100644 --- a/doc/classes/VisualShaderNodeColorOp.xml +++ b/doc/classes/VisualShaderNodeColorOp.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisualShaderNodeColorUniform.xml b/doc/classes/VisualShaderNodeColorUniform.xml index 0a8d3a0449..ec61729782 100644 --- a/doc/classes/VisualShaderNodeColorUniform.xml +++ b/doc/classes/VisualShaderNodeColorUniform.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeCubeMap.xml b/doc/classes/VisualShaderNodeCubeMap.xml index 8427d012f1..09f68a0e9a 100644 --- a/doc/classes/VisualShaderNodeCubeMap.xml +++ b/doc/classes/VisualShaderNodeCubeMap.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisualShaderNodeCubeMapUniform.xml b/doc/classes/VisualShaderNodeCubeMapUniform.xml index 958c740082..b06fc97b14 100644 --- a/doc/classes/VisualShaderNodeCubeMapUniform.xml +++ b/doc/classes/VisualShaderNodeCubeMapUniform.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeDeterminant.xml b/doc/classes/VisualShaderNodeDeterminant.xml new file mode 100644 index 0000000000..a86db216c4 --- /dev/null +++ b/doc/classes/VisualShaderNodeDeterminant.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeDeterminant" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeDotProduct.xml b/doc/classes/VisualShaderNodeDotProduct.xml index 8d6182bcd6..f07827db29 100644 --- a/doc/classes/VisualShaderNodeDotProduct.xml +++ b/doc/classes/VisualShaderNodeDotProduct.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeFaceForward.xml b/doc/classes/VisualShaderNodeFaceForward.xml new file mode 100644 index 0000000000..ea8589e6cb --- /dev/null +++ b/doc/classes/VisualShaderNodeFaceForward.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeFaceForward" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeIf.xml b/doc/classes/VisualShaderNodeIf.xml new file mode 100644 index 0000000000..374a1e379a --- /dev/null +++ b/doc/classes/VisualShaderNodeIf.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeIf" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeInput.xml b/doc/classes/VisualShaderNodeInput.xml index 1d82716086..32398b8545 100644 --- a/doc/classes/VisualShaderNodeInput.xml +++ b/doc/classes/VisualShaderNodeInput.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisualShaderNodeOuterProduct.xml b/doc/classes/VisualShaderNodeOuterProduct.xml new file mode 100644 index 0000000000..3debde0634 --- /dev/null +++ b/doc/classes/VisualShaderNodeOuterProduct.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeOuterProduct" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeOutput.xml b/doc/classes/VisualShaderNodeOutput.xml index d2b80dc3b4..ff109a949e 100644 --- a/doc/classes/VisualShaderNodeOutput.xml +++ b/doc/classes/VisualShaderNodeOutput.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeScalarClamp.xml b/doc/classes/VisualShaderNodeScalarClamp.xml new file mode 100644 index 0000000000..4c5309d1e7 --- /dev/null +++ b/doc/classes/VisualShaderNodeScalarClamp.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeScalarClamp" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeScalarConstant.xml b/doc/classes/VisualShaderNodeScalarConstant.xml index 81103d3b96..b18ac46fbd 100644 --- a/doc/classes/VisualShaderNodeScalarConstant.xml +++ b/doc/classes/VisualShaderNodeScalarConstant.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml b/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml new file mode 100644 index 0000000000..6c3eb95b6f --- /dev/null +++ b/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeScalarDerivativeFunc" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <members> + <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeScalarDerivativeFunc.Function"> + </member> + </members> + <constants> + <constant name="FUNC_SUM" value="0" enum="Function"> + </constant> + <constant name="FUNC_X" value="1" enum="Function"> + </constant> + <constant name="FUNC_Y" value="2" enum="Function"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeScalarFunc.xml b/doc/classes/VisualShaderNodeScalarFunc.xml index 67a5d3f5c4..163276b3bc 100644 --- a/doc/classes/VisualShaderNodeScalarFunc.xml +++ b/doc/classes/VisualShaderNodeScalarFunc.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -55,5 +53,27 @@ </constant> <constant name="FUNC_NEGATE" value="19" enum="Function"> </constant> + <constant name="FUNC_ACOSH" value="20" enum="Function"> + </constant> + <constant name="FUNC_ASINH" value="21" enum="Function"> + </constant> + <constant name="FUNC_ATANH" value="22" enum="Function"> + </constant> + <constant name="FUNC_DEGREES" value="23" enum="Function"> + </constant> + <constant name="FUNC_EXP2" value="24" enum="Function"> + </constant> + <constant name="FUNC_INVERSE_SQRT" value="25" enum="Function"> + </constant> + <constant name="FUNC_LOG2" value="26" enum="Function"> + </constant> + <constant name="FUNC_RADIANS" value="27" enum="Function"> + </constant> + <constant name="FUNC_RECIPROCAL" value="28" enum="Function"> + </constant> + <constant name="FUNC_ROUNDEVEN" value="29" enum="Function"> + </constant> + <constant name="FUNC_TRUNC" value="30" enum="Function"> + </constant> </constants> </class> diff --git a/doc/classes/VisualShaderNodeScalarInterp.xml b/doc/classes/VisualShaderNodeScalarInterp.xml index 96f664fb1c..9d01e20b8d 100644 --- a/doc/classes/VisualShaderNodeScalarInterp.xml +++ b/doc/classes/VisualShaderNodeScalarInterp.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeScalarOp.xml b/doc/classes/VisualShaderNodeScalarOp.xml index 34ec89f8be..b2939a55ee 100644 --- a/doc/classes/VisualShaderNodeScalarOp.xml +++ b/doc/classes/VisualShaderNodeScalarOp.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -33,5 +31,7 @@ </constant> <constant name="OP_ATAN2" value="8" enum="Operator"> </constant> + <constant name="OP_STEP" value="9" enum="Operator"> + </constant> </constants> </class> diff --git a/doc/classes/VisualShaderNodeScalarSmoothStep.xml b/doc/classes/VisualShaderNodeScalarSmoothStep.xml new file mode 100644 index 0000000000..737c535659 --- /dev/null +++ b/doc/classes/VisualShaderNodeScalarSmoothStep.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeScalarSmoothStep" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeScalarUniform.xml b/doc/classes/VisualShaderNodeScalarUniform.xml index 979574c6d8..bdbb02561b 100644 --- a/doc/classes/VisualShaderNodeScalarUniform.xml +++ b/doc/classes/VisualShaderNodeScalarUniform.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeSwitch.xml b/doc/classes/VisualShaderNodeSwitch.xml new file mode 100644 index 0000000000..930d914035 --- /dev/null +++ b/doc/classes/VisualShaderNodeSwitch.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeSwitch" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeTexture.xml b/doc/classes/VisualShaderNodeTexture.xml index 30c193d03a..7a73868c2b 100644 --- a/doc/classes/VisualShaderNodeTexture.xml +++ b/doc/classes/VisualShaderNodeTexture.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisualShaderNodeTextureUniform.xml b/doc/classes/VisualShaderNodeTextureUniform.xml index 7000392261..9312c24fe7 100644 --- a/doc/classes/VisualShaderNodeTextureUniform.xml +++ b/doc/classes/VisualShaderNodeTextureUniform.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisualShaderNodeTransformCompose.xml b/doc/classes/VisualShaderNodeTransformCompose.xml index f103df2442..0939eb393d 100644 --- a/doc/classes/VisualShaderNodeTransformCompose.xml +++ b/doc/classes/VisualShaderNodeTransformCompose.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeTransformConstant.xml b/doc/classes/VisualShaderNodeTransformConstant.xml index cf3ba87ccc..5b4c046ce1 100644 --- a/doc/classes/VisualShaderNodeTransformConstant.xml +++ b/doc/classes/VisualShaderNodeTransformConstant.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisualShaderNodeTransformDecompose.xml b/doc/classes/VisualShaderNodeTransformDecompose.xml index ba4cbd0510..d986e6b7a8 100644 --- a/doc/classes/VisualShaderNodeTransformDecompose.xml +++ b/doc/classes/VisualShaderNodeTransformDecompose.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeTransformFunc.xml b/doc/classes/VisualShaderNodeTransformFunc.xml new file mode 100644 index 0000000000..e74f449212 --- /dev/null +++ b/doc/classes/VisualShaderNodeTransformFunc.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeTransformFunc" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <members> + <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeTransformFunc.Function"> + </member> + </members> + <constants> + <constant name="FUNC_INVERSE" value="0" enum="Function"> + </constant> + <constant name="FUNC_TRANSPOSE" value="1" enum="Function"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeTransformMult.xml b/doc/classes/VisualShaderNodeTransformMult.xml index 47026b400e..6082434beb 100644 --- a/doc/classes/VisualShaderNodeTransformMult.xml +++ b/doc/classes/VisualShaderNodeTransformMult.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -19,5 +17,9 @@ </constant> <constant name="OP_BxA" value="1" enum="Operator"> </constant> + <constant name="OP_AxB_COMP" value="2" enum="Operator"> + </constant> + <constant name="OP_BxA_COMP" value="3" enum="Operator"> + </constant> </constants> </class> diff --git a/doc/classes/VisualShaderNodeTransformUniform.xml b/doc/classes/VisualShaderNodeTransformUniform.xml index f4f81b9000..0a28e0c1f6 100644 --- a/doc/classes/VisualShaderNodeTransformUniform.xml +++ b/doc/classes/VisualShaderNodeTransformUniform.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeTransformVecMult.xml b/doc/classes/VisualShaderNodeTransformVecMult.xml index 358ce31258..46d3be2ded 100644 --- a/doc/classes/VisualShaderNodeTransformVecMult.xml +++ b/doc/classes/VisualShaderNodeTransformVecMult.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisualShaderNodeUniform.xml b/doc/classes/VisualShaderNodeUniform.xml index c685148161..09371f91de 100644 --- a/doc/classes/VisualShaderNodeUniform.xml +++ b/doc/classes/VisualShaderNodeUniform.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisualShaderNodeVec3Constant.xml b/doc/classes/VisualShaderNodeVec3Constant.xml index 401e937b52..1ea7e8115e 100644 --- a/doc/classes/VisualShaderNodeVec3Constant.xml +++ b/doc/classes/VisualShaderNodeVec3Constant.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/VisualShaderNodeVec3Uniform.xml b/doc/classes/VisualShaderNodeVec3Uniform.xml index e7ca1f90fb..a1c9b14566 100644 --- a/doc/classes/VisualShaderNodeVec3Uniform.xml +++ b/doc/classes/VisualShaderNodeVec3Uniform.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeVectorClamp.xml b/doc/classes/VisualShaderNodeVectorClamp.xml new file mode 100644 index 0000000000..a5d1e94e2f --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorClamp.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorClamp" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorCompose.xml b/doc/classes/VisualShaderNodeVectorCompose.xml index 49a7caf5e3..b7f650c82e 100644 --- a/doc/classes/VisualShaderNodeVectorCompose.xml +++ b/doc/classes/VisualShaderNodeVectorCompose.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeVectorDecompose.xml b/doc/classes/VisualShaderNodeVectorDecompose.xml index f498f95ad0..30727379e6 100644 --- a/doc/classes/VisualShaderNodeVectorDecompose.xml +++ b/doc/classes/VisualShaderNodeVectorDecompose.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml b/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml new file mode 100644 index 0000000000..678df0fe3b --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorDerivativeFunc" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <members> + <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeVectorDerivativeFunc.Function"> + </member> + </members> + <constants> + <constant name="FUNC_SUM" value="0" enum="Function"> + </constant> + <constant name="FUNC_X" value="1" enum="Function"> + </constant> + <constant name="FUNC_Y" value="2" enum="Function"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorDistance.xml b/doc/classes/VisualShaderNodeVectorDistance.xml new file mode 100644 index 0000000000..f7c9acecf7 --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorDistance.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorDistance" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorFunc.xml b/doc/classes/VisualShaderNodeVectorFunc.xml index 968d73d4e1..71ce32c04e 100644 --- a/doc/classes/VisualShaderNodeVectorFunc.xml +++ b/doc/classes/VisualShaderNodeVectorFunc.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -27,5 +25,61 @@ </constant> <constant name="FUNC_HSV2RGB" value="5" enum="Function"> </constant> + <constant name="FUNC_ABS" value="6" enum="Function"> + </constant> + <constant name="FUNC_ACOS" value="7" enum="Function"> + </constant> + <constant name="FUNC_ACOSH" value="8" enum="Function"> + </constant> + <constant name="FUNC_ASIN" value="9" enum="Function"> + </constant> + <constant name="FUNC_ASINH" value="10" enum="Function"> + </constant> + <constant name="FUNC_ATAN" value="11" enum="Function"> + </constant> + <constant name="FUNC_ATANH" value="12" enum="Function"> + </constant> + <constant name="FUNC_CEIL" value="13" enum="Function"> + </constant> + <constant name="FUNC_COS" value="14" enum="Function"> + </constant> + <constant name="FUNC_COSH" value="15" enum="Function"> + </constant> + <constant name="FUNC_DEGREES" value="16" enum="Function"> + </constant> + <constant name="FUNC_EXP" value="17" enum="Function"> + </constant> + <constant name="FUNC_EXP2" value="18" enum="Function"> + </constant> + <constant name="FUNC_FLOOR" value="19" enum="Function"> + </constant> + <constant name="FUNC_FRAC" value="20" enum="Function"> + </constant> + <constant name="FUNC_INVERSE_SQRT" value="21" enum="Function"> + </constant> + <constant name="FUNC_LOG" value="22" enum="Function"> + </constant> + <constant name="FUNC_LOG2" value="23" enum="Function"> + </constant> + <constant name="FUNC_RADIANS" value="24" enum="Function"> + </constant> + <constant name="FUNC_ROUND" value="25" enum="Function"> + </constant> + <constant name="FUNC_ROUNDEVEN" value="26" enum="Function"> + </constant> + <constant name="FUNC_SIGN" value="27" enum="Function"> + </constant> + <constant name="FUNC_SIN" value="28" enum="Function"> + </constant> + <constant name="FUNC_SINH" value="29" enum="Function"> + </constant> + <constant name="FUNC_SQRT" value="30" enum="Function"> + </constant> + <constant name="FUNC_TAN" value="31" enum="Function"> + </constant> + <constant name="FUNC_TANH" value="32" enum="Function"> + </constant> + <constant name="FUNC_TRUNC" value="33" enum="Function"> + </constant> </constants> </class> diff --git a/doc/classes/VisualShaderNodeVectorInterp.xml b/doc/classes/VisualShaderNodeVectorInterp.xml index 89c441f1f9..2a398c653d 100644 --- a/doc/classes/VisualShaderNodeVectorInterp.xml +++ b/doc/classes/VisualShaderNodeVectorInterp.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeVectorLen.xml b/doc/classes/VisualShaderNodeVectorLen.xml index 8e3ad6757a..f6fdc0aff3 100644 --- a/doc/classes/VisualShaderNodeVectorLen.xml +++ b/doc/classes/VisualShaderNodeVectorLen.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/doc/classes/VisualShaderNodeVectorOp.xml b/doc/classes/VisualShaderNodeVectorOp.xml index 16c1f8f25d..cfcd16bbff 100644 --- a/doc/classes/VisualShaderNodeVectorOp.xml +++ b/doc/classes/VisualShaderNodeVectorOp.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -33,5 +31,11 @@ </constant> <constant name="OP_CROSS" value="8" enum="Operator"> </constant> + <constant name="OP_ATAN2" value="9" enum="Operator"> + </constant> + <constant name="OP_REFLECT" value="10" enum="Operator"> + </constant> + <constant name="OP_STEP" value="11" enum="Operator"> + </constant> </constants> </class> diff --git a/doc/classes/VisualShaderNodeVectorRefract.xml b/doc/classes/VisualShaderNodeVectorRefract.xml new file mode 100644 index 0000000000..4df072040a --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorRefract.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorRefract" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorScalarSmoothStep.xml b/doc/classes/VisualShaderNodeVectorScalarSmoothStep.xml new file mode 100644 index 0000000000..4334eee7c1 --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorScalarSmoothStep.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorScalarSmoothStep" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorScalarStep.xml b/doc/classes/VisualShaderNodeVectorScalarStep.xml new file mode 100644 index 0000000000..ad8cac059b --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorScalarStep.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorScalarStep" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorSmoothStep.xml b/doc/classes/VisualShaderNodeVectorSmoothStep.xml new file mode 100644 index 0000000000..59acff7d05 --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorSmoothStep.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorSmoothStep" inherits="VisualShaderNode" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/WeakRef.xml b/doc/classes/WeakRef.xml index 4b8f3de45b..6845c81a13 100644 --- a/doc/classes/WeakRef.xml +++ b/doc/classes/WeakRef.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_ref" qualifiers="const"> <return type="Variant"> diff --git a/doc/classes/WindowDialog.xml b/doc/classes/WindowDialog.xml index aac5b975ba..16b7bd84ad 100644 --- a/doc/classes/WindowDialog.xml +++ b/doc/classes/WindowDialog.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_close_button"> <return type="TextureButton"> diff --git a/doc/classes/World.xml b/doc/classes/World.xml index de5d290dea..c8e6944b83 100644 --- a/doc/classes/World.xml +++ b/doc/classes/World.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/World2D.xml b/doc/classes/World2D.xml index 6bc33fd7da..66ef18f8f4 100644 --- a/doc/classes/World2D.xml +++ b/doc/classes/World2D.xml @@ -9,8 +9,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/WorldEnvironment.xml b/doc/classes/WorldEnvironment.xml index 63e9b012dd..b4524bfea0 100644 --- a/doc/classes/WorldEnvironment.xml +++ b/doc/classes/WorldEnvironment.xml @@ -4,20 +4,18 @@ Default environment properties for the entire scene (post-processing effects, lighting and background settings). </brief_description> <description> - The [code]WorldEnvironment[/code] node is used to configure the default [Environment] for the scene. - The parameters defined in the [code]WorldEnvironment[/code] can be overridden by an [Environment] node set on the current [Camera]. Additionally, only one [code]WorldEnvironment[/code] may be instanced in a given scene at a time. - The [code]WorldEnvironment[/code] allows the user to specify default lighting parameters (e.g. ambient lighting), various post-processing effects (e.g. SSAO, DOF, Tonemapping), and how to draw the background (e.g. solid color, skybox). Usually, these are added in order to improve the realism/color balance of the scene. + The [WorldEnvironment] node is used to configure the default [Environment] for the scene. + The parameters defined in the [WorldEnvironment] can be overridden by an [Environment] node set on the current [Camera]. Additionally, only one [WorldEnvironment] may be instanced in a given scene at a time. + The [WorldEnvironment] allows the user to specify default lighting parameters (e.g. ambient lighting), various post-processing effects (e.g. SSAO, DOF, Tonemapping), and how to draw the background (e.g. solid color, skybox). Usually, these are added in order to improve the realism/color balance of the scene. </description> <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/environment_and_post_processing.html</link> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="environment" type="Environment" setter="set_environment" getter="get_environment"> - The [Environment] resource used by this [code]WorldEnvironment[/code], defining the default properties. + The [Environment] resource used by this [WorldEnvironment], defining the default properties. </member> </members> <constants> diff --git a/doc/classes/XMLParser.xml b/doc/classes/XMLParser.xml index 8882f5cc6e..a11e95db79 100644 --- a/doc/classes/XMLParser.xml +++ b/doc/classes/XMLParser.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_attribute_count" qualifiers="const"> <return type="int"> diff --git a/doc/classes/YSort.xml b/doc/classes/YSort.xml index 7eb87f38c2..b9f3fa914b 100644 --- a/doc/classes/YSort.xml +++ b/doc/classes/YSort.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/doc/classes/bool.xml b/doc/classes/bool.xml index 6f4c4771cd..15e4146ef4 100644 --- a/doc/classes/bool.xml +++ b/doc/classes/bool.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="bool"> <return type="bool"> diff --git a/doc/classes/float.xml b/doc/classes/float.xml index 48d27f54b1..506fb643e1 100644 --- a/doc/classes/float.xml +++ b/doc/classes/float.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="float"> <return type="float"> diff --git a/doc/classes/int.xml b/doc/classes/int.xml index 88256cc84e..dad0f0d8c0 100644 --- a/doc/classes/int.xml +++ b/doc/classes/int.xml @@ -6,7 +6,7 @@ <description> Signed 64-bit integer type. It can take values in the interval [code][-2^63, 2^63 - 1][/code], i.e. [code][-9223372036854775808, 9223372036854775807][/code]. Exceeding those bounds will wrap around. - [code]int[/code] is a [Variant] type, and will thus be used when assigning an integer value to a [Variant]. It can also be enforced with the [code]: int[/code] type hint. + [int] is a [Variant] type, and will thus be used when assigning an integer value to a [Variant]. It can also be enforced with the [code]: int[/code] type hint. [codeblock] var my_variant = 0 # int, value 0 my_variant += 4.2 # float, value 4.2 @@ -22,8 +22,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="int"> <return type="int"> diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py index 4bb4342d5f..974ac2d05c 100644 --- a/doc/tools/doc_status.py +++ b/doc/tools/doc_status.py @@ -261,7 +261,7 @@ class ClassStatus: for sub_tag in list(tag): status.progresses[tag.tag].increment(len(sub_tag.text.strip()) > 0) - elif tag.tag in ['tutorials', 'demos']: + elif tag.tag in ['tutorials']: pass # Ignore those tags for now elif tag.tag in ['theme_items']: diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py index 40dde48432..e81b4db13e 100755 --- a/doc/tools/makerst.py +++ b/doc/tools/makerst.py @@ -1,11 +1,10 @@ #!/usr/bin/env python3 import argparse -import sys import os import re import xml.etree.ElementTree as ET -from collections import defaultdict, OrderedDict +from collections import OrderedDict # Uncomment to do type checks. I have it commented out so it works below Python 3.5 #from typing import List, Dict, TextIO, Tuple, Iterable, Optional, DefaultDict, Any, Union @@ -702,7 +701,11 @@ def rstize_text(text, state): # type: (str, State) -> str escape_post = False if tag_text in state.classes: - tag_text = make_type(tag_text, state) + if tag_text == state.current_class: + # We don't want references to the same class + tag_text = '``{}``'.format(tag_text) + else: + tag_text = make_type(tag_text, state) escape_post = True else: # command cmd = tag_text @@ -757,14 +760,25 @@ def rstize_text(text, state): # type: (str, State) -> str elif cmd.startswith("constant"): found = False - if method_param in class_def.constants: - found = True - else: - for enum in class_def.enums.values(): - if method_param in enum.values: - found = True - break + # Search in the current class + search_class_defs = [class_def] + + if param.find('.') == -1: + # Also search in @GlobalScope as a last resort if no class was specified + search_class_defs.append(state.classes["@GlobalScope"]) + + for search_class_def in search_class_defs: + if method_param in search_class_def.constants: + class_param = search_class_def.name + found = True + + else: + for enum in search_class_def.enums.values(): + if method_param in enum.values: + class_param = search_class_def.name + found = True + break if not found: print_error("Unresolved constant '{}', file: {}".format(param, state.current_class), state) @@ -917,6 +931,9 @@ def make_enum(t, state): # type: (str, State) -> str if c in state.classes and e not in state.classes[c].enums: c = "@GlobalScope" + if not c in state.classes and c.startswith("_"): + c = c[1:] # Remove the underscore prefix + if c in state.classes and e in state.classes[c].enums: return ":ref:`{0}<enum_{1}_{0}>`".format(e, c) print_error("Unresolved enum '{}', file: {}".format(t, state.current_class), state) diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h index ddf4d1d85c..c0e07e0b8d 100644 --- a/drivers/dummy/rasterizer_dummy.h +++ b/drivers/dummy/rasterizer_dummy.h @@ -692,6 +692,7 @@ public: RID render_target_create() { return RID(); } void render_target_set_size(RID p_render_target, int p_width, int p_height) {} RID render_target_get_texture(RID p_render_target) const { return RID(); } + void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) {} void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) {} bool render_target_was_used(RID p_render_target) { return false; } void render_target_clear_used(RID p_render_target) {} @@ -781,7 +782,7 @@ public: void initialize() {} void begin_frame(double frame_step) {} void set_current_render_target(RID p_render_target) {} - void restore_render_target() {} + void restore_render_target(bool p_3d_was_drawn) {} void clear_render_target(const Color &p_color) {} void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0) {} void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) {} diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 9eb159cb31..7232d2d95a 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -943,7 +943,8 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } break; - default: {} + default: { + } } } } @@ -1032,7 +1033,8 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } break; - default: {} + default: { + } } } } diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index 71b826d689..d3c6b041a8 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -32,7 +32,6 @@ #include "core/os/os.h" #include "core/project_settings.h" -#include "drivers/gl_context/context_gl.h" #define _EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 #define _EXT_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 @@ -321,7 +320,7 @@ void RasterizerGLES2::set_current_render_target(RID p_render_target) { } } -void RasterizerGLES2::restore_render_target() { +void RasterizerGLES2::restore_render_target(bool p_3d_was_drawn) { ERR_FAIL_COND(storage->frame.current_rt == NULL); RasterizerStorageGLES2::RenderTarget *rt = storage->frame.current_rt; glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); @@ -410,7 +409,11 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re glDisable(GL_BLEND); glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo); glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); - glBindTexture(GL_TEXTURE_2D, rt->color); + if (rt->external.fbo != 0) { + glBindTexture(GL_TEXTURE_2D, rt->external.color); + } else { + glBindTexture(GL_TEXTURE_2D, rt->color); + } // TODO normals diff --git a/drivers/gles2/rasterizer_gles2.h b/drivers/gles2/rasterizer_gles2.h index ed4eeb84d2..eeed86e263 100644 --- a/drivers/gles2/rasterizer_gles2.h +++ b/drivers/gles2/rasterizer_gles2.h @@ -56,7 +56,7 @@ public: virtual void initialize(); virtual void begin_frame(double frame_step); virtual void set_current_render_target(RID p_render_target); - virtual void restore_render_target(); + virtual void restore_render_target(bool p_3d_was_drawn); virtual void clear_render_target(const Color &p_color); virtual void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0); virtual void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index de31288838..e790d5b031 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -1208,7 +1208,8 @@ void RasterizerSceneGLES2::_fill_render_list(InstanceBase **p_cull_result, int p } break; - default: {} + default: { + } } } } @@ -1369,7 +1370,8 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } break; - default: {} + default: { + } } } } @@ -1530,7 +1532,8 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } break; - default: {} + default: { + } } } } @@ -1550,7 +1553,8 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste case VS::INSTANCE_IMMEDIATE: { } break; - default: {} + default: { + } } } @@ -1566,8 +1570,10 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { if (s->index_array_len > 0) { glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0); + storage->info.render.vertices_count += s->index_array_len; } else { glDrawArrays(gl_primitive[s->primitive], 0, s->array_len); + storage->info.render.vertices_count += s->array_len; } /* if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) { @@ -1637,8 +1643,10 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { if (s->index_array_len > 0) { glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0); + storage->info.render.vertices_count += s->index_array_len; } else { glDrawArrays(gl_primitive[s->primitive], 0, s->array_len); + storage->info.render.vertices_count += s->array_len; } } @@ -1754,7 +1762,8 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { } } break; - default: {} + default: { + } } } @@ -2070,7 +2079,8 @@ void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shado } } break; - default: {} + default: { + } } } @@ -2182,6 +2192,8 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, float lightmap_energy = 1.0; bool prev_use_lightmap_capture = false; + storage->info.render.draw_call_count += p_element_count; + for (int i = 0; i < p_element_count; i++) { RenderList::Element *e = p_elements[i]; @@ -2389,11 +2401,17 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, if (e->owner != prev_owner || e->geometry != prev_geometry || skeleton != prev_skeleton) { _setup_geometry(e, skeleton); + storage->info.render.surface_switch_count++; } bool shader_rebind = false; if (rebind || material != prev_material) { + + storage->info.render.material_switch_count++; shader_rebind = _setup_material(material, p_reverse_cull, p_alpha_pass, Size2i(skeleton ? skeleton->size * 3 : 0, 0)); + if (shader_rebind) { + storage->info.render.shader_rebind_count++; + } } if (i == 0 || shader_rebind) { //first time must rebind @@ -2637,6 +2655,8 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const Transform cam_transform = p_cam_transform; + storage->info.render.object_count += p_cull_count; + GLuint current_fb = 0; Environment *env = NULL; @@ -2667,7 +2687,11 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const } else { state.render_no_shadows = false; - current_fb = storage->frame.current_rt->fbo; + if (storage->frame.current_rt->external.fbo != 0) { + current_fb = storage->frame.current_rt->external.fbo; + } else { + current_fb = storage->frame.current_rt->fbo; + } env = environment_owner.getornull(p_environment); viewport_width = storage->frame.current_rt->width; diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 92a4c2a3b4..1e32d8166f 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -1557,6 +1557,10 @@ void RasterizerStorageGLES2::shader_get_param_list(RID p_shader, List<PropertyIn case ShaderLanguage::TYPE_FLOAT: { pi.type = Variant::REAL; + if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { + pi.hint = PROPERTY_HINT_RANGE; + pi.hint_string = rtos(u.hint_range[0]) + "," + rtos(u.hint_range[1]) + "," + rtos(u.hint_range[2]); + } } break; case ShaderLanguage::TYPE_VEC2: { @@ -3737,7 +3741,8 @@ void RasterizerStorageGLES2::light_set_param(RID p_light, VS::LightParam p_param light->version++; light->instance_change_notify(true, false); } break; - default: {} + default: { + } } light->param[p_param] = p_value; @@ -4664,6 +4669,23 @@ void RasterizerStorageGLES2::_render_target_clear(RenderTarget *rt) { rt->fbo = 0; } + if (rt->external.fbo != 0) { + // free this + glDeleteFramebuffers(1, &rt->external.fbo); + + // clean up our texture + Texture *t = texture_owner.get(rt->external.texture); + t->alloc_height = 0; + t->alloc_width = 0; + t->width = 0; + t->height = 0; + t->active = false; + texture_owner.free(rt->external.texture); + memdelete(t); + + rt->external.fbo = 0; + } + if (rt->depth) { if (config.support_depth_texture) { glDeleteTextures(1, &rt->depth); @@ -4742,7 +4764,108 @@ RID RasterizerStorageGLES2::render_target_get_texture(RID p_render_target) const RenderTarget *rt = render_target_owner.getornull(p_render_target); ERR_FAIL_COND_V(!rt, RID()); - return rt->texture; + if (rt->external.fbo == 0) { + return rt->texture; + } else { + return rt->external.texture; + } +} + +void RasterizerStorageGLES2::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + + if (p_texture_id == 0) { + if (rt->external.fbo != 0) { + // free this + glDeleteFramebuffers(1, &rt->external.fbo); + + // clean up our texture + Texture *t = texture_owner.get(rt->external.texture); + t->alloc_height = 0; + t->alloc_width = 0; + t->width = 0; + t->height = 0; + t->active = false; + texture_owner.free(rt->external.texture); + memdelete(t); + + rt->external.fbo = 0; + rt->external.color = 0; + } + } else { + Texture *t; + + if (rt->external.fbo == 0) { + // create our fbo + glGenFramebuffers(1, &rt->external.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo); + + // allocate a texture + t = memnew(Texture); + + t->type = VS::TEXTURE_TYPE_2D; + t->flags = 0; + t->width = 0; + t->height = 0; + t->alloc_height = 0; + t->alloc_width = 0; + t->format = Image::FORMAT_RGBA8; + t->target = GL_TEXTURE_2D; + t->gl_format_cache = 0; + t->gl_internal_format_cache = 0; + t->gl_type_cache = 0; + t->data_size = 0; + t->compressed = false; + t->srgb = false; + t->total_data_size = 0; + t->ignore_mipmaps = false; + t->mipmaps = 1; + t->active = true; + t->tex_id = 0; + t->render_target = rt; + + rt->external.texture = texture_owner.make_rid(t); + } else { + // bind our frame buffer + glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo); + + // find our texture + t = texture_owner.get(rt->external.texture); + } + + // set our texture + t->tex_id = p_texture_id; + rt->external.color = p_texture_id; + + // size shouldn't be different + t->width = rt->width; + t->height = rt->height; + t->alloc_height = rt->width; + t->alloc_width = rt->height; + + // is there a point to setting the internal formats? we don't know them.. + + // set our texture as the destination for our framebuffer + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_texture_id, 0); + + // seeing we're rendering into this directly, better also use our depth buffer, just use our existing one :) + if (config.support_depth_texture) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); + } else { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); + } + + // check status and unbind + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + printf("framebuffer fail, status: %x\n", status); + } + + ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE); + } } void RasterizerStorageGLES2::render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) { @@ -4762,7 +4885,8 @@ void RasterizerStorageGLES2::render_target_set_flag(RID p_render_target, RenderT _render_target_allocate(rt); } break; - default: {} + default: { + } } } @@ -5208,18 +5332,72 @@ void RasterizerStorageGLES2::set_debug_generate_wireframes(bool p_generate) { } void RasterizerStorageGLES2::render_info_begin_capture() { + + info.snap = info.render; } void RasterizerStorageGLES2::render_info_end_capture() { + + info.snap.object_count = info.render.object_count - info.snap.object_count; + info.snap.draw_call_count = info.render.draw_call_count - info.snap.draw_call_count; + info.snap.material_switch_count = info.render.material_switch_count - info.snap.material_switch_count; + info.snap.surface_switch_count = info.render.surface_switch_count - info.snap.surface_switch_count; + info.snap.shader_rebind_count = info.render.shader_rebind_count - info.snap.shader_rebind_count; + info.snap.vertices_count = info.render.vertices_count - info.snap.vertices_count; } int RasterizerStorageGLES2::get_captured_render_info(VS::RenderInfo p_info) { - return get_render_info(p_info); + switch (p_info) { + case VS::INFO_OBJECTS_IN_FRAME: { + return info.snap.object_count; + } break; + case VS::INFO_VERTICES_IN_FRAME: { + return info.snap.vertices_count; + } break; + case VS::INFO_MATERIAL_CHANGES_IN_FRAME: { + return info.snap.material_switch_count; + } break; + case VS::INFO_SHADER_CHANGES_IN_FRAME: { + return info.snap.shader_rebind_count; + } break; + case VS::INFO_SURFACE_CHANGES_IN_FRAME: { + return info.snap.surface_switch_count; + } break; + case VS::INFO_DRAW_CALLS_IN_FRAME: { + return info.snap.draw_call_count; + } break; + default: { + return get_render_info(p_info); + } + } } int RasterizerStorageGLES2::get_render_info(VS::RenderInfo p_info) { - return 0; + switch (p_info) { + case VS::INFO_OBJECTS_IN_FRAME: + return info.render_final.object_count; + case VS::INFO_VERTICES_IN_FRAME: + return info.render_final.vertices_count; + case VS::INFO_MATERIAL_CHANGES_IN_FRAME: + return info.render_final.material_switch_count; + case VS::INFO_SHADER_CHANGES_IN_FRAME: + return info.render_final.shader_rebind_count; + case VS::INFO_SURFACE_CHANGES_IN_FRAME: + return info.render_final.surface_switch_count; + case VS::INFO_DRAW_CALLS_IN_FRAME: + return info.render_final.draw_call_count; + case VS::INFO_USAGE_VIDEO_MEM_TOTAL: + return 0; //no idea + case VS::INFO_VIDEO_MEM_USED: + return info.vertex_mem + info.texture_mem; + case VS::INFO_TEXTURE_MEM_USED: + return info.texture_mem; + case VS::INFO_VERTEX_MEM_USED: + return info.vertex_mem; + default: + return 0; //no idea either + } } void RasterizerStorageGLES2::initialize() { diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index 361f304a17..2ac68fa47a 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -1156,6 +1156,16 @@ public: Effect copy_screen_effect; + struct External { + GLuint fbo; + GLuint color; + RID texture; + + External() : + fbo(0) { + } + } external; + int width, height; bool flags[RENDER_TARGET_FLAG_MAX]; @@ -1176,6 +1186,7 @@ public: for (int i = 0; i < RENDER_TARGET_FLAG_MAX; ++i) { flags[i] = false; } + external.fbo = 0; } }; @@ -1187,6 +1198,7 @@ public: virtual RID render_target_create(); virtual void render_target_set_size(RID p_render_target, int p_width, int p_height); virtual RID render_target_get_texture(RID p_render_target) const; + virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id); virtual void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value); virtual bool render_target_was_used(RID p_render_target); diff --git a/drivers/gles2/shaders/effect_blur.glsl b/drivers/gles2/shaders/effect_blur.glsl index a531802c75..df79e89931 100644 --- a/drivers/gles2/shaders/effect_blur.glsl +++ b/drivers/gles2/shaders/effect_blur.glsl @@ -116,12 +116,13 @@ void main() { #ifdef GAUSSIAN_HORIZONTAL vec2 pix_size = pixel_size; pix_size *= 0.5; //reading from larger buffer, so use more samples + // sigma 2 vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pix_size, lod) * 0.214607; color += textureLod(source_color, uv_interp + vec2(1.0, 0.0) * pix_size, lod) * 0.189879; - color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.157305; + color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.131514; color += textureLod(source_color, uv_interp + vec2(3.0, 0.0) * pix_size, lod) * 0.071303; color += textureLod(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size, lod) * 0.189879; - color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.157305; + color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.131514; color += textureLod(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size, lod) * 0.071303; frag_color = color; #endif diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index ec8202d92c..30aa22732c 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -826,6 +826,8 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); } + state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * mesh->transform); + RasterizerStorageGLES3::Mesh *mesh_data = storage->mesh_owner.getornull(mesh->mesh); if (mesh_data) { @@ -834,7 +836,7 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur // materials are ignored in 2D meshes, could be added but many things (ie, lighting mode, reading from screen, etc) would break as they are not meant be set up at this point of drawing glBindVertexArray(s->array_id); - glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); + glVertexAttrib4f(VS::ARRAY_COLOR, mesh->modulate.r, mesh->modulate.g, mesh->modulate.b, mesh->modulate.a); if (s->index_array_len) { glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0); @@ -845,6 +847,8 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur glBindVertexArray(0); } } + state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform); + } break; case Item::Command::TYPE_MULTIMESH: { @@ -2043,7 +2047,7 @@ void RasterizerCanvasGLES3::initialize() { glEnableVertexAttribArray(VS::ARRAY_VERTEX); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0); glEnableVertexAttribArray(VS::ARRAY_TEX_UV); - glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(2)); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(8)); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind } diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index a4e042ae0e..bdffb1ecdc 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -32,7 +32,6 @@ #include "core/os/os.h" #include "core/project_settings.h" -#include "drivers/gl_context/context_gl.h" RasterizerStorage *RasterizerGLES3::get_storage() { @@ -253,11 +252,16 @@ void RasterizerGLES3::set_current_render_target(RID p_render_target) { } } -void RasterizerGLES3::restore_render_target() { +void RasterizerGLES3::restore_render_target(bool p_3d_was_drawn) { ERR_FAIL_COND(storage->frame.current_rt == NULL); RasterizerStorageGLES3::RenderTarget *rt = storage->frame.current_rt; - glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); + if (p_3d_was_drawn && rt->external.fbo != 0) { + // our external render buffer is now leading, render 2d into that. + glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo); + } else { + glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); + } glViewport(0, 0, rt->width, rt->height); } @@ -339,7 +343,11 @@ void RasterizerGLES3::blit_render_target_to_screen(RID p_render_target, const Re #if 1 Size2 win_size = OS::get_singleton()->get_window_size(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); + if (rt->external.fbo != 0) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo); + } else { + glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); + } glReadBuffer(GL_COLOR_ATTACHMENT0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); glBlitFramebuffer(0, 0, rt->width, rt->height, p_screen_rect.position.x, win_size.height - p_screen_rect.position.y - p_screen_rect.size.height, p_screen_rect.position.x + p_screen_rect.size.width, win_size.height - p_screen_rect.position.y, GL_COLOR_BUFFER_BIT, GL_NEAREST); diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 7541c55c82..ad0d004c9d 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -56,7 +56,7 @@ public: virtual void initialize(); virtual void begin_frame(double frame_step); virtual void set_current_render_target(RID p_render_target); - virtual void restore_render_target(); + virtual void restore_render_target(bool p_3d_was_drawn); virtual void clear_render_target(const Color &p_color); virtual void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0); virtual void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index df8bd9feb0..56a6c51460 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1278,7 +1278,8 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m } break; - default: {} + default: { + } } } @@ -1525,7 +1526,8 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo } } break; - default: {} + default: { + } } } @@ -1847,7 +1849,8 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) { } } break; - default: {} + default: { + } } } @@ -3248,7 +3251,8 @@ void RasterizerSceneGLES3::_fill_render_list(InstanceBase **p_cull_result, int p } } break; - default: {} + default: { + } } } } @@ -3660,10 +3664,14 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p if (!env || storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT] || storage->frame.current_rt->width < 4 || storage->frame.current_rt->height < 4) { //no post process on small render targets //no environment or transparent render, simply return and convert to SRGB - glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); + if (storage->frame.current_rt->external.fbo != 0) { + glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->external.fbo); + } else { + glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); + } glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->effects.mip_maps[0].color); - storage->shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, true); + storage->shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_KEEP_3D_LINEAR]); storage->shaders.copy.set_conditional(CopyShaderGLES3::V_FLIP, storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP]); storage->shaders.copy.set_conditional(CopyShaderGLES3::DISABLE_ALPHA, !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]); storage->shaders.copy.bind(); @@ -4003,7 +4011,11 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height); } - glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); + if (storage->frame.current_rt->external.fbo != 0) { + glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->external.fbo); + } else { + glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); + } glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, composite_from); @@ -4415,7 +4427,8 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); break; - default: {} + default: { + } } } diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 51e36c1d7e..be44d62efc 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -642,7 +642,8 @@ void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_ p_flags &= ~VS::TEXTURE_FLAG_FILTER; } } break; - default: {} + default: { + } } #endif @@ -2751,7 +2752,8 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy gui[14] = v.origin.z; gui[15] = 1; } break; - default: {} + default: { + } } } @@ -2919,7 +2921,8 @@ _FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, gui[i] = value[i].real; } } break; - default: {} + default: { + } } } @@ -2962,7 +2965,8 @@ _FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, zeromem(data, 64); } break; - default: {} + default: { + } } } @@ -4131,7 +4135,7 @@ void RasterizerStorageGLES3::mesh_render_blend_shapes(Surface *s, const float *p for (int ti = 0; ti < mtc; ti++) { float weight = p_weights[ti]; - if (weight < 0.001) //not bother with this one + if (weight < 0.00001) //not bother with this one continue; glBindVertexArray(s->blend_shapes[ti].array_id); @@ -5246,7 +5250,8 @@ void RasterizerStorageGLES3::light_set_param(RID p_light, VS::LightParam p_param light->version++; light->instance_change_notify(true, false); } break; - default: {} + default: { + } } light->param[p_param] = p_value; @@ -5448,7 +5453,8 @@ AABB RasterizerStorageGLES3::light_get_aabb(RID p_light) const { return AABB(); } break; - default: {} + default: { + } } ERR_FAIL_V(AABB()); @@ -6080,7 +6086,7 @@ void RasterizerStorageGLES3::particles_set_amount(RID p_particles, int p_amount) for (int j = 0; j < 6; j++) { glEnableVertexAttribArray(j); - glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4 * 6, CAST_INT_TO_UCHAR_PTR((j * 16))); + glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4 * 6, CAST_INT_TO_UCHAR_PTR(j * 16)); } } @@ -6794,6 +6800,24 @@ void RasterizerStorageGLES3::_render_target_clear(RenderTarget *rt) { glDeleteTextures(1, &rt->exposure.color); rt->exposure.fbo = 0; } + + if (rt->external.fbo != 0) { + // free this + glDeleteFramebuffers(1, &rt->external.fbo); + + // clean up our texture + Texture *t = texture_owner.get(rt->external.texture); + t->alloc_height = 0; + t->alloc_width = 0; + t->width = 0; + t->height = 0; + t->active = false; + texture_owner.free(rt->external.texture); + memdelete(t); + + rt->external.fbo = 0; + } + Texture *tex = texture_owner.get(rt->texture); tex->alloc_height = 0; tex->alloc_width = 0; @@ -7249,7 +7273,99 @@ RID RasterizerStorageGLES3::render_target_get_texture(RID p_render_target) const RenderTarget *rt = render_target_owner.getornull(p_render_target); ERR_FAIL_COND_V(!rt, RID()); - return rt->texture; + if (rt->external.fbo == 0) { + return rt->texture; + } else { + return rt->external.texture; + } +} + +void RasterizerStorageGLES3::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + + if (p_texture_id == 0) { + if (rt->external.fbo != 0) { + // free this + glDeleteFramebuffers(1, &rt->external.fbo); + + // clean up our texture + Texture *t = texture_owner.get(rt->external.texture); + t->alloc_height = 0; + t->alloc_width = 0; + t->width = 0; + t->height = 0; + t->active = false; + texture_owner.free(rt->external.texture); + memdelete(t); + + rt->external.fbo = 0; + } + } else { + Texture *t; + + if (rt->external.fbo == 0) { + // create our fbo + glGenFramebuffers(1, &rt->external.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo); + + // allocate a texture + t = memnew(Texture); + + t->type = VS::TEXTURE_TYPE_2D; + t->flags = 0; + t->width = 0; + t->height = 0; + t->alloc_height = 0; + t->alloc_width = 0; + t->format = Image::FORMAT_RGBA8; + t->target = GL_TEXTURE_2D; + t->gl_format_cache = 0; + t->gl_internal_format_cache = 0; + t->gl_type_cache = 0; + t->data_size = 0; + t->compressed = false; + t->srgb = false; + t->total_data_size = 0; + t->ignore_mipmaps = false; + t->mipmaps = 1; + t->active = true; + t->tex_id = 0; + t->render_target = rt; + + rt->external.texture = texture_owner.make_rid(t); + } else { + // bind our frame buffer + glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo); + + // find our texture + t = texture_owner.get(rt->external.texture); + } + + // set our texture + t->tex_id = p_texture_id; + + // size shouldn't be different + t->width = rt->width; + t->height = rt->height; + t->alloc_height = rt->width; + t->alloc_width = rt->height; + + // is there a point to setting the internal formats? we don't know them.. + + // set our texture as the destination for our framebuffer + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_texture_id, 0); + + // check status and unbind + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + printf("framebuffer fail, status: %x\n", status); + } + + ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE); + } } void RasterizerStorageGLES3::render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) { @@ -7269,7 +7385,8 @@ void RasterizerStorageGLES3::render_target_set_flag(RID p_render_target, RenderT _render_target_allocate(rt); } break; - default: {} + default: { + } } } bool RasterizerStorageGLES3::render_target_was_used(RID p_render_target) { diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 330139e179..92916ed808 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -1345,6 +1345,15 @@ public: fbo(0) {} } exposure; + // External FBO to render our final result to (mostly used for ARVR) + struct External { + GLuint fbo; + RID texture; + + External() : + fbo(0) {} + } external; + uint64_t last_exposure_tick; int width, height; @@ -1366,6 +1375,7 @@ public: msaa(VS::VIEWPORT_MSAA_DISABLED) { exposure.fbo = 0; buffers.fbo = 0; + external.fbo = 0; for (int i = 0; i < RENDER_TARGET_FLAG_MAX; i++) { flags[i] = false; } @@ -1383,6 +1393,7 @@ public: virtual RID render_target_create(); virtual void render_target_set_size(RID p_render_target, int p_width, int p_height); virtual RID render_target_get_texture(RID p_render_target) const; + virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id); virtual void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value); virtual bool render_target_was_used(RID p_render_target); diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index 1ed30986bf..be2c34ba07 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -289,7 +289,9 @@ private: glUniformMatrix4fv(p_uniform, 1, false, matrix); } break; - default: { ERR_FAIL(); } // do nothing + default: { + ERR_FAIL(); + } // do nothing } } diff --git a/drivers/gles3/shaders/effect_blur.glsl b/drivers/gles3/shaders/effect_blur.glsl index fc15ca31b1..ff5a9f326f 100644 --- a/drivers/gles3/shaders/effect_blur.glsl +++ b/drivers/gles3/shaders/effect_blur.glsl @@ -117,12 +117,13 @@ void main() { #ifdef GAUSSIAN_HORIZONTAL vec2 pix_size = pixel_size; pix_size *= 0.5; //reading from larger buffer, so use more samples + // sigma 2 vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pix_size, lod) * 0.214607; color += textureLod(source_color, uv_interp + vec2(1.0, 0.0) * pix_size, lod) * 0.189879; - color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.157305; + color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.131514; color += textureLod(source_color, uv_interp + vec2(3.0, 0.0) * pix_size, lod) * 0.071303; color += textureLod(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size, lod) * 0.189879; - color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.157305; + color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.131514; color += textureLod(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size, lod) * 0.071303; frag_color = color; #endif diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index e7550903a8..2778590658 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -108,6 +108,7 @@ void OS_Unix::initialize_debugging() { if (ScriptDebugger::get_singleton() != NULL) { struct sigaction action; + memset(&action, 0, sizeof(action)); action.sa_handler = handle_interrupt; sigaction(SIGINT, &action, NULL); } diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index 2f705ec87d..9eac15f774 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -394,7 +394,7 @@ DirAccessWindows::DirAccessWindows() { if (mask & (1 << i)) { //DRIVE EXISTS - drives[drive_count] = 'a' + i; + drives[drive_count] = 'A' + i; drive_count++; } } diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index babb393dcb..c4c5e0d709 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -197,12 +197,14 @@ void FileAccessWindows::seek(size_t p_position) { last_error = OK; if (fseek(f, p_position, SEEK_SET)) check_errors(); + prev_op = 0; } void FileAccessWindows::seek_end(int64_t p_position) { ERR_FAIL_COND(!f); if (fseek(f, p_position, SEEK_END)) check_errors(); + prev_op = 0; } size_t FileAccessWindows::get_position() const { @@ -234,6 +236,12 @@ bool FileAccessWindows::eof_reached() const { uint8_t FileAccessWindows::get_8() const { ERR_FAIL_COND_V(!f, 0); + if (flags == READ_WRITE || flags == WRITE_READ) { + if (prev_op == WRITE) { + fflush(f); + } + prev_op = READ; + } uint8_t b; if (fread(&b, 1, 1, f) == 0) { check_errors(); @@ -246,6 +254,12 @@ uint8_t FileAccessWindows::get_8() const { int FileAccessWindows::get_buffer(uint8_t *p_dst, int p_length) const { ERR_FAIL_COND_V(!f, -1); + if (flags == READ_WRITE || flags == WRITE_READ) { + if (prev_op == WRITE) { + fflush(f); + } + prev_op = READ; + } int read = fread(p_dst, 1, p_length, f); check_errors(); return read; @@ -260,16 +274,34 @@ void FileAccessWindows::flush() { ERR_FAIL_COND(!f); fflush(f); + if (prev_op == WRITE) + prev_op = 0; } void FileAccessWindows::store_8(uint8_t p_dest) { ERR_FAIL_COND(!f); + if (flags == READ_WRITE || flags == WRITE_READ) { + if (prev_op == READ) { + if (last_error != ERR_FILE_EOF) { + fseek(f, 0, SEEK_CUR); + } + } + prev_op = WRITE; + } fwrite(&p_dest, 1, 1, f); } void FileAccessWindows::store_buffer(const uint8_t *p_src, int p_length) { ERR_FAIL_COND(!f); + if (flags == READ_WRITE || flags == WRITE_READ) { + if (prev_op == READ) { + if (last_error != ERR_FILE_EOF) { + fseek(f, 0, SEEK_CUR); + } + } + prev_op = WRITE; + } ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != p_length); } @@ -310,6 +342,7 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { FileAccessWindows::FileAccessWindows() : f(NULL), flags(0), + prev_op(0), last_error(OK) { } FileAccessWindows::~FileAccessWindows() { diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index c105eb5265..35b1f0b2d8 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -47,6 +47,7 @@ class FileAccessWindows : public FileAccess { FILE *f; int flags; void check_errors() const; + mutable int prev_op; mutable Error last_error; String path; String path_src; diff --git a/drivers/windows/semaphore_windows.cpp b/drivers/windows/semaphore_windows.cpp index 718bb81836..55b47e45eb 100644 --- a/drivers/windows/semaphore_windows.cpp +++ b/drivers/windows/semaphore_windows.cpp @@ -54,7 +54,8 @@ int SemaphoreWindows::get() const { case WAIT_TIMEOUT: { return 0; } break; - default: {} + default: { + } } ERR_FAIL_V(-1); diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp index d1f63f4866..8674d24af6 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.cpp +++ b/drivers/xaudio2/audio_driver_xaudio2.cpp @@ -45,12 +45,12 @@ Error AudioDriverXAudio2::init() { pcm_open = false; samples_in = NULL; - mix_rate = 48000; + mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); // FIXME: speaker_mode seems unused in the Xaudio2 driver so far speaker_mode = SPEAKER_MODE_STEREO; channels = 2; - int latency = GLOBAL_DEF_RST("audio/output_latency", 25); + int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); buffer_size = closest_power_of_2(latency * mix_rate / 1000); samples_in = memnew_arr(int32_t, buffer_size * channels); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 8807a01f64..9f6fa74dd3 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -109,9 +109,17 @@ public: ERR_FAIL_COND_V(key == -1, false); String name = p_name; - if (name == "time") { + if (name == "time" || name == "frame") { float new_time = p_value; + + if (name == "frame") { + float fps = animation->get_step(); + if (fps > 0) { + fps = 1.0 / fps; + } + new_time /= fps; + } if (new_time == key_ofs) return true; @@ -413,6 +421,13 @@ public: if (name == "time") { r_ret = key_ofs; return true; + } else if (name == "frame") { + float fps = animation->get_step(); + if (fps > 0) { + fps = 1.0 / fps; + } + r_ret = key_ofs * fps; + return true; } else if (name == "easing") { r_ret = animation->track_get_key_transition(track, key); return true; @@ -527,7 +542,12 @@ public: int key = animation->track_find_key(track, key_ofs, true); ERR_FAIL_COND(key == -1); - p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01")); + if (use_fps && animation->get_step() > 0) { + float max_frame = animation->get_length() / animation->get_step(); + p_list->push_back(PropertyInfo(Variant::REAL, "frame", PROPERTY_HINT_RANGE, "0," + rtos(max_frame) + ",0.01")); + } else { + p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01")); + } switch (animation->track_get_type(track)) { @@ -648,6 +668,7 @@ public: PropertyInfo hint; NodePath base; + bool use_fps; void notify_change() { @@ -658,7 +679,13 @@ public: return root_path; } + void set_use_fps(bool p_enable) { + use_fps = p_enable; + _change_notify(); + } + AnimationTrackKeyEdit() { + use_fps = false; key_ofs = 0; track = -1; setting = false; @@ -690,6 +717,9 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) { return; p_new_len = MAX(0.001, p_new_len); + if (use_fps && animation->get_step() > 0) { + p_new_len *= animation->get_step(); + } editing = true; undo_redo->create_action(TTR("Change Animation Length")); @@ -887,24 +917,53 @@ void AnimationTimelineEdit::_notification(int p_what) { decimals = 0; } - for (int i = 0; i < zoomw; i++) { + if (use_fps) { + + float step_size = animation->get_step(); + if (step_size > 0) { + + int prev_frame_ofs = -10000000; - float pos = get_value() + double(i) / scale; - float prev = get_value() + (double(i) - 1.0) / scale; + for (int i = 0; i < zoomw; i++) { - int sc = int(Math::floor(pos * SC_ADJ)); - int prev_sc = int(Math::floor(prev * SC_ADJ)); - bool sub = (sc % SC_ADJ); + float pos = get_value() + double(i) / scale; + float prev = get_value() + (double(i) - 1.0) / scale; - if ((sc / step) != (prev_sc / step) || (prev_sc < 0 && sc >= 0)) { + int frame = pos / step_size; + int prev_frame = prev / step_size; - int scd = sc < 0 ? prev_sc : sc; - draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor); - draw_string(font, Point2(get_name_limit() + i + 3, (h - font->get_height()) / 2 + font->get_ascent()).floor(), String::num((scd - (scd % step)) / double(SC_ADJ), decimals), sub ? color_time_dec : color_time_sec, zoomw - i); + bool sub = Math::floor(prev) == Math::floor(pos); + + if (frame != prev_frame && i >= prev_frame_ofs) { + + draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor, Math::round(EDSCALE)); + + draw_string(font, Point2(get_name_limit() + i + 3 * EDSCALE, (h - font->get_height()) / 2 + font->get_ascent()).floor(), itos(frame), sub ? color_time_dec : color_time_sec, zoomw - i); + prev_frame_ofs = i + font->get_string_size(itos(frame)).x + 5 * EDSCALE; + } + } + } + + } else { + for (int i = 0; i < zoomw; i++) { + + float pos = get_value() + double(i) / scale; + float prev = get_value() + (double(i) - 1.0) / scale; + + int sc = int(Math::floor(pos * SC_ADJ)); + int prev_sc = int(Math::floor(prev * SC_ADJ)); + bool sub = (sc % SC_ADJ); + + if ((sc / step) != (prev_sc / step) || (prev_sc < 0 && sc >= 0)) { + + int scd = sc < 0 ? prev_sc : sc; + draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor, Math::round(EDSCALE)); + draw_string(font, Point2(get_name_limit() + i + 3, (h - font->get_height()) / 2 + font->get_ascent()).floor(), String::num((scd - (scd % step)) / double(SC_ADJ), decimals), sub ? color_time_dec : color_time_sec, zoomw - i); + } } } - draw_line(Vector2(0, get_size().height), get_size(), linecolor); + draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } } @@ -961,7 +1020,11 @@ void AnimationTimelineEdit::update_values() { return; editing = true; - length->set_value(animation->get_length()); + if (use_fps && animation->get_step() > 0) { + length->set_value(animation->get_length() / animation->get_step()); + } else { + length->set_value(animation->get_length()); + } loop->set_pressed(animation->has_loop()); editing = false; } @@ -978,7 +1041,7 @@ void AnimationTimelineEdit::_play_position_draw() { if (px >= get_name_limit() && px < (play_position->get_size().width - get_buttons_width())) { Color color = get_color("accent_color", "Editor"); - play_position->draw_line(Point2(px, 0), Point2(px, h), color); + play_position->draw_line(Point2(px, 0), Point2(px, h), color, Math::round(EDSCALE)); } } @@ -1046,6 +1109,15 @@ void AnimationTimelineEdit::_gui_input(const Ref<InputEvent> &p_event) { } } +void AnimationTimelineEdit::set_use_fps(bool p_use_fps) { + use_fps = p_use_fps; + update_values(); + update(); +} +bool AnimationTimelineEdit::is_using_fps() const { + return use_fps; +} + void AnimationTimelineEdit::set_hscroll(HScrollBar *p_hscroll) { hscroll = p_hscroll; @@ -1072,6 +1144,7 @@ void AnimationTimelineEdit::_bind_methods() { AnimationTimelineEdit::AnimationTimelineEdit() { + use_fps = false; editing = false; name_limit = 150; zoom = NULL; @@ -1099,7 +1172,7 @@ AnimationTimelineEdit::AnimationTimelineEdit() { len_hb->add_child(time_icon); length = memnew(EditorSpinSlider); length->set_min(0.001); - length->set_max(3600); + length->set_max(36000); length->set_step(0.01); length->set_allow_greater(true); length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0)); @@ -1237,7 +1310,7 @@ void AnimationTrackEdit::_notification(int p_what) { string_pos = string_pos.floor(); draw_string(font, string_pos, text, text_color, limit - ofs - hsep); - draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor); + draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor, Math::round(EDSCALE)); } // KEYFAMES // @@ -1297,7 +1370,7 @@ void AnimationTrackEdit::_notification(int p_what) { Ref<Texture> down_icon = get_icon("select_arrow", "Tree"); - draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor); + draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; { @@ -1344,7 +1417,7 @@ void AnimationTrackEdit::_notification(int p_what) { } ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; } @@ -1377,7 +1450,7 @@ void AnimationTrackEdit::_notification(int p_what) { } ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; } @@ -1410,7 +1483,7 @@ void AnimationTrackEdit::_notification(int p_what) { } ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; } @@ -1428,17 +1501,17 @@ void AnimationTrackEdit::_notification(int p_what) { } if (in_group) { - draw_line(Vector2(timeline->get_name_limit(), get_size().height), get_size(), linecolor); + draw_line(Vector2(timeline->get_name_limit(), get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } else { - draw_line(Vector2(0, get_size().height), get_size(), linecolor); + draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } if (dropping_at != 0) { Color drop_color = get_color("accent_color", "Editor"); if (dropping_at < 0) { - draw_line(Vector2(0, 0), Vector2(get_size().width, 0), drop_color); + draw_line(Vector2(0, 0), Vector2(get_size().width, 0), drop_color, Math::round(EDSCALE)); } else { - draw_line(Vector2(0, get_size().height), get_size(), drop_color); + draw_line(Vector2(0, get_size().height), get_size(), drop_color, Math::round(EDSCALE)); } } } @@ -1487,7 +1560,7 @@ void AnimationTrackEdit::draw_key_link(int p_index, float p_pixels_sec, int p_x, int from_x = MAX(p_x, p_clip_left); int to_x = MIN(p_next_x, p_clip_right); - draw_line(Point2(from_x + 1, get_size().height / 2), Point2(to_x, get_size().height / 2), color, 2); + draw_line(Point2(from_x + 1, get_size().height / 2), Point2(to_x, get_size().height / 2), color, Math::round(2 * EDSCALE)); } void AnimationTrackEdit::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) { @@ -1667,7 +1740,7 @@ void AnimationTrackEdit::_play_position_draw() { if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) { Color color = get_color("accent_color", "Editor"); - play_position->draw_line(Point2(px, 0), Point2(px, h), color); + play_position->draw_line(Point2(px, 0), Point2(px, h), color, Math::round(EDSCALE)); } } @@ -2370,9 +2443,9 @@ void AnimationTrackEditGroup::_notification(int p_what) { Color linecolor = color; linecolor.a = 0.2; - draw_line(Point2(), Point2(get_size().width, 0), linecolor); - draw_line(Point2(timeline->get_name_limit(), 0), Point2(timeline->get_name_limit(), get_size().height), linecolor); - draw_line(Point2(get_size().width - timeline->get_buttons_width(), 0), Point2(get_size().width - timeline->get_buttons_width(), get_size().height), linecolor); + draw_line(Point2(), Point2(get_size().width, 0), linecolor, Math::round(EDSCALE)); + draw_line(Point2(timeline->get_name_limit(), 0), Point2(timeline->get_name_limit(), get_size().height), linecolor, Math::round(EDSCALE)); + draw_line(Point2(get_size().width - timeline->get_buttons_width(), 0), Point2(get_size().width - timeline->get_buttons_width(), get_size().height), linecolor, Math::round(EDSCALE)); int ofs = 0; draw_texture(icon, Point2(ofs, int(get_size().height - icon->get_height()) / 2)); @@ -2383,7 +2456,7 @@ void AnimationTrackEditGroup::_notification(int p_what) { if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) { Color accent = get_color("accent_color", "Editor"); - draw_line(Point2(px, 0), Point2(px, get_size().height), accent); + draw_line(Point2(px, 0), Point2(px, get_size().height), accent, Math::round(EDSCALE)); } } } @@ -2462,10 +2535,12 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) { hscroll->show(); edit->set_disabled(false); step->set_block_signals(true); - step->set_value(animation->get_step()); + + _update_step_spinbox(); step->set_block_signals(false); step->set_read_only(false); snap->set_disabled(false); + snap_mode->set_disabled(false); } else { hscroll->hide(); edit->set_disabled(true); @@ -2474,6 +2549,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) { step->set_block_signals(false); step->set_read_only(true); snap->set_disabled(true); + snap_mode->set_disabled(true); } } @@ -2518,6 +2594,43 @@ void AnimationTrackEditor::update_keying() { bool AnimationTrackEditor::has_keying() const { return keying; } +Dictionary AnimationTrackEditor::get_state() const { + Dictionary state; + state["fps_mode"] = timeline->is_using_fps(); + state["zoom"] = zoom->get_value(); + state["offset"] = timeline->get_value(); + state["v_scroll"] = scroll->get_v_scrollbar()->get_value(); + return state; +} +void AnimationTrackEditor::set_state(const Dictionary &p_state) { + if (p_state.has("fps_mode")) { + bool fps_mode = p_state["fps_mode"]; + if (fps_mode) { + snap_mode->select(1); + } else { + snap_mode->select(0); + } + _snap_mode_changed(snap_mode->get_selected()); + } else { + snap_mode->select(0); + _snap_mode_changed(snap_mode->get_selected()); + } + if (p_state.has("zoom")) { + zoom->set_value(p_state["zoom"]); + } else { + zoom->set_value(1.0); + } + if (p_state.has("offset")) { + timeline->set_value(p_state["offset"]); + } else { + timeline->set_value(0); + } + if (p_state.has("v_scroll")) { + scroll->get_v_scrollbar()->set_value(p_state["v_scroll"]); + } else { + scroll->get_v_scrollbar()->set_value(0); + } +} void AnimationTrackEditor::cleanup() { set_animation(Ref<Animation>()); @@ -3417,6 +3530,34 @@ void AnimationTrackEditor::_animation_changed() { call_deferred("_animation_update"); } +void AnimationTrackEditor::_snap_mode_changed(int p_mode) { + + timeline->set_use_fps(p_mode == 1); + if (key_edit) { + key_edit->set_use_fps(p_mode == 1); + } + _update_step_spinbox(); +} + +void AnimationTrackEditor::_update_step_spinbox() { + if (!animation.is_valid()) { + return; + } + step->set_block_signals(true); + + if (timeline->is_using_fps()) { + if (animation->get_step() == 0) { + step->set_value(0); + } else { + step->set_value(1.0 / animation->get_step()); + } + + } else { + step->set_value(animation->get_step()); + } + + step->set_block_signals(false); +} void AnimationTrackEditor::_animation_update() { timeline->update(); @@ -3454,9 +3595,7 @@ void AnimationTrackEditor::_animation_update() { bezier_edit->update(); - step->set_block_signals(true); - step->set_value(animation->get_step()); - step->set_block_signals(false); + _update_step_spinbox(); animation_changing_awaiting_update = false; } @@ -3497,12 +3636,18 @@ void AnimationTrackEditor::_update_scroll(double) { void AnimationTrackEditor::_update_step(double p_new_step) { undo_redo->create_action(TTR("Change Animation Step")); - undo_redo->add_do_method(animation.ptr(), "set_step", p_new_step); + float step_value = p_new_step; + if (timeline->is_using_fps()) { + if (step_value != 0.0) { + step_value = 1.0 / step_value; + } + } + undo_redo->add_do_method(animation.ptr(), "set_step", step_value); undo_redo->add_undo_method(animation.ptr(), "set_step", animation->get_step()); step->set_block_signals(true); undo_redo->commit_action(); step->set_block_signals(false); - emit_signal("animation_step_changed", p_new_step); + emit_signal("animation_step_changed", step_value); } void AnimationTrackEditor::_update_length(double p_new_len) { @@ -4787,6 +4932,7 @@ void AnimationTrackEditor::_bind_methods() { ClassDB::bind_method("_edit_menu_pressed", &AnimationTrackEditor::_edit_menu_pressed); ClassDB::bind_method("_view_group_toggle", &AnimationTrackEditor::_view_group_toggle); ClassDB::bind_method("_selection_changed", &AnimationTrackEditor::_selection_changed); + ClassDB::bind_method("_snap_mode_changed", &AnimationTrackEditor::_snap_mode_changed); ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::REAL, "position"), PropertyInfo(Variant::BOOL, "drag"))); ADD_SIGNAL(MethodInfo("keying_changed")); @@ -4875,7 +5021,7 @@ AnimationTrackEditor::AnimationTrackEditor() { bottom_hb->add_child(memnew(VSeparator)); snap = memnew(ToolButton); - snap->set_text(TTR("Snap (s): ")); + snap->set_text(TTR("Snap: ")); bottom_hb->add_child(snap); snap->set_disabled(true); snap->set_toggle_mode(true); @@ -4883,7 +5029,7 @@ AnimationTrackEditor::AnimationTrackEditor() { step = memnew(EditorSpinSlider); step->set_min(0); - step->set_max(1000); + step->set_max(1000000); step->set_step(0.01); step->set_hide_slider(true); step->set_custom_minimum_size(Size2(100, 0) * EDSCALE); @@ -4892,6 +5038,13 @@ AnimationTrackEditor::AnimationTrackEditor() { step->connect("value_changed", this, "_update_step"); step->set_read_only(true); + snap_mode = memnew(OptionButton); + snap_mode->add_item(TTR("Seconds")); + snap_mode->add_item(TTR("FPS")); + bottom_hb->add_child(snap_mode); + snap_mode->connect("item_selected", this, "_snap_mode_changed"); + snap_mode->set_disabled(true); + bottom_hb->add_child(memnew(VSeparator)); zoom_icon = memnew(TextureRect); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 29ce4f189e..5ac5999b68 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -76,6 +76,7 @@ class AnimationTimelineEdit : public Range { Rect2 hsize_rect; bool editing; + bool use_fps; bool panning_timeline; float panning_timeline_from; @@ -110,6 +111,9 @@ public: void update_values(); + void set_use_fps(bool p_use_fps); + bool is_using_fps() const; + void set_hscroll(HScrollBar *p_hscroll); AnimationTimelineEdit(); @@ -303,7 +307,9 @@ class AnimationTrackEditor : public VBoxContainer { EditorSpinSlider *step; TextureRect *zoom_icon; ToolButton *snap; + OptionButton *snap_mode; + void _snap_mode_changed(int p_mode); Vector<AnimationTrackEdit *> track_edits; Vector<AnimationTrackEditGroup *> groups; @@ -328,6 +334,8 @@ class AnimationTrackEditor : public VBoxContainer { void _new_track_node_selected(NodePath p_path); void _new_track_property_selected(String p_name); + void _update_step_spinbox(); + PropertySelector *prop_selector; PropertySelector *method_selector; SceneTreeDialog *pick_track; @@ -484,6 +492,9 @@ public: void update_keying(); bool has_keying() const; + Dictionary get_state() const; + void set_state(const Dictionary &p_state); + void cleanup(); void set_anim_pos(float p_pos); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 0b06839ecf..134384c167 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -30,6 +30,7 @@ #include "code_editor.h" +#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/string_builder.h" #include "editor/editor_scale.h" @@ -432,7 +433,11 @@ void FindReplaceBar::_search_text_changed(const String &p_text) { void FindReplaceBar::_search_text_entered(const String &p_text) { - search_next(); + if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + search_prev(); + } else { + search_next(); + } } void FindReplaceBar::_replace_text_entered(const String &p_text) { diff --git a/editor/collada/collada.cpp b/editor/collada/collada.cpp index 94e6d4ded0..94a37a3118 100644 --- a/editor/collada/collada.cpp +++ b/editor/collada/collada.cpp @@ -166,7 +166,8 @@ Transform Collada::Node::compute_transform(Collada &state) const { } } break; - default: {} + default: { + } } xform = xform * xform_step; diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index f775a0a14b..685c5de76c 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -37,6 +37,25 @@ #include "scene/gui/label.h" #include "scene/gui/popup_menu.h" +static Node *_find_first_script(Node *p_root, Node *p_node) { + if (p_node != p_root && p_node->get_owner() != p_root) { + return NULL; + } + if (!p_node->get_script().is_null()) { + return p_node; + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + + Node *ret = _find_first_script(p_root, p_node->get_child(i)); + if (ret) { + return ret; + } + } + + return NULL; +} + class ConnectDialogBinds : public Object { GDCLASS(ConnectDialogBinds, Object); @@ -122,17 +141,8 @@ void ConnectDialog::_tree_node_selected() { Node *current = tree->get_selected(); - if (!current) { - make_callback->hide(); - return; - } - - if (current->get_script().is_null()) - make_callback->hide(); - else - make_callback->show(); - - dst_path->set_text(source->get_path_to(current)); + dst_path = source->get_path_to(current); + get_ok()->set_disabled(false); } /* @@ -160,7 +170,9 @@ void ConnectDialog::_add_bind() { case Variant::BASIS: value = Basis(); break; case Variant::TRANSFORM: value = Transform(); break; case Variant::COLOR: value = Color(); break; - default: { ERR_FAIL(); } break; + default: { + ERR_FAIL(); + } break; } ERR_FAIL_COND(value.get_type() == Variant::NIL); @@ -193,6 +205,7 @@ void ConnectDialog::_notification(int p_what) { void ConnectDialog::_bind_methods() { + ClassDB::bind_method("_advanced_pressed", &ConnectDialog::_advanced_pressed); ClassDB::bind_method("_cancel", &ConnectDialog::_cancel_pressed); ClassDB::bind_method("_tree_node_selected", &ConnectDialog::_tree_node_selected); ClassDB::bind_method("_add_bind", &ConnectDialog::_add_bind); @@ -213,7 +226,7 @@ StringName ConnectDialog::get_signal_name() const { NodePath ConnectDialog::get_dst_path() const { - return dst_path->get_text(); + return dst_path; } void ConnectDialog::set_dst_node(Node *p_node) { @@ -270,8 +283,13 @@ void ConnectDialog::init(Connection c, bool bEdit) { tree->set_selected(NULL); tree->set_marked(source, true); - set_dst_node(static_cast<Node *>(c.target)); - set_dst_method(c.method); + if (c.target) { + get_ok()->set_disabled(false); + set_dst_node(static_cast<Node *>(c.target)); + set_dst_method(c.method); + } else { + get_ok()->set_disabled(true); + } bool bDeferred = (c.flags & CONNECT_DEFERRED) == CONNECT_DEFERRED; bool bOneshot = (c.flags & CONNECT_ONESHOT) == CONNECT_ONESHOT; @@ -286,6 +304,36 @@ void ConnectDialog::init(Connection c, bool bEdit) { bEditMode = bEdit; } +void ConnectDialog::popup_dialog(const String &p_for_signal, bool p_advanced) { + + advanced->set_pressed(p_advanced); + from_signal->set_text(p_for_signal); + error_label->add_color_override("font_color", get_color("error_color", "Editor")); + + if (p_advanced) { + + popup_centered(Size2(900, 500) * EDSCALE); + connect_to_label->set_text("Connect to Node:"); + tree->set_connect_to_script_mode(false); + error_label->hide(); + } else { + popup_centered(Size2(700, 500) * EDSCALE); + connect_to_label->set_text("Connect to Script:"); + tree->set_connect_to_script_mode(true); + + if (!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root())) { + error_label->show(); + } else { + error_label->hide(); + } + } +} + +void ConnectDialog::_advanced_pressed() { + vbc_right->set_visible(advanced->is_pressed()); + popup_dialog(from_signal->get_text(), advanced->is_pressed()); +} + ConnectDialog::ConnectDialog() { VBoxContainer *vbc = memnew(VBoxContainer); @@ -299,15 +347,27 @@ ConnectDialog::ConnectDialog() { main_hb->add_child(vbc_left); vbc_left->set_h_size_flags(SIZE_EXPAND_FILL); + from_signal = memnew(LineEdit); + from_signal->set_editable(false); + vbc_left->add_margin_child(TTR("From Signal:"), from_signal); + tree = memnew(SceneTreeEditor(false)); tree->get_scene_tree()->connect("item_activated", this, "_ok"); tree->connect("node_selected", this, "_tree_node_selected"); + tree->set_connect_to_script_mode(true); - vbc_left->add_margin_child(TTR("Connect To Node:"), tree, true); + Node *mc = vbc_left->add_margin_child(TTR("Connect To Script:"), tree, true); + connect_to_label = Object::cast_to<Label>(vbc_left->get_child(mc->get_index() - 1)); - VBoxContainer *vbc_right = memnew(VBoxContainer); + error_label = memnew(Label); + error_label->set_text(TTR("Scene does not contain any script.")); + vbc_left->add_child(error_label); + error_label->hide(); + + vbc_right = memnew(VBoxContainer); main_hb->add_child(vbc_right); vbc_right->set_h_size_flags(SIZE_EXPAND_FILL); + vbc_right->hide(); HBoxContainer *add_bind_hb = memnew(HBoxContainer); @@ -345,16 +405,18 @@ ConnectDialog::ConnectDialog() { vbc_right->add_margin_child(TTR("Extra Call Arguments:"), bind_editor, true); - dst_path = memnew(LineEdit); - vbc->add_margin_child(TTR("Path to Node:"), dst_path); - HBoxContainer *dstm_hb = memnew(HBoxContainer); - vbc->add_margin_child("Method In Node:", dstm_hb); + vbc_left->add_margin_child("Method to Create:", dstm_hb); dst_method = memnew(LineEdit); dst_method->set_h_size_flags(SIZE_EXPAND_FILL); dstm_hb->add_child(dst_method); + advanced = memnew(CheckBox); + dstm_hb->add_child(advanced); + advanced->set_text(TTR("Advanced..")); + advanced->connect("pressed", this, "_advanced_pressed"); + /* dst_method_list = memnew( MenuButton ); dst_method_list->set_text("List..."); @@ -366,19 +428,13 @@ ConnectDialog::ConnectDialog() { dst_method_list->set_end( Point2( 15,39 ) ); */ - make_callback = memnew(CheckButton); - make_callback->set_toggle_mode(true); - make_callback->set_pressed(EDITOR_DEF("text_editor/tools/create_signal_callbacks", true)); - make_callback->set_text(TTR("Make Function")); - dstm_hb->add_child(make_callback); - deferred = memnew(CheckButton); deferred->set_text(TTR("Deferred")); - dstm_hb->add_child(deferred); + vbc_right->add_child(deferred); oneshot = memnew(CheckButton); oneshot->set_text(TTR("Oneshot")); - dstm_hb->add_child(oneshot); + vbc_right->add_child(oneshot); set_as_toplevel(true); @@ -427,7 +483,8 @@ void ConnectionsDock::_make_or_edit_connection() { bool oshot = connect_dialog->get_oneshot(); cToMake.flags = CONNECT_PERSIST | (defer ? CONNECT_DEFERRED : 0) | (oshot ? CONNECT_ONESHOT : 0); - bool add_script_function = connect_dialog->get_make_callback(); + //conditions to add function, must have a script and must have a method + bool add_script_function = !target->get_script().is_null() && !ClassDB::has_method(target->get_class(), cToMake.method); PoolStringArray script_function_args; if (add_script_function) { // pick up args here before "it" is deleted by update_tree @@ -566,6 +623,7 @@ bool ConnectionsDock::_is_item_signal(TreeItem &item) { /* Open connection dialog with TreeItem data to CREATE a brand-new connection. */ + void ConnectionsDock::_open_connection_dialog(TreeItem &item) { String signal = item.get_metadata(0).operator Dictionary()["name"]; @@ -588,6 +646,10 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &item) { } Node *dst_node = selectedNode->get_owner() ? selectedNode->get_owner() : selectedNode; + if (!dst_node || dst_node->get_script().is_null()) { + dst_node = _find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root()); + } + StringName dst_method = "_on_" + midname + "_" + signal; Connection c; @@ -596,9 +658,10 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &item) { c.target = dst_node; c.method = dst_method; + //connect_dialog->set_title(TTR("Connect Signal: ") + signalname); + connect_dialog->popup_dialog(signalname, false); connect_dialog->init(c); - connect_dialog->set_title(TTR("Connect Signal: ") + signalname); - connect_dialog->popup_centered_ratio(); + connect_dialog->set_title(TTR("Connect a Signal to a Method")); } /* @@ -610,9 +673,9 @@ void ConnectionsDock::_open_connection_dialog(Connection cToEdit) { Node *dst = static_cast<Node *>(cToEdit.target); if (src && dst) { - connect_dialog->init(cToEdit, true); - connect_dialog->set_title(TTR("Edit Connection: ") + cToEdit.signal); + connect_dialog->set_title(TTR("Edit Connection:") + cToEdit.signal); connect_dialog->popup_centered_ratio(); + connect_dialog->init(cToEdit, true); } } diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 0e7e172ebb..59fe6dacfe 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -53,12 +53,15 @@ class ConnectDialog : public ConfirmationDialog { GDCLASS(ConnectDialog, ConfirmationDialog); + Label *connect_to_label; + LineEdit *from_signal; Node *source; StringName signal; - LineEdit *dst_path; LineEdit *dst_method; ConnectDialogBinds *cdbinds; bool bEditMode; + NodePath dst_path; + VBoxContainer *vbc_right; SceneTreeEditor *tree; ConfirmationDialog *error; @@ -66,13 +69,16 @@ class ConnectDialog : public ConfirmationDialog { OptionButton *type_list; CheckButton *deferred; CheckButton *oneshot; - CheckButton *make_callback; + CheckBox *advanced; + + Label *error_label; void ok_pressed(); void _cancel_pressed(); void _tree_node_selected(); void _add_bind(); void _remove_bind(); + void _advanced_pressed(); protected: void _notification(int p_what); @@ -87,13 +93,13 @@ public: void set_dst_method(const StringName &p_method); Vector<Variant> get_binds() const; - bool get_make_callback() { return make_callback->is_visible() && make_callback->is_pressed(); } bool get_deferred() const; bool get_oneshot() const; bool is_editing() const; void init(Connection c, bool bEdit = false); + void popup_dialog(const String &p_for_signal, bool p_advanced); ConnectDialog(); ~ConnectDialog(); }; diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 364c5126d7..26bd651c2b 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -238,6 +238,26 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p p_types[p_type] = item; } +bool CreateDialog::_is_class_disabled_by_feature_profile(const StringName &p_class) { + + Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); + if (profile.is_null()) { + return false; + } + + StringName class_name = p_class; + + while (class_name != StringName()) { + + if (profile->is_class_disabled(class_name)) { + return true; + } + class_name = ClassDB::get_parent_class(class_name); + } + + return false; +} + void CreateDialog::_update_search() { search_options->clear(); @@ -264,6 +284,10 @@ void CreateDialog::_update_search() { for (List<StringName>::Element *I = type_list.front(); I; I = I->next()) { String type = I->get(); + + if (_is_class_disabled_by_feature_profile(type)) { + continue; + } bool cpp_type = ClassDB::class_exists(type); if (base_type == "Node" && type.begins_with("Editor")) diff --git a/editor/create_dialog.h b/editor/create_dialog.h index 2636e2ddef..d859f7cbe4 100644 --- a/editor/create_dialog.h +++ b/editor/create_dialog.h @@ -86,6 +86,8 @@ class CreateDialog : public ConfirmationDialog { bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + bool _is_class_disabled_by_feature_profile(const StringName &p_class); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index f559eb6cb2..5df2b687cc 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -496,14 +496,32 @@ void DependencyRemoveDialog::ok_pressed() { res->set_path(""); } - // If the file we are deleting is the main scene or default environment, clear its definition. + // If the file we are deleting for e.g. the main scene, default environment, + // or audio bus layout, we must clear its definition in Project Settings. + if (files_to_delete[i] == ProjectSettings::get_singleton()->get("application/config/icon")) { + ProjectSettings::get_singleton()->set("application/config/icon", ""); + } if (files_to_delete[i] == ProjectSettings::get_singleton()->get("application/run/main_scene")) { ProjectSettings::get_singleton()->set("application/run/main_scene", ""); } - + if (files_to_delete[i] == ProjectSettings::get_singleton()->get("application/boot_splash/image")) { + ProjectSettings::get_singleton()->set("application/boot_splash/image", ""); + } if (files_to_delete[i] == ProjectSettings::get_singleton()->get("rendering/environment/default_environment")) { ProjectSettings::get_singleton()->set("rendering/environment/default_environment", ""); } + if (files_to_delete[i] == ProjectSettings::get_singleton()->get("display/mouse_cursor/custom_image")) { + ProjectSettings::get_singleton()->set("display/mouse_cursor/custom_image", ""); + } + if (files_to_delete[i] == ProjectSettings::get_singleton()->get("gui/theme/custom")) { + ProjectSettings::get_singleton()->set("gui/theme/custom", ""); + } + if (files_to_delete[i] == ProjectSettings::get_singleton()->get("gui/theme/custom_font")) { + ProjectSettings::get_singleton()->set("gui/theme/custom_font", ""); + } + if (files_to_delete[i] == ProjectSettings::get_singleton()->get("audio/default_bus_layout")) { + ProjectSettings::get_singleton()->set("audio/default_bus_layout", ""); + } String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/"); print_verbose("Moving to trash: " + path); diff --git a/editor/doc/doc_data.cpp b/editor/doc/doc_data.cpp index 8f1d0d9677..c2a492bc9a 100644 --- a/editor/doc/doc_data.cpp +++ b/editor/doc/doc_data.cpp @@ -54,7 +54,6 @@ void DocData::merge_from(const DocData &p_data) { c.description = cf.description; c.brief_description = cf.brief_description; c.tutorials = cf.tutorials; - c.demos = cf.demos; for (int i = 0; i < c.methods.size(); i++) { @@ -837,10 +836,6 @@ Error DocData::_load(Ref<XMLParser> parser) { } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "tutorials") break; //end of <tutorials> } - } else if (name2 == "demos") { - parser->read(); - if (parser->get_node_type() == XMLParser::NODE_TEXT) - c.demos = parser->get_node_data(); } else if (name2 == "methods") { Error err2 = _parse_methods(parser, c.methods); @@ -987,8 +982,7 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri FileAccessRef f = FileAccess::open(save_file, FileAccess::WRITE, &err); if (err) { ERR_EXPLAIN("Can't write doc file: " + save_file); - - ERR_FAIL_V(err); + ERR_CONTINUE(err); } _write_string(f, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); @@ -1015,9 +1009,6 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri _write_string(f, 2, "<link>" + c.tutorials.get(i).xml_escape() + "</link>"); } _write_string(f, 1, "</tutorials>"); - _write_string(f, 1, "<demos>"); - _write_string(f, 2, c.demos.strip_edges().xml_escape()); - _write_string(f, 1, "</demos>"); _write_string(f, 1, "<methods>"); c.methods.sort(); diff --git a/editor/doc/doc_data.h b/editor/doc/doc_data.h index 8156acd3d0..d3844adb7e 100644 --- a/editor/doc/doc_data.h +++ b/editor/doc/doc_data.h @@ -86,7 +86,6 @@ public: String brief_description; String description; Vector<String> tutorials; - String demos; Vector<MethodDoc> methods; Vector<MethodDoc> signals; Vector<ConstantDoc> constants; diff --git a/editor/doc/doc_dump.cpp b/editor/doc/doc_dump.cpp index 1a68fe5bf2..6ccf0e26ea 100644 --- a/editor/doc/doc_dump.cpp +++ b/editor/doc/doc_dump.cpp @@ -199,7 +199,8 @@ void DocDump::dump(const String &p_file) { case Variant::ARRAY: case Variant::_RID: - default: {} + default: { + } } _write_string(f, 3, "<argument index=\"" + itos(i) + "\" name=\"" + _escape_string(arginfo.name) + "\" type=\"" + type_name + "\" default=\"" + _escape_string(default_arg_text) + "\">"); @@ -227,7 +228,8 @@ void DocDump::dump(const String &p_file) { } break; case PROPERTY_HINT_FILE: hint = "A file:"; break; - default: {} + default: { + } //case PROPERTY_HINT_RESOURCE_TYPE: hint="Type: "+arginfo.hint_string; break; }; if (hint != "") diff --git a/editor/editor_atlas_packer.cpp b/editor/editor_atlas_packer.cpp new file mode 100644 index 0000000000..4e1d98399a --- /dev/null +++ b/editor/editor_atlas_packer.cpp @@ -0,0 +1,265 @@ +#include "editor_atlas_packer.h" + +void EditorAtlasPacker::_plot_triangle(Ref<BitMap> p_bitmap, Vector2i *vertices) { + + int width = p_bitmap->get_size().width; + int height = p_bitmap->get_size().height; + int x[3]; + int y[3]; + + for (int j = 0; j < 3; j++) { + + x[j] = vertices[j].x; + y[j] = vertices[j].y; + } + + // sort the points vertically + if (y[1] > y[2]) { + SWAP(x[1], x[2]); + SWAP(y[1], y[2]); + } + if (y[0] > y[1]) { + SWAP(x[0], x[1]); + SWAP(y[0], y[1]); + } + if (y[1] > y[2]) { + SWAP(x[1], x[2]); + SWAP(y[1], y[2]); + } + + double dx_far = double(x[2] - x[0]) / (y[2] - y[0] + 1); + double dx_upper = double(x[1] - x[0]) / (y[1] - y[0] + 1); + double dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1); + double xf = x[0]; + double xt = x[0] + dx_upper; // if y[0] == y[1], special case + for (int yi = y[0]; yi <= (y[2] > height - 1 ? height - 1 : y[2]); yi++) { + if (yi >= 0) { + for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < width ? xt : width - 1); xi++) { + //pixels[int(x + y * width)] = color; + + p_bitmap->set_bit(Point2(xi, yi), true); + } + + for (int xi = (xf < width ? int(xf) : width - 1); xi >= (xt > 0 ? xt : 0); xi--) { + + p_bitmap->set_bit(Point2(xi, yi), true); + } + } + xf += dx_far; + if (yi < y[1]) + xt += dx_upper; + else + xt += dx_low; + } +} +void EditorAtlasPacker::chart_pack(Vector<Chart> &charts, int &r_width, int &r_height, int p_atlas_max_size, int p_cell_resolution) { + + int divide_by = MIN(64, p_cell_resolution); + Vector<PlottedBitmap> bitmaps; + + int max_w = 0; + + for (int i = 0; i < charts.size(); i++) { + + const Chart &chart = charts[i]; + + //generate aabb + + Rect2i aabb; + int vertex_count = chart.vertices.size(); + const Vector2 *vertices = chart.vertices.ptr(); + + for (int j = 0; j < vertex_count; j++) { + + if (j == 0) { + aabb.position = vertices[j]; + } else { + aabb.expand_to(vertices[j]); + } + } + + Ref<BitMap> src_bitmap; + src_bitmap.instance(); + src_bitmap->create(aabb.size / divide_by); + + int w = src_bitmap->get_size().width; + int h = src_bitmap->get_size().height; + + //plot triangles, using divisor + + for (int j = 0; j < chart.faces.size(); j++) { + + Vector2i v[3]; + for (int k = 0; k < 3; k++) { + Vector2 vtx = chart.vertices[chart.faces[j].vertex[k]]; + vtx -= aabb.position; + vtx /= divide_by; + v[k] = vtx; + } + + _plot_triangle(src_bitmap, v); + } + + //src_bitmap->convert_to_image()->save_png("bitmap" + itos(i) + ".png"); + + //grow by 1 for each side + + int bmw = src_bitmap->get_size().width + 2; + int bmh = src_bitmap->get_size().height + 2; + + int heights_size = -1; + bool transpose = false; + if (chart.can_transpose && bmh > bmw) { + heights_size = bmh; + transpose = true; + } else { + heights_size = bmw; + } + + max_w = MAX(max_w, heights_size); + + Vector<int> top_heights; + Vector<int> bottom_heights; + top_heights.resize(heights_size); + bottom_heights.resize(heights_size); + + for (int x = 0; x < heights_size; x++) { + top_heights.write[x] = -1; + bottom_heights.write[x] = 0x7FFFFFFF; + } + + for (int x = 0; x < bmw; x++) { + for (int y = 0; y < bmh; y++) { + bool found_pixel = false; + for (int lx = x - 1; lx < x + 2 && !found_pixel; lx++) { + for (int ly = y - 1; ly < y + 2 && !found_pixel; ly++) { + + int px = lx - 1; + if (px < 0 || px >= w) + continue; + int py = ly - 1; + if (py < 0 || py >= h) + continue; + + if (src_bitmap->get_bit(Vector2(px, py))) { + found_pixel = true; + } + } + } + if (found_pixel) { + + if (transpose) { + if (x > top_heights[y]) { + top_heights.write[y] = x; + } + if (x < bottom_heights[y]) { + bottom_heights.write[y] = x; + } + } else { + if (y > top_heights[x]) { + top_heights.write[x] = y; + } + if (y < bottom_heights[x]) { + bottom_heights.write[x] = y; + } + } + } + } + } + + String row; + for (int j = 0; j < top_heights.size(); j++) { + row += "(" + itos(top_heights[j]) + "-" + itos(bottom_heights[j]) + "),"; + } + + PlottedBitmap plotted_bitmap; + plotted_bitmap.offset = aabb.position; + plotted_bitmap.top_heights = top_heights; + plotted_bitmap.bottom_heights = bottom_heights; + plotted_bitmap.chart_index = i; + plotted_bitmap.transposed = transpose; + plotted_bitmap.area = bmw * bmh; + + bitmaps.push_back(plotted_bitmap); + } + + bitmaps.sort(); + + int atlas_max_width = nearest_power_of_2_templated(p_atlas_max_size) / divide_by; + int atlas_w = nearest_power_of_2_templated(max_w); + int atlas_h; + while (true) { + atlas_h = 0; + + //do a tetris + Vector<int> heights; + heights.resize(atlas_w); + for (int i = 0; i < atlas_w; i++) { + heights.write[i] = 0; + } + + int *atlas_ptr = heights.ptrw(); + + for (int i = 0; i < bitmaps.size(); i++) { + + int best_height = 0x7FFFFFFF; + int best_height_offset = -1; + int w = bitmaps[i].top_heights.size(); + + const int *top_heights = bitmaps[i].top_heights.ptr(); + const int *bottom_heights = bitmaps[i].bottom_heights.ptr(); + + for (int j = 0; j < atlas_w - w; j++) { + + int height = 0; + + for (int k = 0; k < w; k++) { + + int pixmap_h = bottom_heights[k]; + if (pixmap_h == -1) { + continue; //no pixel here, anything is fine + } + + int h = MAX(0, atlas_ptr[j + k] - pixmap_h); + if (h > height) { + height = h; + } + } + + if (height < best_height) { + best_height = height; + best_height_offset = j; + } + } + + for (int j = 0; j < w; j++) { //add + if (top_heights[j] == -1) { //unused + continue; + } + int height = best_height + top_heights[j] + 1; + atlas_ptr[j + best_height_offset] = height; + atlas_h = MAX(atlas_h, height); + } + + // set + Vector2 offset = bitmaps[i].offset; + if (bitmaps[i].transposed) { + SWAP(offset.x, offset.y); + } + + Vector2 final_pos = Vector2(best_height_offset * divide_by, best_height * divide_by) + Vector2(divide_by, divide_by) - offset; + charts.write[bitmaps[i].chart_index].final_offset = final_pos; + charts.write[bitmaps[i].chart_index].transposed = bitmaps[i].transposed; + } + + if (atlas_h <= atlas_w * 2 || atlas_w >= atlas_max_width) { + break; //ok this one is enough + } + + //try again + atlas_w *= 2; + } + + r_width = atlas_w * divide_by; + r_height = atlas_h * divide_by; +} diff --git a/editor/editor_atlas_packer.h b/editor/editor_atlas_packer.h new file mode 100644 index 0000000000..dd9caa340e --- /dev/null +++ b/editor/editor_atlas_packer.h @@ -0,0 +1,45 @@ +#ifndef EDITOR_ATLAS_PACKER_H +#define EDITOR_ATLAS_PACKER_H + +#include "core/math/vector2.h" + +#include "core/vector.h" +#include "scene/resources/bit_map.h" + +class EditorAtlasPacker { +public: + struct Chart { + Vector<Vector2> vertices; + struct Face { + int vertex[3]; + }; + Vector<Face> faces; + bool can_transpose; + + Vector2 final_offset; + bool transposed; + }; + +private: + struct PlottedBitmap { + int chart_index; + Vector2i offset; + int area; + Vector<int> top_heights; + Vector<int> bottom_heights; + bool transposed; + + Vector2 final_pos; + + bool operator<(const PlottedBitmap &p_bm) const { + return area > p_bm.area; + } + }; + + static void _plot_triangle(Ref<BitMap> p_bitmap, Vector2i *vertices); + +public: + static void chart_pack(Vector<Chart> &charts, int &r_width, int &r_height, int p_atlas_max_size = 2048, int p_cell_resolution = 4); +}; + +#endif // EDITOR_ATLAS_PACKER_H diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index 29e9cd01c5..49d40e6d90 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -34,6 +34,7 @@ #include "core/os/keyboard.h" #include "editor_node.h" #include "filesystem_dock.h" +#include "scene/resources/font.h" #include "servers/audio_server.h" void EditorAudioBus::_update_visible_channels() { @@ -71,7 +72,6 @@ void EditorAudioBus::_notification(int p_what) { channel[i].vu_r->set_progress_texture(get_icon("BusVuFull", "EditorIcons")); channel[i].prev_active = true; } - scale->set_texture(get_icon("BusVuDb", "EditorIcons")); disabled_vu = get_icon("BusVuFrozen", "EditorIcons"); @@ -167,7 +167,6 @@ void EditorAudioBus::_notification(int p_what) { channel[i].vu_r->set_progress_texture(get_icon("BusVuFull", "EditorIcons")); channel[i].prev_active = true; } - scale->set_texture(get_icon("BusVuDb", "EditorIcons")); disabled_vu = get_icon("BusVuFrozen", "EditorIcons"); @@ -211,7 +210,8 @@ void EditorAudioBus::update_bus() { int index = get_index(); - slider->set_value(AudioServer::get_singleton()->get_bus_volume_db(index)); + float db_value = AudioServer::get_singleton()->get_bus_volume_db(index); + slider->set_value(_scaled_db_to_normalized_volume(db_value)); track_name->set_text(AudioServer::get_singleton()->get_bus_name(index)); if (is_master) track_name->set_editable(false); @@ -300,13 +300,15 @@ void EditorAudioBus::_name_changed(const String &p_new_name) { track_name->release_focus(); } -void EditorAudioBus::_volume_db_changed(float p_db) { +void EditorAudioBus::_volume_changed(float p_normalized) { if (updating_bus) return; updating_bus = true; + float p_db = this->_normalized_volume_to_scaled_db(p_normalized); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); ur->create_action(TTR("Change Audio Bus Volume"), UndoRedo::MERGE_ENDS); ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db", get_index(), p_db); @@ -317,6 +319,69 @@ void EditorAudioBus::_volume_db_changed(float p_db) { updating_bus = false; } + +float EditorAudioBus::_normalized_volume_to_scaled_db(float normalized) { + /* There are three different formulas for the conversion from normalized + * values to relative decibal values. + * One formula is an exponential graph which intends to counteract + * the logorithmic nature of human hearing. This is an approximation + * of the behaviour of a 'logarithmic potentiometer' found on most + * musical instruments and also emulated in popular software. + * The other two equations are hand-tuned linear tapers that intend to + * try to ease the exponential equation in areas where it makes sense.*/ + + if (normalized > 0.6f) { + return 22.22f * normalized - 16.2f; + } else if (normalized < 0.05f) { + return 830.72 * normalized - 80.0f; + } else { + return 45.0f * Math::pow(normalized - 1.0, 3); + } +} + +float EditorAudioBus::_scaled_db_to_normalized_volume(float db) { + /* Inversion of equations found in _normalized_volume_to_scaled_db. + * IMPORTANT: If one function changes, the other much change to reflect it. */ + if (db > -2.88) { + return (db + 16.2f) / 22.22f; + } else if (db < -38.602f) { + return (db + 80.00f) / 830.72f; + } else { + if (db < 0.0) { + /* To acommodate for NaN on negative numbers for root, we will mirror the + * results of the postive db range in order to get the desired numerical + * value on the negative side. */ + float positive_x = Math::pow(Math::abs(db) / 45.0f, 1.0f / 3.0f) + 1.0f; + Vector2 translation = Vector2(1.0f, 0.0f) - Vector2(positive_x, Math::abs(db)); + Vector2 reflected_position = Vector2(1.0, 0.0f) + translation; + return reflected_position.x; + } else { + return Math::pow(db / 45.0f, 1.0f / 3.0f) + 1.0f; + } + } +} + +void EditorAudioBus::_show_value(float slider_value) { + String text = vformat("%10.1f dB", _normalized_volume_to_scaled_db(slider_value)); + + audio_value_preview_label->set_text(text); + Vector2 slider_size = slider->get_size(); + Vector2 slider_position = slider->get_global_position(); + float left_padding = 5.0f; + float vert_padding = 10.0f; + Vector2 box_position = Vector2(slider_size.x + left_padding, (slider_size.y - vert_padding) * (1.0f - slider_value) - vert_padding); + audio_value_preview_box->set_position(slider_position + box_position); + audio_value_preview_box->set_size(audio_value_preview_label->get_size()); + if (slider->has_focus() && !audio_value_preview_box->is_visible()) { + audio_value_preview_box->show(); + } + preview_timer->start(); +} + +void EditorAudioBus::_hide_value_preview() { + audio_value_preview_box->hide(); +} + void EditorAudioBus::_solo_toggled() { updating_bus = true; @@ -653,7 +718,9 @@ void EditorAudioBus::_bind_methods() { ClassDB::bind_method("update_bus", &EditorAudioBus::update_bus); ClassDB::bind_method("update_send", &EditorAudioBus::update_send); ClassDB::bind_method("_name_changed", &EditorAudioBus::_name_changed); - ClassDB::bind_method("_volume_db_changed", &EditorAudioBus::_volume_db_changed); + ClassDB::bind_method("_volume_changed", &EditorAudioBus::_volume_changed); + ClassDB::bind_method("_show_value", &EditorAudioBus::_show_value); + ClassDB::bind_method("_hide_value_preview", &EditorAudioBus::_hide_value_preview); ClassDB::bind_method("_solo_toggled", &EditorAudioBus::_solo_toggled); ClassDB::bind_method("_mute_toggled", &EditorAudioBus::_mute_toggled); ClassDB::bind_method("_bypass_toggled", &EditorAudioBus::_bypass_toggled); @@ -738,11 +805,42 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { HBoxContainer *hb = memnew(HBoxContainer); vb->add_child(hb); slider = memnew(VSlider); - slider->set_min(-80); - slider->set_max(24); - slider->set_step(0.1); - - slider->connect("value_changed", this, "_volume_db_changed"); + slider->set_min(0.0); + slider->set_max(1.0); + slider->set_step(0.0001); + slider->set_clip_contents(false); + + audio_value_preview_box = memnew(Panel); + { + HBoxContainer *audioprev_hbc = memnew(HBoxContainer); + audioprev_hbc->set_v_size_flags(SIZE_EXPAND_FILL); + audioprev_hbc->set_h_size_flags(SIZE_EXPAND_FILL); + audioprev_hbc->set_mouse_filter(MOUSE_FILTER_PASS); + audio_value_preview_box->add_child(audioprev_hbc); + + audio_value_preview_label = memnew(Label); + audio_value_preview_label->set_v_size_flags(SIZE_EXPAND_FILL); + audio_value_preview_label->set_h_size_flags(SIZE_EXPAND_FILL); + audio_value_preview_label->set_mouse_filter(MOUSE_FILTER_PASS); + + audioprev_hbc->add_child(audio_value_preview_label); + } + slider->add_child(audio_value_preview_box); + audio_value_preview_box->set_as_toplevel(true); + Ref<StyleBoxFlat> panel_style = memnew(StyleBoxFlat); + panel_style->set_bg_color(Color(0.0f, 0.0f, 0.0f, 0.8f)); + audio_value_preview_box->add_style_override("panel", panel_style); + audio_value_preview_box->set_mouse_filter(MOUSE_FILTER_PASS); + audio_value_preview_box->hide(); + + preview_timer = memnew(Timer); + preview_timer->set_wait_time(0.8f); + preview_timer->set_one_shot(true); + add_child(preview_timer); + + slider->connect("value_changed", this, "_volume_changed"); + slider->connect("value_changed", this, "_show_value"); + preview_timer->connect("timeout", this, "_hide_value_preview"); hb->add_child(slider); cc = 0; @@ -765,7 +863,12 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { channel[i].peak_r = 0.0f; } - scale = memnew(TextureRect); + scale = memnew(EditorAudioMeterNotches); + + for (float db = 6.0f; db >= -80.0f; db -= 6.0f) { + bool renderNotch = (db >= -6.0f || db == -24.0f || db == -72.0f); + scale->add_notch(_scaled_db_to_normalized_volume(db), db, renderNotch); + } hb->add_child(scale); effects = memnew(Tree); @@ -1077,14 +1180,16 @@ void EditorAudioBuses::_load_layout() { void EditorAudioBuses::_load_default_layout() { - Ref<AudioBusLayout> state = ResourceLoader::load("res://default_bus_layout.tres"); + String layout_path = ProjectSettings::get_singleton()->get("audio/default_bus_layout"); + + Ref<AudioBusLayout> state = ResourceLoader::load(layout_path); if (state.is_null()) { - EditorNode::get_singleton()->show_warning(TTR("There is no 'res://default_bus_layout.tres' file.")); + EditorNode::get_singleton()->show_warning(vformat(TTR("There is no '%s' file."), layout_path)); return; } - edited_path = "res://default_bus_layout.tres"; - file->set_text(edited_path.get_file()); + edited_path = layout_path; + file->set_text(layout_path.get_file()); AudioServer::get_singleton()->set_bus_layout(state); _update_buses(); EditorNode::get_singleton()->get_undo_redo()->clear_history(); @@ -1213,7 +1318,7 @@ EditorAudioBuses::EditorAudioBuses() { set_v_size_flags(SIZE_EXPAND_FILL); - edited_path = "res://default_bus_layout.tres"; + edited_path = ProjectSettings::get_singleton()->get("audio/default_bus_layout"); file_dialog = memnew(EditorFileDialog); List<String> ext; @@ -1226,6 +1331,7 @@ EditorAudioBuses::EditorAudioBuses() { set_process(true); } + void EditorAudioBuses::open_layout(const String &p_path) { EditorNode::get_singleton()->make_bottom_panel_item_visible(this); @@ -1270,3 +1376,50 @@ AudioBusesEditorPlugin::AudioBusesEditorPlugin(EditorAudioBuses *p_node) { AudioBusesEditorPlugin::~AudioBusesEditorPlugin() { } + +void EditorAudioMeterNotches::add_notch(float normalized_offset, float db_value, bool render_value) { + notches.push_back(AudioNotch(normalized_offset, db_value, render_value)); +} + +void EditorAudioMeterNotches::_bind_methods() { + ClassDB::bind_method("add_notch", &EditorAudioMeterNotches::add_notch); + ClassDB::bind_method("_draw_audio_notches", &EditorAudioMeterNotches::_draw_audio_notches); +} + +void EditorAudioMeterNotches::_notification(int p_what) { + if (p_what == NOTIFICATION_DRAW) { + notch_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1.0f, 1.0f, 1.0f, 0.8f) : Color(0.0f, 0.0f, 0.0f, 0.8f); + _draw_audio_notches(); + } +} + +void EditorAudioMeterNotches::_draw_audio_notches() { + Ref<Font> font = get_font("source", "EditorFonts"); + float font_height = font->get_height(); + + for (uint8_t i = 0; i < notches.size(); i++) { + AudioNotch n = notches[i]; + draw_line(Vector2(0.0f, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding), + Vector2(line_length, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding), + notch_color, + 1.0f); + + if (n.render_db_value) { + draw_string(font, + Vector2(line_length + label_space, + (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + (font_height / 4) + top_padding), + String("{0}dB").format(varray(Math::abs(n.db_value))), + notch_color); + } + } +} + +EditorAudioMeterNotches::EditorAudioMeterNotches() : + line_length(5.0f), + label_space(2.0f), + btm_padding(9.0f), + top_padding(5.0f) { + this->set_v_size_flags(SIZE_EXPAND_FILL); + this->set_h_size_flags(SIZE_EXPAND_FILL); + notch_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1.0f, 1.0f, 1.0f, 0.8f) : Color(0.0f, 0.0f, 0.0f, 0.8f); +} diff --git a/editor/editor_audio_buses.h b/editor/editor_audio_buses.h index 37ff9b28dc..50f2101fd8 100644 --- a/editor/editor_audio_buses.h +++ b/editor/editor_audio_buses.h @@ -35,6 +35,7 @@ #include "editor_plugin.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" +#include "scene/gui/control.h" #include "scene/gui/line_edit.h" #include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" @@ -71,13 +72,17 @@ class EditorAudioBus : public PanelContainer { TextureProgress *vu_r; } channel[CHANNELS_MAX]; - TextureRect *scale; + class EditorAudioMeterNotches *scale; OptionButton *send; PopupMenu *effect_options; PopupMenu *bus_popup; PopupMenu *delete_effect_popup; + Panel *audio_value_preview_box; + Label *audio_value_preview_label; + Timer *preview_timer; + Button *solo; Button *mute; Button *bypass; @@ -93,7 +98,11 @@ class EditorAudioBus : public PanelContainer { void _name_changed(const String &p_new_name); void _name_focus_exit() { _name_changed(track_name->get_text()); } - void _volume_db_changed(float p_db); + void _volume_changed(float p_normalized); + float _normalized_volume_to_scaled_db(float normalized); + float _scaled_db_to_normalized_volume(float db); + void _show_value(float slider_value); + void _hide_value_preview(); void _solo_toggled(); void _mute_toggled(); void _bypass_toggled(); @@ -200,6 +209,50 @@ public: EditorAudioBuses(); }; +class EditorAudioMeterNotches : public Control { + GDCLASS(EditorAudioMeterNotches, Control); + +private: + struct AudioNotch { + float relative_position; + float db_value; + bool render_db_value; + + _FORCE_INLINE_ AudioNotch(float r_pos, float db_v, bool rndr_val) { + relative_position = r_pos; + db_value = db_v; + render_db_value = rndr_val; + } + + _FORCE_INLINE_ AudioNotch(const AudioNotch &n) { + relative_position = n.relative_position; + db_value = n.db_value; + render_db_value = n.render_db_value; + } + + _FORCE_INLINE_ AudioNotch() {} + }; + + List<AudioNotch> notches; + +public: + float line_length; + float label_space; + float btm_padding; + float top_padding; + Color notch_color; + + void add_notch(float normalized_offset, float db_value, bool render_value = false); + +private: + static void _bind_methods(); + void _notification(int p_what); + void _draw_audio_notches(); + +public: + EditorAudioMeterNotches(); +}; + class AudioBusesEditorPlugin : public EditorPlugin { GDCLASS(AudioBusesEditorPlugin, EditorPlugin); diff --git a/editor/editor_builders.py b/editor/editor_builders.py index 9e9fe752b4..19c5e0b924 100644 --- a/editor/editor_builders.py +++ b/editor/editor_builders.py @@ -6,7 +6,7 @@ All such functions are invoked in a subprocess on Windows to prevent build flaki import os import os.path from platform_methods import subprocess_main -from compat import encode_utf8, byte_to_str, open_utf8, escape_string +from compat import encode_utf8, byte_to_str, open_utf8 def make_doc_header(target, source, env): diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp new file mode 100644 index 0000000000..714df44e25 --- /dev/null +++ b/editor/editor_feature_profile.cpp @@ -0,0 +1,889 @@ +#include "editor_feature_profile.h" +#include "core/io/json.h" +#include "core/os/dir_access.h" +#include "editor/editor_settings.h" +#include "editor_node.h" +#include "editor_scale.h" + +const char *EditorFeatureProfile::feature_names[FEATURE_MAX] = { + TTRC("3D Editor"), + TTRC("Script Editor"), + TTRC("Asset Library"), + TTRC("Scene Tree Editing"), + TTRC("Import Dock"), + TTRC("Node Dock"), + TTRC("Filesystem Dock") +}; + +const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = { + "3d", + "script", + "asset_lib", + "scene_tree", + "import_dock", + "node_dock", + "filesystem_dock" +}; + +void EditorFeatureProfile::set_disable_class(const StringName &p_class, bool p_disabled) { + if (p_disabled) { + disabled_classes.insert(p_class); + } else { + disabled_classes.erase(p_class); + } +} + +bool EditorFeatureProfile::is_class_disabled(const StringName &p_class) const { + return disabled_classes.has(p_class); +} + +void EditorFeatureProfile::set_disable_class_editor(const StringName &p_class, bool p_disabled) { + if (p_disabled) { + disabled_editors.insert(p_class); + } else { + disabled_editors.erase(p_class); + } +} + +bool EditorFeatureProfile::is_class_editor_disabled(const StringName &p_class) const { + return disabled_editors.has(p_class); +} + +void EditorFeatureProfile::set_disable_class_property(const StringName &p_class, const StringName &p_property, bool p_disabled) { + + if (p_disabled) { + if (!disabled_properties.has(p_class)) { + disabled_properties[p_class] = Set<StringName>(); + } + + disabled_properties[p_class].insert(p_property); + } else { + ERR_FAIL_COND(!disabled_properties.has(p_class)); + disabled_properties[p_class].erase(p_property); + if (disabled_properties[p_class].empty()) { + disabled_properties.erase(p_class); + } + } +} +bool EditorFeatureProfile::is_class_property_disabled(const StringName &p_class, const StringName &p_property) const { + + if (!disabled_properties.has(p_class)) { + return false; + } + + if (!disabled_properties[p_class].has(p_property)) { + return false; + } + + return true; +} + +bool EditorFeatureProfile::has_class_properties_disabled(const StringName &p_class) const { + return disabled_properties.has(p_class); +} + +void EditorFeatureProfile::set_disable_feature(Feature p_feature, bool p_disable) { + + ERR_FAIL_INDEX(p_feature, FEATURE_MAX); + features_disabled[p_feature] = p_disable; +} +bool EditorFeatureProfile::is_feature_disabled(Feature p_feature) const { + ERR_FAIL_INDEX_V(p_feature, FEATURE_MAX, false); + return features_disabled[p_feature]; +} + +String EditorFeatureProfile::get_feature_name(Feature p_feature) { + ERR_FAIL_INDEX_V(p_feature, FEATURE_MAX, String()); + return feature_names[p_feature]; +} + +Error EditorFeatureProfile::save_to_file(const String &p_path) { + + Dictionary json; + json["type"] = "feature_profile"; + Array dis_classes; + for (Set<StringName>::Element *E = disabled_classes.front(); E; E = E->next()) { + dis_classes.push_back(String(E->get())); + } + dis_classes.sort(); + json["disabled_classes"] = dis_classes; + + Array dis_editors; + for (Set<StringName>::Element *E = disabled_editors.front(); E; E = E->next()) { + dis_editors.push_back(String(E->get())); + } + dis_editors.sort(); + json["disabled_editors"] = dis_editors; + + Array dis_props; + + for (Map<StringName, Set<StringName> >::Element *E = disabled_properties.front(); E; E = E->next()) { + for (Set<StringName>::Element *F = E->get().front(); F; F = F->next()) { + dis_props.push_back(String(E->key()) + ":" + String(F->get())); + } + } + + json["disabled_properties"] = dis_props; + + Array dis_features; + for (int i = 0; i < FEATURE_MAX; i++) { + if (features_disabled[i]) { + dis_features.push_back(feature_identifiers[i]); + } + } + + json["disabled_features"] = dis_features; + + FileAccessRef f = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); + + String text = JSON::print(json, "\t"); + f->store_string(text); + f->close(); + return OK; +} + +Error EditorFeatureProfile::load_from_file(const String &p_path) { + + Error err; + String text = FileAccess::get_file_as_string(p_path, &err); + if (err != OK) { + return err; + } + + String err_str; + int err_line; + Variant v; + err = JSON::parse(text, v, err_str, err_line); + if (err != OK) { + ERR_PRINTS("Error parsing '" + p_path + "' on line " + itos(err_line) + ": " + err_str); + return ERR_PARSE_ERROR; + } + + Dictionary json = v; + + if (!json.has("type") || String(json["type"]) != "feature_profile") { + ERR_PRINTS("Error parsing '" + p_path + "', it's not a feature profile."); + return ERR_PARSE_ERROR; + } + + disabled_classes.clear(); + + if (json.has("disabled_classes")) { + Array disabled_classes_arr = json["disabled_classes"]; + for (int i = 0; i < disabled_classes_arr.size(); i++) { + disabled_classes.insert(disabled_classes_arr[i]); + } + } + + disabled_editors.clear(); + + if (json.has("disabled_editors")) { + Array disabled_editors_arr = json["disabled_editors"]; + for (int i = 0; i < disabled_editors_arr.size(); i++) { + disabled_editors.insert(disabled_editors_arr[i]); + } + } + + disabled_properties.clear(); + + if (json.has("disabled_properties")) { + Array disabled_properties_arr = json["disabled_properties"]; + for (int i = 0; i < disabled_properties_arr.size(); i++) { + String s = disabled_properties_arr[i]; + set_disable_class_property(s.get_slice(":", 0), s.get_slice(":", 1), true); + } + } + + if (json.has("disabled_features")) { + + Array disabled_features_arr = json["disabled_features"]; + for (int i = 0; i < FEATURE_MAX; i++) { + bool found = false; + String f = feature_identifiers[i]; + for (int j = 0; j < disabled_features_arr.size(); j++) { + String fd = disabled_features_arr[j]; + if (fd == f) { + found = true; + break; + } + } + + features_disabled[i] = found; + } + } + + return OK; +} + +void EditorFeatureProfile::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_disable_class", "class_name", "disable"), &EditorFeatureProfile::set_disable_class); + ClassDB::bind_method(D_METHOD("is_class_disabled", "class_name"), &EditorFeatureProfile::is_class_disabled); + + ClassDB::bind_method(D_METHOD("set_disable_class_editor", "class_name", "disable"), &EditorFeatureProfile::set_disable_class_editor); + ClassDB::bind_method(D_METHOD("is_class_editor_disabled", "class_name"), &EditorFeatureProfile::is_class_editor_disabled); + + ClassDB::bind_method(D_METHOD("set_disable_class_property", "class_name", "property"), &EditorFeatureProfile::set_disable_class_property); + ClassDB::bind_method(D_METHOD("is_class_property_disabled", "class_name"), &EditorFeatureProfile::is_class_property_disabled); + + ClassDB::bind_method(D_METHOD("set_disable_feature", "feature", "disable"), &EditorFeatureProfile::set_disable_feature); + ClassDB::bind_method(D_METHOD("is_feature_disabled", "feature"), &EditorFeatureProfile::is_feature_disabled); + + ClassDB::bind_method(D_METHOD("get_feature_name", "feature"), &EditorFeatureProfile::_get_feature_name); + + ClassDB::bind_method(D_METHOD("save_to_file", "path"), &EditorFeatureProfile::save_to_file); + ClassDB::bind_method(D_METHOD("load_from_file", "path"), &EditorFeatureProfile::load_from_file); + + BIND_ENUM_CONSTANT(FEATURE_3D); + BIND_ENUM_CONSTANT(FEATURE_SCRIPT); + BIND_ENUM_CONSTANT(FEATURE_ASSET_LIB); + BIND_ENUM_CONSTANT(FEATURE_SCENE_TREE); + BIND_ENUM_CONSTANT(FEATURE_IMPORT_DOCK); + BIND_ENUM_CONSTANT(FEATURE_NODE_DOCK); + BIND_ENUM_CONSTANT(FEATURE_FILESYSTEM_DOCK); + BIND_ENUM_CONSTANT(FEATURE_MAX); +} + +EditorFeatureProfile::EditorFeatureProfile() { + + for (int i = 0; i < FEATURE_MAX; i++) { + features_disabled[i] = false; + } +} + +////////////////////////// + +void EditorFeatureProfileManager::_notification(int p_what) { + if (p_what == NOTIFICATION_READY) { + + current_profile = EDITOR_GET("_default_feature_profile"); + if (current_profile != String()) { + current.instance(); + Error err = current->load_from_file(EditorSettings::get_singleton()->get_feature_profiles_dir().plus_file(current_profile + ".profile")); + if (err != OK) { + ERR_PRINTS("Error loading default feature profile: " + current_profile); + current_profile = String(); + current.unref(); + } + } + _update_profile_list(current_profile); + } +} + +String EditorFeatureProfileManager::_get_selected_profile() { + int idx = profile_list->get_selected(); + if (idx < 0) { + return String(); + } + + return profile_list->get_item_metadata(idx); +} + +void EditorFeatureProfileManager::_update_profile_list(const String &p_select_profile) { + + String selected_profile; + if (p_select_profile == String()) { //default, keep + if (profile_list->get_selected() >= 0) { + selected_profile = profile_list->get_item_metadata(profile_list->get_selected()); + if (!FileAccess::exists(EditorSettings::get_singleton()->get_feature_profiles_dir().plus_file(selected_profile + ".profile"))) { + selected_profile = String(); //does not exist + } + } + } else { + selected_profile = p_select_profile; + } + + Vector<String> profiles; + DirAccessRef d = DirAccess::open(EditorSettings::get_singleton()->get_feature_profiles_dir()); + ERR_FAIL_COND(!d); + d->list_dir_begin(); + while (true) { + String f = d->get_next(); + if (f == String()) { + break; + } + + if (!d->current_is_dir()) { + int last_pos = f.find_last(".profile"); + if (last_pos != -1) { + profiles.push_back(f.substr(0, last_pos)); + } + } + } + + profiles.sort(); + + profile_list->clear(); + + for (int i = 0; i < profiles.size(); i++) { + String name = profiles[i]; + + if (i == 0 && selected_profile == String()) { + selected_profile = name; + } + + if (name == current_profile) { + name += " (current)"; + } + profile_list->add_item(name); + int index = profile_list->get_item_count() - 1; + profile_list->set_item_metadata(index, profiles[i]); + if (profiles[i] == selected_profile) { + profile_list->select(index); + } + } + + profile_actions[PROFILE_CLEAR]->set_disabled(current_profile == String()); + profile_actions[PROFILE_ERASE]->set_disabled(selected_profile == String()); + profile_actions[PROFILE_EXPORT]->set_disabled(selected_profile == String()); + profile_actions[PROFILE_SET]->set_disabled(selected_profile == String()); + + current_profile_name->set_text(current_profile); + + _update_selected_profile(); +} + +void EditorFeatureProfileManager::_profile_action(int p_action) { + + switch (p_action) { + case PROFILE_CLEAR: { + EditorSettings::get_singleton()->set("_default_feature_profile", ""); + EditorSettings::get_singleton()->save(); + current_profile = ""; + current.unref(); + _update_profile_list(); + } break; + case PROFILE_SET: { + + String selected = _get_selected_profile(); + ERR_FAIL_COND(selected == String()); + if (selected == current_profile) { + return; //nothing to do here + } + EditorSettings::get_singleton()->set("_default_feature_profile", selected); + EditorSettings::get_singleton()->save(); + current_profile = selected; + current = edited; + + _update_profile_list(); + + } break; + case PROFILE_IMPORT: { + + import_profiles->popup_centered_ratio(); + } break; + case PROFILE_EXPORT: { + + export_profile->popup_centered_ratio(); + export_profile->set_current_file(_get_selected_profile() + ".profile"); + } break; + case PROFILE_NEW: { + + new_profile_dialog->popup_centered_minsize(); + new_profile_name->clear(); + new_profile_name->grab_focus(); + } break; + case PROFILE_ERASE: { + String selected = _get_selected_profile(); + ERR_FAIL_COND(selected == String()); + + erase_profile_dialog->set_text(vformat(TTR("Erase profile '%s'? (no undo)"), selected)); + erase_profile_dialog->popup_centered_minsize(); + } break; + } +} + +void EditorFeatureProfileManager::_erase_selected_profile() { + + String selected = _get_selected_profile(); + ERR_FAIL_COND(selected == String()); + DirAccessRef da = DirAccess::open(EditorSettings::get_singleton()->get_feature_profiles_dir()); + ERR_FAIL_COND(!da); + da->remove(selected + ".profile"); + if (selected == current_profile) { + _profile_action(PROFILE_CLEAR); + } else { + _update_profile_list(); + } +} + +void EditorFeatureProfileManager::_create_new_profile() { + String name = new_profile_name->get_text().strip_edges(); + if (!name.is_valid_filename() || name.find(".") != -1) { + EditorNode::get_singleton()->show_warning(TTR("Profile must be a valid filename and must not contain '.'")); + return; + } + String file = EditorSettings::get_singleton()->get_feature_profiles_dir().plus_file(name + ".profile"); + if (FileAccess::exists(file)) { + EditorNode::get_singleton()->show_warning(TTR("Profile with this name already exists.")); + return; + } + + Ref<EditorFeatureProfile> new_profile; + new_profile.instance(); + new_profile->save_to_file(file); + + _update_profile_list(name); +} + +void EditorFeatureProfileManager::_profile_selected(int p_what) { + + _update_selected_profile(); +} + +void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected) { + + TreeItem *class_item = class_list->create_item(p_parent); + class_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + class_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_class, "Node")); + String text = p_class; + + bool disabled = edited->is_class_disabled(p_class); + bool disabled_editor = edited->is_class_editor_disabled(p_class); + bool disabled_properties = edited->has_class_properties_disabled(p_class); + if (disabled) { + class_item->set_custom_color(0, get_color("disabled_font_color", "Editor")); + } else if (disabled_editor && disabled_properties) { + text += " " + TTR("(Editor Disabled, Properties Disabled)"); + } else if (disabled_properties) { + text += " " + TTR("(Properties Disabled)"); + } else if (disabled_editor) { + text += " " + TTR("(Editor Disabled)"); + } + class_item->set_text(0, text); + class_item->set_editable(0, true); + class_item->set_selectable(0, true); + class_item->set_metadata(0, p_class); + + if (p_class == p_selected) { + class_item->select(0); + } + if (disabled) { + //class disabled, do nothing else (do not show further) + return; + } + + class_item->set_checked(0, true); // if its not disabled, its checked + + List<StringName> child_classes; + ClassDB::get_direct_inheriters_from_class(p_class, &child_classes); + child_classes.sort_custom<StringName::AlphCompare>(); + + for (List<StringName>::Element *E = child_classes.front(); E; E = E->next()) { + String name = E->get(); + if (name.begins_with("Editor") || ClassDB::get_api_type(name) != ClassDB::API_CORE) { + continue; + } + _fill_classes_from(class_item, name, p_selected); + } +} + +void EditorFeatureProfileManager::_class_list_item_selected() { + + if (updating_features) + return; + + property_list->clear(); + + TreeItem *item = class_list->get_selected(); + if (!item) { + return; + } + + Variant md = item->get_metadata(0); + if (md.get_type() != Variant::STRING) { + return; + } + + String class_name = md; + + if (edited->is_class_disabled(class_name)) { + return; + } + + updating_features = true; + TreeItem *root = property_list->create_item(); + TreeItem *options = property_list->create_item(root); + options->set_text(0, TTR("Class Options:")); + + { + TreeItem *option = property_list->create_item(options); + option->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + option->set_editable(0, true); + option->set_selectable(0, true); + option->set_checked(0, !edited->is_class_editor_disabled(class_name)); + option->set_text(0, TTR("Enable Contextual Editor")); + option->set_metadata(0, CLASS_OPTION_DISABLE_EDITOR); + } + + TreeItem *properties = property_list->create_item(root); + properties->set_text(0, TTR("Enabled Properties:")); + + List<PropertyInfo> props; + + ClassDB::get_property_list(class_name, &props, true); + + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + + String name = E->get().name; + if (!(E->get().usage & PROPERTY_USAGE_EDITOR)) + continue; + TreeItem *property = property_list->create_item(properties); + property->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + property->set_editable(0, true); + property->set_selectable(0, true); + property->set_checked(0, !edited->is_class_property_disabled(class_name, name)); + property->set_text(0, name.capitalize()); + property->set_metadata(0, name); + String icon_type = Variant::get_type_name(E->get().type); + property->set_icon(0, EditorNode::get_singleton()->get_class_icon(icon_type)); + } + + updating_features = false; +} + +void EditorFeatureProfileManager::_class_list_item_edited() { + + if (updating_features) + return; + + TreeItem *item = class_list->get_edited(); + if (!item) { + return; + } + + bool checked = item->is_checked(0); + + Variant md = item->get_metadata(0); + if (md.get_type() == Variant::STRING) { + String class_selected = md; + edited->set_disable_class(class_selected, !checked); + _save_and_update(); + _update_selected_profile(); + } else if (md.get_type() == Variant::INT) { + int feature_selected = md; + edited->set_disable_feature(EditorFeatureProfile::Feature(feature_selected), !checked); + _save_and_update(); + } +} + +void EditorFeatureProfileManager::_property_item_edited() { + if (updating_features) + return; + + TreeItem *class_item = class_list->get_selected(); + if (!class_item) { + return; + } + + Variant md = class_item->get_metadata(0); + if (md.get_type() != Variant::STRING) { + return; + } + + String class_name = md; + + TreeItem *item = property_list->get_edited(); + if (!item) { + return; + } + bool checked = item->is_checked(0); + + md = item->get_metadata(0); + if (md.get_type() == Variant::STRING) { + String property_selected = md; + edited->set_disable_class_property(class_name, property_selected, !checked); + _save_and_update(); + _update_selected_profile(); + } else if (md.get_type() == Variant::INT) { + int feature_selected = md; + switch (feature_selected) { + case CLASS_OPTION_DISABLE_EDITOR: { + edited->set_disable_class_editor(class_name, !checked); + _save_and_update(); + _update_selected_profile(); + } break; + } + } +} + +void EditorFeatureProfileManager::_update_selected_profile() { + + String class_selected; + int feature_selected = -1; + + if (class_list->get_selected()) { + Variant md = class_list->get_selected()->get_metadata(0); + if (md.get_type() == Variant::STRING) { + class_selected = md; + } else if (md.get_type() == Variant::INT) { + feature_selected = md; + } + } + + class_list->clear(); + + String profile = _get_selected_profile(); + if (profile == String()) { //nothing selected, nothing edited + property_list->clear(); + edited.unref(); + return; + } + + if (profile == current_profile) { + edited = current; //reuse current profile (which is what editor uses) + ERR_FAIL_COND(current.is_null()); //nothing selected, current should never be null + } else { + //reload edited, if different from current + edited.instance(); + Error err = edited->load_from_file(EditorSettings::get_singleton()->get_feature_profiles_dir().plus_file(profile + ".profile")); + ERR_FAIL_COND(err != OK); + } + + updating_features = true; + + TreeItem *root = class_list->create_item(); + + TreeItem *features = class_list->create_item(root); + features->set_text(0, TTR("Enabled Features:")); + for (int i = 0; i < EditorFeatureProfile::FEATURE_MAX; i++) { + + TreeItem *feature = class_list->create_item(features); + feature->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + feature->set_text(0, TTRGET(EditorFeatureProfile::get_feature_name(EditorFeatureProfile::Feature(i)))); + feature->set_selectable(0, true); + feature->set_editable(0, true); + feature->set_metadata(0, i); + if (!edited->is_feature_disabled(EditorFeatureProfile::Feature(i))) { + feature->set_checked(0, true); + } + + if (i == feature_selected) { + feature->select(0); + } + } + + TreeItem *classes = class_list->create_item(root); + classes->set_text(0, TTR("Enabled Classes:")); + + _fill_classes_from(classes, "Node", class_selected); + _fill_classes_from(classes, "Resource", class_selected); + + updating_features = false; + + _class_list_item_selected(); +} + +void EditorFeatureProfileManager::_import_profiles(const Vector<String> &p_paths) { + + //test it first + for (int i = 0; i < p_paths.size(); i++) { + Ref<EditorFeatureProfile> profile; + profile.instance(); + Error err = profile->load_from_file(p_paths[i]); + String basefile = p_paths[i].get_file(); + if (err != OK) { + EditorNode::get_singleton()->show_warning(vformat(TTR("File '%s' format is invalid, import aborted."), basefile)); + return; + } + + String dst_file = EditorSettings::get_singleton()->get_feature_profiles_dir().plus_file(basefile); + + if (FileAccess::exists(dst_file)) { + EditorNode::get_singleton()->show_warning(vformat(TTR("Profile '%s' already exists. Remote it first before importing, import aborted."), basefile.get_basename())); + return; + } + } + + //do it second + for (int i = 0; i < p_paths.size(); i++) { + Ref<EditorFeatureProfile> profile; + profile.instance(); + Error err = profile->load_from_file(p_paths[i]); + ERR_CONTINUE(err != OK); + String basefile = p_paths[i].get_file(); + String dst_file = EditorSettings::get_singleton()->get_feature_profiles_dir().plus_file(basefile); + profile->save_to_file(dst_file); + } + + _update_profile_list(); +} + +void EditorFeatureProfileManager::_export_profile(const String &p_path) { + + ERR_FAIL_COND(edited.is_null()); + Error err = edited->save_to_file(p_path); + if (err != OK) { + EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving profile to path: '%s'."), p_path)); + } +} + +void EditorFeatureProfileManager::_save_and_update() { + + String edited_path = _get_selected_profile(); + ERR_FAIL_COND(edited_path == String()); + ERR_FAIL_COND(edited.is_null()); + + edited->save_to_file(EditorSettings::get_singleton()->get_feature_profiles_dir().plus_file(edited_path + ".profile")); + + if (edited == current) { + update_timer->start(); + } +} + +void EditorFeatureProfileManager::_emit_current_profile_changed() { + + emit_signal("current_feature_profile_changed"); +} + +void EditorFeatureProfileManager::notify_changed() { + _emit_current_profile_changed(); +} + +Ref<EditorFeatureProfile> EditorFeatureProfileManager::get_current_profile() { + return current; +} + +EditorFeatureProfileManager *EditorFeatureProfileManager::singleton = NULL; + +void EditorFeatureProfileManager::_bind_methods() { + + ClassDB::bind_method("_update_selected_profile", &EditorFeatureProfileManager::_update_selected_profile); + ClassDB::bind_method("_profile_action", &EditorFeatureProfileManager::_profile_action); + ClassDB::bind_method("_create_new_profile", &EditorFeatureProfileManager::_create_new_profile); + ClassDB::bind_method("_profile_selected", &EditorFeatureProfileManager::_profile_selected); + ClassDB::bind_method("_erase_selected_profile", &EditorFeatureProfileManager::_erase_selected_profile); + ClassDB::bind_method("_import_profiles", &EditorFeatureProfileManager::_import_profiles); + ClassDB::bind_method("_export_profile", &EditorFeatureProfileManager::_export_profile); + ClassDB::bind_method("_class_list_item_selected", &EditorFeatureProfileManager::_class_list_item_selected); + ClassDB::bind_method("_class_list_item_edited", &EditorFeatureProfileManager::_class_list_item_edited); + ClassDB::bind_method("_property_item_edited", &EditorFeatureProfileManager::_property_item_edited); + ClassDB::bind_method("_emit_current_profile_changed", &EditorFeatureProfileManager::_emit_current_profile_changed); + + ADD_SIGNAL(MethodInfo("current_feature_profile_changed")); +} + +EditorFeatureProfileManager::EditorFeatureProfileManager() { + + VBoxContainer *main_vbc = memnew(VBoxContainer); + add_child(main_vbc); + + HBoxContainer *name_hbc = memnew(HBoxContainer); + current_profile_name = memnew(LineEdit); + name_hbc->add_child(current_profile_name); + current_profile_name->set_editable(false); + current_profile_name->set_h_size_flags(SIZE_EXPAND_FILL); + profile_actions[PROFILE_CLEAR] = memnew(Button(TTR("Unset"))); + name_hbc->add_child(profile_actions[PROFILE_CLEAR]); + profile_actions[PROFILE_CLEAR]->set_disabled(true); + profile_actions[PROFILE_CLEAR]->connect("pressed", this, "_profile_action", varray(PROFILE_CLEAR)); + + main_vbc->add_margin_child(TTR("Current Profile"), name_hbc); + + HBoxContainer *profiles_hbc = memnew(HBoxContainer); + profile_list = memnew(OptionButton); + profile_list->set_h_size_flags(SIZE_EXPAND_FILL); + profiles_hbc->add_child(profile_list); + profile_list->connect("item_selected", this, "_profile_selected"); + + profile_actions[PROFILE_SET] = memnew(Button(TTR("Make Current"))); + profiles_hbc->add_child(profile_actions[PROFILE_SET]); + profile_actions[PROFILE_SET]->set_disabled(true); + profile_actions[PROFILE_SET]->connect("pressed", this, "_profile_action", varray(PROFILE_SET)); + + profile_actions[PROFILE_ERASE] = memnew(Button(TTR("Remove"))); + profiles_hbc->add_child(profile_actions[PROFILE_ERASE]); + profile_actions[PROFILE_ERASE]->set_disabled(true); + profile_actions[PROFILE_ERASE]->connect("pressed", this, "_profile_action", varray(PROFILE_ERASE)); + + profiles_hbc->add_child(memnew(VSeparator)); + + profile_actions[PROFILE_NEW] = memnew(Button(TTR("New"))); + profiles_hbc->add_child(profile_actions[PROFILE_NEW]); + profile_actions[PROFILE_NEW]->connect("pressed", this, "_profile_action", varray(PROFILE_NEW)); + + profiles_hbc->add_child(memnew(VSeparator)); + + profile_actions[PROFILE_IMPORT] = memnew(Button(TTR("Import"))); + profiles_hbc->add_child(profile_actions[PROFILE_IMPORT]); + profile_actions[PROFILE_IMPORT]->connect("pressed", this, "_profile_action", varray(PROFILE_IMPORT)); + + profile_actions[PROFILE_EXPORT] = memnew(Button(TTR("Export"))); + profiles_hbc->add_child(profile_actions[PROFILE_EXPORT]); + profile_actions[PROFILE_EXPORT]->set_disabled(true); + profile_actions[PROFILE_EXPORT]->connect("pressed", this, "_profile_action", varray(PROFILE_EXPORT)); + + main_vbc->add_margin_child(TTR("Available Profiles"), profiles_hbc); + + h_split = memnew(HSplitContainer); + h_split->set_v_size_flags(SIZE_EXPAND_FILL); + main_vbc->add_child(h_split); + + VBoxContainer *class_list_vbc = memnew(VBoxContainer); + h_split->add_child(class_list_vbc); + class_list_vbc->set_h_size_flags(SIZE_EXPAND_FILL); + + class_list = memnew(Tree); + class_list_vbc->add_margin_child(TTR("Enabled Classes"), class_list, true); + class_list->set_hide_root(true); + class_list->set_hide_folding(true); + class_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true); + class_list->connect("cell_selected", this, "_class_list_item_selected"); + class_list->connect("item_edited", this, "_class_list_item_edited", varray(), CONNECT_DEFERRED); + + VBoxContainer *property_list_vbc = memnew(VBoxContainer); + h_split->add_child(property_list_vbc); + property_list_vbc->set_h_size_flags(SIZE_EXPAND_FILL); + + property_list = memnew(Tree); + property_list_vbc->add_margin_child(TTR("Class Options"), property_list, true); + property_list->set_hide_root(true); + property_list->set_hide_folding(true); + property_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true); + property_list->connect("item_edited", this, "_property_item_edited", varray(), CONNECT_DEFERRED); + + new_profile_dialog = memnew(ConfirmationDialog); + new_profile_dialog->set_title(TTR("New profile name:")); + new_profile_name = memnew(LineEdit); + new_profile_dialog->add_child(new_profile_name); + new_profile_name->set_custom_minimum_size(Size2(300 * EDSCALE, 1)); + add_child(new_profile_dialog); + new_profile_dialog->connect("confirmed", this, "_create_new_profile"); + new_profile_dialog->register_text_enter(new_profile_name); + new_profile_dialog->get_ok()->set_text(TTR("Create")); + + erase_profile_dialog = memnew(ConfirmationDialog); + add_child(erase_profile_dialog); + erase_profile_dialog->set_title(TTR("Erase Profile")); + erase_profile_dialog->connect("confirmed", this, "_erase_selected_profile"); + + import_profiles = memnew(EditorFileDialog); + add_child(import_profiles); + import_profiles->set_mode(EditorFileDialog::MODE_OPEN_FILES); + import_profiles->add_filter("*.profile; Godot Feature Profile"); + import_profiles->connect("files_selected", this, "_import_profiles"); + import_profiles->set_title(TTR("Import Profile(s)")); + import_profiles->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + + export_profile = memnew(EditorFileDialog); + add_child(export_profile); + export_profile->set_mode(EditorFileDialog::MODE_SAVE_FILE); + export_profile->add_filter("*.profile; Godot Feature Profile"); + export_profile->connect("file_selected", this, "_export_profile"); + export_profile->set_title(TTR("Export Profile")); + export_profile->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + + set_title(TTR("Manage Editor Feature Profiles")); + EDITOR_DEF("_default_feature_profile", ""); + + update_timer = memnew(Timer); + update_timer->set_wait_time(1); //wait a second before updating editor + add_child(update_timer); + update_timer->connect("timeout", this, "_emit_current_profile_changed"); + update_timer->set_one_shot(true); + + updating_features = false; + + singleton = this; +} diff --git a/editor/editor_feature_profile.h b/editor/editor_feature_profile.h new file mode 100644 index 0000000000..b7c2ebc1b2 --- /dev/null +++ b/editor/editor_feature_profile.h @@ -0,0 +1,142 @@ +#ifndef EDITOR_FEATURE_PROFILE_H +#define EDITOR_FEATURE_PROFILE_H + +#include "core/os/file_access.h" +#include "core/reference.h" +#include "editor/editor_file_dialog.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/option_button.h" +#include "scene/gui/separator.h" +#include "scene/gui/split_container.h" +#include "scene/gui/tree.h" + +class EditorFeatureProfile : public Reference { + GDCLASS(EditorFeatureProfile, Reference); + +public: + enum Feature { + FEATURE_3D, + FEATURE_SCRIPT, + FEATURE_ASSET_LIB, + FEATURE_SCENE_TREE, + FEATURE_IMPORT_DOCK, + FEATURE_NODE_DOCK, + FEATURE_FILESYSTEM_DOCK, + FEATURE_MAX + }; + +private: + Set<StringName> disabled_classes; + Set<StringName> disabled_editors; + Map<StringName, Set<StringName> > disabled_properties; + + bool features_disabled[FEATURE_MAX]; + static const char *feature_names[FEATURE_MAX]; + static const char *feature_identifiers[FEATURE_MAX]; + + String _get_feature_name(Feature p_feature) { return get_feature_name(p_feature); } + +protected: + static void _bind_methods(); + +public: + void set_disable_class(const StringName &p_class, bool p_disabled); + bool is_class_disabled(const StringName &p_class) const; + + void set_disable_class_editor(const StringName &p_class, bool p_disabled); + bool is_class_editor_disabled(const StringName &p_class) const; + + void set_disable_class_property(const StringName &p_class, const StringName &p_property, bool p_disabled); + bool is_class_property_disabled(const StringName &p_class, const StringName &p_property) const; + bool has_class_properties_disabled(const StringName &p_class) const; + + void set_disable_feature(Feature p_feature, bool p_disable); + bool is_feature_disabled(Feature p_feature) const; + + Error save_to_file(const String &p_path); + Error load_from_file(const String &p_path); + + static String get_feature_name(Feature p_feature); + + EditorFeatureProfile(); +}; + +VARIANT_ENUM_CAST(EditorFeatureProfile::Feature) + +class EditorFeatureProfileManager : public AcceptDialog { + + GDCLASS(EditorFeatureProfileManager, AcceptDialog); + + enum Action { + PROFILE_CLEAR, + PROFILE_SET, + PROFILE_IMPORT, + PROFILE_EXPORT, + PROFILE_NEW, + PROFILE_ERASE, + PROFILE_MAX + }; + + enum ClassOptions { + CLASS_OPTION_DISABLE_EDITOR + }; + + ConfirmationDialog *erase_profile_dialog; + ConfirmationDialog *new_profile_dialog; + LineEdit *new_profile_name; + + LineEdit *current_profile_name; + OptionButton *profile_list; + Button *profile_actions[PROFILE_MAX]; + + HSplitContainer *h_split; + + Tree *class_list; + Tree *property_list; + + EditorFileDialog *import_profiles; + EditorFileDialog *export_profile; + + void _profile_action(int p_action); + void _profile_selected(int p_what); + + String current_profile; + void _update_profile_list(const String &p_select_profile = String()); + void _update_selected_profile(); + void _fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected); + + Ref<EditorFeatureProfile> current; + Ref<EditorFeatureProfile> edited; + + void _erase_selected_profile(); + void _create_new_profile(); + String _get_selected_profile(); + + void _import_profiles(const Vector<String> &p_paths); + void _export_profile(const String &p_path); + + bool updating_features; + + void _class_list_item_selected(); + void _class_list_item_edited(); + void _property_item_edited(); + void _save_and_update(); + + Timer *update_timer; + void _emit_current_profile_changed(); + + static EditorFeatureProfileManager *singleton; + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + Ref<EditorFeatureProfile> get_current_profile(); + void notify_changed(); + + static EditorFeatureProfileManager *get_singleton() { return singleton; } + EditorFeatureProfileManager(); +}; + +#endif // EDITOR_FEATURE_PROFILE_H diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 4c9f58f312..e6a6d9e6a6 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -1499,7 +1499,7 @@ EditorFileDialog::EditorFileDialog() { dir_next = memnew(ToolButton); dir_next->set_tooltip(TTR("Next Folder")); dir_up = memnew(ToolButton); - dir_up->set_tooltip(TTR("Go to parent folder")); + dir_up->set_tooltip(TTR("Go to parent folder.")); pathhb->add_child(dir_prev); pathhb->add_child(dir_next); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 3d9d5e26be..abd3bea951 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -43,7 +43,7 @@ EditorFileSystem *EditorFileSystem::singleton = NULL; //the name is the version, to keep compatibility with different versions of Godot -#define CACHE_FILE_NAME "filesystem_cache5" +#define CACHE_FILE_NAME "filesystem_cache6" void EditorFileSystemDirectory::sort_files() { @@ -241,7 +241,7 @@ void EditorFileSystem::_scan_filesystem() { } else { Vector<String> split = l.split("::"); - ERR_CONTINUE(split.size() != 7); + ERR_CONTINUE(split.size() != 8); String name = split[0]; String file; @@ -253,11 +253,12 @@ void EditorFileSystem::_scan_filesystem() { fc.modification_time = split[2].to_int64(); fc.import_modification_time = split[3].to_int64(); fc.import_valid = split[4].to_int64() != 0; - fc.script_class_name = split[5].get_slice("<>", 0); - fc.script_class_extends = split[5].get_slice("<>", 1); - fc.script_class_icon_path = split[5].get_slice("<>", 2); + fc.import_group_file = split[5].strip_edges(); + fc.script_class_name = split[6].get_slice("<>", 0); + fc.script_class_extends = split[6].get_slice("<>", 1); + fc.script_class_icon_path = split[6].get_slice("<>", 2); - String deps = split[6].strip_edges(); + String deps = split[7].strip_edges(); if (deps.length()) { Vector<String> dp = deps.split("<>"); for (int i = 0; i < dp.size(); i++) { @@ -318,6 +319,9 @@ void EditorFileSystem::_scan_filesystem() { } void EditorFileSystem::_save_filesystem_cache() { + + group_file_cache.clear(); + String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(CACHE_FILE_NAME); FileAccess *f = FileAccess::open(fscache, FileAccess::WRITE); @@ -771,6 +775,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess fi->import_valid = fc->import_valid; fi->script_class_name = fc->script_class_name; + fi->import_group_file = fc->import_group_file; fi->script_class_extends = fc->script_class_extends; fi->script_class_icon_path = fc->script_class_icon_path; @@ -784,6 +789,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess if (fc->type == String()) { fi->type = ResourceLoader::get_resource_type(path); + fi->import_group_file = ResourceLoader::get_import_group_file(path); //there is also the chance that file type changed due to reimport, must probably check this somehow here (or kind of note it for next time in another file?) //note: I think this should not happen any longer.. } @@ -791,6 +797,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess } else { fi->type = ResourceFormatImporter::get_singleton()->get_resource_type(path); + fi->import_group_file = ResourceFormatImporter::get_singleton()->get_import_group_file(path); fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path); fi->modified_time = 0; fi->import_modified_time = 0; @@ -918,6 +925,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const fi->type = ResourceLoader::get_resource_type(path); fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path); fi->import_valid = ResourceLoader::is_import_valid(path); + fi->import_group_file = ResourceLoader::get_import_group_file(path); { ItemAction ia; @@ -1187,7 +1195,10 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir, for (int i = 0; i < p_dir->files.size(); i++) { - String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends + "<>" + p_dir->files[i]->script_class_icon_path; + if (p_dir->files[i]->import_group_file != String()) { + group_file_cache.insert(p_dir->files[i]->import_group_file); + } + String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->import_group_file + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends + "<>" + p_dir->files[i]->script_class_icon_path; s += "::"; for (int j = 0; j < p_dir->files[i]->deps.size(); j++) { @@ -1523,6 +1534,7 @@ void EditorFileSystem::update_file(const String &p_file) { fs->files[cpos]->type = type; fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path); + fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(p_file); fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file); fs->files[cpos]->deps = _get_dependencies(p_file); fs->files[cpos]->import_valid = ResourceLoader::is_import_valid(p_file); @@ -1534,6 +1546,168 @@ void EditorFileSystem::update_file(const String &p_file) { _queue_update_script_classes(); } +Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector<String> &p_files) { + + String importer_name; + + Map<String, Map<StringName, Variant> > source_file_options; + Map<String, String> base_paths; + for (int i = 0; i < p_files.size(); i++) { + + Ref<ConfigFile> config; + config.instance(); + Error err = config->load(p_files[i] + ".import"); + ERR_CONTINUE(err != OK); + ERR_CONTINUE(!config->has_section_key("remap", "importer")); + String file_importer_name = config->get_value("remap", "importer"); + ERR_CONTINUE(file_importer_name == String()); + + if (importer_name != String() && importer_name != file_importer_name) { + print_line("one importer: " + importer_name + " the other: " + file_importer_name); + EditorNode::get_singleton()->show_warning(vformat(TTR("There are multiple importers for different types pointing to file %s, import aborted"), p_group_file)); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + source_file_options[p_files[i]] = Map<StringName, Variant>(); + importer_name = file_importer_name; + + Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name); + ERR_FAIL_COND_V(!importer.is_valid(), ERR_FILE_CORRUPT); + List<ResourceImporter::ImportOption> options; + importer->get_import_options(&options); + //set default values + for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) { + + source_file_options[p_files[i]][E->get().option.name] = E->get().default_value; + } + + if (config->has_section("params")) { + List<String> sk; + config->get_section_keys("params", &sk); + for (List<String>::Element *E = sk.front(); E; E = E->next()) { + String param = E->get(); + Variant value = config->get_value("params", param); + //override with whathever is in file + source_file_options[p_files[i]][param] = value; + } + } + + base_paths[p_files[i]] = ResourceFormatImporter::get_singleton()->get_import_base_path(p_files[i]); + } + + ERR_FAIL_COND_V(importer_name == String(), ERR_UNCONFIGURED); + + Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name); + + Error err = importer->import_group_file(p_group_file, source_file_options, base_paths); + + //all went well, overwrite config files with proper remaps and md5s + for (Map<String, Map<StringName, Variant> >::Element *E = source_file_options.front(); E; E = E->next()) { + + String file = E->key(); + String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(file); + FileAccessRef f = FileAccess::open(file + ".import", FileAccess::WRITE); + ERR_FAIL_COND_V(!f, ERR_FILE_CANT_OPEN); + + //write manually, as order matters ([remap] has to go first for performance). + f->store_line("[remap]"); + f->store_line(""); + f->store_line("importer=\"" + importer->get_importer_name() + "\""); + if (importer->get_resource_type() != "") { + f->store_line("type=\"" + importer->get_resource_type() + "\""); + } + + Vector<String> dest_paths; + + if (err == OK) { + String path = base_path + "." + importer->get_save_extension(); + f->store_line("path=\"" + path + "\""); + dest_paths.push_back(path); + } + + f->store_line("group_file=" + Variant(p_group_file).get_construct_string()); + + if (err == OK) { + f->store_line("valid=true"); + } else { + f->store_line("valid=false"); + } + f->store_line("[deps]\n"); + + f->store_line(""); + + f->store_line("source_file=" + Variant(file).get_construct_string()); + if (dest_paths.size()) { + Array dp; + for (int i = 0; i < dest_paths.size(); i++) { + dp.push_back(dest_paths[i]); + } + f->store_line("dest_files=" + Variant(dp).get_construct_string() + "\n"); + } + f->store_line("[params]"); + f->store_line(""); + + //store options in provided order, to avoid file changing. Order is also important because first match is accepted first. + + List<ResourceImporter::ImportOption> options; + importer->get_import_options(&options); + //set default values + for (List<ResourceImporter::ImportOption>::Element *F = options.front(); F; F = F->next()) { + + String base = F->get().option.name; + Variant v = F->get().default_value; + if (source_file_options[file].has(base)) { + v = source_file_options[file][base]; + } + String value; + VariantWriter::write_to_string(v, value); + f->store_line(base + "=" + value); + } + + f->close(); + + // Store the md5's of the various files. These are stored separately so that the .import files can be version controlled. + FileAccessRef md5s = FileAccess::open(base_path + ".md5", FileAccess::WRITE); + ERR_FAIL_COND_V(!md5s, ERR_FILE_CANT_OPEN); + + md5s->store_line("source_md5=\"" + FileAccess::get_md5(file) + "\""); + if (dest_paths.size()) { + md5s->store_line("dest_md5=\"" + FileAccess::get_multiple_md5(dest_paths) + "\"\n"); + } + md5s->close(); + + EditorFileSystemDirectory *fs = NULL; + int cpos = -1; + bool found = _find_file(file, &fs, cpos); + ERR_FAIL_COND_V(!found, ERR_UNCONFIGURED); + + //update modified times, to avoid reimport + fs->files[cpos]->modified_time = FileAccess::get_modified_time(file); + fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(file + ".import"); + fs->files[cpos]->deps = _get_dependencies(file); + fs->files[cpos]->type = importer->get_resource_type(); + fs->files[cpos]->import_valid = err == OK; + + //if file is currently up, maybe the source it was loaded from changed, so import math must be updated for it + //to reload properly + if (ResourceCache::has(file)) { + + Resource *r = ResourceCache::get(file); + + if (r->get_import_path() != String()) { + + String dst_path = ResourceFormatImporter::get_singleton()->get_internal_resource_path(file); + r->set_import_path(dst_path); + r->set_import_last_modified_time(0); + } + } + + EditorResourcePreview::get_singleton()->check_for_invalidation(file); + } + + return err; +} + void EditorFileSystem::_reimport_file(const String &p_file) { EditorFileSystemDirectory *fs = NULL; @@ -1738,6 +1912,24 @@ void EditorFileSystem::_reimport_file(const String &p_file) { EditorResourcePreview::get_singleton()->check_for_invalidation(p_file); } +void EditorFileSystem::_find_group_files(EditorFileSystemDirectory *efd, Map<String, Vector<String> > &group_files, Set<String> &groups_to_reimport) { + + int fc = efd->files.size(); + const EditorFileSystemDirectory::FileInfo *const *files = efd->files.ptr(); + for (int i = 0; i < fc; i++) { + if (groups_to_reimport.has(files[i]->import_group_file)) { + if (!group_files.has(files[i]->import_group_file)) { + group_files[files[i]->import_group_file] = Vector<String>(); + } + group_files[files[i]->import_group_file].push_back(efd->get_file_path(i)); + } + } + + for (int i = 0; i < efd->get_subdir_count(); i++) { + _find_group_files(efd->get_subdir(i), group_files, groups_to_reimport); + } +} + void EditorFileSystem::reimport_files(const Vector<String> &p_files) { { //check that .import folder exists @@ -1757,22 +1949,58 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { EditorProgress pr("reimport", TTR("(Re)Importing Assets"), p_files.size()); Vector<ImportFile> files; + Set<String> groups_to_reimport; for (int i = 0; i < p_files.size(); i++) { - ImportFile ifile; - ifile.path = p_files[i]; - ifile.order = ResourceFormatImporter::get_singleton()->get_import_order(p_files[i]); - files.push_back(ifile); + + String group_file = ResourceFormatImporter::get_singleton()->get_import_group_file(p_files[i]); + + if (group_file_cache.has(p_files[i])) { + //maybe the file itself is a group! + groups_to_reimport.insert(p_files[i]); + //groups do not belong to grups + group_file = String(); + } else if (group_file != String()) { + //it's a group file, add group to import and skip this file + groups_to_reimport.insert(group_file); + } else { + //it's a regular file + ImportFile ifile; + ifile.path = p_files[i]; + ifile.order = ResourceFormatImporter::get_singleton()->get_import_order(p_files[i]); + files.push_back(ifile); + } + + //group may have changed, so also update group reference + EditorFileSystemDirectory *fs = NULL; + int cpos = -1; + if (_find_file(p_files[i], &fs, cpos)) { + + fs->files.write[cpos]->import_group_file = group_file; + } } files.sort(); for (int i = 0; i < files.size(); i++) { pr.step(files[i].path.get_file(), i); - _reimport_file(files[i].path); } + //reimport groups + + if (groups_to_reimport.size()) { + Map<String, Vector<String> > group_files; + _find_group_files(filesystem, group_files, groups_to_reimport); + for (Map<String, Vector<String> >::Element *E = group_files.front(); E; E = E->next()) { + + Error err = _reimport_group(E->key(), E->get()); + if (err == OK) { + _reimport_file(E->key()); + } + } + } + _save_filesystem_cache(); importing = false; if (!is_scanning()) { @@ -1793,6 +2021,63 @@ Error EditorFileSystem::_resource_import(const String &p_path) { return OK; } +bool EditorFileSystem::is_group_file(const String &p_path) const { + return group_file_cache.has(p_path); +} + +void EditorFileSystem::_move_group_files(EditorFileSystemDirectory *efd, const String &p_group_file, const String &p_new_location) { + + int fc = efd->files.size(); + EditorFileSystemDirectory::FileInfo *const *files = efd->files.ptrw(); + for (int i = 0; i < fc; i++) { + + if (files[i]->import_group_file == p_group_file) { + + files[i]->import_group_file = p_new_location; + + Ref<ConfigFile> config; + config.instance(); + String path = efd->get_file_path(i) + ".import"; + Error err = config->load(path); + if (err != OK) { + continue; + } + if (config->has_section_key("remap", "group_file")) { + + config->set_value("remap", "group_file", p_new_location); + } + + List<String> sk; + config->get_section_keys("params", &sk); + for (List<String>::Element *E = sk.front(); E; E = E->next()) { + //not very clean, but should work + String param = E->get(); + String value = config->get_value("params", param); + if (value == p_group_file) { + config->set_value("params", param, p_new_location); + } + } + + config->save(path); + } + } + + for (int i = 0; i < efd->get_subdir_count(); i++) { + _move_group_files(efd->get_subdir(i), p_group_file, p_new_location); + } +} + +void EditorFileSystem::move_group_file(const String &p_path, const String &p_new_path) { + + if (get_filesystem()) { + _move_group_files(get_filesystem(), p_path, p_new_path); + if (group_file_cache.has(p_path)) { + group_file_cache.erase(p_path); + group_file_cache.insert(p_new_path); + } + } +} + void EditorFileSystem::_bind_methods() { ClassDB::bind_method(D_METHOD("get_filesystem"), &EditorFileSystem::get_filesystem); diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 2a9e325454..8943706202 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -56,6 +56,7 @@ class EditorFileSystemDirectory : public Object { uint64_t modified_time; uint64_t import_modified_time; bool import_valid; + String import_group_file; Vector<String> deps; bool verified; //used for checking changes String script_class_name; @@ -167,6 +168,7 @@ class EditorFileSystem : public Node { uint64_t import_modification_time; Vector<String> deps; bool import_valid; + String import_group_file; String script_class_name; String script_class_extends; String script_class_icon_path; @@ -211,6 +213,7 @@ class EditorFileSystem : public Node { void _update_extensions(); void _reimport_file(const String &p_file); + Error _reimport_group(const String &p_group_file, const Vector<String> &p_files); bool _test_for_reimport(const String &p_path, bool p_only_imported_files); @@ -236,6 +239,12 @@ class EditorFileSystem : public Node { bool using_fat_32; //workaround for projects in FAT32 filesystem (pendrives, most of the time) + void _find_group_files(EditorFileSystemDirectory *efd, Map<String, Vector<String> > &group_files, Set<String> &groups_to_reimport); + + void _move_group_files(EditorFileSystemDirectory *efd, const String &p_group_file, const String &p_new_location); + + Set<String> group_file_cache; + protected: void _notification(int p_what); static void _bind_methods(); @@ -260,6 +269,9 @@ public: void update_script_classes(); + bool is_group_file(const String &p_path) const; + void move_group_file(const String &p_path, const String &p_new_path); + EditorFileSystem(); ~EditorFileSystem(); }; diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp index 77c0f7491e..f6079624de 100644 --- a/editor/editor_folding.cpp +++ b/editor/editor_folding.cpp @@ -92,7 +92,7 @@ void EditorFolding::load_resource_folding(RES p_resource, const String &p_path) _set_unfolds(p_resource.ptr(), unfolds); } -void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources) { +void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array& nodes_folded,Set<RES> &resources) { if (p_root != p_node) { if (!p_node->get_owner()) { return; //not owned, bye @@ -102,6 +102,9 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p } } + if (p_node->is_displayed_folded()) { + nodes_folded.push_back(p_root->get_path_to(p_node)); + } PoolVector<String> unfolds = _get_unfolds(p_node); if (unfolds.size()) { @@ -112,20 +115,22 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p List<PropertyInfo> plist; p_node->get_property_list(&plist); for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (E->get().type == Variant::OBJECT) { - RES res = p_node->get(E->get().name); - if (res.is_valid() && !resources.has(res) && res->get_path() != String() && !res->get_path().is_resource_file()) { - - PoolVector<String> res_unfolds = _get_unfolds(res.ptr()); - resource_folds.push_back(res->get_path()); - resource_folds.push_back(res_unfolds); - resources.insert(res); + if (E->get().usage & PROPERTY_USAGE_EDITOR) { + if (E->get().type == Variant::OBJECT) { + RES res = p_node->get(E->get().name); + if (res.is_valid() && !resources.has(res) && res->get_path() != String() && !res->get_path().is_resource_file()) { + + PoolVector<String> res_unfolds = _get_unfolds(res.ptr()); + resource_folds.push_back(res->get_path()); + resource_folds.push_back(res_unfolds); + resources.insert(res); + } } } } for (int i = 0; i < p_node->get_child_count(); i++) { - _fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, resources); + _fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, nodes_folded,resources); } } void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path) { @@ -135,10 +140,12 @@ void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path Array unfolds, res_unfolds; Set<RES> resources; - _fill_folds(p_scene, p_scene, unfolds, res_unfolds, resources); + Array nodes_folded; + _fill_folds(p_scene, p_scene, unfolds, res_unfolds, nodes_folded, resources); config->set_value("folding", "node_unfolds", unfolds); config->set_value("folding", "resource_unfolds", res_unfolds); + config->set_value("folding", "nodes_folded", nodes_folded); String path = EditorSettings::get_singleton()->get_project_settings_dir(); String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; @@ -166,6 +173,10 @@ void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) { if (config->has_section_key("folding", "resource_unfolds")) { res_unfolds = config->get_value("folding", "resource_unfolds"); } + Array nodes_folded; + if (config->has_section_key("folding", "nodes_folded")) { + nodes_folded = config->get_value("folding", "nodes_folded"); + } ERR_FAIL_COND(unfolds.size() & 1); ERR_FAIL_COND(res_unfolds.size() & 1); @@ -193,6 +204,14 @@ void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) { PoolVector<String> unfolds2 = res_unfolds[i + 1]; _set_unfolds(res.ptr(), unfolds2); } + + for(int i=0;i<nodes_folded.size();i++) { + NodePath fold_path = nodes_folded[i]; + if (p_scene->has_node(fold_path)) { + Node *node = p_scene->get_node(fold_path); + node->set_display_folded(true); + } + } } bool EditorFolding::has_folding_data(const String &p_path) { diff --git a/editor/editor_folding.h b/editor/editor_folding.h index e4f7dbba80..5fc980c4a9 100644 --- a/editor/editor_folding.h +++ b/editor/editor_folding.h @@ -38,7 +38,7 @@ class EditorFolding { PoolVector<String> _get_unfolds(const Object *p_object); void _set_unfolds(Object *p_object, const PoolVector<String> &p_unfolds); - void _fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources); + void _fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array &nodes_folded, Set<RES> &resources); void _do_object_unfolds(Object *p_object, Set<RES> &resources); void _do_node_unfolds(Node *p_root, Node *p_node, Set<RES> &resources); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 1842d5e1e9..e0a2bbf477 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -97,8 +97,8 @@ void EditorHelp::_class_desc_select(const String &p_select) { emit_signal("go_to_help", "class_name:" + p_select.substr(1, p_select.length())); return; } else if (p_select.begins_with("@")) { - String tag = p_select.substr(1, 6); - String link = p_select.substr(7, p_select.length()); + String tag = p_select.substr(1, 8).rstrip(" "); + String link = p_select.substr(9, p_select.length()); String topic; Map<String, int> *table = NULL; @@ -109,24 +109,50 @@ void EditorHelp::_class_desc_select(const String &p_select) { } else if (tag == "member") { topic = "class_property"; table = &this->property_line; - } else if (tag == "enum ") { + } else if (tag == "enum") { topic = "class_enum"; table = &this->enum_line; } else if (tag == "signal") { topic = "class_signal"; table = &this->signal_line; + } else if (tag == "constant") { + topic = "class_constant"; + table = &this->constant_line; } else { return; } if (link.find(".") != -1) { - emit_signal("go_to_help", topic + ":" + link.get_slice(".", 0) + ":" + link.get_slice(".", 1)); } else { - - if (!table->has(link)) - return; - class_desc->scroll_to_line((*table)[link]); + if (table->has(link)) { + // Found in the current page + class_desc->scroll_to_line((*table)[link]); + } else { + if (topic == "class_enum") { + // Try to find the enum in @GlobalScope + const DocData::ClassDoc &cd = doc->class_list["@GlobalScope"]; + + for (int i = 0; i < cd.constants.size(); i++) { + if (cd.constants[i].enumeration == link) { + // Found in @GlobalScope + emit_signal("go_to_help", topic + ":@GlobalScope:" + link); + break; + } + } + } else if (topic == "class_constant") { + // Try to find the constant in @GlobalScope + const DocData::ClassDoc &cd = doc->class_list["@GlobalScope"]; + + for (int i = 0; i < cd.constants.size(); i++) { + if (cd.constants[i].name == link) { + // Found in @GlobalScope + emit_signal("go_to_help", topic + ":@GlobalScope:" + link); + break; + } + } + } + } } } else if (p_select.begins_with("http")) { OS::get_singleton()->shell_open(p_select); @@ -734,6 +760,9 @@ void EditorHelp::_update_doc() { if (cd.name == "@GlobalScope") enumValuesContainer[enum_list[i].name] = enumStartingLine; + // Add the enum constant line to the constant_line map so we can locate it as a constant + constant_line[enum_list[i].name] = class_desc->get_line_count() - 2; + class_desc->push_font(doc_code_font); class_desc->push_color(headline_color); _add_text(enum_list[i].name); @@ -1161,10 +1190,10 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) { p_rt->add_text("["); pos = brk_pos + 1; - } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ")) { + } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ")) { String link_target = tag.substr(tag.find(" ") + 1, tag.length()); - String link_tag = tag.substr(0, tag.find(" ")).rpad(6); + String link_tag = tag.substr(0, tag.find(" ")).rpad(8); p_rt->push_color(link_color); p_rt->push_meta("@" + link_tag + link_target); p_rt->add_text(link_target + (tag.begins_with("method ") ? "()" : "")); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 60fa5ff16f..c705f9af2b 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1370,6 +1370,30 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<Edit ped->added_editors.clear(); } +bool EditorInspector::_is_property_disabled_by_feature_profile(const StringName &p_property) { + + Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); + if (profile.is_null()) { + return false; + } + + StringName class_name = object->get_class(); + + while (class_name != StringName()) { + + if (profile->is_class_property_disabled(class_name, p_property)) { + return true; + } + if (profile->is_class_disabled(class_name)) { + //won't see properties of a disabled class + return true; + } + class_name = ClassDB::get_parent_class(class_name); + } + + return false; +} + void EditorInspector::update_tree() { //to update properly if all is refreshed @@ -1513,7 +1537,7 @@ void EditorInspector::update_tree() { continue; - } else if (!(p.usage & PROPERTY_USAGE_EDITOR)) + } else if (!(p.usage & PROPERTY_USAGE_EDITOR) || _is_property_disabled_by_feature_profile(p.name)) continue; if (p.usage & PROPERTY_USAGE_HIGH_END_GFX && VS::get_singleton()->is_low_end()) @@ -2132,6 +2156,10 @@ void EditorInspector::_node_removed(Node *p_node) { void EditorInspector::_notification(int p_what) { + if (p_what == NOTIFICATION_READY) { + EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", this, "_feature_profile_changed"); + } + if (p_what == NOTIFICATION_ENTER_TREE) { if (sub_inspector) { @@ -2238,6 +2266,11 @@ String EditorInspector::get_object_class() const { return object_class; } +void EditorInspector::_feature_profile_changed() { + + update_tree(); +} + void EditorInspector::_bind_methods() { ClassDB::bind_method("_property_changed", &EditorInspector::_property_changed, DEFVAL(""), DEFVAL(false)); @@ -2254,6 +2287,7 @@ void EditorInspector::_bind_methods() { ClassDB::bind_method("_resource_selected", &EditorInspector::_resource_selected); ClassDB::bind_method("_object_id_selected", &EditorInspector::_object_id_selected); ClassDB::bind_method("_vscroll_changed", &EditorInspector::_vscroll_changed); + ClassDB::bind_method("_feature_profile_changed", &EditorInspector::_feature_profile_changed); ClassDB::bind_method("refresh", &EditorInspector::refresh); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 3d22cdb9a3..494e8b5833 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -323,6 +323,10 @@ class EditorInspector : public ScrollContainer { void _vscroll_changed(double); + void _feature_profile_changed(); + + bool _is_property_disabled_by_feature_profile(const StringName &p_property); + protected: static void _bind_methods(); void _notification(int p_what); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 2f03a9943f..b729de2e2f 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -65,6 +65,7 @@ #include "editor/import/resource_importer_obj.h" #include "editor/import/resource_importer_scene.h" #include "editor/import/resource_importer_texture.h" +#include "editor/import/resource_importer_texture_atlas.h" #include "editor/import/resource_importer_wav.h" #include "editor/plugins/animation_blend_space_1d_editor.h" #include "editor/plugins/animation_blend_space_2d_editor.h" @@ -306,7 +307,14 @@ void EditorNode::_notification(int p_what) { VisualServer::get_singleton()->viewport_set_hide_canvas(get_scene_root()->get_viewport_rid(), true); VisualServer::get_singleton()->viewport_set_disable_environment(get_viewport()->get_viewport_rid(), true); - _editor_select(EDITOR_3D); + feature_profile_manager->notify_changed(); + + if (!main_editor_buttons[EDITOR_3D]->is_visible()) { //may be hidden due to feature profile + _editor_select(EDITOR_2D); + } else { + _editor_select(EDITOR_3D); + } + _update_debug_options(); /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */ @@ -558,11 +566,14 @@ void EditorNode::_editor_select_next() { int editor = _get_current_main_editor(); - if (editor == editor_table.size() - 1) { - editor = 0; - } else { - editor++; - } + do { + if (editor == editor_table.size() - 1) { + editor = 0; + } else { + editor++; + } + } while (main_editor_buttons[editor]->is_visible()); + _editor_select(editor); } @@ -570,11 +581,14 @@ void EditorNode::_editor_select_prev() { int editor = _get_current_main_editor(); - if (editor == 0) { - editor = editor_table.size() - 1; - } else { - editor--; - } + do { + if (editor == 0) { + editor = editor_table.size() - 1; + } else { + editor--; + } + } while (main_editor_buttons[editor]->is_visible()); + _editor_select(editor); } @@ -911,7 +925,8 @@ bool EditorNode::_find_and_save_edited_subresources(Object *obj, Map<RES, bool> ret_changed = true; } } break; - default: {} + default: { + } } } @@ -1292,6 +1307,7 @@ void EditorNode::_dialog_action(String p_file) { _save_default_environment(); _save_scene_with_preview(p_file, scene_idx); _add_to_recent_scenes(p_file); + save_layout(); if (scene_idx != -1) _discard_changes(); @@ -1440,17 +1456,48 @@ void EditorNode::_dialog_action(String p_file) { bool EditorNode::item_has_editor(Object *p_object) { + if (_is_class_editor_disabled_by_feature_profile(p_object->get_class())) { + return false; + } + return editor_data.get_subeditors(p_object).size() > 0; } void EditorNode::edit_item_resource(RES p_resource) { edit_item(p_resource.ptr()); } + +bool EditorNode::_is_class_editor_disabled_by_feature_profile(const StringName &p_class) { + + Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); + if (profile.is_null()) { + return false; + } + + StringName class_name = p_class; + + while (class_name != StringName()) { + + if (profile->is_class_disabled(class_name)) { + return true; + } + if (profile->is_class_editor_disabled(class_name)) { + return true; + } + class_name = ClassDB::get_parent_class(class_name); + } + + return false; +} + void EditorNode::edit_item(Object *p_object) { Vector<EditorPlugin *> sub_plugins; if (p_object) { + if (_is_class_editor_disabled_by_feature_profile(p_object->get_class())) { + return; + } sub_plugins = editor_data.get_subeditors(p_object); } @@ -1640,6 +1687,12 @@ void EditorNode::_edit_current() { EditorPlugin *main_plugin = editor_data.get_editor(current_obj); + for (int i = 0; i < editor_table.size(); i++) { + if (editor_table[i] == main_plugin && !main_editor_buttons[i]->is_visible()) { + main_plugin = NULL; //if button is not visible, then no plugin active + } + } + if (main_plugin) { // special case if use of external editor is true @@ -1677,7 +1730,11 @@ void EditorNode::_edit_current() { } } - Vector<EditorPlugin *> sub_plugins = editor_data.get_subeditors(current_obj); + Vector<EditorPlugin *> sub_plugins; + + if (!_is_class_editor_disabled_by_feature_profile(current_obj->get_class())) { + sub_plugins = editor_data.get_subeditors(current_obj); + } if (!sub_plugins.empty()) { _display_top_editors(false); @@ -1685,7 +1742,6 @@ void EditorNode::_edit_current() { _set_top_editors(sub_plugins); _set_editing_top_editors(current_obj); _display_top_editors(true); - } else if (!editor_plugins_over->get_plugins_list().empty()) { hide_top_editors(); @@ -1900,7 +1956,8 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { break; } - } // fallthrough + FALLTHROUGH; + } case SCENE_TAB_CLOSE: case FILE_SAVE_SCENE: { @@ -1919,8 +1976,8 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { break; } - // fallthrough to save_as - }; + FALLTHROUGH; + } case FILE_SAVE_AS_SCENE: { int scene_idx = (p_option == FILE_SAVE_SCENE || p_option == FILE_SAVE_AS_SCENE) ? -1 : tab_closing; @@ -2358,6 +2415,11 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { export_template_manager->popup_manager(); } break; + case SETTINGS_MANAGE_FEATURE_PROFILES: { + + feature_profile_manager->popup_centered_ratio(); + + } break; case SETTINGS_TOGGLE_FULLSCREEN: { OS::get_singleton()->set_window_fullscreen(!OS::get_singleton()->is_window_fullscreen()); @@ -2540,10 +2602,13 @@ void EditorNode::_editor_select(int p_which) { if (selecting || changing_scene) return; - selecting = true; - ERR_FAIL_INDEX(p_which, editor_table.size()); + if (!main_editor_buttons[p_which]->is_visible()) //button hidden, no editor + return; + + selecting = true; + for (int i = 0; i < main_editor_buttons.size(); i++) { main_editor_buttons[i]->set_pressed(i == p_which); } @@ -3283,6 +3348,7 @@ void EditorNode::register_editor_types() { ClassDB::register_class<EditorProperty>(); ClassDB::register_class<AnimationTrackEditPlugin>(); ClassDB::register_class<ScriptCreateDialog>(); + ClassDB::register_class<EditorFeatureProfile>(); // FIXME: Is this stuff obsolete, or should it be ported to new APIs? ClassDB::register_class<EditorScenePostImport>(); @@ -3820,7 +3886,13 @@ void EditorNode::_update_dock_slots_visibility() { } else { for (int i = 0; i < DOCK_SLOT_MAX; i++) { - if (dock_slot[i]->get_tab_count()) + int tabs_visible = 0; + for (int j = 0; j < dock_slot[i]->get_tab_count(); j++) { + if (!dock_slot[i]->get_tab_hidden(j)) { + tabs_visible++; + } + } + if (tabs_visible) dock_slot[i]->show(); else dock_slot[i]->hide(); @@ -3999,6 +4071,7 @@ void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout, const S for (int i = 0; i < scenes.size(); i++) { load_scene(scenes[i]); } + save_layout(); restoring_scenes = false; } @@ -4805,6 +4878,39 @@ void EditorNode::_resource_loaded(RES p_resource, const String &p_path) { singleton->editor_folding.load_resource_folding(p_resource, p_path); } +void EditorNode::_feature_profile_changed() { + + Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile(); + TabContainer *import_tabs = cast_to<TabContainer>(import_dock->get_parent()); + TabContainer *node_tabs = cast_to<TabContainer>(node_dock->get_parent()); + TabContainer *fs_tabs = cast_to<TabContainer>(filesystem_dock->get_parent()); + if (profile.is_valid()) { + + import_tabs->set_tab_hidden(import_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_IMPORT_DOCK)); + node_tabs->set_tab_hidden(node_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_NODE_DOCK)); + fs_tabs->set_tab_hidden(filesystem_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_FILESYSTEM_DOCK)); + + main_editor_buttons[EDITOR_3D]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)); + main_editor_buttons[EDITOR_SCRIPT]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT)); + main_editor_buttons[EDITOR_ASSETLIB]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB)); + if (profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D) || profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB) || profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB)) { + _editor_select(EDITOR_2D); + } + } else { + + import_tabs->set_tab_hidden(import_dock->get_index(), false); + node_tabs->set_tab_hidden(node_dock->get_index(), false); + fs_tabs->set_tab_hidden(filesystem_dock->get_index(), false); + import_dock->set_visible(true); + node_dock->set_visible(true); + filesystem_dock->set_visible(true); + main_editor_buttons[EDITOR_3D]->set_visible(true); + main_editor_buttons[EDITOR_ASSETLIB]->set_visible(true); + } + + _update_dock_slots_visibility(); +} + void EditorNode::_bind_methods() { ClassDB::bind_method("_menu_option", &EditorNode::_menu_option); @@ -4883,6 +4989,7 @@ void EditorNode::_bind_methods() { ClassDB::bind_method(D_METHOD("_video_driver_selected"), &EditorNode::_video_driver_selected); ClassDB::bind_method(D_METHOD("_resources_changed"), &EditorNode::_resources_changed); + ClassDB::bind_method(D_METHOD("_feature_profile_changed"), &EditorNode::_feature_profile_changed); ADD_SIGNAL(MethodInfo("play_pressed")); ADD_SIGNAL(MethodInfo("pause_pressed")); @@ -5015,6 +5122,10 @@ EditorNode::EditorNode() { import_image.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_image); + Ref<ResourceImporterTextureAtlas> import_texture_atlas; + import_texture_atlas.instance(); + ResourceFormatImporter::get_singleton()->add_importer(import_texture_atlas); + Ref<ResourceImporterCSVTranslation> import_csv_translation; import_csv_translation.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_csv_translation); @@ -5396,8 +5507,11 @@ EditorNode::EditorNode() { export_template_manager = memnew(ExportTemplateManager); gui_base->add_child(export_template_manager); + feature_profile_manager = memnew(EditorFeatureProfileManager); + gui_base->add_child(feature_profile_manager); about = memnew(EditorAbout); gui_base->add_child(about); + feature_profile_manager->connect("current_feature_profile_changed", this, "_feature_profile_changed"); warning = memnew(AcceptDialog); gui_base->add_child(warning); @@ -5437,7 +5551,7 @@ EditorNode::EditorNode() { p->add_shortcut(ED_SHORTCUT("editor/undo", TTR("Undo"), KEY_MASK_CMD + KEY_Z), EDIT_UNDO, true); p->add_shortcut(ED_SHORTCUT("editor/redo", TTR("Redo"), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_Z), EDIT_REDO, true); p->add_separator(); - p->add_item(TTR("Revert Scene"), EDIT_REVERT); + p->add_shortcut(ED_SHORTCUT("editor/revert_scene", TTR("Revert Scene")), EDIT_REVERT); recent_scenes = memnew(PopupMenu); recent_scenes->set_name("RecentScenes"); @@ -5457,10 +5571,10 @@ EditorNode::EditorNode() { p = project_menu->get_popup(); p->set_hide_on_window_lose_focus(true); - p->add_item(TTR("Project Settings"), RUN_SETTINGS); + p->add_shortcut(ED_SHORTCUT("editor/project_settings", TTR("Project Settings")), RUN_SETTINGS); p->add_separator(); p->connect("id_pressed", this, "_menu_option"); - p->add_item(TTR("Export"), FILE_EXPORT_PROJECT); + p->add_shortcut(ED_SHORTCUT("editor/export", TTR("Export")), FILE_EXPORT_PROJECT); plugin_config_dialog = memnew(PluginConfigDialog); plugin_config_dialog->connect("plugin_ready", this, "_on_plugin_ready"); @@ -5471,10 +5585,10 @@ EditorNode::EditorNode() { tool_menu->connect("index_pressed", this, "_tool_menu_option"); p->add_child(tool_menu); p->add_submenu_item(TTR("Tools"), "Tools"); - tool_menu->add_item(TTR("Orphan Resource Explorer"), TOOLS_ORPHAN_RESOURCES); + tool_menu->add_shortcut(ED_SHORTCUT("editor/orphan_resource_explorer", TTR("Orphan Resource Explorer")), TOOLS_ORPHAN_RESOURCES); p->add_separator(); - p->add_item(TTR("Open Project Data Folder"), RUN_PROJECT_DATA_FOLDER); + p->add_shortcut(ED_SHORTCUT("editor/open_project_data_folder", TTR("Open Project Data Folder")), RUN_PROJECT_DATA_FOLDER); p->add_separator(); #ifdef OSX_ENABLED @@ -5498,21 +5612,21 @@ EditorNode::EditorNode() { p = debug_menu->get_popup(); p->set_hide_on_window_lose_focus(true); p->set_hide_on_item_selection(false); - p->add_check_item(TTR("Deploy with Remote Debug"), RUN_DEPLOY_REMOTE_DEBUG); + p->add_check_shortcut(ED_SHORTCUT("editor/deploy_with_remote_debug", TTR("Deploy with Remote Debug")), RUN_DEPLOY_REMOTE_DEBUG); p->set_item_tooltip(p->get_item_count() - 1, TTR("When exporting or deploying, the resulting executable will attempt to connect to the IP of this computer in order to be debugged.")); - p->add_check_item(TTR("Small Deploy with Network FS"), RUN_FILE_SERVER); + p->add_check_shortcut(ED_SHORTCUT("editor/small_deploy_with_network_fs", TTR("Small Deploy with Network FS")), RUN_FILE_SERVER); p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is enabled, export or deploy will produce a minimal executable.\nThe filesystem will be provided from the project by the editor over the network.\nOn Android, deploy will use the USB cable for faster performance. This option speeds up testing for games with a large footprint.")); p->add_separator(); - p->add_check_item(TTR("Visible Collision Shapes"), RUN_DEBUG_COLLISONS); + p->add_check_shortcut(ED_SHORTCUT("editor/visible_collision_shapes", TTR("Visible Collision Shapes")), RUN_DEBUG_COLLISONS); p->set_item_tooltip(p->get_item_count() - 1, TTR("Collision shapes and raycast nodes (for 2D and 3D) will be visible on the running game if this option is turned on.")); - p->add_check_item(TTR("Visible Navigation"), RUN_DEBUG_NAVIGATION); + p->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION); p->set_item_tooltip(p->get_item_count() - 1, TTR("Navigation meshes and polygons will be visible on the running game if this option is turned on.")); p->add_separator(); //those are now on by default, since they are harmless - p->add_check_item(TTR("Sync Scene Changes"), RUN_LIVE_DEBUG); + p->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Sync Scene Changes")), RUN_LIVE_DEBUG); p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any changes made to the scene in the editor will be replicated in the running game.\nWhen used remotely on a device, this is more efficient with network filesystem.")); p->set_item_checked(p->get_item_count() - 1, true); - p->add_check_item(TTR("Sync Script Changes"), RUN_RELOAD_SCRIPTS); + p->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Sync Script Changes")), RUN_RELOAD_SCRIPTS); p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any script that is saved will be reloaded on the running game.\nWhen used remotely on a device, this is more efficient with network filesystem.")); p->set_item_checked(p->get_item_count() - 1, true); p->connect("id_pressed", this, "_menu_option"); @@ -5528,7 +5642,7 @@ EditorNode::EditorNode() { p = settings_menu->get_popup(); p->set_hide_on_window_lose_focus(true); - p->add_item(TTR("Editor Settings"), SETTINGS_PREFERENCES); + p->add_shortcut(ED_SHORTCUT("editor/editor_settings", TTR("Editor Settings")), SETTINGS_PREFERENCES); p->add_separator(); editor_layouts = memnew(PopupMenu); @@ -5553,6 +5667,10 @@ EditorNode::EditorNode() { } p->add_separator(); + p->add_item(TTR("Manage Editor Features"), SETTINGS_MANAGE_FEATURE_PROFILES); + + p->add_separator(); + p->add_item(TTR("Manage Export Templates"), SETTINGS_MANAGE_EXPORT_TEMPLATES); // Help Menu @@ -5566,14 +5684,14 @@ EditorNode::EditorNode() { p = help_menu->get_popup(); p->set_hide_on_window_lose_focus(true); p->connect("id_pressed", this, "_menu_option"); - p->add_icon_shortcut(gui_base->get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("editor/editor_help", TTR("Search"), KEY_F4), HELP_SEARCH); + p->add_icon_shortcut(gui_base->get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("editor/editor_help", TTR("Search"), KEY_MASK_SHIFT | KEY_F1), HELP_SEARCH); p->add_separator(); - p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Online Docs"), HELP_DOCS); - p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Q&A"), HELP_QA); - p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Issue Tracker"), HELP_ISSUES); - p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Community"), HELP_COMMUNITY); + p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/online_docs", TTR("Online Docs")), HELP_DOCS); + p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/q&a", TTR("Q&A")), HELP_QA); + p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/issue_tracker", TTR("Issue Tracker")), HELP_ISSUES); + p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/community", TTR("Community")), HELP_COMMUNITY); p->add_separator(); - p->add_icon_item(gui_base->get_icon("Godot", "EditorIcons"), TTR("About"), HELP_ABOUT); + p->add_icon_shortcut(gui_base->get_icon("Godot", "EditorIcons"), ED_SHORTCUT("editor/about", TTR("About")), HELP_ABOUT); HBoxContainer *play_hb = memnew(HBoxContainer); menu_hb->add_child(play_hb); diff --git a/editor/editor_node.h b/editor/editor_node.h index 267c70c773..5ed0bd4ac3 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -38,6 +38,7 @@ #include "editor/editor_about.h" #include "editor/editor_data.h" #include "editor/editor_export.h" +#include "editor/editor_feature_profile.h" #include "editor/editor_folding.h" #include "editor/editor_inspector.h" #include "editor/editor_log.h" @@ -84,7 +85,6 @@ #include "scene/gui/tool_button.h" #include "scene/gui/tree.h" #include "scene/gui/viewport_container.h" - /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -175,6 +175,7 @@ private: SETTINGS_EDITOR_DATA_FOLDER, SETTINGS_EDITOR_CONFIG_FOLDER, SETTINGS_MANAGE_EXPORT_TEMPLATES, + SETTINGS_MANAGE_FEATURE_PROFILES, SETTINGS_PICK_MAIN_SCENE, SETTINGS_TOGGLE_FULLSCREEN, SETTINGS_HELP, @@ -294,6 +295,7 @@ private: ProjectSettingsEditor *project_settings; EditorFileDialog *file; ExportTemplateManager *export_template_manager; + EditorFeatureProfileManager *feature_profile_manager; EditorFileDialog *file_templates; EditorFileDialog *file_export; EditorFileDialog *file_export_lib; @@ -610,6 +612,9 @@ private: void _resources_changed(const PoolVector<String> &p_resources); + void _feature_profile_changed(); + bool _is_class_editor_disabled_by_feature_profile(const StringName &p_class); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index c2493729a3..8af4ee8017 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -240,6 +240,10 @@ bool EditorInterface::is_plugin_enabled(const String &p_plugin) const { return EditorNode::get_singleton()->is_addon_plugin_enabled(p_plugin); } +EditorInspector *EditorInterface::get_inspector() const { + return EditorNode::get_singleton()->get_inspector(); +} + Error EditorInterface::save_scene() { if (!get_edited_scene_root()) return ERR_CANT_CREATE; @@ -279,6 +283,8 @@ void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("set_plugin_enabled", "plugin", "enabled"), &EditorInterface::set_plugin_enabled); ClassDB::bind_method(D_METHOD("is_plugin_enabled", "plugin"), &EditorInterface::is_plugin_enabled); + ClassDB::bind_method(D_METHOD("get_inspector"), &EditorInterface::get_inspector); + ClassDB::bind_method(D_METHOD("save_scene"), &EditorInterface::save_scene); ClassDB::bind_method(D_METHOD("save_scene_as", "path", "with_preview"), &EditorInterface::save_scene_as, DEFVAL(true)); } diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 3e41bb5612..2fcc487377 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -95,6 +95,8 @@ public: void set_plugin_enabled(const String &p_plugin, bool p_enabled); bool is_plugin_enabled(const String &p_plugin) const; + EditorInspector *get_inspector() const; + Error save_scene(); void save_scene_as(const String &p_scene, bool p_with_preview = true); diff --git a/editor/editor_profiler.cpp b/editor/editor_profiler.cpp index dee589ad3f..f73cd0beb5 100644 --- a/editor/editor_profiler.cpp +++ b/editor/editor_profiler.cpp @@ -625,6 +625,63 @@ bool EditorProfiler::is_profiling() { return activate->is_pressed(); } +Vector<Vector<String> > EditorProfiler::get_data_as_csv() const { + Vector<Vector<String> > res; + + if (frame_metrics.empty()) { + return res; + } + + // signatures + Vector<String> signatures; + const Vector<EditorProfiler::Metric::Category> &categories = frame_metrics[0].categories; + + for (int j = 0; j < categories.size(); j++) { + + const EditorProfiler::Metric::Category &c = categories[j]; + signatures.push_back(c.signature); + + for (int k = 0; k < c.items.size(); k++) { + signatures.push_back(c.items[k].signature); + } + } + res.push_back(signatures); + + // values + Vector<String> values; + values.resize(signatures.size()); + + int index = last_metric; + + for (int i = 0; i < frame_metrics.size(); i++) { + + ++index; + + if (index >= frame_metrics.size()) { + index = 0; + } + + if (!frame_metrics[index].valid) { + continue; + } + int it = 0; + const Vector<EditorProfiler::Metric::Category> &frame_cat = frame_metrics[index].categories; + + for (int j = 0; j < frame_cat.size(); j++) { + + const EditorProfiler::Metric::Category &c = frame_cat[j]; + values.write[it++] = String::num_real(c.total_time); + + for (int k = 0; k < c.items.size(); k++) { + values.write[it++] = String::num_real(c.items[k].total); + } + } + res.push_back(values); + } + + return res; +} + EditorProfiler::EditorProfiler() { HBoxContainer *hb = memnew(HBoxContainer); diff --git a/editor/editor_profiler.h b/editor/editor_profiler.h index 8fa09f8494..e62213887d 100644 --- a/editor/editor_profiler.h +++ b/editor/editor_profiler.h @@ -169,6 +169,8 @@ public: void clear(); + Vector<Vector<String> > get_data_as_csv() const; + EditorProfiler(); }; diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 2d0d212af9..9d936d9b99 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2289,6 +2289,16 @@ void EditorPropertyResource::_update_menu_items() { E = E->next(); } + List<StringName> global_classes; + ScriptServer::get_global_class_list(&global_classes); + E = global_classes.front(); + while (E) { + if (EditorNode::get_editor_data().script_class_is_parent(E->get(), base_type)) { + valid_inheritors.insert(E->get()); + } + E = E->next(); + } + for (Set<String>::Element *F = valid_inheritors.front(); F; F = F->next()) { String t = F->get(); @@ -2305,7 +2315,7 @@ void EditorPropertyResource::_update_menu_items() { } } - if (!is_custom_resource && !ClassDB::can_instance(t)) + if (!is_custom_resource && !(ScriptServer::is_global_class(t) || ClassDB::can_instance(t))) continue; inheritors_array.push_back(t); @@ -2542,6 +2552,7 @@ void EditorPropertyResource::update_property() { sub_inspector->edit(res.ptr()); } + sub_inspector->refresh(); } else { if (sub_inspector) { set_bottom_editor(NULL); @@ -2872,7 +2883,8 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ case PROPERTY_HINT_LAYERS_3D_PHYSICS: lt = EditorPropertyLayers::LAYER_PHYSICS_3D; break; - default: {} //compiler could be smarter here and realize this can't happen + default: { + } //compiler could be smarter here and realize this can't happen } EditorPropertyLayers *editor = memnew(EditorPropertyLayers); editor->setup(lt); @@ -2981,13 +2993,17 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ EditorPropertyClassName *editor = memnew(EditorPropertyClassName); editor->setup("Object", p_hint_text); add_property_editor(p_path, editor); - } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) { + } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) { Vector<String> extensions = p_hint_text.split(","); bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE; bool folder = p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_GLOBAL_DIR; + bool save = p_hint == PROPERTY_HINT_SAVE_FILE; EditorPropertyPath *editor = memnew(EditorPropertyPath); editor->setup(extensions, folder, global); + if (save) { + editor->set_save_mode(); + } add_property_editor(p_path, editor); } else if (p_hint == PROPERTY_HINT_METHOD_OF_VARIANT_TYPE || p_hint == PROPERTY_HINT_METHOD_OF_BASE_TYPE || @@ -3009,7 +3025,8 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ case PROPERTY_HINT_PROPERTY_OF_BASE_TYPE: type = EditorPropertyMember::MEMBER_PROPERTY_OF_BASE_TYPE; break; case PROPERTY_HINT_PROPERTY_OF_INSTANCE: type = EditorPropertyMember::MEMBER_PROPERTY_OF_INSTANCE; break; case PROPERTY_HINT_PROPERTY_OF_SCRIPT: type = EditorPropertyMember::MEMBER_PROPERTY_OF_SCRIPT; break; - default: {} + default: { + } } editor->setup(type, p_hint_text); add_property_editor(p_path, editor); @@ -3273,7 +3290,8 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ editor->setup(Variant::POOL_COLOR_ARRAY); add_property_editor(p_path, editor); } break; - default: {} + default: { + } } return false; //can be overridden, although it will most likely be last anyway diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 0f6c6349ed..e3dc517a39 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -251,7 +251,8 @@ void EditorPropertyArray::update_property() { case Variant::POOL_COLOR_ARRAY: { arrtype = "PoolColorArray"; } break; - default: {} + default: { + } } if (array.get_type() == Variant::NIL) { @@ -862,7 +863,8 @@ void EditorPropertyDictionary::update_property() { editor->setup(Variant::POOL_COLOR_ARRAY); prop = editor; } break; - default: {} + default: { + } } if (i == amount) { diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 5c257b86db..f54c51940f 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -675,14 +675,14 @@ bool EditorSettings::_save_text_editor_theme(String p_file) { static Dictionary _get_builtin_script_templates() { Dictionary templates; - //No Comments + // No Comments templates["no_comments.gd"] = "extends %BASE%\n" "\n" - "func _ready():\n" + "func _ready()%VOID_RETURN%:\n" "%TS%pass\n"; - //Empty + // Empty templates["empty.gd"] = "extends %BASE%" "\n" @@ -832,6 +832,13 @@ void EditorSettings::create() { } else { dir->change_dir(".."); } + + if (dir->change_dir("feature_profiles") != OK) { + dir->make_dir("feature_profiles"); + } else { + dir->change_dir(".."); + } + _create_script_templates(dir->get_current_dir().plus_file("script_templates")); if (dir->change_dir("projects") != OK) { @@ -1157,6 +1164,11 @@ String EditorSettings::get_cache_dir() const { return cache_dir; } +String EditorSettings::get_feature_profiles_dir() const { + + return get_settings_dir().plus_file("feature_profiles"); +} + // Metadata void EditorSettings::set_project_metadata(const String &p_section, const String &p_key, Variant p_data) { diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 1b44c15c54..43a8cbf739 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -169,6 +169,7 @@ public: String get_text_editor_themes_dir() const; String get_script_templates_dir() const; String get_cache_dir() const; + String get_feature_profiles_dir() const; void set_project_metadata(const String &p_section, const String &p_key, Variant p_data); Variant get_project_metadata(const String &p_section, const String &p_key, Variant p_default) const; diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index c930824b98..9641e10114 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -204,7 +204,7 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = } // generate thumb files with the given thumb size - bool force_filter = !(p_thumb_size == 64 && p_thumb_size == 32); // we don't need filter with original resolution + bool force_filter = p_thumb_size != 64 && p_thumb_size != 32; // we don't need filter with original resolution if (p_thumb_size >= 64) { float scale = (float)p_thumb_size / 64.0 * EDSCALE; for (int i = 0; i < editor_bg_thumbs_count; i++) { @@ -403,7 +403,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // this is the most commonly used stylebox, variations should be made as duplicate of this Ref<StyleBoxFlat> style_default = make_flat_stylebox(base_color, default_margin_size, default_margin_size, default_margin_size, default_margin_size); style_default->set_border_width_all(border_width); - style_default->set_border_color_all(base_color); + style_default->set_border_color(base_color); style_default->set_draw_center(true); // Button and widgets @@ -415,20 +415,20 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_widget->set_default_margin(MARGIN_RIGHT, (extra_spacing + 6) * EDSCALE); style_widget->set_default_margin(MARGIN_BOTTOM, (extra_spacing + default_margin_size) * EDSCALE); style_widget->set_bg_color(dark_color_1); - style_widget->set_border_color_all(dark_color_2); + style_widget->set_border_color(dark_color_2); Ref<StyleBoxFlat> style_widget_disabled = style_widget->duplicate(); - style_widget_disabled->set_border_color_all(color_disabled); + style_widget_disabled->set_border_color(color_disabled); style_widget_disabled->set_bg_color(color_disabled_bg); Ref<StyleBoxFlat> style_widget_focus = style_widget->duplicate(); - style_widget_focus->set_border_color_all(accent_color); + style_widget_focus->set_border_color(accent_color); Ref<StyleBoxFlat> style_widget_pressed = style_widget->duplicate(); - style_widget_pressed->set_border_color_all(accent_color); + style_widget_pressed->set_border_color(accent_color); Ref<StyleBoxFlat> style_widget_hover = style_widget->duplicate(); - style_widget_hover->set_border_color_all(contrast_color_1); + style_widget_hover->set_border_color(contrast_color_1); // style for windows, popups, etc.. Ref<StyleBoxFlat> style_popup = style_default->duplicate(); @@ -437,7 +437,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_popup->set_default_margin(MARGIN_TOP, popup_margin_size); style_popup->set_default_margin(MARGIN_RIGHT, popup_margin_size); style_popup->set_default_margin(MARGIN_BOTTOM, popup_margin_size); - style_popup->set_border_color_all(contrast_color_1); + style_popup->set_border_color(contrast_color_1); style_popup->set_border_width_all(MAX(EDSCALE, border_width)); const Color shadow_color = Color(0, 0, 0, dark_theme ? 0.3 : 0.1); style_popup->set_shadow_color(shadow_color); @@ -470,7 +470,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_tab_selected->set_border_width_all(border_width); style_tab_selected->set_border_width(MARGIN_BOTTOM, 0); - style_tab_selected->set_border_color_all(dark_color_3); + style_tab_selected->set_border_color(dark_color_3); style_tab_selected->set_expand_margin_size(MARGIN_BOTTOM, border_width); style_tab_selected->set_default_margin(MARGIN_LEFT, tab_default_margin_side); style_tab_selected->set_default_margin(MARGIN_RIGHT, tab_default_margin_side); @@ -480,11 +480,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Ref<StyleBoxFlat> style_tab_unselected = style_tab_selected->duplicate(); style_tab_unselected->set_bg_color(dark_color_1); - style_tab_unselected->set_border_color_all(dark_color_2); + style_tab_unselected->set_border_color(dark_color_2); Ref<StyleBoxFlat> style_tab_disabled = style_tab_selected->duplicate(); style_tab_disabled->set_bg_color(color_disabled_bg); - style_tab_disabled->set_border_color_all(color_disabled); + style_tab_disabled->set_border_color(color_disabled); // Editor background theme->set_stylebox("Background", "EditorStyles", make_flat_stylebox(background_color, default_margin_size, default_margin_size, default_margin_size, default_margin_size)); @@ -492,7 +492,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Focus Ref<StyleBoxFlat> style_focus = style_default->duplicate(); style_focus->set_draw_center(false); - style_focus->set_border_color_all(contrast_color_2); + style_focus->set_border_color(contrast_color_2); theme->set_stylebox("Focus", "EditorStyles", style_focus); // Menu @@ -514,7 +514,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_menu_hover_border->set_draw_center(false); style_menu_hover_border->set_border_width_all(0); style_menu_hover_border->set_border_width(MARGIN_BOTTOM, border_width); - style_menu_hover_border->set_border_color_all(accent_color); + style_menu_hover_border->set_border_color(accent_color); Ref<StyleBoxFlat> style_menu_hover_bg = style_widget->duplicate(); style_menu_hover_bg->set_border_width_all(0); @@ -644,11 +644,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Ref<StyleBoxFlat> sub_inspector_bg = make_flat_stylebox(dark_color_1.linear_interpolate(accent_color, 0.08), 2, 0, 2, 2); sub_inspector_bg->set_border_width(MARGIN_LEFT, 2); - sub_inspector_bg->set_border_color(MARGIN_LEFT, accent_color * Color(1, 1, 1, 0.3)); sub_inspector_bg->set_border_width(MARGIN_RIGHT, 2); - sub_inspector_bg->set_border_color(MARGIN_RIGHT, accent_color * Color(1, 1, 1, 0.3)); sub_inspector_bg->set_border_width(MARGIN_BOTTOM, 2); - sub_inspector_bg->set_border_color(MARGIN_BOTTOM, accent_color * Color(1, 1, 1, 0.3)); + sub_inspector_bg->set_border_color(accent_color * Color(1, 1, 1, 0.3)); sub_inspector_bg->set_draw_center(true); theme->set_stylebox("sub_inspector_bg", "Editor", sub_inspector_bg); @@ -657,7 +655,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Tree & ItemList background Ref<StyleBoxFlat> style_tree_bg = style_default->duplicate(); style_tree_bg->set_bg_color(dark_color_1); - style_tree_bg->set_border_color_all(dark_color_3); + style_tree_bg->set_border_color(dark_color_3); theme->set_stylebox("bg", "Tree", style_tree_bg); const Color guide_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.05); @@ -689,7 +687,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_constant("button_margin", "Tree", default_margin_size * EDSCALE); theme->set_constant("draw_relationship_lines", "Tree", relationship_line_opacity >= 0.01); theme->set_constant("draw_guides", "Tree", relationship_line_opacity < 0.01); - theme->set_constant("scroll_border", "Tree", default_margin_size * EDSCALE); + theme->set_constant("scroll_border", "Tree", 40 * EDSCALE); theme->set_constant("scroll_speed", "Tree", 12); Ref<StyleBoxFlat> style_tree_btn = style_default->duplicate(); @@ -708,7 +706,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Ref<StyleBoxFlat> style_tree_cursor = style_default->duplicate(); style_tree_cursor->set_draw_center(false); style_tree_cursor->set_border_width_all(border_width); - style_tree_cursor->set_border_color_all(contrast_color_1); + style_tree_cursor->set_border_color(contrast_color_1); Ref<StyleBoxFlat> style_tree_title = style_default->duplicate(); style_tree_title->set_bg_color(dark_color_3); @@ -731,12 +729,12 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Ref<StyleBoxFlat> style_itemlist_bg = style_default->duplicate(); style_itemlist_bg->set_bg_color(dark_color_1); style_itemlist_bg->set_border_width_all(border_width); - style_itemlist_bg->set_border_color_all(dark_color_3); + style_itemlist_bg->set_border_color(dark_color_3); Ref<StyleBoxFlat> style_itemlist_cursor = style_default->duplicate(); style_itemlist_cursor->set_draw_center(false); style_itemlist_cursor->set_border_width_all(border_width); - style_itemlist_cursor->set_border_color_all(highlight_color); + style_itemlist_cursor->set_border_color(highlight_color); theme->set_stylebox("cursor", "ItemList", style_itemlist_cursor); theme->set_stylebox("cursor_unfocused", "ItemList", style_itemlist_cursor); theme->set_stylebox("selected_focus", "ItemList", style_tree_focus); @@ -781,7 +779,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Content of each tab Ref<StyleBoxFlat> style_content_panel = style_default->duplicate(); - style_content_panel->set_border_color_all(dark_color_3); + style_content_panel->set_border_color(dark_color_3); style_content_panel->set_border_width_all(border_width); // compensate the border style_content_panel->set_default_margin(MARGIN_TOP, margin_size_extra * EDSCALE); @@ -834,7 +832,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_constant("side_margin", "TabContainer", 0); theme->set_icon("tab", "TextEdit", theme->get_icon("GuiTab", "EditorIcons")); theme->set_color("font_color", "TextEdit", font_color); - theme->set_color("caret_color", "TextEdit", highlight_color); + theme->set_color("caret_color", "TextEdit", font_color); theme->set_color("selection_color", "TextEdit", font_color_selection); // H/VSplitContainer @@ -860,7 +858,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // WindowDialog Ref<StyleBoxFlat> style_window = style_popup->duplicate(); - style_window->set_border_color_all(tab_color); + style_window->set_border_color(tab_color); style_window->set_border_width(MARGIN_TOP, 24 * EDSCALE); style_window->set_expand_margin_size(MARGIN_TOP, 24 * EDSCALE); theme->set_stylebox("panel", "WindowDialog", style_window); @@ -875,7 +873,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // complex window, for now only Editor settings and Project settings Ref<StyleBoxFlat> style_complex_window = style_window->duplicate(); style_complex_window->set_bg_color(dark_color_2); - style_complex_window->set_border_color_all(highlight_tabs ? tab_color : dark_color_2); + style_complex_window->set_border_color(highlight_tabs ? tab_color : dark_color_2); theme->set_stylebox("panel", "EditorSettingsDialog", style_complex_window); theme->set_stylebox("panel", "ProjectSettingsEditor", style_complex_window); theme->set_stylebox("panel", "EditorAbout", style_complex_window); @@ -954,7 +952,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_tooltip->set_default_margin(MARGIN_BOTTOM, v); style_tooltip->set_bg_color(Color(mono_color.r, mono_color.g, mono_color.b, 0.9)); style_tooltip->set_border_width_all(border_width); - style_tooltip->set_border_color_all(mono_color); + style_tooltip->set_border_color(mono_color); theme->set_color("font_color", "TooltipLabel", font_color.inverted()); theme->set_color("font_color_shadow", "TooltipLabel", mono_color.inverted() * Color(1, 1, 1, 0.1)); theme->set_stylebox("panel", "TooltipPanel", style_tooltip); @@ -989,32 +987,32 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const int gn_margin_side = 28; Ref<StyleBoxFlat> graphsb = make_flat_stylebox(Color(mv, mv, mv, 0.7), gn_margin_side, 24, gn_margin_side, 5); graphsb->set_border_width_all(border_width); - graphsb->set_border_color_all(Color(mv2, mv2, mv2, 0.9)); + graphsb->set_border_color(Color(mv2, mv2, mv2, 0.9)); Ref<StyleBoxFlat> graphsbselected = make_flat_stylebox(Color(mv, mv, mv, 0.9), gn_margin_side, 24, gn_margin_side, 5); graphsbselected->set_border_width_all(border_width); - graphsbselected->set_border_color_all(Color(accent_color.r, accent_color.g, accent_color.b, 0.9)); + graphsbselected->set_border_color(Color(accent_color.r, accent_color.g, accent_color.b, 0.9)); graphsbselected->set_shadow_size(8 * EDSCALE); graphsbselected->set_shadow_color(shadow_color); Ref<StyleBoxFlat> graphsbcomment = make_flat_stylebox(Color(mv, mv, mv, 0.3), gn_margin_side, 24, gn_margin_side, 5); graphsbcomment->set_border_width_all(border_width); - graphsbcomment->set_border_color_all(Color(mv2, mv2, mv2, 0.9)); + graphsbcomment->set_border_color(Color(mv2, mv2, mv2, 0.9)); Ref<StyleBoxFlat> graphsbcommentselected = make_flat_stylebox(Color(mv, mv, mv, 0.4), gn_margin_side, 24, gn_margin_side, 5); graphsbcommentselected->set_border_width_all(border_width); - graphsbcommentselected->set_border_color_all(Color(mv2, mv2, mv2, 0.9)); + graphsbcommentselected->set_border_color(Color(mv2, mv2, mv2, 0.9)); Ref<StyleBoxFlat> graphsbbreakpoint = graphsbselected->duplicate(); graphsbbreakpoint->set_draw_center(false); - graphsbbreakpoint->set_border_color_all(warning_color); + graphsbbreakpoint->set_border_color(warning_color); graphsbbreakpoint->set_shadow_color(warning_color * Color(1.0, 1.0, 1.0, 0.1)); Ref<StyleBoxFlat> graphsbposition = graphsbselected->duplicate(); graphsbposition->set_draw_center(false); - graphsbposition->set_border_color_all(error_color); + graphsbposition->set_border_color(error_color); graphsbposition->set_shadow_color(error_color * Color(1.0, 1.0, 1.0, 0.2)); Ref<StyleBoxFlat> smgraphsb = make_flat_stylebox(Color(mv, mv, mv, 0.7), gn_margin_side, 24, gn_margin_side, 5); smgraphsb->set_border_width_all(border_width); - smgraphsb->set_border_color_all(Color(mv2, mv2, mv2, 0.9)); + smgraphsb->set_border_color(Color(mv2, mv2, mv2, 0.9)); Ref<StyleBoxFlat> smgraphsbselected = make_flat_stylebox(Color(mv, mv, mv, 0.9), gn_margin_side, 24, gn_margin_side, 5); smgraphsbselected->set_border_width_all(border_width); - smgraphsbselected->set_border_color_all(Color(accent_color.r, accent_color.g, accent_color.b, 0.9)); + smgraphsbselected->set_border_color(Color(accent_color.r, accent_color.g, accent_color.b, 0.9)); smgraphsbselected->set_shadow_size(8 * EDSCALE); smgraphsbselected->set_shadow_color(shadow_color); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index c99786bb07..aade606412 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -86,6 +86,13 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory // Create all items for the files in the subdirectory if (display_mode == DISPLAY_MODE_TREE_ONLY) { for (int i = 0; i < p_dir->get_file_count(); i++) { + + String file_type = p_dir->get_file_type(i); + + if (_is_file_type_disabled_by_feature_profile(file_type)) { + //if type is disabled, file wont be displayed. + continue; + } String file_name = p_dir->get_file(i); if (searched_string.length() > 0) { @@ -276,6 +283,7 @@ void FileSystemDock::_notification(int p_what) { if (initialized) return; initialized = true; + EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", this, "_feature_profile_changed"); EditorFileSystem::get_singleton()->connect("filesystem_changed", this, "_fs_changed"); EditorResourcePreview::get_singleton()->connect("preview_invalidated", this, "_preview_invalidated"); @@ -520,6 +528,26 @@ void FileSystemDock::_set_file_display(bool p_active) { _update_file_list(true); } +bool FileSystemDock::_is_file_type_disabled_by_feature_profile(const StringName &p_class) { + + Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); + if (profile.is_null()) { + return false; + } + + StringName class_name = p_class; + + while (class_name != StringName()) { + + if (profile->is_class_disabled(class_name)) { + return true; + } + class_name = ClassDB::get_parent_class(class_name); + } + + return false; +} + void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> *matches, int p_max_items) { if (matches->size() > p_max_items) @@ -541,6 +569,11 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> * fi.import_broken = !p_path->get_file_import_is_valid(i); fi.import_status = 0; + if (_is_file_type_disabled_by_feature_profile(fi.type)) { + //this type is disabled, will not appear here + continue; + } + matches->push_back(fi); if (matches->size() > p_max_items) return; @@ -988,6 +1021,7 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_ for (int j = 0; j < ed->get_edited_scene_count(); j++) { if (ed->get_scene_path(j) == file_changed_paths[i]) { ed->get_edited_scene_root(j)->set_filename(new_item_path); + editor->save_layout(); break; } } @@ -1223,6 +1257,10 @@ void FileSystemDock::_rename_operation_confirm() { return; } + if (EditorFileSystem::get_singleton()->is_group_file(old_path)) { + EditorFileSystem::get_singleton()->move_group_file(old_path, new_path); + } + //Present a more user friendly warning for name conflict DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); #if defined(WINDOWS_ENABLED) || defined(UWP_ENABLED) @@ -1320,6 +1358,16 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool overw } } + //check groups + for (int i = 0; i < to_move.size(); i++) { + + print_line("is group: " + to_move[i].path + ": " + itos(EditorFileSystem::get_singleton()->is_group_file(to_move[i].path))); + if (to_move[i].is_file && EditorFileSystem::get_singleton()->is_group_file(to_move[i].path)) { + print_line("move to: " + p_to_path.plus_file(to_move[i].path.get_file())); + EditorFileSystem::get_singleton()->move_group_file(to_move[i].path, p_to_path.plus_file(to_move[i].path.get_file())); + } + } + Map<String, String> file_renames; Map<String, String> folder_renames; bool is_moved = false; @@ -1429,6 +1477,10 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> p_selected) case FILE_SHOW_IN_EXPLORER: { // Show the file / folder in the OS explorer String fpath = path; + if (path == "Favorites") { + fpath = p_selected[0]; + } + if (!fpath.ends_with("/")) { fpath = fpath.get_base_dir(); } @@ -2023,10 +2075,10 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str if (p_paths.size() >= 1) { if (!all_favorites) { - p_popup->add_item(TTR("Add to favorites"), FILE_ADD_FAVORITE); + p_popup->add_item(TTR("Add to Favorites"), FILE_ADD_FAVORITE); } if (!all_not_favorites) { - p_popup->add_item(TTR("Remove from favorites"), FILE_REMOVE_FAVORITE); + p_popup->add_item(TTR("Remove from Favorites"), FILE_REMOVE_FAVORITE); } p_popup->add_separator(); } @@ -2088,6 +2140,10 @@ void FileSystemDock::_tree_rmb_select(const Vector2 &p_pos) { } } +void FileSystemDock::_tree_empty_selected() { + tree->deselect_all(); +} + void FileSystemDock::_file_list_rmb_select(int p_item, const Vector2 &p_pos) { // Right click is pressed in the file list Vector<String> paths; @@ -2255,6 +2311,11 @@ void FileSystemDock::_update_import_dock() { import_dock_needs_update = false; } +void FileSystemDock::_feature_profile_changed() { + + _update_display_mode(true); +} + void FileSystemDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_file_list_gui_input"), &FileSystemDock::_file_list_gui_input); @@ -2266,9 +2327,10 @@ void FileSystemDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_toggle_split_mode"), &FileSystemDock::_toggle_split_mode); ClassDB::bind_method(D_METHOD("_tree_rmb_option", "option"), &FileSystemDock::_tree_rmb_option); - ClassDB::bind_method(D_METHOD("_file_list_rmb_option", "option"), &FileSystemDock::_file_list_rmb_option); - ClassDB::bind_method(D_METHOD("_tree_rmb_select"), &FileSystemDock::_tree_rmb_select); + ClassDB::bind_method(D_METHOD("_tree_empty_selected"), &FileSystemDock::_tree_empty_selected); + + ClassDB::bind_method(D_METHOD("_file_list_rmb_option", "option"), &FileSystemDock::_file_list_rmb_option); ClassDB::bind_method(D_METHOD("_file_list_rmb_select"), &FileSystemDock::_file_list_rmb_select); ClassDB::bind_method(D_METHOD("_file_list_rmb_pressed"), &FileSystemDock::_file_list_rmb_pressed); @@ -2304,6 +2366,8 @@ void FileSystemDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_file_multi_selected"), &FileSystemDock::_file_multi_selected); ClassDB::bind_method(D_METHOD("_update_import_dock"), &FileSystemDock::_update_import_dock); + ClassDB::bind_method(D_METHOD("_feature_profile_changed"), &FileSystemDock::_feature_profile_changed); + ADD_SIGNAL(MethodInfo("instance", PropertyInfo(Variant::POOL_STRING_ARRAY, "files"))); ADD_SIGNAL(MethodInfo("open")); @@ -2336,13 +2400,13 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { button_hist_prev = memnew(ToolButton); button_hist_prev->set_disabled(true); button_hist_prev->set_focus_mode(FOCUS_NONE); - button_hist_prev->set_tooltip(TTR("Previous Directory")); + button_hist_prev->set_tooltip(TTR("Previous Folder/File")); toolbar_hbc->add_child(button_hist_prev); button_hist_next = memnew(ToolButton); button_hist_next->set_disabled(true); button_hist_next->set_focus_mode(FOCUS_NONE); - button_hist_next->set_tooltip(TTR("Next Directory")); + button_hist_next->set_tooltip(TTR("Next Folder/File")); toolbar_hbc->add_child(button_hist_next); current_path = memnew(LineEdit); @@ -2363,7 +2427,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { button_toggle_display_mode->set_toggle_mode(true); button_toggle_display_mode->connect("toggled", this, "_toggle_split_mode"); button_toggle_display_mode->set_focus_mode(FOCUS_NONE); - button_toggle_display_mode->set_tooltip(TTR("Toggle split mode")); + button_toggle_display_mode->set_tooltip(TTR("Toggle Split Mode")); toolbar_hbc->add_child(button_toggle_display_mode); HBoxContainer *toolbar2_hbc = memnew(HBoxContainer); @@ -2401,6 +2465,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { tree->connect("item_activated", this, "_tree_activate_file"); tree->connect("multi_selected", this, "_tree_multi_selected"); tree->connect("item_rmb_selected", this, "_tree_rmb_select"); + tree->connect("nothing_selected", this, "_tree_empty_selected"); tree->connect("gui_input", this, "_tree_gui_input"); file_list_vb = memnew(VBoxContainer); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index cd052f9700..8b27938c8a 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -237,6 +237,7 @@ private: void _tree_rmb_select(const Vector2 &p_pos); void _file_list_rmb_select(int p_item, const Vector2 &p_pos); void _file_list_rmb_pressed(const Vector2 &p_pos); + void _tree_empty_selected(); struct FileInfo { String name; @@ -268,6 +269,10 @@ private: Vector<String> _tree_get_selected(bool remove_self_inclusion = true); + bool _is_file_type_disabled_by_feature_profile(const StringName &p_class); + + void _feature_profile_changed(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/icons/icon_auto_key.svg b/editor/icons/icon_auto_key.svg new file mode 100644 index 0000000000..cbafe1ac38 --- /dev/null +++ b/editor/icons/icon_auto_key.svg @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg6" + sodipodi:docname="icon_auto_key.svg" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1854" + inkscape:window-height="1016" + id="namedview8" + showgrid="false" + inkscape:zoom="10.429825" + inkscape:cx="10.199345" + inkscape:cy="-4.0344119" + inkscape:window-x="66" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="svg6" /> + <path + style="fill:#e0e0e0;fill-opacity:1;stroke-width:0.0333107" + d="M 3.5469681,13.426786 C 2.7965829,13.263778 2.2774312,12.503915 2.4037297,11.753472 c 0.1081234,-0.642451 0.6006808,-1.135008 1.2431317,-1.243131 0.9667125,-0.162696 1.8555225,0.726112 1.6928259,1.692826 -0.103766,0.616558 -0.5592173,1.098057 -1.1588427,1.225117 -0.2719576,0.05763 -0.3626872,0.05741 -0.6338765,-0.0014 z m 8.0861339,-0.08275 c -0.746862,-0.13829 -1.23937,-0.720718 -1.23937,-1.465649 0,-0.527377 0.244831,-0.978806 0.679757,-1.253362 0.471386,-0.297574 1.114188,-0.297574 1.585574,0 0.682727,0.430986 0.892336,1.362194 0.460575,2.046149 -0.307786,0.487563 -0.940521,0.773963 -1.486536,0.672862 z M 0.60726032,9.8305658 V 7.7161233 L 1.1770842,7.7070075 1.7469079,7.6978939 3.1889882,5.1995916 4.6310686,2.7012893 h 3.1726318 3.1726316 l 1.442755,2.4983023 1.442755,2.4983023 0.651097,0.00903 0.651096,0.00903 v 2.1145264 2.1145257 h -0.566282 -0.566281 v -0.161225 c 0,-0.234927 -0.113135,-0.639704 -0.255664,-0.914727 -0.16895,-0.326004 -0.574198,-0.731251 -0.900202,-0.9002019 -0.656732,-0.3403483 -1.428549,-0.3403483 -2.085281,0 -0.326004,0.1689519 -0.731252,0.5741989 -0.9002019,0.9002029 -0.1425297,0.275023 -0.2556639,0.6798 -0.2556639,0.914727 v 0.161225 H 7.8570969 6.0797346 L 6.0617736,11.686851 C 6.006289,10.889347 5.447548,10.170679 4.6603773,9.884336 4.4466221,9.8065798 4.3737631,9.797427 3.9716406,9.7978134 3.5871254,9.7981885 3.4905638,9.809405 3.3054265,9.8752358 2.5067319,10.159236 1.9362359,10.884501 1.8813215,11.68568 l -0.017772,0.259329 H 1.2354063 0.60726287 Z M 12.399247,7.7466889 c 0,-0.037287 -0.02623,-0.1073444 -0.0583,-0.1556843 -0.03206,-0.04834 -0.561225,-0.958444 -1.17592,-2.0224529 L 10.047407,3.6339894 7.6977565,3.6254406 C 5.4917229,3.6174174 5.3450379,3.6204563 5.2979001,3.6754094 5.1898818,3.8013046 2.9723198,7.6840061 2.9723198,7.7472381 c 0,0.067139 0.00758,0.067247 4.7134636,0.067247 h 4.7134636 z" + id="path6243" + inkscape:connector-curvature="0" /> +</svg> diff --git a/editor/icons/icon_sprite_sheet.svg b/editor/icons/icon_sprite_sheet.svg new file mode 100644 index 0000000000..eeb804f8b9 --- /dev/null +++ b/editor/icons/icon_sprite_sheet.svg @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg6" + sodipodi:docname="icon_sprite_sheet.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="773" + inkscape:window-height="480" + id="namedview8" + showgrid="false" + inkscape:zoom="14.75" + inkscape:cx="8" + inkscape:cy="8" + inkscape:window-x="551" + inkscape:window-y="278" + inkscape:window-maximized="0" + inkscape:current-layer="g4" /> + <g + transform="translate(0 -1036.4)" + id="g4"> + <path + transform="translate(0 1036.4)" + d="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2h-10zm0 2h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm-8 4h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm-8 4h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2z" + fill="#a5efac" + id="path2" + style="fill:#e0e0e0;fill-opacity:1" /> + </g> +</svg> diff --git a/editor/import/atlas_import_failed.xpm b/editor/import/atlas_import_failed.xpm new file mode 100644 index 0000000000..52db6b76a6 --- /dev/null +++ b/editor/import/atlas_import_failed.xpm @@ -0,0 +1,414 @@ +/* XPM */ +static const char * atlas_import_failed_xpm[] = { +"128 128 283 2", +" c None", +". c #FFFFFF", +"+ c #FFDADA", +"@ c #FF0000", +"# c #FFD8D8", +"$ c #FFF7F7", +"% c #FF2E2E", +"& c #FFD4D4", +"* c #FFD6D6", +"= c #FFE3E3", +"- c #FFB3B3", +"; c #FFC8C8", +"> c #FF3535", +", c #FF8D8D", +"' c #FF7878", +") c #FF6E6E", +"! c #FFB5B5", +"~ c #FF0D0D", +"{ c #FFF0F0", +"] c #FFE8E8", +"^ c #FFC2C2", +"/ c #FFEDED", +"( c #FFBBBB", +"_ c #FFB9B9", +": c #FFA4A4", +"< c #FFFEFE", +"[ c #FFD9D9", +"} c #FF9393", +"| c #FF5858", +"1 c #FF3232", +"2 c #FF7575", +"3 c #FFC9C9", +"4 c #FFFCFC", +"5 c #FFBDBD", +"6 c #FF3838", +"7 c #FF9494", +"8 c #FFE2E2", +"9 c #FFD1D1", +"0 c #FFDEDE", +"a c #FFCACA", +"b c #FF6969", +"c c #FF8484", +"d c #FFEAEA", +"e c #FFE9E9", +"f c #FF3B3B", +"g c #FFC0C0", +"h c #FF6868", +"i c #FF7373", +"j c #FFF6F6", +"k c #FFADAD", +"l c #FF5D5D", +"m c #FF2626", +"n c #FF5C5C", +"o c #FFABAB", +"p c #FFCECE", +"q c #FF7070", +"r c #FF5555", +"s c #FF1C1C", +"t c #FFF4F4", +"u c #FF8282", +"v c #FF6060", +"w c #FFE7E7", +"x c #FF9D9D", +"y c #FF5656", +"z c #FF4242", +"A c #FF9B9B", +"B c #FFD0D0", +"C c #FFF8F8", +"D c #FF6A6A", +"E c #FF5151", +"F c #FFFBFB", +"G c #FF4949", +"H c #FFCDCD", +"I c #FFDDDD", +"J c #FF9E9E", +"K c #FFF9F9", +"L c #FFDCDC", +"M c #FF8F8F", +"N c #FFCBCB", +"O c #FFF5F5", +"P c #FF4747", +"Q c #FF9C9C", +"R c #FFEEEE", +"S c #FFFAFA", +"T c #FF1616", +"U c #FF8888", +"V c #FFC5C5", +"W c #FF2222", +"X c #FF4B4B", +"Y c #FFB8B8", +"Z c #FF7F7F", +"` c #FFE6E6", +" . c #FF8080", +".. c #FFB4B4", +"+. c #FFC3C3", +"@. c #FFD2D2", +"#. c #FFD7D7", +"$. c #FFDFDF", +"%. c #FFB7B7", +"&. c #FFF1F1", +"*. c #FF6262", +"=. c #FF8A8A", +"-. c #FFA9A9", +";. c #FFAEAE", +">. c #FFAAAA", +",. c #FF8B8B", +"'. c #FF4F4F", +"). c #FFFDFD", +"!. c #FFA3A3", +"~. c #FF2A2A", +"{. c #FFCFCF", +"]. c #FF8585", +"^. c #FF7676", +"/. c #FFD3D3", +"(. c #FFD5D5", +"_. c #FF8181", +":. c #FFC6C6", +"<. c #FFDBDB", +"[. c #FF9090", +"}. c #FFAFAF", +"|. c #FFA1A1", +"1. c #FFBABA", +"2. c #FF6C6C", +"3. c #FF5F5F", +"4. c #FF3D3D", +"5. c #FF9999", +"6. c #FFE0E0", +"7. c #FF8383", +"8. c #FFEFEF", +"9. c #FFF3F3", +"0. c #FFA8A8", +"a. c #FFB6B6", +"b. c #FF9F9F", +"c. c #FF4545", +"d. c #FFE5E5", +"e. c #FFE4E4", +"f. c #FFC7C7", +"g. c #FF6565", +"h. c #FFACAC", +"i. c #FF5A5A", +"j. c #FF7272", +"k. c #FF7C7C", +"l. c #FFBFBF", +"m. c #FF7171", +"n. c #FFECEC", +"o. c #FF8989", +"p. c #FF7777", +"q. c #FFC4C4", +"r. c #FF9898", +"s. c #FF8C8C", +"t. c #FF7A7A", +"u. c #FF8E8E", +"v. c #FFF2F2", +"w. c #FF9797", +"x. c #FFC1C1", +"y. c #FFA6A6", +"z. c #FFEBEB", +"A. c #FF4040", +"B. c #EDEDED", +"C. c #000000", +"D. c #AAAAAA", +"E. c #F6F6F6", +"F. c #1C1C1C", +"G. c #888888", +"H. c #7C7C7C", +"I. c #626262", +"J. c #B3B3B3", +"K. c #2A2A2A", +"L. c #959595", +"M. c #FDFDFD", +"N. c #C5C5C5", +"O. c #666666", +"P. c #353535", +"Q. c #777777", +"R. c #DEDEDE", +"S. c #6C6C6C", +"T. c #F5F5F5", +"U. c #ADADAD", +"V. c #DDDDDD", +"W. c #D8D8D8", +"X. c #B4B4B4", +"Y. c #FAFAFA", +"Z. c #949494", +"`. c #3B3B3B", +" + c #A8A8A8", +".+ c #C8C8C8", +"++ c #D4D4D4", +"@+ c #B9B9B9", +"#+ c #2E2E2E", +"$+ c #FEFEFE", +"%+ c #BABABA", +"&+ c #FCFCFC", +"*+ c #B2B2B2", +"=+ c #CACACA", +"-+ c #696969", +";+ c #222222", +">+ c #F2F2F2", +",+ c #555555", +"'+ c #C4C4C4", +")+ c #EBEBEB", +"!+ c #727272", +"~+ c #585858", +"{+ c #0D0D0D", +"]+ c #B1B1B1", +"^+ c #E5E5E5", +"/+ c #C0C0C0", +"(+ c #8F8F8F", +"_+ c #4D4D4D", +":+ c #F4F4F4", +"<+ c #7D7D7D", +"[+ c #E4E4E4", +"}+ c #F3F3F3", +"|+ c #383838", +"1+ c #A9A9A9", +"2+ c #D6D6D6", +"3+ c #D5D5D5", +"4+ c #5F5F5F", +"5+ c #C6C6C6", +"6+ c #E2E2E2", +"7+ c #FBFBFB", +"8+ c #404040", +"9+ c #909090", +"0+ c #EEEEEE", +"a+ c #878787", +"b+ c #E8E8E8", +"c+ c #494949", +"d+ c #424242", +"e+ c #E6E6E6", +"f+ c #CFCFCF", +"g+ c #DCDCDC", +"h+ c #161616", +"i+ c #BBBBBB", +"j+ c #CCCCCC", +"k+ c #B0B0B0", +"l+ c #C7C7C7", +"m+ c #858585", +"n+ c #F8F8F8", +"o+ c #D7D7D7", +"p+ c #BDBDBD", +"q+ c #ECECEC", +"r+ c #939393", +"s+ c #A1A1A1", +"t+ c #7A7A7A", +"u+ c #4B4B4B", +"v+ c #E9E9E9", +"w+ c #717171", +"x+ c #AFAFAF", +"y+ c #454545", +"z+ c #F9F9F9", +"A+ c #DBDBDB", +"B+ c #C1C1C1", +"C+ c #707070", +"D+ c #323232", +"E+ c #9D9D9D", +"F+ c #D1D1D1", +"G+ c #6D6D6D", +"H+ c #262626", +"I+ c #6E6E6E", +"J+ c #808080", +"K+ c #BFBFBF", +"L+ c #999999", +"M+ c #F1F1F1", +"N+ c #DADADA", +"O+ c #9F9F9F", +"P+ c #8B8B8B", +"Q+ c #7F7F7F", +"R+ c #9E9E9E", +"S+ c #F0F0F0", +"T+ c #A4A4A4", +"U+ c #A5A5A5", +"V+ c #CDCDCD", +"W+ c #CBCBCB", +"X+ c #9B9B9B", +"Y+ c #D9D9D9", +"Z+ c #A0A0A0", +"`+ c #9C9C9C", +" @ c #C2C2C2", +".@ c #636363", +"+@ c #D0D0D0", +"@@ c #6A6A6A", +"#@ c #898989", +"$@ c #C3C3C3", +"%@ c #A7A7A7", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . $ % @ & . . . . . * @ + . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . . . . ; @ > , . . . . . * @ + . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . . . . ' ) ! ~ { . . . . * @ + . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . . . ] @ ^ / @ ( . . . _ @ @ @ @ @ : . + @ # . . < [ } | 1 2 3 . . . . 4 5 ) 6 | 7 8 . . . . . . . . = @ - . . 9 @ 0 a b > c d . e , f | g . . . . 9 @ 0 a h % i & . . . . j k l m n o j . . . 9 @ 0 p q m r @ @ @ @ @ : . . . . . . . . . . ", +". . . . . . . . . . . . k s t . u v < . . _ @ @ @ @ @ : . + @ # . . w @ @ @ @ @ @ 5 . . . k @ @ @ @ @ % . . . . . . . . = @ - . . 9 @ x @ @ @ @ y d z @ @ @ @ * . . . 9 @ A @ @ @ @ @ B . . C D @ @ @ @ @ h C . . 9 @ x @ @ @ E @ @ @ @ @ : . . . . . . . . . . ", +". . . . . . . . . . . F G 7 . . H @ I . . . * @ + . . . . + @ # . . ] J 8 j K 0 h 6 K . . l l = F j L M . . . . . . . . = @ - . . 9 @ % N j O J @ P Q R S & T U . . . 9 @ s ^ O j V W X F . Y @ Z ` S w .@ ... . 9 @ s +.t . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . . @.@ #.. . K 6 x . . . * @ + . . . . + @ # . . . . . . . . # @ $.. . ' M . . . . . . . . . . . . . = @ - . . 9 @ %.. . . &.@ *.4 . . . =.r . . . 9 @ -.. . . . ;.@ 0 . q 6 t . . . t f h . . 9 @ >.. . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . . ,.'.).. . . !.~.j . . * @ + . . . . + @ # . . . {.].P ~ @ @ @ 9 . . 8 r ^.- /.j . . . . . . . . . = @ - . . 9 @ + . . . . @ A . . . . !.~ . . . 9 @ (.. . . . # @ N . 1 _.. . . . . c s . . 9 @ (.. . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . { ~ @ @ @ @ @ @ @ :.. . * @ + . . . . + @ # . . ^ @ @ @ @ @ @ @ H . . . F <._ [.> }.. . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ (.. . . . # @ N . 1 u . . . . . c s . . 9 @ 0 . . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . 1.@ @ @ @ @ @ @ @ ^.. . #.@ # . . . . + @ # . . 2.r $.$ < . [ @ H . . . . . . . + @ * . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ -.. . . . ;.@ 0 . q 6 t . . . O f h . . 9 @ 0 . . . . #.@ # . . . . . . . . . . . . . ", +". . . . . . . . . < 3.4.K . . . . . 5.@ w . 6.@ ;.F . . . + @ # . . 2.n ` 4 $ + *.@ H . 4 7.@.8.4 9.k @ ^ . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ s ^ O j V W X F . %.@ u w F ] u @ - . . 9 @ 0 . . . . 6.@ ;.F . . . . . . . . . . . . ", +". . . . . . . . . L @ 0.. . . . . . L @ o . K > @ @ @ : . + @ # . . a.@ @ @ @ @ b.@ H . F @ @ @ @ @ @ > / . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ A @ @ @ @ @ B . . C D @ @ @ @ @ h $ . . 9 @ 0 . . . . K > @ @ @ : . . . . . . . . . . ", +". . . . . . . . . A @ ` . . . . . . < *.c.F . # ^.~.@ : . + @ # . . 4 _ *.% q N d.@ H . . e.: h % l b.e . . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ 0 f.g.~.i /.. . . . j h.n W i.>.O . . . 9 @ 0 . . . . . # ^.~.@ : . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @ # . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ @ # . . . . . . . . . . + @ # . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ @ # . . . . . . . . . . . . . . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . < [ } | 1 2 3 . . . . + @ # . . + @ # . . . 4 l.m.% G 5.n.. . . . &.Q z c.x O j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . w @ @ @ @ @ @ 5 . . . + @ # . . + @ # . . ).o.@ @ @ @ @ c./ . . $ v @ @ @ @ p.2.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ U ] J 8 j K 0 h 6 K . . + @ # . . + @ # . . q.@ } w F O q.s r.. . a.@ s.d S 8 ) @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ U . . . . . . # @ $.. . + @ # . . + @ # . . t.| ).. . . . !.> < . q G C . . . d @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . {.].P ~ @ @ @ 9 . . + @ # . . + @ # . . > @ @ @ @ @ @ @ @ O . % u.. . . . . y k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . ^ @ @ @ @ @ @ @ H . . + @ # . . + @ # . . 1 @ @ @ @ @ @ @ @ v.. % u.. . . . . r k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . 2.r $.$ < . [ @ H . . + @ # . . + @ # . . ^.h < . . . . . . . . q G C . . . d @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . 2.n ` 4 $ + *.@ H . . + @ # . . + @ # . . +.@ w.e.C S d x.y.. . a.@ s.z.S 8 ) @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . a.@ @ @ @ @ b.@ H . . + @ # . . + @ # . . ).M @ @ @ @ @ @ s.. . $ v @ @ @ @ ^.2.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . 4 _ *.% q N d.@ H . . + @ # . . + @ # . . . ).f.k.6 z ' Y v.. . . &.A A.z A O j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.B.C.D.B.C.D.. . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.C.C.C.F.. B.C.D.B.C.D.B.C.D.. . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . . . . B.C.D.B.C.D.. . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . I.C.C.C.J.B.C.C.C.K.L.M.. N.O.P.Q.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.C.C.C.H.. B.C.D.B.C.D.B.C.D.. . . . C.G.. . B.C.S.T.U.C.V.W.C.X.Y.Z.`.E.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . . C.G.. . B.C. +. .+C.++@+C.C.C.C.C.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . . #+Q.$+. B.C.D.. .+C.++W.C.%+&+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . . *+`.C.=+B.C.D.. .+C.++. N.-+;+C.C.Y.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . B.C.D.B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Y.C.>+,+R.. . '+C.C.)+. . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . E.C.C.C.C.F.. B.C.D.B.C.D.. . . . . . . Y.C.>+,+R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Y.C.>+,+R.. . !+~+{+]+. . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . E.C.H.. . . . . . . B.C.D.. . . . . . . Y.C.>+,+R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Y.C.>+,+R.. ^+C./+(+_+&+. I.C.C.C.J.B.C.D.:+C.C.P.<+[+. }+G.|+C.C.>+. . . E.C.H.. . . . B.C.D.B.C.D.. N.O.P.Q.R.. Y.C.>+,+R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1+;+T.2+C.3+. . C.G.. . B.C.D.. . . T.4+Q.. 5+C.X.6+Y.. . . . E.C.C.C.C.H.. B.C.D.B.C.D.W.C.X.Y.Z.`.E.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7+8+C.C.C.C.9+. . C.G.. . B.C.D.0+a+8+C.C.|+. b+c+C.C.d+e+. . . E.C.H.. . . . B.C.D.B.C.D.@+C.C.C.C.C.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . f+C.g+. . J.h+>+. #+Q.$+. B.C.D.i+C.j+0+,+#+. . &+e+k+C.l+. . . E.C.H.. . . . B.C.D.B.C.D.W.C.%+&+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . m+h+n+. . o+C.p+. *+`.C.=+B.C.D.q+!+{+C.C.#+. =+C.C.8+r+E.. . . E.C.H.. . . . B.C.D.B.C.D.. N.-+;+C.C.Y.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . V.4+C.s+C.U.. . . . . . . B.C.D.. . . . N.C.W.. . . B.C.D.. . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . t+u+Y.v+C.U.. . . . . . . B.C.D.. . . . N.C.W.. . . B.C.D.. . . . . . . . . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . u+w+. . . . . . . . . . . B.C.D.. . . . N.C.W.. . . . . . . . . . . . . . . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . H.C.C.C.C.C.U.. N.O.P.Q.R.. B.C.D.. x+d+C.C.C.W.. . . B.C.D.B.C.C.C.K.L.M.. . . I.C.C.C.J.B.C.C.C.K.L.M.. N.O.P.Q.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.W.C.X.Y.Z.`.E.B.C.D.2+C.x+z+(+C.W.. . . B.C.D.B.C.S.T.U.C.V.. . . . C.G.. . B.C.S.T.U.C.V.W.C.X.Y.Z.`.E.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.@+C.C.C.C.C.R.B.C.D.%+C.A+. B+C.W.. . . B.C.D.B.C. +. .+C.++. . . . C.G.. . B.C. +. .+C.++@+C.C.C.C.C.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.W.C.%+&+. . . B.C.D.2+C.k+z+9+C.W.. . . B.C.D.B.C.D.. .+C.++. . . . #+Q.$+. B.C.D.. .+C.++W.C.%+&+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.. N.-+;+C.C.Y.B.C.D.. x+d+C.C.C.W.. . . B.C.D.B.C.D.. .+C.++. . . . *+`.C.=+B.C.D.. .+C.++. N.-+;+C.C.Y.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . N.C.W.. . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . N.C.W.. . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.)+C.C.C.K.C+O.K.!+B.B.C.C.C.D+E+7+. '+O.P.C+F+. B.C.C.C.,+I.C.C.C.J.. . . . x+d+C.C.C.W.. '+O.P.C+F+. . .+G+H+C.R.B.C.D.>+I+I.q+. . . :+C.C.P.<+[+. B.C.C.C.K.L.M.. x+d+C.C.C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.)+C.!+T.J+C.=+g+C.]+B.C.I+E.=+C.K+W.C.]+Y.L+h+b+B.C.O.M+. . C.G.. . . . . 2+C.x+z+(+C.W.W.C.]+Y.L+h+b+N+C.O+z+. . B.C.P+4+Q+T.. . . . . . . T.4+Q.. B.C.S.T.U.C.V.2+C.x+z+(+C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.)+C. +. R+C.M+S+C.T+B.C.U+. M+C.E+@+C.V.. =+C.V+B.C. +. . . C.G.. . . . . %+C.A+. B+C.W.@+C.V.. =+C.V+@+C.A+. . . B.C.C.C.R.. . . . . 0+a+8+C.C.|+. B.C. +. .+C.++%+C.A+. B+C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C+E.W+C.K+W.C.*+Y.X+{+b+B.C.D.. . . #+Q.$+. . . . 2+C.k+z+9+C.W.W.C.*+Y.X+{+b+Y+C.s+z+. . B.C.Z+m+|+V.. . . . i+C.j+0+,+#+. B.C.D.. .+C.++2+C.k+z+9+C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C.C.D+`+7+. @.@D+G++@. B.C.D.. . . *+`.C.=+. . . . x+d+C.C.C.W.. @.@D+G++@. . l+@@H+C.R.B.C.D.Y.#@K.2+. . . q+!+{+C.C.#+. B.C.D.. .+C.++. x+d+C.C.C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.C.C.,+. N.O.P.Q.R.. . . . . B.C.D.)+C.C.C.K.C+O.K.!+B.B.C.C.C.D+E+7+. '+O.P.C+F+. B.C.C.C.,+I.C.C.C.J.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.O.M+. W.C.X.Y.Z.`.E.$@C.C.%@B.C.D.)+C.!+T.J+C.=+g+C.]+B.C.I+E.=+C.K+W.C.]+Y.L+h+b+B.C.O.M+. . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C. +. . @+C.C.C.C.C.R.. . . . B.C.D.)+C. +. R+C.M+S+C.T+B.C.U+. M+C.E+@+C.V.. =+C.V+B.C. +. . . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . W.C.%+&+. . . . . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C+E.W+C.K+W.C.*+Y.X+{+b+B.C.D.. . . #+Q.$+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . N.-+;+C.C.Y.. . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C.C.D+`+7+. @.@D+G++@. B.C.D.. . . *+`.C.=+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "}; diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index 7cdac7da33..daa423e1d9 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -581,7 +581,9 @@ int EditorSceneImporterGLTF::_get_component_type_size(int component_type) { case COMPONENT_TYPE_UNSIGNED_SHORT: return 2; break; case COMPONENT_TYPE_INT: return 4; break; case COMPONENT_TYPE_FLOAT: return 4; break; - default: { ERR_FAIL_V(0); } + default: { + ERR_FAIL_V(0); + } } return 0; } @@ -631,7 +633,8 @@ Vector<double> EditorSceneImporterGLTF::_decode_accessor(GLTFState &state, int p element_size = 16; //override for this case } } break; - default: {} + default: { + } } Vector<double> dst_buffer; @@ -1697,6 +1700,22 @@ void EditorSceneImporterGLTF::_assign_scene_names(GLTFState &state) { } } +void EditorSceneImporterGLTF::_reparent_skeleton(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, Node *p_parent_node) { + //reparent skeletons to proper place + Vector<int> nodes = state.skeleton_nodes[p_node]; + for (int i = 0; i < nodes.size(); i++) { + Skeleton *skeleton = skeletons[nodes[i]]; + Node *owner = skeleton->get_owner(); + skeleton->get_parent()->remove_child(skeleton); + p_parent_node->add_child(skeleton); + skeleton->set_owner(owner); + //may have meshes as children, set owner in them too + for (int j = 0; j < skeleton->get_child_count(); j++) { + skeleton->get_child(j)->set_owner(owner); + } + } +} + void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node *p_parent, Node *p_owner, Vector<Skeleton *> &skeletons) { ERR_FAIL_INDEX(p_node, state.nodes.size()); @@ -1768,24 +1787,17 @@ void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node _generate_node(state, n->children[i], node, p_owner, skeletons); } } + + if (state.skeleton_nodes.has(p_node)) { + _reparent_skeleton(state, p_node, skeletons, node); + } } void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, Node *p_parent_node) { ERR_FAIL_INDEX(p_node, state.nodes.size()); if (state.skeleton_nodes.has(p_node)) { - //reparent skeletons to proper place - Vector<int> nodes = state.skeleton_nodes[p_node]; - for (int i = 0; i < nodes.size(); i++) { - Node *owner = skeletons[i]->get_owner(); - skeletons[i]->get_parent()->remove_child(skeletons[i]); - p_parent_node->add_child(skeletons[i]); - skeletons[i]->set_owner(owner); - //may have meshes as children, set owner in them too - for (int j = 0; j < skeletons[i]->get_child_count(); j++) { - skeletons[i]->get_child(j)->set_owner(owner); - } - } + _reparent_skeleton(state, p_node, skeletons, p_parent_node); } GLTFNode *n = state.nodes[p_node]; diff --git a/editor/import/editor_scene_importer_gltf.h b/editor/import/editor_scene_importer_gltf.h index 0dff45abaf..ebf20e122a 100644 --- a/editor/import/editor_scene_importer_gltf.h +++ b/editor/import/editor_scene_importer_gltf.h @@ -310,6 +310,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Vector<Basis> _decode_accessor_as_basis(GLTFState &state, int p_accessor, bool p_for_vertex); Vector<Transform> _decode_accessor_as_xform(GLTFState &state, int p_accessor, bool p_for_vertex); + void _reparent_skeleton(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, Node *p_parent_node); void _generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, Node *p_parent_node); void _generate_node(GLTFState &state, int p_node, Node *p_parent, Node *p_owner, Vector<Skeleton *> &skeletons); void _import_animation(GLTFState &state, AnimationPlayer *ap, int index, int bake_fps, Vector<Skeleton *> skeletons); diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index fb2e3c0401..ab515785da 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -279,7 +279,24 @@ static String _fixstr(const String &p_what, const String &p_str) { return what; } -Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<ArrayMesh>, Ref<Shape> > &collision_map, LightBakeMode p_light_bake_mode) { +static void _gen_shape_list(const Ref<Mesh> &mesh, List<Ref<Shape> > &r_shape_list, bool p_convex) { + + if (!p_convex) { + + Ref<Shape> shape = mesh->create_trimesh_shape(); + r_shape_list.push_back(shape); + } else { + + Vector<Ref<Shape> > cd = mesh->convex_decompose(); + if (cd.size()) { + for (int i = 0; i < cd.size(); i++) { + r_shape_list.push_back(cd[i]); + } + } + } +} + +Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape> > > &collision_map, LightBakeMode p_light_bake_mode) { // children first for (int i = 0; i < p_node->get_child_count(); i++) { @@ -365,37 +382,52 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Array return p_node; MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); if (mi) { - Node *col = NULL; - - if (_teststr(name, "colonly")) { - col = mi->create_trimesh_collision_node(); - if (col == NULL) { - ERR_PRINTS("Error generating collision for mesh: " + name); - } else { - - col->set_name(_fixstr(name, "colonly")); + Ref<Mesh> mesh = mi->get_mesh(); + + if (mesh.is_valid()) { + List<Ref<Shape> > shapes; + String fixed_name; + if (collision_map.has(mesh)) { + shapes = collision_map[mesh]; + } else if (_teststr(name, "colonly")) { + _gen_shape_list(mesh, shapes, false); + collision_map[mesh] = shapes; + } else if (_teststr(name, "convcolonly")) { + _gen_shape_list(mesh, shapes, true); + collision_map[mesh] = shapes; } - } else { - col = mi->create_convex_collision_node(); - if (col == NULL) { - ERR_PRINTS("Error generating collision for mesh: " + name); - } else { - col->set_name(_fixstr(name, "convcolonly")); + if (_teststr(name, "colonly")) { + fixed_name = _fixstr(name, "colonly"); + } else if (_teststr(name, "convcolonly")) { + fixed_name = _fixstr(name, "convcolonly"); } - } - if (col) { - Object::cast_to<Spatial>(col)->set_transform(mi->get_transform()); - p_node->replace_by(col); - memdelete(p_node); - p_node = col; + ERR_FAIL_COND_V(fixed_name == String(), NULL); + + if (shapes.size()) { + + StaticBody *col = memnew(StaticBody); + col->set_transform(mi->get_transform()); + col->set_name(fixed_name); + p_node->replace_by(col); + memdelete(p_node); + p_node = col; + + int idx = 0; + for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { + + CollisionShape *cshape = memnew(CollisionShape); + cshape->set_shape(E->get()); + col->add_child(cshape); - StaticBody *sb = Object::cast_to<StaticBody>(col); - CollisionShape *colshape = Object::cast_to<CollisionShape>(sb->get_child(0)); - colshape->set_name("shape"); - colshape->set_owner(p_node->get_owner()); + cshape->set_name("shape" + itos(idx)); + cshape->set_owner(col->get_owner()); + idx++; + } + } } + } else if (p_node->has_meta("empty_draw_type")) { String empty_draw_type = String(p_node->get_meta("empty_draw_type")); StaticBody *sb = memnew(StaticBody); @@ -434,77 +466,91 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Array if (isroot) return p_node; - // get mesh instance and bounding box MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); - AABB aabb = mi->get_aabb(); - - // create a new rigid body collision node - RigidBody *rigid_body = memnew(RigidBody); - Node *col = rigid_body; - ERR_FAIL_COND_V(!col, NULL); - - // remove node name postfix - col->set_name(_fixstr(name, "rigid")); - // get mesh instance xform matrix to the rigid body collision node - Object::cast_to<Spatial>(col)->set_transform(mi->get_transform()); - // save original node by duplicating it into a new instance and correcting the name - Node *mesh = p_node->duplicate(); - mesh->set_name(_fixstr(name, "rigid")); - // reset the xform matrix of the duplicated node so it can inherit parent node xform - Object::cast_to<Spatial>(mesh)->set_transform(Transform(Basis())); - // reparent the new mesh node to the rigid body collision node - p_node->add_child(mesh); - mesh->set_owner(p_node->get_owner()); - // replace the original node with the rigid body collision node - p_node->replace_by(col); - memdelete(p_node); - p_node = col; - - // create an alias for the rigid body collision node - RigidBody *rb = Object::cast_to<RigidBody>(col); - // create a new Box collision shape and set the right extents - Ref<BoxShape> shape = memnew(BoxShape); - shape->set_extents(aabb.get_size() * 0.5); - CollisionShape *colshape = memnew(CollisionShape); - colshape->set_name("shape"); - colshape->set_shape(shape); - // reparent the new collision shape to the rigid body collision node - rb->add_child(colshape); - colshape->set_owner(p_node->get_owner()); + Ref<Mesh> mesh = mi->get_mesh(); + + if (mesh.is_valid()) { + List<Ref<Shape> > shapes; + if (collision_map.has(mesh)) { + shapes = collision_map[mesh]; + } else { + _gen_shape_list(mesh, shapes, true); + } + + RigidBody *rigid_body = memnew(RigidBody); + rigid_body->set_name(_fixstr(name, "rigid")); + p_node->replace_by(rigid_body); + rigid_body->set_transform(mi->get_transform()); + p_node = rigid_body; + mi->set_name("mesh"); + mi->set_transform(Transform()); + rigid_body->add_child(mi); + mi->set_owner(rigid_body->get_owner()); + + int idx = 0; + for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { + + CollisionShape *cshape = memnew(CollisionShape); + cshape->set_shape(E->get()); + rigid_body->add_child(cshape); + + cshape->set_name("shape" + itos(idx)); + cshape->set_owner(p_node->get_owner()); + idx++; + } + } } else if ((_teststr(name, "col") || (_teststr(name, "convcol"))) && Object::cast_to<MeshInstance>(p_node)) { MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); - Node *col; - if (_teststr(name, "col")) { - String new_name = _fixstr(name, "col"); - if (mi->get_parent() && !mi->get_parent()->has_node(new_name)) { - mi->set_name(new_name); + Ref<Mesh> mesh = mi->get_mesh(); + + if (mesh.is_valid()) { + List<Ref<Shape> > shapes; + String fixed_name; + if (collision_map.has(mesh)) { + shapes = collision_map[mesh]; + } else if (_teststr(name, "col")) { + _gen_shape_list(mesh, shapes, false); + collision_map[mesh] = shapes; + } else if (_teststr(name, "convcol")) { + _gen_shape_list(mesh, shapes, true); + collision_map[mesh] = shapes; } - col = mi->create_trimesh_collision_node(); - ERR_FAIL_COND_V(!col, NULL); - col->set_name("col"); - } else { - String new_name = _fixstr(name, "convcol"); - if (mi->get_parent() && !mi->get_parent()->has_node(new_name)) { - mi->set_name(new_name); + if (_teststr(name, "col")) { + fixed_name = _fixstr(name, "col"); + } else if (_teststr(name, "convcol")) { + fixed_name = _fixstr(name, "convcol"); } - col = mi->create_convex_collision_node(); - ERR_FAIL_COND_V(!col, NULL); - col->set_name("convcol"); - } + if (fixed_name != String()) { + if (mi->get_parent() && !mi->get_parent()->has_node(fixed_name)) { + mi->set_name(fixed_name); + } + } + + if (shapes.size()) { + StaticBody *col = memnew(StaticBody); + col->set_name("static_collision"); + mi->add_child(col); + col->set_owner(mi->get_owner()); - p_node->add_child(col); + int idx = 0; + for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { - StaticBody *sb = Object::cast_to<StaticBody>(col); - CollisionShape *colshape = Object::cast_to<CollisionShape>(sb->get_child(0)); - colshape->set_name("shape"); - col->add_child(colshape); - colshape->set_owner(p_node->get_owner()); - sb->set_owner(p_node->get_owner()); + CollisionShape *cshape = memnew(CollisionShape); + cshape->set_shape(E->get()); + col->add_child(cshape); + + cshape->set_name("shape" + itos(idx)); + cshape->set_owner(p_node->get_owner()); + + idx++; + } + } + } } else if (_teststr(name, "navmesh") && Object::cast_to<MeshInstance>(p_node)) { @@ -574,48 +620,35 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Array Ref<ArrayMesh> mesh = mi->get_mesh(); if (!mesh.is_null()) { - if (_teststr(mesh->get_name(), "col") || _teststr(mesh->get_name(), "convcol")) { - Ref<Shape> shape; - if (_teststr(mesh->get_name(), "col")) { - mesh->set_name(_fixstr(mesh->get_name(), "col")); - - if (collision_map.has(mesh)) { - shape = collision_map[mesh]; - - } else { - - shape = mesh->create_trimesh_shape(); - if (!shape.is_null()) - collision_map[mesh] = shape; - } - } else if (_teststr(mesh->get_name(), "convcol")) { - mesh->set_name(_fixstr(mesh->get_name(), "convcol")); + List<Ref<Shape> > shapes; + if (collision_map.has(mesh)) { + shapes = collision_map[mesh]; + } else if (_teststr(mesh->get_name(), "col")) { + _gen_shape_list(mesh, shapes, false); + collision_map[mesh] = shapes; + mesh->set_name(_fixstr(mesh->get_name(), "col")); + } else if (_teststr(mesh->get_name(), "convcol")) { + _gen_shape_list(mesh, shapes, true); + collision_map[mesh] = shapes; + mesh->set_name(_fixstr(mesh->get_name(), "convcol")); + } - if (collision_map.has(mesh)) { - shape = collision_map[mesh]; + if (shapes.size()) { + StaticBody *col = memnew(StaticBody); + col->set_name("static_collision"); + p_node->add_child(col); + col->set_owner(p_node->get_owner()); - } else { + int idx = 0; + for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { - shape = mesh->create_convex_shape(); - if (!shape.is_null()) - collision_map[mesh] = shape; - } - } - - if (!shape.is_null()) { - StaticBody *col = memnew(StaticBody); CollisionShape *cshape = memnew(CollisionShape); - cshape->set_shape(shape); + cshape->set_shape(E->get()); col->add_child(cshape); - col->set_transform(mi->get_transform()); - col->set_name(mi->get_name()); - p_node->replace_by(col); - memdelete(p_node); - p_node = col; - - cshape->set_name("shape"); + cshape->set_name("shape" + itos(idx)); cshape->set_owner(p_node->get_owner()); + idx++; } } } @@ -1269,7 +1302,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p float anim_optimizer_maxang = p_options["animation/optimizer/max_angle"]; int light_bake_mode = p_options["meshes/light_baking"]; - Map<Ref<ArrayMesh>, Ref<Shape> > collision_map; + Map<Ref<Mesh>, List<Ref<Shape> > > collision_map; scene = _fix_node(scene, scene, collision_map, LightBakeMode(light_bake_mode)); diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 99f8b1a8e0..b10c4da2e5 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -146,7 +146,7 @@ public: void _make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_keep_animations, bool p_make_materials, bool p_keep_materials, bool p_make_meshes, Map<Ref<Animation>, Ref<Animation> > &p_animations, Map<Ref<Material>, Ref<Material> > &p_materials, Map<Ref<ArrayMesh>, Ref<ArrayMesh> > &p_meshes); - Node *_fix_node(Node *p_node, Node *p_root, Map<Ref<ArrayMesh>, Ref<Shape> > &collision_map, LightBakeMode p_light_bake_mode); + Node *_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape> > > &collision_map, LightBakeMode p_light_bake_mode); void _create_clips(Node *scene, const Array &p_clips, bool p_bake_all); void _filter_anim_tracks(Ref<Animation> anim, Set<String> &keep); diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp new file mode 100644 index 0000000000..35fdd32e2c --- /dev/null +++ b/editor/import/resource_importer_texture_atlas.cpp @@ -0,0 +1,382 @@ +#include "resource_importer_texture_atlas.h" + +#include "atlas_import_failed.xpm" +#include "core/io/image_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" +#include "editor/editor_atlas_packer.h" +#include "scene/resources/mesh.h" +#include "scene/resources/texture.h" + +String ResourceImporterTextureAtlas::get_importer_name() const { + + return "texture_atlas"; +} + +String ResourceImporterTextureAtlas::get_visible_name() const { + + return "TextureAtlas"; +} +void ResourceImporterTextureAtlas::get_recognized_extensions(List<String> *p_extensions) const { + + ImageLoader::get_recognized_extensions(p_extensions); +} + +String ResourceImporterTextureAtlas::get_save_extension() const { + return "res"; +} + +String ResourceImporterTextureAtlas::get_resource_type() const { + + return "Texture"; +} + +bool ResourceImporterTextureAtlas::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { + + return true; +} + +int ResourceImporterTextureAtlas::get_preset_count() const { + return 0; +} +String ResourceImporterTextureAtlas::get_preset_name(int p_idx) const { + + return String(); +} + +void ResourceImporterTextureAtlas::get_import_options(List<ImportOption> *r_options, int p_preset) const { + + r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "atlas_file", PROPERTY_HINT_SAVE_FILE, "*.png"), "")); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_mode", PROPERTY_HINT_ENUM, "Region,Mesh2D"), 0)); +} + +String ResourceImporterTextureAtlas::get_option_group_file() const { + return "atlas_file"; +} + +Error ResourceImporterTextureAtlas::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + + /* If this happens, it's because the atlas_file field was not filled, so just import a broken texture */ + + //use an xpm because it's size independent, the editor images are vector and size dependent + //it's a simple hack + Ref<Image> broken = memnew(Image((const char **)atlas_import_failed_xpm)); + Ref<ImageTexture> broken_texture; + broken_texture.instance(); + broken_texture->create_from_image(broken); + + String target_file = p_save_path + ".tex"; + + ResourceSaver::save(target_file, broken_texture); + + return OK; +} + +static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_transposed, Ref<Image> p_image, const Ref<Image> &p_src_image) { + + int width = p_image->get_width(); + int height = p_image->get_height(); + int src_width = p_src_image->get_width(); + int src_height = p_src_image->get_height(); + + int x[3]; + int y[3]; + + for (int j = 0; j < 3; j++) { + + x[j] = vertices[j].x; + y[j] = vertices[j].y; + } + + // sort the points vertically + if (y[1] > y[2]) { + SWAP(x[1], x[2]); + SWAP(y[1], y[2]); + } + if (y[0] > y[1]) { + SWAP(x[0], x[1]); + SWAP(y[0], y[1]); + } + if (y[1] > y[2]) { + SWAP(x[1], x[2]); + SWAP(y[1], y[2]); + } + + double dx_far = double(x[2] - x[0]) / (y[2] - y[0] + 1); + double dx_upper = double(x[1] - x[0]) / (y[1] - y[0] + 1); + double dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1); + double xf = x[0]; + double xt = x[0] + dx_upper; // if y[0] == y[1], special case + for (int yi = y[0]; yi <= (y[2] > height - 1 ? height - 1 : y[2]); yi++) { + if (yi >= 0) { + for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < width ? xt : width - 1); xi++) { + + int px = xi, py = yi; + int sx = px, sy = py; + sx = CLAMP(sx, 0, src_width); + sy = CLAMP(sy, 0, src_height); + Color color = p_src_image->get_pixel(sx, sy); + if (p_transposed) { + SWAP(px, py); + } + px += p_offset.x; + py += p_offset.y; + + //may have been cropped, so don't blit what is not visible? + if (px < 0 || px >= width) { + continue; + } + if (py < 0 || py >= height) { + continue; + } + p_image->set_pixel(px, py, color); + } + + for (int xi = (xf < width ? int(xf) : width - 1); xi >= (xt > 0 ? xt : 0); xi--) { + int px = xi, py = yi; + int sx = px, sy = py; + sx = CLAMP(sx, 0, src_width); + sy = CLAMP(sy, 0, src_height); + Color color = p_src_image->get_pixel(sx, sy); + if (p_transposed) { + SWAP(px, py); + } + px += p_offset.x; + py += p_offset.y; + + //may have been cropped, so don't blit what is not visible? + if (px < 0 || px >= width) { + continue; + } + if (py < 0 || py >= height) { + continue; + } + p_image->set_pixel(px, py, color); + } + } + xf += dx_far; + if (yi < y[1]) + xt += dx_upper; + else + xt += dx_low; + } +} + +Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant> > &p_source_file_options, const Map<String, String> &p_base_paths) { + + ERR_FAIL_COND_V(p_source_file_options.size() == 0, ERR_BUG); //should never happen + + Vector<EditorAtlasPacker::Chart> charts; + Vector<PackData> pack_data_files; + + pack_data_files.resize(p_source_file_options.size()); + + int idx = 0; + for (const Map<String, Map<StringName, Variant> >::Element *E = p_source_file_options.front(); E; E = E->next(), idx++) { + + PackData &pack_data = pack_data_files.write[idx]; + String source = E->key(); + const Map<StringName, Variant> &options = E->get(); + + Ref<Image> image; + image.instance(); + Error err = ImageLoader::load_image(source, image); + ERR_CONTINUE(err != OK); + + pack_data.image = image; + + int mode = options["import_mode"]; + + if (mode == IMPORT_MODE_REGION) { + + pack_data.is_mesh = false; + + EditorAtlasPacker::Chart chart; + + //clip a region from the image + Rect2 used_rect = image->get_used_rect(); + pack_data.region = used_rect; + + chart.vertices.push_back(used_rect.position); + chart.vertices.push_back(used_rect.position + Vector2(used_rect.size.x, 0)); + chart.vertices.push_back(used_rect.position + Vector2(used_rect.size.x, used_rect.size.y)); + chart.vertices.push_back(used_rect.position + Vector2(0, used_rect.size.y)); + EditorAtlasPacker::Chart::Face f; + f.vertex[0] = 0; + f.vertex[1] = 1; + f.vertex[2] = 2; + chart.faces.push_back(f); + f.vertex[0] = 0; + f.vertex[1] = 2; + f.vertex[2] = 3; + chart.faces.push_back(f); + chart.can_transpose = false; + pack_data.chart_vertices.push_back(chart.vertices); + pack_data.chart_pieces.push_back(charts.size()); + charts.push_back(chart); + + } else { + pack_data.is_mesh = true; + + Ref<BitMap> bit_map; + bit_map.instance(); + bit_map->create_from_image_alpha(image); + Vector<Vector<Vector2> > polygons = bit_map->clip_opaque_to_polygons(Rect2(0, 0, image->get_width(), image->get_height())); + + for (int j = 0; j < polygons.size(); j++) { + + EditorAtlasPacker::Chart chart; + chart.vertices = polygons[j]; + chart.can_transpose = true; + + Vector<int> poly = Geometry::triangulate_polygon(polygons[j]); + for (int i = 0; i < poly.size(); i += 3) { + + EditorAtlasPacker::Chart::Face f; + f.vertex[0] = poly[i + 0]; + f.vertex[1] = poly[i + 1]; + f.vertex[2] = poly[i + 2]; + chart.faces.push_back(f); + } + + pack_data.chart_pieces.push_back(charts.size()); + charts.push_back(chart); + + pack_data.chart_vertices.push_back(polygons[j]); + } + } + } + + //pack the charts + int atlas_width, atlas_height; + EditorAtlasPacker::chart_pack(charts, atlas_width, atlas_height); + + //blit the atlas + Ref<Image> new_atlas; + new_atlas.instance(); + new_atlas->create(atlas_width, atlas_height, false, Image::FORMAT_RGBA8); + + new_atlas->lock(); + + for (int i = 0; i < pack_data_files.size(); i++) { + + PackData &pack_data = pack_data_files.write[i]; + pack_data.image->lock(); + for (int j = 0; j < pack_data.chart_pieces.size(); j++) { + const EditorAtlasPacker::Chart &chart = charts[pack_data.chart_pieces[j]]; + for (int k = 0; k < chart.faces.size(); k++) { + Vector2 positions[3]; + for (int l = 0; l < 3; l++) { + int vertex_idx = chart.faces[k].vertex[l]; + positions[l] = chart.vertices[vertex_idx]; + } + + _plot_triangle(positions, chart.final_offset, chart.transposed, new_atlas, pack_data.image); + } + } + pack_data.image->unlock(); + } + new_atlas->unlock(); + + //save the atlas + + new_atlas->save_png(p_group_file); + + //update cache if existing, else create + Ref<Texture> cache; + if (ResourceCache::has(p_group_file)) { + Resource *resptr = ResourceCache::get(p_group_file); + cache.reference_ptr(resptr); + } else { + Ref<ImageTexture> res_cache; + res_cache.instance(); + res_cache->create_from_image(new_atlas); + res_cache->set_path(p_group_file); + cache = res_cache; + } + + //save the images + idx = 0; + for (const Map<String, Map<StringName, Variant> >::Element *E = p_source_file_options.front(); E; E = E->next(), idx++) { + + PackData &pack_data = pack_data_files.write[idx]; + + Ref<Texture> texture; + + if (!pack_data.is_mesh) { + Vector2 offset = charts[pack_data.chart_pieces[0]].vertices[0] + charts[pack_data.chart_pieces[0]].final_offset; + + //region + Ref<AtlasTexture> atlas_texture; + atlas_texture.instance(); + atlas_texture->set_atlas(cache); + atlas_texture->set_region(Rect2(offset, pack_data.region.size)); + atlas_texture->set_margin(Rect2(pack_data.region.position, Size2(pack_data.image->get_width(), pack_data.image->get_height()) - pack_data.region.size)); + + texture = atlas_texture; + } else { + Ref<ArrayMesh> mesh; + mesh.instance(); + + for (int i = 0; i < pack_data.chart_pieces.size(); i++) { + const EditorAtlasPacker::Chart &chart = charts[pack_data.chart_pieces[i]]; + PoolVector<Vector2> vertices; + PoolVector<int> indices; + PoolVector<Vector2> uvs; + int vc = chart.vertices.size(); + int fc = chart.faces.size(); + vertices.resize(vc); + uvs.resize(vc); + indices.resize(fc * 3); + + { + PoolVector<Vector2>::Write vw = vertices.write(); + PoolVector<int>::Write iw = indices.write(); + PoolVector<Vector2>::Write uvw = uvs.write(); + + for (int j = 0; j < vc; j++) { + vw[j] = chart.vertices[j]; + Vector2 uv = chart.vertices[j]; + if (chart.transposed) { + SWAP(uv.x, uv.y); + } + uv += chart.final_offset; + uv /= new_atlas->get_size(); //normalize uv to 0-1 range + uvw[j] = uv; + } + + for (int j = 0; j < fc; j++) { + iw[j * 3 + 0] = chart.faces[j].vertex[0]; + iw[j * 3 + 1] = chart.faces[j].vertex[1]; + iw[j * 3 + 2] = chart.faces[j].vertex[2]; + } + } + + Array arrays; + arrays.resize(Mesh::ARRAY_MAX); + arrays[Mesh::ARRAY_VERTEX] = vertices; + arrays[Mesh::ARRAY_TEX_UV] = uvs; + arrays[Mesh::ARRAY_INDEX] = indices; + + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays); + } + + Ref<MeshTexture> mesh_texture; + mesh_texture.instance(); + mesh_texture->set_base_texture(cache); + mesh_texture->set_image_size(pack_data.image->get_size()); + mesh_texture->set_mesh(mesh); + + texture = mesh_texture; + //mesh + } + + String save_path = p_base_paths[E->key()] + ".res"; + ResourceSaver::save(save_path, texture); + } + + return OK; +} + +ResourceImporterTextureAtlas::ResourceImporterTextureAtlas() { +} diff --git a/editor/import/resource_importer_texture_atlas.h b/editor/import/resource_importer_texture_atlas.h new file mode 100644 index 0000000000..62be570dc6 --- /dev/null +++ b/editor/import/resource_importer_texture_atlas.h @@ -0,0 +1,42 @@ +#ifndef RESOURCE_IMPORTER_TEXTURE_ATLAS_H +#define RESOURCE_IMPORTER_TEXTURE_ATLAS_H + +#include "core/image.h" +#include "core/io/resource_importer.h" +class ResourceImporterTextureAtlas : public ResourceImporter { + GDCLASS(ResourceImporterTextureAtlas, ResourceImporter) + + struct PackData { + Rect2 region; + bool is_mesh; + Vector<int> chart_pieces; //one for region, many for mesh + Vector<Vector<Vector2> > chart_vertices; //for mesh + Ref<Image> image; + }; + +public: + enum ImportMode { + IMPORT_MODE_REGION, + IMPORT_MODE_2D_MESH + }; + + virtual String get_importer_name() const; + virtual String get_visible_name() const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual String get_save_extension() const; + virtual String get_resource_type() const; + + virtual int get_preset_count() const; + virtual String get_preset_name(int p_idx) const; + + virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; + virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; + virtual String get_option_group_file() const; + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); + virtual Error import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant> > &p_source_file_options, const Map<String, String> &p_base_paths); + + ResourceImporterTextureAtlas(); +}; + +#endif // RESOURCE_IMPORTER_TEXTURE_ATLAS_H diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index 857d8992fd..52dec47343 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -35,6 +35,9 @@ #include "core/os/file_access.h" #include "scene/resources/audio_stream_sample.h" +const float TRIM_DB_LIMIT = -50; +const int TRIM_FADE_OUT_FRAMES = 500; + String ResourceImporterWAV::get_importer_name() const { return "wav"; @@ -393,31 +396,42 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s if (trim && !loop && format_channels > 0) { int first = 0; - int last = (frames * format_channels) - 1; + int last = (frames / format_channels) - 1; bool found = false; - float limit = Math::db2linear((float)-30); - for (int i = 0; i < data.size(); i++) { - float amp = Math::abs(data[i]); + float limit = Math::db2linear(TRIM_DB_LIMIT); + + for (int i = 0; i < data.size() / format_channels; i++) { + float ampChannelSum = 0; + for (int j = 0; j < format_channels; j++) { + ampChannelSum += Math::abs(data[(i * format_channels) + j]); + } + + float amp = Math::abs(ampChannelSum / (float)format_channels); if (!found && amp > limit) { - first = i; + first = i / format_channels; found = true; } if (found && amp > limit) { - last = i; + last = i / format_channels; } } - first /= format_channels; - last /= format_channels; - if (first < last) { - Vector<float> new_data; - new_data.resize((last - first + 1) * format_channels); - for (int i = first * format_channels; i < (last + 1) * format_channels; i++) { - new_data.write[i - first * format_channels] = data[i]; + new_data.resize((last - first) * format_channels); + for (int i = first; i < last; i++) { + + float fadeOutMult = 1; + + if (last - i < TRIM_FADE_OUT_FRAMES) { + fadeOutMult = ((float)(last - i - 1) / (float)TRIM_FADE_OUT_FRAMES); + } + + for (int j = 0; j < format_channels; j++) { + new_data.write[((i - first) * format_channels) + j] = data[(i * format_channels) + j] * fadeOutMult; + } } data = new_data; diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 15539ee3db..b307ec649a 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -438,6 +438,8 @@ void ImportDock::_reimport() { Error err = config->load(params->paths[i] + ".import"); ERR_CONTINUE(err != OK); + String importer_name = params->importer->get_importer_name(); + if (params->checking) { //update only what edited (checkboxes) for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) { @@ -447,7 +449,7 @@ void ImportDock::_reimport() { } } else { //override entirely - config->set_value("remap", "importer", params->importer->get_importer_name()); + config->set_value("remap", "importer", importer_name); config->erase_section("params"); for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) { @@ -455,6 +457,19 @@ void ImportDock::_reimport() { } } + //handle group file + Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name); + ERR_CONTINUE(!importer.is_valid()); + String group_file_property = importer->get_option_group_file(); + if (group_file_property != String()) { + //can import from a group (as in, atlas) + ERR_CONTINUE(!params->values.has(group_file_property)); + String group_file = params->values[group_file_property]; + config->set_value("remap", "group_file", group_file); + } else { + config->set_value("remap", "group_file", Variant()); //clear group file if unused + } + config->save(params->paths[i] + ".import"); } diff --git a/editor/output_strings.cpp b/editor/output_strings.cpp index 24b44f078f..baabaff9a8 100644 --- a/editor/output_strings.cpp +++ b/editor/output_strings.cpp @@ -105,7 +105,8 @@ void OutputStrings::_notification(int p_what) { case LINE_LINK: { } break; - default: {} + default: { + } } line_ofs.y += font->get_ascent(); diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 28f786e99a..4a4e7f25b8 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -638,6 +638,13 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl const Color modulate = vertex == active_point ? Color(0.5, 1, 2) : Color(1, 1, 1); p_overlay->draw_texture(handle, point - handle->get_size() * 0.5, modulate); + + if (vertex == hover_point) { + Ref<Font> font = get_font("font", "Label"); + String num = String::num(vertex.vertex); + Size2 num_size = font->get_string_size(num); + p_overlay->draw_string(font, point - num_size * 0.5, num, Color(1.0, 1.0, 1.0, 0.5)); + } } } diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index afe2573898..f13c2170ea 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -80,6 +80,7 @@ void AnimationNodeBlendTreeEditor::_update_options_menu() { } add_node->get_popup()->add_separator(); add_node->get_popup()->add_item(TTR("Load..."), MENU_LOAD_FILE); + use_popup_menu_position = false; } Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { @@ -240,7 +241,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { Ref<StyleBoxFlat> sb = node->get_stylebox("frame", "GraphNode"); - Color c = sb->get_border_color(MARGIN_TOP); + Color c = sb->get_border_color(); Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); mono_color.a = 0.85; c = mono_color; @@ -317,7 +318,15 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { EditorNode::get_singleton()->show_warning(TTR("Output node can't be added to the blend tree.")); return; } - Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5; + + Point2 instance_pos = graph->get_scroll_ofs(); + if (use_popup_menu_position) { + instance_pos += popup_menu_position; + } else { + instance_pos += graph->get_size() * 0.5; + } + + instance_pos /= graph->get_zoom(); int base = 1; String name = base_name; @@ -412,6 +421,40 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { undo_redo->commit_action(); } +void AnimationNodeBlendTreeEditor::_delete_nodes_request() { + + List<StringName> to_erase; + + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + if (gn->is_selected() && gn->is_close_button_visible()) { + to_erase.push_back(gn->get_name()); + } + } + } + + if (to_erase.empty()) + return; + + undo_redo->create_action(TTR("Delete Node(s)")); + + for (List<StringName>::Element *F = to_erase.front(); F; F = F->next()) { + _delete_request(F->get()); + } + + undo_redo->commit_action(); +} + +void AnimationNodeBlendTreeEditor::_popup_request(const Vector2 &p_position) { + + _update_options_menu(); + use_popup_menu_position = true; + popup_menu_position = graph->get_local_mouse_position(); + add_node->get_popup()->set_position(p_position); + add_node->get_popup()->popup(); +} + void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) { GraphNode *gn = Object::cast_to<GraphNode>(p_node); @@ -730,6 +773,8 @@ void AnimationNodeBlendTreeEditor::_bind_methods() { ClassDB::bind_method("_open_in_editor", &AnimationNodeBlendTreeEditor::_open_in_editor); ClassDB::bind_method("_scroll_changed", &AnimationNodeBlendTreeEditor::_scroll_changed); ClassDB::bind_method("_delete_request", &AnimationNodeBlendTreeEditor::_delete_request); + ClassDB::bind_method("_delete_nodes_request", &AnimationNodeBlendTreeEditor::_delete_nodes_request); + ClassDB::bind_method("_popup_request", &AnimationNodeBlendTreeEditor::_popup_request); ClassDB::bind_method("_edit_filters", &AnimationNodeBlendTreeEditor::_edit_filters); ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters); ClassDB::bind_method("_filter_edited", &AnimationNodeBlendTreeEditor::_filter_edited); @@ -850,6 +895,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { singleton = this; updating = false; + use_popup_menu_position = false; graph = memnew(GraphEdit); add_child(graph); @@ -860,6 +906,8 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { graph->connect("disconnection_request", this, "_disconnection_request", varray(), CONNECT_DEFERRED); graph->connect("node_selected", this, "_node_selected"); graph->connect("scroll_offset_changed", this, "_scroll_changed"); + graph->connect("delete_nodes_request", this, "_delete_nodes_request"); + graph->connect("popup_request", this, "_popup_request"); VSeparator *vs = memnew(VSeparator); graph->get_zoom_hbox()->add_child(vs); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index f2a77cecb4..cb40159a40 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -51,6 +51,8 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { Ref<AnimationNodeBlendTree> blend_tree; GraphEdit *graph; MenuButton *add_node; + Vector2 popup_menu_position; + bool use_popup_menu_position; PanelContainer *error_panel; Label *error_label; @@ -97,6 +99,8 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _open_in_editor(const String &p_which); void _anim_selected(int p_index, Array p_options, const String &p_node); void _delete_request(const String &p_which); + void _delete_nodes_request(); + void _popup_request(const Vector2 &p_position); bool _update_filters(const Ref<AnimationNode> &anode); void _edit_filters(const String &p_which); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index bbaf41e3cc..41f35c3bed 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -670,6 +670,7 @@ Dictionary AnimationPlayerEditor::get_state() const { if (EditorNode::get_singleton()->get_edited_scene() && is_visible_in_tree() && player) { d["player"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(player); d["animation"] = player->get_assigned_animation(); + d["track_editor_state"] = track_editor->get_state(); } return d; @@ -696,6 +697,10 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) { _animation_edit(); } } + + if (p_state.has("track_editor_state")) { + track_editor->set_state(p_state["track_editor_state"]); + } } } diff --git a/editor/plugins/animation_tree_player_editor_plugin.cpp b/editor/plugins/animation_tree_player_editor_plugin.cpp index e2a44069d9..e21ae4834d 100644 --- a/editor/plugins/animation_tree_player_editor_plugin.cpp +++ b/editor/plugins/animation_tree_player_editor_plugin.cpp @@ -200,7 +200,8 @@ void AnimationTreePlayerEditor::_edit_dialog_changed() { if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected()) anim_tree->transition_node_set_current(edited_node, edit_option->get_selected()); } break; - default: {} + default: { + } } } @@ -457,7 +458,8 @@ void AnimationTreePlayerEditor::_popup_edit_dialog() { edit_dialog->set_size(Size2(150, 100)); } break; - default: {} + default: { + } } } @@ -555,7 +557,8 @@ void AnimationTreePlayerEditor::_draw_node(const StringName &p_node) { text += "->"; break; - default: {} + default: { + } } font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color); @@ -740,7 +743,8 @@ void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { //open editor //_node_edit_property(click_node); } break; - default: {} + default: { + } } } if (mb->get_button_index() == 2) { @@ -817,7 +821,8 @@ void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { anim_tree->node_set_position(click_node, new_pos); } break; - default: {} + default: { + } } click_type = CLICK_NONE; diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 6d2cdfc583..386bc1738e 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -454,7 +454,8 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) { progress->set_max(1); progress->set_value(0); } break; - default: {} + default: { + } } prev_status = cstatus; } @@ -612,7 +613,8 @@ void EditorAssetLibrary::_notification(int p_what) { case HTTPClient::STATUS_BODY: { load_status->set_value(0.4); } break; - default: {} + default: { + } } } diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp index d90ff95c1f..d75f06de12 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.cpp +++ b/editor/plugins/baked_lightmap_editor_plugin.cpp @@ -50,7 +50,8 @@ 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: {} + default: { + } } } } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index dc1a8ade9f..b2923a1ff2 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1072,36 +1072,36 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { } } - if (drag_type == DRAG_NONE) { + if (!panning) { if (b->is_pressed() && (b->get_button_index() == BUTTON_MIDDLE || (b->get_button_index() == BUTTON_LEFT && tool == TOOL_PAN) || (b->get_button_index() == BUTTON_LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) { // Pan the viewport - drag_type = DRAG_PAN; + panning = true; } } - if (drag_type == DRAG_PAN) { + if (panning) { if (!b->is_pressed()) { // Stop panning the viewport (for any mouse button press) - drag_type = DRAG_NONE; + panning = false; } } } Ref<InputEventKey> k = p_event; if (k.is_valid()) { - if (k->get_scancode() == KEY_SPACE && EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning")) { - if (drag_type == DRAG_NONE) { + if (k->get_scancode() == KEY_SPACE && (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") || drag_type != DRAG_NONE)) { + if (!panning) { if (k->is_pressed() && !k->is_echo()) { //Pan the viewport - drag_type = DRAG_PAN; + panning = true; } - } else if (drag_type == DRAG_PAN) { + } else if (panning) { if (!k->is_pressed()) { // Stop panning the viewport (for any mouse button press) - drag_type = DRAG_NONE; + panning = false; } } } @@ -1109,7 +1109,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> m = p_event; if (m.is_valid()) { - if (drag_type == DRAG_PAN) { + if (panning) { // Pan the viewport Point2i relative; if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) { @@ -1340,6 +1340,10 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { // Confirms the node rotation if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { _commit_canvas_item_state(drag_selection, TTR("Rotate CanvasItem")); + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(false, true, false, true); + } + drag_type = DRAG_NONE; return true; } @@ -1641,6 +1645,9 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) { // Confirm resize if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { _commit_canvas_item_state(drag_selection, TTR("Resize CanvasItem")); + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(false, false, true, true); + } drag_type = DRAG_NONE; viewport->update(); return true; @@ -1747,6 +1754,10 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { // Confirm resize if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { _commit_canvas_item_state(drag_selection, TTR("Scale CanvasItem")); + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(false, false, true, true); + } + drag_type = DRAG_NONE; viewport->update(); return true; @@ -1852,6 +1863,9 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { _commit_canvas_item_state(drag_selection, TTR("Move CanvasItem"), true); } + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(true, false, false, true); + } drag_type = DRAG_NONE; viewport->update(); return true; @@ -2190,32 +2204,34 @@ bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) { void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { bool accepted = false; - if ((accepted = _gui_input_rulers_and_guides(p_event))) { - //printf("Rulers and guides\n"); - } else if ((accepted = editor->get_editor_plugins_over()->forward_gui_input(p_event))) { - //printf("Plugin\n"); - } else if ((accepted = _gui_input_open_scene_on_double_click(p_event))) { - //printf("Open scene on double click\n"); - } else if ((accepted = _gui_input_anchors(p_event))) { - //printf("Anchors\n"); - } else if ((accepted = _gui_input_scale(p_event))) { - //printf("Set scale\n"); - } else if ((accepted = _gui_input_pivot(p_event))) { - //printf("Set pivot\n"); - } else if ((accepted = _gui_input_resize(p_event))) { - //printf("Resize\n"); - } else if ((accepted = _gui_input_rotate(p_event))) { - //printf("Rotate\n"); - } else if ((accepted = _gui_input_move(p_event))) { - //printf("Move\n"); - } else if ((accepted = _gui_input_zoom_or_pan(p_event))) { - //printf("Zoom or pan\n"); - } else if ((accepted = _gui_input_select(p_event))) { - //printf("Selection\n"); - } else { - //printf("Not accepted\n"); + if (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") || !Input::get_singleton()->is_key_pressed(KEY_SPACE)) { + if ((accepted = _gui_input_rulers_and_guides(p_event))) { + //printf("Rulers and guides\n"); + } else if ((accepted = editor->get_editor_plugins_over()->forward_gui_input(p_event))) { + //printf("Plugin\n"); + } else if ((accepted = _gui_input_open_scene_on_double_click(p_event))) { + //printf("Open scene on double click\n"); + } else if ((accepted = _gui_input_anchors(p_event))) { + //printf("Anchors\n"); + } else if ((accepted = _gui_input_scale(p_event))) { + //printf("Set scale\n"); + } else if ((accepted = _gui_input_pivot(p_event))) { + //printf("Set pivot\n"); + } else if ((accepted = _gui_input_resize(p_event))) { + //printf("Resize\n"); + } else if ((accepted = _gui_input_rotate(p_event))) { + //printf("Rotate\n"); + } else if ((accepted = _gui_input_move(p_event))) { + //printf("Move\n"); + } else if ((accepted = _gui_input_select(p_event))) { + //printf("Selection\n"); + } else { + //printf("Not accepted\n"); + } } + accepted = (_gui_input_zoom_or_pan(p_event) || accepted); + if (accepted) accept_event(); @@ -2259,8 +2275,6 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { case DRAG_MOVE: c = CURSOR_MOVE; break; - case DRAG_PAN: - c = CURSOR_DRAG; default: break; } @@ -2597,6 +2611,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_TOP_LEFT: case DRAG_BOTTOM_LEFT: _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(node_pos_in_parent[0], Math::lerp(node_pos_in_parent[1], node_pos_in_parent[3], ratio)); end = start - Vector2(control->get_margin(MARGIN_LEFT), 0); @@ -2611,6 +2626,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_TOP_RIGHT: case DRAG_BOTTOM_RIGHT: _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(node_pos_in_parent[2], Math::lerp(node_pos_in_parent[3], node_pos_in_parent[1], ratio)); end = start - Vector2(control->get_margin(MARGIN_RIGHT), 0); @@ -2625,6 +2641,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_TOP_LEFT: case DRAG_TOP_RIGHT: _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2)) + Vector2(5, 0), MARGIN_RIGHT); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(Math::lerp(node_pos_in_parent[0], node_pos_in_parent[2], ratio), node_pos_in_parent[1]); end = start - Vector2(0, control->get_margin(MARGIN_TOP)); @@ -2639,6 +2656,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_BOTTOM_LEFT: case DRAG_BOTTOM_RIGHT: _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2) + Vector2(5, 0)), MARGIN_RIGHT); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(Math::lerp(node_pos_in_parent[2], node_pos_in_parent[0], ratio), node_pos_in_parent[3]); end = start - Vector2(0, control->get_margin(MARGIN_BOTTOM)); @@ -3380,6 +3398,7 @@ void CanvasItemEditor::_notification(int p_what) { key_rot_button->set_icon(get_icon("KeyRotation", "EditorIcons")); key_scale_button->set_icon(get_icon("KeyScale", "EditorIcons")); key_insert_button->set_icon(get_icon("Key", "EditorIcons")); + key_auto_insert_button->set_icon(get_icon("AutoKey", "EditorIcons")); zoom_minus->set_icon(get_icon("ZoomLess", "EditorIcons")); zoom_reset->set_icon(get_icon("ZoomReset", "EditorIcons")); @@ -3712,6 +3731,77 @@ void CanvasItemEditor::_button_tool_select(int p_index) { tool = (Tool)p_index; } +void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing) { + + Map<Node *, Object *> &selection = editor_selection->get_selection(); + + for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { + + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key()); + if (!canvas_item || !canvas_item->is_visible_in_tree()) + continue; + + if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) + continue; + + if (Object::cast_to<Node2D>(canvas_item)) { + Node2D *n2d = Object::cast_to<Node2D>(canvas_item); + + if (key_pos && p_location) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing); + if (key_rot && p_rotation) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation_degrees", Math::rad2deg(n2d->get_rotation()), p_on_existing); + if (key_scale && p_scale) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing); + + if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) { + //look for an IK chain + List<Node2D *> ik_chain; + + Node2D *n = Object::cast_to<Node2D>(n2d->get_parent_item()); + bool has_chain = false; + + while (n) { + + ik_chain.push_back(n); + if (n->has_meta("_edit_ik_")) { + has_chain = true; + break; + } + + if (!n->get_parent_item()) + break; + n = Object::cast_to<Node2D>(n->get_parent_item()); + } + + if (has_chain && ik_chain.size()) { + + for (List<Node2D *>::Element *F = ik_chain.front(); F; F = F->next()) { + + if (key_pos) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "position", F->get()->get_position(), p_on_existing); + if (key_rot) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "rotation_degrees", Math::rad2deg(F->get()->get_rotation()), p_on_existing); + if (key_scale) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "scale", F->get()->get_scale(), p_on_existing); + } + } + } + + } else if (Object::cast_to<Control>(canvas_item)) { + + Control *ctrl = Object::cast_to<Control>(canvas_item); + + if (key_pos) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing); + if (key_rot) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation_degrees(), p_on_existing); + if (key_scale) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing); + } + } +} + void CanvasItemEditor::_popup_callback(int p_op) { last_option = MenuOption(p_op); @@ -3979,73 +4069,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { bool existing = p_op == ANIM_INSERT_KEY_EXISTING; - Map<Node *, Object *> &selection = editor_selection->get_selection(); - - for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { - - CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key()); - if (!canvas_item || !canvas_item->is_visible_in_tree()) - continue; - - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - - if (Object::cast_to<Node2D>(canvas_item)) { - Node2D *n2d = Object::cast_to<Node2D>(canvas_item); - - if (key_pos) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), existing); - if (key_rot) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation_degrees", Math::rad2deg(n2d->get_rotation()), existing); - if (key_scale) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), existing); - - if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) { - //look for an IK chain - List<Node2D *> ik_chain; - - Node2D *n = Object::cast_to<Node2D>(n2d->get_parent_item()); - bool has_chain = false; - - while (n) { - - ik_chain.push_back(n); - if (n->has_meta("_edit_ik_")) { - has_chain = true; - break; - } - - if (!n->get_parent_item()) - break; - n = Object::cast_to<Node2D>(n->get_parent_item()); - } - - if (has_chain && ik_chain.size()) { - - for (List<Node2D *>::Element *F = ik_chain.front(); F; F = F->next()) { - - if (key_pos) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "position", F->get()->get_position(), existing); - if (key_rot) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "rotation_degrees", Math::rad2deg(F->get()->get_rotation()), existing); - if (key_scale) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "scale", F->get()->get_scale(), existing); - } - } - } - - } else if (Object::cast_to<Control>(canvas_item)) { - - Control *ctrl = Object::cast_to<Control>(canvas_item); - - if (key_pos) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), existing); - if (key_rot) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation_degrees(), existing); - if (key_scale) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), existing); - } - } + _insert_animation_keys(true, true, true, existing); } break; case ANIM_INSERT_POS: { @@ -4151,6 +4175,14 @@ void CanvasItemEditor::_popup_callback(int p_op) { _focus_selection(p_op); } break; + case PREVIEW_CANVAS_SCALE: { + + bool preview = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(PREVIEW_CANVAS_SCALE)); + preview = !preview; + VS::get_singleton()->canvas_set_disable_scale(!preview); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(PREVIEW_CANVAS_SCALE), preview); + + } break; case SKELETON_MAKE_BONES: { Map<Node *, Object *> &selection = editor_selection->get_selection(); @@ -4169,6 +4201,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (!skeleton_show_bones) skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); } + _queue_update_bone_list(); viewport->update(); } break; @@ -4188,6 +4221,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (!skeleton_show_bones) skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); } + _queue_update_bone_list(); viewport->update(); } break; @@ -4576,6 +4610,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { drag_to = Vector2(); dragged_guide_pos = Point2(); dragged_guide_index = -1; + panning = false; bone_last_frame = 0; @@ -4824,6 +4859,8 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p->add_separator(); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/center_selection", TTR("Center Selection"), KEY_F), VIEW_CENTER_TO_SELECTION); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/frame_selection", TTR("Frame Selection"), KEY_MASK_SHIFT | KEY_F), VIEW_FRAME_TO_SELECTION); + p->add_separator(); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/preview_canvas_scale", TTR("Preview Canvas Scale"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_P), PREVIEW_CANVAS_SCALE); presets_menu = memnew(MenuButton); presets_menu->set_text(TTR("Layout")); @@ -4849,6 +4886,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { key_loc_button->set_pressed(true); key_loc_button->set_focus_mode(FOCUS_NONE); key_loc_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_POS)); + key_loc_button->set_tooltip(TTR("Translation mask for inserting keys.")); animation_hb->add_child(key_loc_button); key_rot_button = memnew(Button); key_rot_button->set_toggle_mode(true); @@ -4856,21 +4894,30 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { key_rot_button->set_pressed(true); key_rot_button->set_focus_mode(FOCUS_NONE); key_rot_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_ROT)); + key_rot_button->set_tooltip(TTR("Rotation mask for inserting keys.")); animation_hb->add_child(key_rot_button); key_scale_button = memnew(Button); key_scale_button->set_toggle_mode(true); key_scale_button->set_flat(true); key_scale_button->set_focus_mode(FOCUS_NONE); key_scale_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_SCALE)); + key_scale_button->set_tooltip(TTR("Scale mask for inserting keys.")); animation_hb->add_child(key_scale_button); key_insert_button = memnew(Button); key_insert_button->set_flat(true); key_insert_button->set_focus_mode(FOCUS_NONE); key_insert_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_KEY)); - key_insert_button->set_tooltip(TTR("Insert keys.")); + key_insert_button->set_tooltip(TTR("Insert keys (based on mask).")); key_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_insert_key", TTR("Insert Key"), KEY_INSERT)); - animation_hb->add_child(key_insert_button); + key_auto_insert_button = memnew(Button); + key_auto_insert_button->set_flat(true); + key_auto_insert_button->set_toggle_mode(true); + key_auto_insert_button->set_focus_mode(FOCUS_NONE); + //key_auto_insert_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_KEY)); + key_auto_insert_button->set_tooltip(TTR("Auto insert keys when objects are translated, rotated on scaled (based on mask).\nKeys are only added to existing tracks, no new tracks will be created.\nKeys must be inserted manually for the first time.")); + key_auto_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_auto_insert_key", TTR("Auto Insert Key"))); + animation_hb->add_child(key_auto_insert_button); animation_menu = memnew(MenuButton); animation_menu->set_text(TTR("Animation")); @@ -5421,6 +5468,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte label_desc->add_constant_override("line_spacing", 0); label_desc->hide(); editor->get_gui_base()->add_child(label_desc); + VS::get_singleton()->canvas_set_disable_scale(true); } CanvasItemEditorViewport::~CanvasItemEditorViewport() { diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 7b6563944e..14ea81f302 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -171,6 +171,7 @@ private: ANIM_CLEAR_POSE, VIEW_CENTER_TO_SELECTION, VIEW_FRAME_TO_SELECTION, + PREVIEW_CANVAS_SCALE, SKELETON_MAKE_BONES, SKELETON_CLEAR_BONES, SKELETON_SHOW_BONES, @@ -204,8 +205,7 @@ private: DRAG_V_GUIDE, DRAG_H_GUIDE, DRAG_DOUBLE_GUIDE, - DRAG_KEY_MOVE, - DRAG_PAN + DRAG_KEY_MOVE }; EditorSelection *editor_selection; @@ -261,6 +261,7 @@ private: bool key_pos; bool key_rot; bool key_scale; + bool panning; MenuOption last_option; @@ -350,6 +351,7 @@ private: Button *key_rot_button; Button *key_scale_button; Button *key_insert_button; + Button *key_auto_insert_button; PopupMenu *selection_menu; @@ -421,6 +423,8 @@ private: Object *_get_editor_data(Object *p_what); + void _insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing); + void _keying_changed(); void _unhandled_key_input(const Ref<InputEvent> &p_ev); diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index 10023d88bf..c8561d22a4 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -93,7 +93,7 @@ Variant CollisionShape2DEditor::get_handle_value(int idx) const { case RECTANGLE_SHAPE: { Ref<RectangleShape2D> rect = node->get_shape(); - if (idx < 2) { + if (idx < 3) { return rect->get_extents().abs(); } @@ -175,12 +175,15 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) { } break; case RECTANGLE_SHAPE: { - if (idx < 2) { + if (idx < 3) { Ref<RectangleShape2D> rect = node->get_shape(); Vector2 extents = rect->get_extents(); - extents[idx] = p_point[idx]; - + if (idx == 2) { + extents = p_point; + } else { + extents[idx] = p_point[idx]; + } rect->set_extents(extents.abs()); canvas_item_editor->update_viewport(); @@ -496,13 +499,15 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla case RECTANGLE_SHAPE: { Ref<RectangleShape2D> shape = node->get_shape(); - handles.resize(2); + handles.resize(3); Vector2 ext = shape->get_extents(); handles.write[0] = Point2(ext.x, 0); handles.write[1] = Point2(0, -ext.y); + handles.write[2] = Point2(ext.x, -ext.y); p_overlay->draw_texture(h, gt.xform(handles[0]) - size); p_overlay->draw_texture(h, gt.xform(handles[1]) - size); + p_overlay->draw_texture(h, gt.xform(handles[2]) - size); } break; diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h index 679235e316..701632e576 100644 --- a/editor/plugins/item_list_editor_plugin.h +++ b/editor/plugins/item_list_editor_plugin.h @@ -157,7 +157,7 @@ public: virtual void set_item_enabled(int p_idx, int p_enabled) { pp->set_item_disabled(p_idx, !p_enabled); } virtual bool is_item_enabled(int p_idx) const { return !pp->is_item_disabled(p_idx); } - virtual void set_item_id(int p_idx, int p_id) { pp->set_item_id(p_idx, p_idx); } + virtual void set_item_id(int p_idx, int p_id) { pp->set_item_id(p_idx, p_id); } virtual int get_item_id(int p_idx) const { return pp->get_item_id(p_idx); } virtual void set_item_separator(int p_idx, bool p_separator) { pp->set_item_as_separator(p_idx, p_separator); } diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp index 3e10cdbbfa..cf111dc4ce 100644 --- a/editor/plugins/mesh_instance_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_editor_plugin.cpp @@ -95,10 +95,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { return; } - if (trimesh_shape) - ur->create_action(TTR("Create Static Trimesh Body")); - else - ur->create_action(TTR("Create Static Convex Body")); + ur->create_action(TTR("Create Static Trimesh Body")); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { @@ -132,8 +129,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { } break; - case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE: - case MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE: { + case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE: { if (node == get_tree()->get_edited_scene_root()) { err_dialog->set_text(TTR("This doesn't work on scene root!")); @@ -141,9 +137,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { return; } - bool trimesh_shape = (p_option == MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE); - - Ref<Shape> shape = trimesh_shape ? mesh->create_trimesh_shape() : mesh->create_convex_shape(); + Ref<Shape> shape = mesh->create_trimesh_shape(); if (shape.is_null()) return; @@ -154,10 +148,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - if (trimesh_shape) - ur->create_action(TTR("Create Trimesh Shape")); - else - ur->create_action(TTR("Create Convex Shape")); + ur->create_action(TTR("Create Trimesh Static Shape")); ur->add_do_method(node->get_parent(), "add_child", cshape); ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); @@ -165,6 +156,40 @@ void MeshInstanceEditor::_menu_option(int p_option) { ur->add_do_reference(cshape); ur->add_undo_method(node->get_parent(), "remove_child", cshape); ur->commit_action(); + } break; + case MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE: { + + if (node == get_tree()->get_edited_scene_root()) { + err_dialog->set_text(TTR("This doesn't work on scene root!")); + err_dialog->popup_centered_minsize(); + return; + } + + Vector<Ref<Shape> > shapes = mesh->convex_decompose(); + + if (!shapes.size()) { + err_dialog->set_text(TTR("Failed creating shapes!")); + err_dialog->popup_centered_minsize(); + return; + } + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action(TTR("Create Convex Shape(s)")); + + for (int i = 0; i < shapes.size(); i++) { + + CollisionShape *cshape = memnew(CollisionShape); + cshape->set_shape(shapes[i]); + + Node *owner = node->get_owner(); + + ur->add_do_method(node->get_parent(), "add_child", cshape); + ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); + ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_reference(cshape); + ur->add_undo_method(node->get_parent(), "remove_child", cshape); + } + ur->commit_action(); } break; @@ -393,10 +418,9 @@ MeshInstanceEditor::MeshInstanceEditor() { options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MeshInstance", "EditorIcons")); options->get_popup()->add_item(TTR("Create Trimesh Static Body"), MENU_OPTION_CREATE_STATIC_TRIMESH_BODY); - options->get_popup()->add_item(TTR("Create Convex Static Body"), MENU_OPTION_CREATE_STATIC_CONVEX_BODY); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Create Trimesh Collision Sibling"), MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE); - options->get_popup()->add_item(TTR("Create Convex Collision Sibling"), MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE); + options->get_popup()->add_item(TTR("Create Convex Collision Sibling(s)"), MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH); options->get_popup()->add_separator(); diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index 89eb253afe..18586b2fe5 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -61,7 +61,8 @@ void MeshLibraryEditor::_menu_confirm() { _import_scene_cbk(existing); } break; - default: {}; + default: { + }; } } diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 0dbbaf4177..af43f679fd 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -905,7 +905,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y); } break; - default: {} + default: { + } } if (bone_painting) { diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 89e3327a3a..d7d4cec07d 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -849,8 +849,7 @@ void ScriptEditor::_file_dialog_action(String p_file) { } file->close(); memdelete(file); - - // fallthrough to open the file. + FALLTHROUGH; } case FILE_OPEN: { @@ -1736,7 +1735,7 @@ void ScriptEditor::_update_script_names() { String name = eh->get_class(); Ref<Texture> icon = get_icon("Help", "EditorIcons"); - String tooltip = name + TTR(" Class Reference"); + String tooltip = vformat(TTR("%s Class Reference"), name); _ScriptEditorItemData sd; sd.icon = icon; @@ -3084,13 +3083,13 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { site_search->set_text(TTR("Online Docs")); site_search->connect("pressed", this, "_menu_option", varray(SEARCH_WEBSITE)); menu_hb->add_child(site_search); - site_search->set_tooltip(TTR("Open Godot online documentation")); + site_search->set_tooltip(TTR("Open Godot online documentation.")); request_docs = memnew(ToolButton); request_docs->set_text(TTR("Request Docs")); request_docs->connect("pressed", this, "_menu_option", varray(REQUEST_DOCS)); menu_hb->add_child(request_docs); - request_docs->set_tooltip(TTR("Help improve the Godot documentation by giving feedback")); + request_docs->set_tooltip(TTR("Help improve the Godot documentation by giving feedback.")); help_search = memnew(ToolButton); help_search->set_text(TTR("Search Help")); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 8c3f8fd3e8..c586985957 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -81,6 +81,8 @@ void ScriptTextEditor::set_edited_resource(const RES &p_res) { emit_signal("name_changed"); code_editor->update_line_and_column(); + + _validate_script(); } void ScriptTextEditor::_update_member_keywords() { @@ -286,7 +288,7 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) { code_editor->get_text_edit()->cursor_set_line(p_line.operator int64_t()); } else if (p_line.get_type() == Variant::DICTIONARY) { Dictionary meta = p_line.operator Dictionary(); - code_editor->get_text_edit()->insert_at("#warning-ignore:" + meta["code"].operator String(), meta["line"].operator int64_t() - 1); + code_editor->get_text_edit()->insert_at("# warning-ignore:" + meta["code"].operator String(), meta["line"].operator int64_t() - 1); _validate_script(); } } @@ -910,7 +912,7 @@ void ScriptTextEditor::_edit_option(int p_op) { tx->set_line_as_breakpoint(line, dobreak); ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak); } - } + } break; case DEBUG_GOTO_NEXT_BREAKPOINT: { List<int> bpoints; diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index e3b51858ec..ba297539d3 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -1637,7 +1637,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { surface->update(); } break; - default: {} + default: { + } } } @@ -1704,7 +1705,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } break; - default: {} + default: { + } } } @@ -1760,7 +1762,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } break; - default: {} + default: { + } } } @@ -3010,7 +3013,7 @@ void SpatialEditorViewport::set_state(const Dictionary &p_state) { previewing = Object::cast_to<Camera>(pv); previewing->connect("tree_exiting", this, "_preview_exited_scene"); VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), previewing->get_camera()); //replace - view_menu->hide(); + view_menu->set_disabled(true); surface->update(); preview_camera->set_pressed(true); preview_camera->show(); diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp index 3854d27567..fbc72b1396 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -31,7 +31,10 @@ #include "sprite_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "scene/2d/collision_polygon_2d.h" +#include "scene/2d/light_occluder_2d.h" #include "scene/2d/mesh_instance_2d.h" +#include "scene/2d/polygon_2d.h" #include "scene/gui/box_container.h" #include "thirdparty/misc/clipper.hpp" @@ -116,8 +119,42 @@ void SpriteEditor::_menu_option(int p_option) { return; } + selected_menu_item = (Menu)p_option; + switch (p_option) { - case MENU_OPTION_CREATE_MESH_2D: { + case MENU_OPTION_CONVERT_TO_MESH_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create Mesh2D")); + debug_uv_dialog->set_title("Mesh2D Preview"); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + + } break; + case MENU_OPTION_CONVERT_TO_POLYGON_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create Polygon2D")); + debug_uv_dialog->set_title("Polygon2D Preview"); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + } break; + case MENU_OPTION_CREATE_COLLISION_POLY_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create CollisionPolygon2D")); + debug_uv_dialog->set_title("CollisionPolygon2D Preview"); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + + } break; + case MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create LightOccluder2D")); + debug_uv_dialog->set_title("LightOccluder2D Preview"); _update_mesh_data(); debug_uv_dialog->popup_centered(); @@ -169,47 +206,107 @@ void SpriteEditor::_update_mesh_data() { computed_indices.clear(); Size2 img_size = Vector2(image->get_width(), image->get_height()); - for (int j = 0; j < lines.size(); j++) { - lines.write[j] = expand(lines[j], rect, epsilon); + for (int i = 0; i < lines.size(); i++) { + lines.write[i] = expand(lines[i], rect, epsilon); + } + + if (selected_menu_item == MENU_OPTION_CONVERT_TO_MESH_2D) { + + for (int j = 0; j < lines.size(); j++) { + int index_ofs = computed_vertices.size(); + + for (int i = 0; i < lines[j].size(); i++) { + Vector2 vtx = lines[j][i]; + computed_uv.push_back(vtx / img_size); - int index_ofs = computed_vertices.size(); + vtx -= rect.position; //offset by rect position - for (int i = 0; i < lines[j].size(); i++) { - Vector2 vtx = lines[j][i]; - computed_uv.push_back(vtx / img_size); + //flip if flipped + if (node->is_flipped_h()) + vtx.x = rect.size.x - vtx.x - 1.0; + if (node->is_flipped_v()) + vtx.y = rect.size.y - vtx.y - 1.0; - vtx -= rect.position; //offset by rect position + if (node->is_centered()) + vtx -= rect.size / 2.0; - //flip if flipped - if (node->is_flipped_h()) - vtx.x = rect.size.x - vtx.x - 1.0; - if (node->is_flipped_v()) - vtx.y = rect.size.y - vtx.y - 1.0; + computed_vertices.push_back(vtx); + } + + Vector<int> poly = Geometry::triangulate_polygon(lines[j]); - if (node->is_centered()) - vtx -= rect.size / 2.0; + for (int i = 0; i < poly.size(); i += 3) { + for (int k = 0; k < 3; k++) { + int idx = i + k; + int idxn = i + (k + 1) % 3; + uv_lines.push_back(lines[j][poly[idx]]); + uv_lines.push_back(lines[j][poly[idxn]]); - computed_vertices.push_back(vtx); + computed_indices.push_back(poly[idx] + index_ofs); + } + } } + } - Vector<int> poly = Geometry::triangulate_polygon(lines[j]); + outline_lines.clear(); + computed_outline_lines.clear(); - for (int i = 0; i < poly.size(); i += 3) { - for (int k = 0; k < 3; k++) { - int idx = i + k; - int idxn = i + (k + 1) % 3; - uv_lines.push_back(lines[j][poly[idx]]); - uv_lines.push_back(lines[j][poly[idxn]]); + if (selected_menu_item == MENU_OPTION_CONVERT_TO_POLYGON_2D || selected_menu_item == MENU_OPTION_CREATE_COLLISION_POLY_2D || selected_menu_item == MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D) { + outline_lines.resize(lines.size()); + computed_outline_lines.resize(lines.size()); + for (int pi = 0; pi < lines.size(); pi++) { + + Vector<Vector2> ol; + Vector<Vector2> col; + + ol.resize(lines[pi].size()); + col.resize(lines[pi].size()); + + for (int i = 0; i < lines[pi].size(); i++) { + Vector2 vtx = lines[pi][i]; + + ol.write[i] = vtx; + + vtx -= rect.position; //offset by rect position + + //flip if flipped + if (node->is_flipped_h()) + vtx.x = rect.size.x - vtx.x - 1.0; + if (node->is_flipped_v()) + vtx.y = rect.size.y - vtx.y - 1.0; + + if (node->is_centered()) + vtx -= rect.size / 2.0; - computed_indices.push_back(poly[idx] + index_ofs); + col.write[i] = vtx; } + + outline_lines.write[pi] = ol; + computed_outline_lines.write[pi] = col; } } debug_uv->update(); } -void SpriteEditor::_create_mesh_node() { +void SpriteEditor::_create_node() { + switch (selected_menu_item) { + case MENU_OPTION_CONVERT_TO_MESH_2D: { + _convert_to_mesh_2d_node(); + } break; + case MENU_OPTION_CONVERT_TO_POLYGON_2D: { + _convert_to_polygon_2d_node(); + } break; + case MENU_OPTION_CREATE_COLLISION_POLY_2D: { + _create_collision_polygon_2d_node(); + } break; + case MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D: { + _create_light_occluder_2d_node(); + } break; + } +} + +void SpriteEditor::_convert_to_mesh_2d_node() { if (computed_vertices.size() < 3) { err_dialog->set_text(TTR("Invalid geometry, can't replace by mesh.")); @@ -233,6 +330,117 @@ void SpriteEditor::_create_mesh_node() { EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, mesh_instance); } +void SpriteEditor::_convert_to_polygon_2d_node() { + Polygon2D *polygon_2d_instance = memnew(Polygon2D); + + int total_point_count = 0; + for (int i = 0; i < computed_outline_lines.size(); i++) + total_point_count += computed_outline_lines[i].size(); + + PoolVector2Array polygon; + polygon.resize(total_point_count); + PoolVector2Array::Write polygon_write = polygon.write(); + + PoolVector2Array uvs; + uvs.resize(total_point_count); + PoolVector2Array::Write uvs_write = uvs.write(); + + int current_point_index = 0; + + Array polys; + polys.resize(computed_outline_lines.size()); + + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector<Vector2> outline = computed_outline_lines[i]; + Vector<Vector2> uv_outline = outline_lines[i]; + + if (outline.size() < 3) { + err_dialog->set_text(TTR("Invalid geometry, can't create polygon.")); + err_dialog->popup_centered_minsize(); + return; + } + + PoolIntArray pia; + pia.resize(outline.size()); + PoolIntArray::Write pia_write = pia.write(); + + for (int pi = 0; pi < outline.size(); pi++) { + polygon_write[current_point_index] = outline[pi]; + uvs_write[current_point_index] = uv_outline[pi]; + pia_write[pi] = current_point_index; + current_point_index++; + } + + polys[i] = pia; + } + + polygon_2d_instance->set_uv(uvs); + polygon_2d_instance->set_polygon(polygon); + polygon_2d_instance->set_polygons(polys); + + EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, polygon_2d_instance); +} + +void SpriteEditor::_create_collision_polygon_2d_node() { + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector<Vector2> outline = computed_outline_lines[i]; + + if (outline.size() < 3) { + err_dialog->set_text(TTR("Invalid geometry, can't create collision polygon.")); + err_dialog->popup_centered_minsize(); + continue; + } + + CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D); + collision_polygon_2d_instance->set_polygon(outline); + + _add_as_sibling_or_child(node, collision_polygon_2d_instance); + } +} + +void SpriteEditor::_create_light_occluder_2d_node() { + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector<Vector2> outline = computed_outline_lines[i]; + + if (outline.size() < 3) { + err_dialog->set_text(TTR("Invalid geometry, can't create light occluder.")); + err_dialog->popup_centered_minsize(); + continue; + } + + Ref<OccluderPolygon2D> polygon; + polygon.instance(); + + PoolVector2Array a; + a.resize(outline.size()); + PoolVector2Array::Write aw = a.write(); + for (int io = 0; io < outline.size(); io++) { + aw[io] = outline[io]; + } + polygon->set_polygon(a); + + LightOccluder2D *light_occluder_2d_instance = memnew(LightOccluder2D); + light_occluder_2d_instance->set_occluder_polygon(polygon); + + _add_as_sibling_or_child(node, light_occluder_2d_instance); + } +} + +void SpriteEditor::_add_as_sibling_or_child(Node2D *p_own_node, Node2D *p_new_node) { + // Can't make sibling if own node is scene root + if (p_own_node != this->get_tree()->get_edited_scene_root()) { + p_own_node->get_parent()->add_child(p_new_node, true); + p_new_node->set_transform(p_own_node->get_transform()); + } else { + p_own_node->add_child(p_new_node, true); + } + + p_new_node->set_owner(this->get_tree()->get_edited_scene_root()); +} + #if 0 void SpriteEditor::_create_uv_lines() { @@ -298,16 +506,26 @@ void SpriteEditor::_create_uv_lines() { #endif void SpriteEditor::_debug_uv_draw() { - if (uv_lines.size() == 0) - return; - Ref<Texture> tex = node->get_texture(); ERR_FAIL_COND(!tex.is_valid()); debug_uv->set_clip_contents(true); debug_uv->draw_texture(tex, Point2()); debug_uv->set_custom_minimum_size(tex->get_size()); //debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size()); - debug_uv->draw_multiline(uv_lines, Color(1.0, 0.8, 0.7)); + + Color color = Color(1.0, 0.8, 0.7); + + if (selected_menu_item == MENU_OPTION_CONVERT_TO_MESH_2D && uv_lines.size() > 0) { + debug_uv->draw_multiline(uv_lines, color); + + } else if ((selected_menu_item == MENU_OPTION_CONVERT_TO_POLYGON_2D || selected_menu_item == MENU_OPTION_CREATE_COLLISION_POLY_2D || selected_menu_item == MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D) && outline_lines.size() > 0) { + for (int i = 0; i < outline_lines.size(); i++) { + Vector<Vector2> outline = outline_lines[i]; + + debug_uv->draw_polyline(outline, color); + debug_uv->draw_line(outline[0], outline[outline.size() - 1], color); + } + } } void SpriteEditor::_bind_methods() { @@ -315,7 +533,7 @@ void SpriteEditor::_bind_methods() { ClassDB::bind_method("_menu_option", &SpriteEditor::_menu_option); ClassDB::bind_method("_debug_uv_draw", &SpriteEditor::_debug_uv_draw); ClassDB::bind_method("_update_mesh_data", &SpriteEditor::_update_mesh_data); - ClassDB::bind_method("_create_mesh_node", &SpriteEditor::_create_mesh_node); + ClassDB::bind_method("_create_node", &SpriteEditor::_create_node); } SpriteEditor::SpriteEditor() { @@ -327,7 +545,10 @@ SpriteEditor::SpriteEditor() { options->set_text(TTR("Sprite")); options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Sprite", "EditorIcons")); - options->get_popup()->add_item(TTR("Convert to 2D Mesh"), MENU_OPTION_CREATE_MESH_2D); + options->get_popup()->add_item(TTR("Convert to Mesh2D"), MENU_OPTION_CONVERT_TO_MESH_2D); + options->get_popup()->add_item(TTR("Convert to Polygon2D"), MENU_OPTION_CONVERT_TO_POLYGON_2D); + options->get_popup()->add_item(TTR("Create CollisionPolygon2D Sibling"), MENU_OPTION_CREATE_COLLISION_POLY_2D); + options->get_popup()->add_item(TTR("Create LightOccluder2D Sibling"), MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D); options->get_popup()->connect("id_pressed", this, "_menu_option"); @@ -335,7 +556,7 @@ SpriteEditor::SpriteEditor() { add_child(err_dialog); debug_uv_dialog = memnew(ConfirmationDialog); - debug_uv_dialog->get_ok()->set_text(TTR("Create 2D Mesh")); + debug_uv_dialog->get_ok()->set_text(TTR("Create Mesh2D")); debug_uv_dialog->set_title("Mesh 2D Preview"); VBoxContainer *vb = memnew(VBoxContainer); debug_uv_dialog->add_child(vb); @@ -347,7 +568,7 @@ SpriteEditor::SpriteEditor() { debug_uv = memnew(Control); debug_uv->connect("draw", this, "_debug_uv_draw"); scroll->add_child(debug_uv); - debug_uv_dialog->connect("confirmed", this, "_create_mesh_node"); + debug_uv_dialog->connect("confirmed", this, "_create_node"); HBoxContainer *hb = memnew(HBoxContainer); hb->add_child(memnew(Label(TTR("Simplification: ")))); diff --git a/editor/plugins/sprite_editor_plugin.h b/editor/plugins/sprite_editor_plugin.h index 7250e3df59..460f5a5707 100644 --- a/editor/plugins/sprite_editor_plugin.h +++ b/editor/plugins/sprite_editor_plugin.h @@ -41,9 +41,14 @@ class SpriteEditor : public Control { GDCLASS(SpriteEditor, Control); enum Menu { - MENU_OPTION_CREATE_MESH_2D, + MENU_OPTION_CONVERT_TO_MESH_2D, + MENU_OPTION_CONVERT_TO_POLYGON_2D, + MENU_OPTION_CREATE_COLLISION_POLY_2D, + MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D }; + Menu selected_menu_item; + Sprite *node; MenuButton *options; @@ -55,7 +60,8 @@ class SpriteEditor : public Control { ConfirmationDialog *debug_uv_dialog; Control *debug_uv; Vector<Vector2> uv_lines; - + Vector<Vector<Vector2> > outline_lines; + Vector<Vector<Vector2> > computed_outline_lines; Vector<Vector2> computed_vertices; Vector<Vector2> computed_uv; Vector<int> computed_indices; @@ -71,7 +77,14 @@ class SpriteEditor : public Control { void _debug_uv_draw(); void _update_mesh_data(); - void _create_mesh_node(); + + void _create_node(); + void _convert_to_mesh_2d_node(); + void _convert_to_polygon_2d_node(); + void _create_collision_polygon_2d_node(); + void _create_light_occluder_2d_node(); + + void _add_as_sibling_or_child(Node2D *p_own_node, Node2D *p_new_node); protected: void _node_removed(Node *p_node); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 89bb7440fe..33b8347f94 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -38,11 +38,167 @@ void SpriteFramesEditor::_gui_input(Ref<InputEvent> p_event) { } +void SpriteFramesEditor::_open_sprite_sheet() { + + file_split_sheet->clear_filters(); + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Texture", &extensions); + for (int i = 0; i < extensions.size(); i++) { + file_split_sheet->add_filter("*." + extensions[i]); + } + + file_split_sheet->popup_centered_ratio(); +} + +void SpriteFramesEditor::_sheet_preview_draw() { + Size2i size = split_sheet_preview->get_size(); + int h = split_sheet_h->get_value(); + int v = split_sheet_v->get_value(); + const float a = 0.3; + for (int i = 1; i < h; i++) { + for (int j = 1; j < v; j++) { + + int x = i * size.width / h; + int y = i * size.height / v; + + split_sheet_preview->draw_line(Point2(x, 0), Point2(x, size.height), Color(1, 1, 1, a)); + split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, size.height), Color(0, 0, 0, a)); + + split_sheet_preview->draw_line(Point2(0, y), Point2(size.width, y), Color(1, 1, 1, a)); + split_sheet_preview->draw_line(Point2(0, y + 1), Point2(size.width, y + 1), Color(0, 0, 0, a)); + } + } + + Color accent = get_color("accent_color", "Editor"); + + for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { + int idx = E->get(); + int x = (idx % h) * size.width / h; + int y = (idx / v) * size.height / v; + int width = size.width / h; + int height = size.height / v; + + split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 0.35), true); + split_sheet_preview->draw_rect(Rect2(x + 0, y + 0, width - 0, height - 0), Color(0, 0, 0, 1), false); + split_sheet_preview->draw_rect(Rect2(x + 1, y + 1, width - 2, height - 2), Color(0, 0, 0, 1), false); + split_sheet_preview->draw_rect(Rect2(x + 2, y + 2, width - 4, height - 4), accent, false); + split_sheet_preview->draw_rect(Rect2(x + 3, y + 3, width - 6, height - 6), accent, false); + split_sheet_preview->draw_rect(Rect2(x + 4, y + 4, width - 8, height - 8), Color(0, 0, 0, 1), false); + split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 1), false); + } + + if (frames_selected.size() == 0) { + split_sheet_dialog->get_ok()->set_disabled(true); + split_sheet_dialog->get_ok()->set_text(TTR("No frames selected")); + } else { + split_sheet_dialog->get_ok()->set_disabled(false); + split_sheet_dialog->get_ok()->set_text(vformat(TTR("Add %d frame(s)"), frames_selected.size())); + } +} +void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) { + + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + Size2i size = split_sheet_preview->get_size(); + int h = split_sheet_h->get_value(); + int v = split_sheet_v->get_value(); + + int x = CLAMP(int(mb->get_position().x) * h / size.width, 0, h - 1); + int y = CLAMP(int(mb->get_position().y) * v / size.height, 0, v - 1); + + int idx = h * y + x; + + if (mb->get_shift() && last_frame_selected >= 0) { + //select multiple + int from = idx; + int to = last_frame_selected; + if (from > to) { + SWAP(from, to); + } + + for (int i = from; i <= to; i++) { + if (mb->get_control()) { + frames_selected.erase(i); + } else { + frames_selected.insert(i); + } + } + } else { + if (frames_selected.has(idx)) { + frames_selected.erase(idx); + } else { + frames_selected.insert(idx); + } + } + + last_frame_selected = idx; + split_sheet_preview->update(); + } +} + +void SpriteFramesEditor::_sheet_add_frames() { + + Size2i size = split_sheet_preview->get_size(); + int h = split_sheet_h->get_value(); + int v = split_sheet_v->get_value(); + + undo_redo->create_action(TTR("Add Frame")); + + int fc = frames->get_frame_count(edited_anim); + + for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { + int idx = E->get(); + int x = (idx % h) * size.width / h; + int y = (idx / v) * size.height / v; + int width = size.width / h; + int height = size.height / v; + + Ref<AtlasTexture> at; + at.instance(); + at->set_atlas(split_sheet_preview->get_texture()); + at->set_region(Rect2(x, y, width, height)); + + undo_redo->add_do_method(frames, "add_frame", edited_anim, at, -1); + undo_redo->add_undo_method(frames, "remove_frame", edited_anim, fc); + } + + undo_redo->add_do_method(this, "_update_library"); + undo_redo->add_undo_method(this, "_update_library"); + undo_redo->commit_action(); +} + +void SpriteFramesEditor::_sheet_spin_changed(double) { + frames_selected.clear(); + last_frame_selected = -1; + split_sheet_preview->update(); +} + +void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) { + + Ref<Resource> texture = ResourceLoader::load(p_file); + if (!texture.is_valid()) { + EditorNode::get_singleton()->show_warning("Unable to load images"); + ERR_FAIL_COND(!texture.is_valid()); + } + if (texture != split_sheet_preview->get_texture()) { + //different texture, reset to 4x4 + split_sheet_h->set_value(4); + split_sheet_v->set_value(4); + } + frames_selected.clear(); + last_frame_selected = -1; + + split_sheet_preview->set_texture(texture); + split_sheet_dialog->popup_centered_ratio(0.65); +} + void SpriteFramesEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { load->set_icon(get_icon("Load", "EditorIcons")); + load_sheet->set_icon(get_icon("SpriteSheet", "EditorIcons")); copy->set_icon(get_icon("ActionCopy", "EditorIcons")); paste->set_icon(get_icon("ActionPaste", "EditorIcons")); empty->set_icon(get_icon("InsertBefore", "EditorIcons")); @@ -72,6 +228,7 @@ void SpriteFramesEditor::_file_load_request(const PoolVector<String> &p_path, in if (resource.is_null()) { dialog->set_text(TTR("ERROR: Couldn't load frame resource!")); dialog->set_title(TTR("Error!")); + //dialog->get_cancel()->set_text("Close"); dialog->get_ok()->set_text(TTR("Close")); dialog->popup_centered_minsize(); @@ -347,9 +504,6 @@ void SpriteFramesEditor::_animation_name_edited() { for (List<Node *>::Element *E = nodes.front(); E; E = E->next()) { String current = E->get()->call("get_animation"); - if (current != edited_anim) - continue; - undo_redo->add_do_method(E->get(), "set_animation", name); undo_redo->add_undo_method(E->get(), "set_animation", edited_anim); } @@ -382,9 +536,6 @@ void SpriteFramesEditor::_animation_add() { for (List<Node *>::Element *E = nodes.front(); E; E = E->next()) { String current = E->get()->call("get_animation"); - if (frames->has_animation(current)) - continue; - undo_redo->add_do_method(E->get(), "set_animation", name); undo_redo->add_undo_method(E->get(), "set_animation", current); } @@ -661,6 +812,12 @@ void SpriteFramesEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &SpriteFramesEditor::get_drag_data_fw); ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &SpriteFramesEditor::can_drop_data_fw); ClassDB::bind_method(D_METHOD("drop_data_fw"), &SpriteFramesEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("_prepare_sprite_sheet"), &SpriteFramesEditor::_prepare_sprite_sheet); + ClassDB::bind_method(D_METHOD("_open_sprite_sheet"), &SpriteFramesEditor::_open_sprite_sheet); + ClassDB::bind_method(D_METHOD("_sheet_preview_draw"), &SpriteFramesEditor::_sheet_preview_draw); + ClassDB::bind_method(D_METHOD("_sheet_preview_input"), &SpriteFramesEditor::_sheet_preview_input); + ClassDB::bind_method(D_METHOD("_sheet_spin_changed"), &SpriteFramesEditor::_sheet_spin_changed); + ClassDB::bind_method(D_METHOD("_sheet_add_frames"), &SpriteFramesEditor::_sheet_add_frames); } SpriteFramesEditor::SpriteFramesEditor() { @@ -718,9 +875,15 @@ SpriteFramesEditor::SpriteFramesEditor() { sub_vb->add_child(hbc); load = memnew(ToolButton); - load->set_tooltip(TTR("Load Resource")); + load->set_tooltip(TTR("Add a Texture from File")); hbc->add_child(load); + load_sheet = memnew(ToolButton); + load_sheet->set_tooltip(TTR("Add frames from a Sprite Sheet")); + hbc->add_child(load_sheet); + + hbc->add_child(memnew(VSeparator)); + copy = memnew(ToolButton); copy->set_tooltip(TTR("Copy")); hbc->add_child(copy); @@ -729,6 +892,8 @@ SpriteFramesEditor::SpriteFramesEditor() { paste->set_tooltip(TTR("Paste")); hbc->add_child(paste); + hbc->add_spacer(false); + empty = memnew(ToolButton); empty->set_tooltip(TTR("Insert Empty (Before)")); hbc->add_child(empty); @@ -737,7 +902,7 @@ SpriteFramesEditor::SpriteFramesEditor() { empty2->set_tooltip(TTR("Insert Empty (After)")); hbc->add_child(empty2); - hbc->add_spacer(false); + hbc->add_child(memnew(VSeparator)); move_up = memnew(ToolButton); move_up->set_tooltip(TTR("Move (Before)")); @@ -772,6 +937,7 @@ SpriteFramesEditor::SpriteFramesEditor() { add_child(dialog); load->connect("pressed", this, "_load_pressed"); + load_sheet->connect("pressed", this, "_open_sprite_sheet"); _delete->connect("pressed", this, "_delete_pressed"); copy->connect("pressed", this, "_copy_pressed"); paste->connect("pressed", this, "_paste_pressed"); @@ -786,6 +952,60 @@ SpriteFramesEditor::SpriteFramesEditor() { updating = false; edited_anim = "default"; + + split_sheet_dialog = memnew(ConfirmationDialog); + add_child(split_sheet_dialog); + VBoxContainer *split_sheet_vb = memnew(VBoxContainer); + split_sheet_dialog->add_child(split_sheet_vb); + split_sheet_dialog->set_title(TTR("Select Frames")); + split_sheet_dialog->connect("confirmed", this, "_sheet_add_frames"); + + ScrollContainer *scroll = memnew(ScrollContainer); + split_sheet_preview = memnew(TextureRect); + split_sheet_preview->set_expand(false); + split_sheet_preview->set_mouse_filter(MOUSE_FILTER_PASS); + split_sheet_preview->connect("draw", this, "_sheet_preview_draw"); + split_sheet_preview->connect("gui_input", this, "_sheet_preview_input"); + + scroll->set_enable_h_scroll(true); + scroll->set_enable_v_scroll(true); + CenterContainer *cc = memnew(CenterContainer); + cc->add_child(split_sheet_preview); + cc->set_h_size_flags(SIZE_EXPAND_FILL); + cc->set_v_size_flags(SIZE_EXPAND_FILL); + scroll->add_child(cc); + + split_sheet_vb->add_margin_child(TTR("Base Image:"), scroll, true); + + HBoxContainer *split_sheet_hb = memnew(HBoxContainer); + split_sheet_hb->add_spacer(); + Label *ss_label = memnew(Label(TTR("Horizontal:"))); + split_sheet_hb->add_child(ss_label); + split_sheet_h = memnew(SpinBox); + split_sheet_h->set_min(1); + split_sheet_h->set_max(128); + split_sheet_h->set_step(1); + split_sheet_hb->add_child(split_sheet_h); + split_sheet_hb->add_spacer(); + split_sheet_h->connect("value_changed", this, "_sheet_spin_changed"); + + ss_label = memnew(Label(TTR("Vertical:"))); + split_sheet_hb->add_child(ss_label); + split_sheet_v = memnew(SpinBox); + split_sheet_v->set_min(1); + split_sheet_v->set_max(128); + split_sheet_v->set_step(1); + split_sheet_hb->add_child(split_sheet_v); + split_sheet_hb->add_spacer(); + split_sheet_v->connect("value_changed", this, "_sheet_spin_changed"); + + split_sheet_vb->add_margin_child(TTR("Split Settings:"), split_sheet_hb); + + file_split_sheet = memnew(EditorFileDialog); + file_split_sheet->set_title(TTR("Create frames from Sprite Sheet")); + file_split_sheet->set_mode(EditorFileDialog::MODE_OPEN_FILE); + add_child(file_split_sheet); + file_split_sheet->connect("file_selected", this, "_prepare_sprite_sheet"); } void SpriteFramesEditorPlugin::edit(Object *p_object) { diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index 55dd10074e..383e99f87e 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -37,6 +37,7 @@ #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" #include "scene/gui/split_container.h" +#include "scene/gui/texture_rect.h" #include "scene/gui/tree.h" class SpriteFramesEditor : public HSplitContainer { @@ -44,6 +45,7 @@ class SpriteFramesEditor : public HSplitContainer { GDCLASS(SpriteFramesEditor, HSplitContainer); ToolButton *load; + ToolButton *load_sheet; ToolButton *_delete; ToolButton *copy; ToolButton *paste; @@ -71,6 +73,14 @@ class SpriteFramesEditor : public HSplitContainer { StringName edited_anim; + ConfirmationDialog *split_sheet_dialog; + TextureRect *split_sheet_preview; + SpinBox *split_sheet_h; + SpinBox *split_sheet_v; + EditorFileDialog *file_split_sheet; + Set<int> frames_selected; + int last_frame_selected; + void _load_pressed(); void _load_scene_pressed(); void _file_load_request(const PoolVector<String> &p_path, int p_at_pos = -1); @@ -99,6 +109,13 @@ class SpriteFramesEditor : public HSplitContainer { bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + void _open_sprite_sheet(); + void _prepare_sprite_sheet(const String &p_file); + void _sheet_preview_draw(); + void _sheet_spin_changed(double); + void _sheet_preview_input(const Ref<InputEvent> &p_event); + void _sheet_add_frames(); + protected: void _notification(int p_what); void _gui_input(Ref<InputEvent> p_event); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 3eeb871380..f741040fa8 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -64,7 +64,7 @@ void TextureRegionEditor::_region_draw() { return; Transform2D mtx; - mtx.elements[2] = -draw_ofs; + mtx.elements[2] = -draw_ofs * draw_zoom; mtx.scale_basis(Vector2(draw_zoom, draw_zoom)); VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(), mtx); @@ -128,7 +128,7 @@ void TextureRegionEditor::_region_draw() { }; for (int i = 0; i < 4; i++) { int next = (i + 1) % 4; - edit_draw->draw_line(endpoints[i] - draw_ofs, endpoints[next] - draw_ofs, Color(0.3, 0.7, 1, 1), 2); + edit_draw->draw_line(endpoints[i] - draw_ofs * draw_zoom, endpoints[next] - draw_ofs * draw_zoom, Color(0.3, 0.7, 1, 1), 2); } } } @@ -153,16 +153,16 @@ void TextureRegionEditor::_region_draw() { Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized(); ofs *= 1.4144 * (select_handle->get_size().width / 2); - edit_draw->draw_line(endpoints[i] - draw_ofs, endpoints[next] - draw_ofs, color, 2); + edit_draw->draw_line(endpoints[i] - draw_ofs * draw_zoom, endpoints[next] - draw_ofs * draw_zoom, color, 2); if (snap_mode != SNAP_AUTOSLICE) - edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs); + edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs * draw_zoom); ofs = (endpoints[next] - endpoints[i]) / 2; ofs += (endpoints[next] - endpoints[i]).tangent().normalized() * (select_handle->get_size().width / 2); if (snap_mode != SNAP_AUTOSLICE) - edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs); + edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs * draw_zoom); scroll_rect.expand_to(endpoints[i]); } @@ -205,10 +205,10 @@ void TextureRegionEditor::_region_draw() { margins[3] = obj_styleBox->get_margin_size(MARGIN_RIGHT); } Vector2 pos[4] = { - mtx.basis_xform(Vector2(0, margins[0])) + Vector2(0, endpoints[0].y - draw_ofs.y), - -mtx.basis_xform(Vector2(0, margins[1])) + Vector2(0, endpoints[2].y - draw_ofs.y), - mtx.basis_xform(Vector2(margins[2], 0)) + Vector2(endpoints[0].x - draw_ofs.x, 0), - -mtx.basis_xform(Vector2(margins[3], 0)) + Vector2(endpoints[2].x - draw_ofs.x, 0) + mtx.basis_xform(Vector2(0, margins[0])) + Vector2(0, endpoints[0].y - draw_ofs.y * draw_zoom), + -mtx.basis_xform(Vector2(0, margins[1])) + Vector2(0, endpoints[2].y - draw_ofs.y * draw_zoom), + mtx.basis_xform(Vector2(margins[2], 0)) + Vector2(endpoints[0].x - draw_ofs.x * draw_zoom, 0), + -mtx.basis_xform(Vector2(margins[3], 0)) + Vector2(endpoints[2].x - draw_ofs.x * draw_zoom, 0) }; draw_margin_line(edit_draw, pos[0], pos[0] + Vector2(edit_draw->get_size().x, 0)); @@ -220,7 +220,7 @@ void TextureRegionEditor::_region_draw() { void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { Transform2D mtx; - mtx.elements[2] = -draw_ofs; + mtx.elements[2] = -draw_ofs * draw_zoom; mtx.scale_basis(Vector2(draw_zoom, draw_zoom)); Vector2 endpoints[8] = { @@ -255,10 +255,10 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { margins[3] = obj_styleBox->get_margin_size(MARGIN_RIGHT); } Vector2 pos[4] = { - mtx.basis_xform(rect.position + Vector2(0, margins[0])) - draw_ofs, - mtx.basis_xform(rect.position + rect.size - Vector2(0, margins[1])) - draw_ofs, - mtx.basis_xform(rect.position + Vector2(margins[2], 0)) - draw_ofs, - mtx.basis_xform(rect.position + rect.size - Vector2(margins[3], 0)) - draw_ofs + mtx.basis_xform(rect.position + Vector2(0, margins[0])) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + rect.size - Vector2(0, margins[1])) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + Vector2(margins[2], 0)) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + rect.size - Vector2(margins[3], 0)) - draw_ofs * draw_zoom }; if (Math::abs(mb->get_position().y - pos[0].y) < 8) { edited_margin = 0; @@ -415,9 +415,9 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } } } else if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) { - _zoom_in(); + _zoom_on_position(draw_zoom * ((0.95 + (0.05 * mb->get_factor())) / 0.95), mb->get_position()); } else if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed()) { - _zoom_out(); + _zoom_on_position(draw_zoom * (1 - (0.05 * mb->get_factor())), mb->get_position()); } } @@ -427,7 +427,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { if (mm->get_button_mask() & BUTTON_MASK_MIDDLE || Input::get_singleton()->is_key_pressed(KEY_SPACE)) { - Vector2 draged(mm->get_relative().x, mm->get_relative().y); + Vector2 draged(mm->get_relative().x / draw_zoom, mm->get_relative().y / draw_zoom); hscroll->set_value(hscroll->get_value() - draged.x); vscroll->set_value(vscroll->get_value() - draged.y); @@ -578,25 +578,30 @@ void TextureRegionEditor::_set_snap_sep_y(float p_val) { edit_draw->update(); } +void TextureRegionEditor::_zoom_on_position(float p_zoom, Point2 p_position) { + if (p_zoom < 0.25 || p_zoom > 8) + return; + + float prev_zoom = draw_zoom; + draw_zoom = p_zoom; + Point2 ofs = p_position; + ofs = ofs / prev_zoom - ofs / draw_zoom; + draw_ofs.x = Math::round(draw_ofs.x + ofs.x); + draw_ofs.y = Math::round(draw_ofs.y + ofs.y); + + edit_draw->update(); +} + void TextureRegionEditor::_zoom_in() { - if (draw_zoom < 8) { - draw_zoom *= 2; - edit_draw->update(); - } + _zoom_on_position(draw_zoom * 1.5, edit_draw->get_size() / 2.0); } void TextureRegionEditor::_zoom_reset() { - if (draw_zoom == 1) - return; - draw_zoom = 1; - edit_draw->update(); + _zoom_on_position(1.0, edit_draw->get_size() / 2.0); } void TextureRegionEditor::_zoom_out() { - if (draw_zoom > 0.25) { - draw_zoom /= 2; - edit_draw->update(); - } + _zoom_on_position(draw_zoom / 1.5, edit_draw->get_size() / 2.0); } void TextureRegionEditor::apply_rect(const Rect2 &p_rect) { @@ -743,6 +748,7 @@ void TextureRegionEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_snap_step_y"), &TextureRegionEditor::_set_snap_step_y); ClassDB::bind_method(D_METHOD("_set_snap_sep_x"), &TextureRegionEditor::_set_snap_sep_x); ClassDB::bind_method(D_METHOD("_set_snap_sep_y"), &TextureRegionEditor::_set_snap_sep_y); + ClassDB::bind_method(D_METHOD("_zoom_on_position"), &TextureRegionEditor::_zoom_on_position); ClassDB::bind_method(D_METHOD("_zoom_in"), &TextureRegionEditor::_zoom_in); ClassDB::bind_method(D_METHOD("_zoom_reset"), &TextureRegionEditor::_zoom_reset); ClassDB::bind_method(D_METHOD("_zoom_out"), &TextureRegionEditor::_zoom_out); diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 19eaef9bc3..a49e0fb96c 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -110,6 +110,7 @@ class TextureRegionEditor : public VBoxContainer { void _set_snap_step_y(float p_val); void _set_snap_sep_x(float p_val); void _set_snap_sep_y(float p_val); + void _zoom_on_position(float p_zoom, Point2 p_position = Point2()); void _zoom_in(); void _zoom_reset(); void _zoom_out(); diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index b1fc14e88a..33e4bb2336 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -299,7 +299,11 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p } } - node->set_cell(p_pos.x, p_pos.y, p_value, p_flip_h, p_flip_v, p_transpose, p_autotile_coord); + Variant v_pos_x = p_pos.x, v_pos_y = p_pos.y, v_tile = p_value, v_flip_h = p_flip_h, v_flip_v = p_flip_v, v_transpose = p_transpose, v_autotile_coord = Vector2(p_autotile_coord.x, p_autotile_coord.y); + const Variant *args[7] = { &v_pos_x, &v_pos_y, &v_tile, &v_flip_h, &v_flip_v, &v_transpose, &v_autotile_coord }; + Variant::CallError ce; + node->call("set_cell", args, 7, ce); + if (manual_autotile || (p_value != -1 && node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE)) { if (current != -1) { node->set_cell_autotile_coord(p_pos.x, p_pos.y, position); @@ -477,10 +481,10 @@ void TileMapEditor::_update_palette() { if (sel_tile != TileMap::INVALID_CELL) { if ((manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) || tileset->tile_get_tile_mode(sel_tile) == TileSet::ATLAS_TILE) { - const Map<Vector2, uint16_t> &tiles2 = tileset->autotile_get_bitmask_map(sel_tile); + const Map<Vector2, uint32_t> &tiles2 = tileset->autotile_get_bitmask_map(sel_tile); Vector<Vector2> entries2; - for (const Map<Vector2, uint16_t>::Element *E = tiles2.front(); E; E = E->next()) { + for (const Map<Vector2, uint32_t>::Element *E = tiles2.front(); E; E = E->next()) { entries2.push_back(E->key()); } entries2.sort(); @@ -1434,9 +1438,9 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { aabb.expand_to(node->world_to_map(xform_inv.xform(screen_size))); Rect2i si = aabb.grow(1.0); - if (node->get_half_offset() != TileMap::HALF_OFFSET_X) { + if (node->get_half_offset() != TileMap::HALF_OFFSET_X && node->get_half_offset() != TileMap::HALF_OFFSET_NEGATIVE_X) { - int max_lines = 2000; //avoid crash if size too smal + int max_lines = 2000; //avoid crash if size too small for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) { @@ -1450,7 +1454,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { } } else { - int max_lines = 10000; //avoid crash if size too smal + int max_lines = 10000; //avoid crash if size too small for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) { @@ -1458,7 +1462,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { Vector2 ofs; if (ABS(j) & 1) { - ofs = cell_xf[0] * 0.5; + ofs = cell_xf[0] * (node->get_half_offset() == TileMap::HALF_OFFSET_X ? 0.5 : -0.5); } Vector2 from = xform.xform(node->map_to_world(Vector2(i, j), true) + ofs); @@ -1477,7 +1481,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { int max_lines = 10000; //avoid crash if size too smal - if (node->get_half_offset() != TileMap::HALF_OFFSET_Y) { + if (node->get_half_offset() != TileMap::HALF_OFFSET_Y && node->get_half_offset() != TileMap::HALF_OFFSET_NEGATIVE_Y) { for (int i = (si.position.y) - 1; i <= (si.position.y + si.size.y); i++) { @@ -1498,7 +1502,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { Vector2 ofs; if (ABS(j) & 1) { - ofs = cell_xf[1] * 0.5; + ofs = cell_xf[1] * (node->get_half_offset() == TileMap::HALF_OFFSET_Y ? 0.5 : -0.5); } Vector2 from = xform.xform(node->map_to_world(Vector2(j, i), true) + ofs); @@ -1539,8 +1543,12 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { for (int i = 0; i < 4; i++) { if (node->get_half_offset() == TileMap::HALF_OFFSET_X && ABS(over_tile.y) & 1) endpoints[i] += cell_xf[0] * 0.5; + if (node->get_half_offset() == TileMap::HALF_OFFSET_NEGATIVE_X && ABS(over_tile.y) & 1) + endpoints[i] += cell_xf[0] * -0.5; if (node->get_half_offset() == TileMap::HALF_OFFSET_Y && ABS(over_tile.x) & 1) endpoints[i] += cell_xf[1] * 0.5; + if (node->get_half_offset() == TileMap::HALF_OFFSET_NEGATIVE_Y && ABS(over_tile.x) & 1) + endpoints[i] += cell_xf[1] * -0.5; endpoints[i] = xform.xform(endpoints[i]); } Color col; diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index dc099b84a7..8cf00cf67d 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -734,7 +734,7 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[SHAPE_KEEP_INSIDE_TILE]->hide(); tools[TOOL_SELECT]->set_pressed(true); - tools[TOOL_SELECT]->set_tooltip(TTR("LMB: Set bit on.\nRMB: Set bit off.\nClick on another Tile to edit it.")); + tools[TOOL_SELECT]->set_tooltip(TTR("LMB: Set bit on.\nRMB: Set bit off.\nShift+LMB: Set wildcard bit.\nClick on another Tile to edit it.")); spin_priority->hide(); } break; case EDITMODE_Z_INDEX: @@ -770,7 +770,8 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { spin_z_index->show(); } } break; - default: {} + default: { + } } _update_toggle_shape_button(); workspace->update(); @@ -818,52 +819,92 @@ void TileSetEditor::_on_workspace_draw() { } break; case EDITMODE_BITMASK: { Color c(1, 0, 0, 0.5); + Color ci(0.3, 0.6, 1, 0.5); for (float x = 0; x < region.size.x / (spacing + size.x); x++) { for (float y = 0; y < region.size.y / (spacing + size.y); y++) { Vector2 coord(x, y); Point2 anchor(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); anchor += WORKSPACE_MARGIN; anchor += region.position; - uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); + uint32_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { - if (mask & TileSet::BIND_TOPLEFT) { + if (mask & TileSet::BIND_IGNORE_TOPLEFT) { + workspace->draw_rect(Rect2(anchor, size / 4), ci); + workspace->draw_rect(Rect2(anchor + size / 4, size / 4), ci); + } else if (mask & TileSet::BIND_TOPLEFT) { workspace->draw_rect(Rect2(anchor, size / 2), c); } - if (mask & TileSet::BIND_TOPRIGHT) { + if (mask & TileSet::BIND_IGNORE_TOPRIGHT) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, 0), size / 4), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 3 / 4, size.y / 4), size / 4), ci); + } else if (mask & TileSet::BIND_TOPRIGHT) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, 0), size / 2), c); } - if (mask & TileSet::BIND_BOTTOMLEFT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMLEFT) { + workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 2), size / 4), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 4, size.y * 3 / 4), size / 4), ci); + } else if (mask & TileSet::BIND_BOTTOMLEFT) { workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 2), size / 2), c); } - if (mask & TileSet::BIND_BOTTOMRIGHT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMRIGHT) { + workspace->draw_rect(Rect2(anchor + size / 2, size / 4), ci); + workspace->draw_rect(Rect2(anchor + size * 3 / 4, size / 4), ci); + } else if (mask & TileSet::BIND_BOTTOMRIGHT) { workspace->draw_rect(Rect2(anchor + size / 2, size / 2), c); } } else { - if (mask & TileSet::BIND_TOPLEFT) { + if (mask & TileSet::BIND_IGNORE_TOPLEFT) { + workspace->draw_rect(Rect2(anchor, size / 6), ci); + workspace->draw_rect(Rect2(anchor + size / 6, size / 6), ci); + } else if (mask & TileSet::BIND_TOPLEFT) { workspace->draw_rect(Rect2(anchor, size / 3), c); } - if (mask & TileSet::BIND_TOP) { + if (mask & TileSet::BIND_IGNORE_TOP) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, 0), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, size.y / 6), size / 6), ci); + } else if (mask & TileSet::BIND_TOP) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, 0), size / 3), c); } - if (mask & TileSet::BIND_TOPRIGHT) { + if (mask & TileSet::BIND_IGNORE_TOPRIGHT) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 4 / 6, 0), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 5 / 6, size.y / 6), size / 6), ci); + } else if (mask & TileSet::BIND_TOPRIGHT) { workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, 0), size / 3), c); } - if (mask & TileSet::BIND_LEFT) { + if (mask & TileSet::BIND_IGNORE_LEFT) { + workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 3), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 6, size.y / 2), size / 6), ci); + } else if (mask & TileSet::BIND_LEFT) { workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 3), size / 3), c); } - if (mask & TileSet::BIND_CENTER) { + if (mask & TileSet::BIND_IGNORE_CENTER) { + workspace->draw_rect(Rect2(anchor + size / 3, size / 6), ci); + workspace->draw_rect(Rect2(anchor + size / 2, size / 6), ci); + } else if (mask & TileSet::BIND_CENTER) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, size.y / 3), size / 3), c); } - if (mask & TileSet::BIND_RIGHT) { + if (mask & TileSet::BIND_IGNORE_RIGHT) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 4 / 6, size.y / 3), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 5 / 6, size.y / 2), size / 6), ci); + } else if (mask & TileSet::BIND_RIGHT) { workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, size.y / 3), size / 3), c); } - if (mask & TileSet::BIND_BOTTOMLEFT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMLEFT) { + workspace->draw_rect(Rect2(anchor + Vector2(0, size.y * 4 / 6), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 6, size.y * 5 / 6), size / 6), ci); + } else if (mask & TileSet::BIND_BOTTOMLEFT) { workspace->draw_rect(Rect2(anchor + Vector2(0, (size.y / 3) * 2), size / 3), c); } - if (mask & TileSet::BIND_BOTTOM) { + if (mask & TileSet::BIND_IGNORE_BOTTOM) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, size.y * 4 / 6), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, size.y * 5 / 6), size / 6), ci); + } else if (mask & TileSet::BIND_BOTTOM) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, (size.y / 3) * 2), size / 3), c); } - if (mask & TileSet::BIND_BOTTOMRIGHT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMRIGHT) { + workspace->draw_rect(Rect2(anchor + size * 4 / 6, size / 6), ci); + workspace->draw_rect(Rect2(anchor + size * 5 / 6, size / 6), ci); + } else if (mask & TileSet::BIND_BOTTOMRIGHT) { workspace->draw_rect(Rect2(anchor + (size / 3) * 2, size / 3), c); } } @@ -882,10 +923,10 @@ void TileSetEditor::_on_workspace_draw() { } break; case EDITMODE_PRIORITY: { spin_priority->set_value(tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord)); - uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), edited_shape_coord); + uint32_t mask = tileset->autotile_get_bitmask(get_current_tile(), edited_shape_coord); Vector<Vector2> queue_others; int total = 0; - for (Map<Vector2, uint16_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { if (E->value() == mask) { total += tileset->autotile_get_subtile_priority(get_current_tile(), E->key()); if (E->key() != edited_shape_coord) { @@ -900,7 +941,8 @@ void TileSetEditor::_on_workspace_draw() { spin_z_index->set_value(tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord)); draw_highlight_subtile(edited_shape_coord); } break; - default: {} + default: { + } } } @@ -996,25 +1038,26 @@ void TileSetEditor::_on_workspace_overlay_draw() { tileset->get_tile_list(tiles); for (List<int>::Element *E = tiles->front(); E; E = E->next()) { int t_id = E->get(); - if (tileset->tile_get_texture(t_id)->get_rid() == current_texture_rid) { - Rect2i region = tileset->tile_get_region(t_id); - region.position += WORKSPACE_MARGIN; - region.position *= workspace->get_scale().x; - Color c; - if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) - c = COLOR_SINGLE; - else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) - c = COLOR_AUTOTILE; - else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) - c = COLOR_ATLAS; - String tile_id_name = String::num(t_id, 0) + ": " + tileset->tile_get_name(t_id); - Ref<Font> font = get_font("font", "Label"); - region.set_size(font->get_string_size(tile_id_name)); - workspace_overlay->draw_rect(region, c); - region.position.y += region.size.y - 2; - c = Color(0.1, 0.1, 0.1); - workspace_overlay->draw_string(font, region.position, tile_id_name, c); - } + if (tileset->tile_get_texture(t_id)->get_rid() != current_texture_rid) + continue; + + Rect2 region = tileset->tile_get_region(t_id); + region.position += WORKSPACE_MARGIN; + region.position *= workspace->get_scale().x; + Color c; + if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) + c = COLOR_SINGLE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) + c = COLOR_AUTOTILE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) + c = COLOR_ATLAS; + String tile_id_name = String::num(t_id, 0) + ": " + tileset->tile_get_name(t_id); + Ref<Font> font = get_font("font", "Label"); + region.set_size(font->get_string_size(tile_id_name)); + workspace_overlay->draw_rect(region, c); + region.position.y += region.size.y - 2; + c = Color(0.1, 0.1, 0.1); + workspace_overlay->draw_string(font, region.position, tile_id_name, c); } } @@ -1037,6 +1080,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { static bool dragging; static bool erasing; + static bool alternative; draw_edited_region = false; Rect2 current_tile_region = Rect2(); @@ -1220,10 +1264,11 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if ((mb->get_button_index() == BUTTON_RIGHT || mb->get_button_index() == BUTTON_LEFT) && current_tile_region.has_point(mb->get_position())) { dragging = true; erasing = (mb->get_button_index() == BUTTON_RIGHT); + alternative = Input::get_singleton()->is_key_pressed(KEY_SHIFT); Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y))); Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); pos = mb->get_position() - (pos + current_tile_region.position); - uint16_t bit = 0; + uint32_t bit = 0; if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { if (pos.x < size.x / 2) { if (pos.y < size.y / 2) { @@ -1266,13 +1311,19 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } - uint16_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); - uint16_t new_mask = old_mask; - if (erasing) { + uint32_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); + uint32_t new_mask = old_mask; + if (alternative) { new_mask &= ~bit; + new_mask |= (bit << 16); + } else if (erasing) { + new_mask &= ~bit; + new_mask &= ~(bit << 16); } else { new_mask |= bit; + new_mask &= ~(bit << 16); } + if (old_mask != new_mask) { undo_redo->create_action(TTR("Edit Tile Bitmask")); undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, new_mask); @@ -1286,6 +1337,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if ((erasing && mb->get_button_index() == BUTTON_RIGHT) || (!erasing && mb->get_button_index() == BUTTON_LEFT)) { dragging = false; erasing = false; + alternative = false; } } } @@ -1294,7 +1346,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { Vector2 coord((int)((mm->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mm->get_position().y - current_tile_region.position.y) / (spacing + size.y))); Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); pos = mm->get_position() - (pos + current_tile_region.position); - uint16_t bit = 0; + uint32_t bit = 0; if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { if (pos.x < size.x / 2) { if (pos.y < size.y / 2) { @@ -1337,12 +1389,17 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } - uint16_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); - uint16_t new_mask = old_mask; - if (erasing) { + uint32_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); + uint32_t new_mask = old_mask; + if (alternative) { new_mask &= ~bit; + new_mask |= (bit << 16); + } else if (erasing) { + new_mask &= ~bit; + new_mask &= ~(bit << 16); } else { new_mask |= bit; + new_mask &= ~(bit << 16); } if (old_mask != new_mask) { undo_redo->create_action(TTR("Edit Tile Bitmask")); @@ -1516,7 +1573,8 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } break; - default: {} + default: { + } } } } @@ -1529,10 +1587,10 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { undo_redo->create_action(TTR("Paste Tile Bitmask")); undo_redo->add_do_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile()); undo_redo->add_undo_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile()); - for (Map<Vector2, uint16_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) { undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value()); } - for (Map<Vector2, uint16_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value()); } undo_redo->add_do_method(workspace, "update"); @@ -1541,7 +1599,7 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { } else if (p_tool == BITMASK_CLEAR) { undo_redo->create_action(TTR("Clear Tile Bitmask")); undo_redo->add_do_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile()); - for (Map<Vector2, uint16_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value()); } undo_redo->add_do_method(workspace, "update"); @@ -1670,7 +1728,8 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { undo_redo->commit_action(); } } break; - default: {} + default: { + } } } } else if (p_tool == TOOL_SELECT) { @@ -1840,7 +1899,8 @@ void TileSetEditor::_select_next_tile() { edited_shape_coord = Vector2(); _select_edited_shape_coord(); } break; - default: {} + default: { + } } } } @@ -1877,7 +1937,8 @@ void TileSetEditor::_select_previous_tile() { edited_shape_coord = cell_count; _select_edited_shape_coord(); } break; - default: {} + default: { + } } } } @@ -2139,8 +2200,8 @@ void TileSetEditor::_undo_tile_removal(int p_id) { for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = navigation_map.front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_navigation_polygon", p_id, E->value(), E->key()); } - Map<Vector2, uint16_t> bitmask_map = tileset->autotile_get_bitmask_map(p_id); - for (Map<Vector2, uint16_t>::Element *E = bitmask_map.front(); E; E = E->next()) { + Map<Vector2, uint32_t> bitmask_map = tileset->autotile_get_bitmask_map(p_id); + for (Map<Vector2, uint32_t>::Element *E = bitmask_map.front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", p_id, E->key(), E->value()); } Map<Vector2, int> priority_map = tileset->autotile_get_priority_map(p_id); @@ -2417,9 +2478,11 @@ void TileSetEditor::draw_polygon_shapes() { colors.push_back(c_bg); } } - if (polygon.size() > 2) { - workspace->draw_polygon(polygon, colors); - } + + if (polygon.size() < 3) + continue; + + workspace->draw_polygon(polygon, colors); if (coord == edited_shape_coord || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { if (!creating_shape) { @@ -2612,7 +2675,8 @@ void TileSetEditor::draw_polygon_shapes() { } } } break; - default: {} + default: { + } } if (creating_shape) { diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index 2827964592..1176e1bb92 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -126,7 +126,7 @@ class TileSetEditor : public HSplitContainer { Vector2 edited_shape_coord; PoolVector2Array current_shape; Map<Vector2i, SubtileData> current_tile_data; - Map<Vector2, uint16_t> bitmask_map_copy; + Map<Vector2, uint32_t> bitmask_map_copy; Vector2 snap_step; Vector2 snap_offset; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 7bd26de092..0aba7f3d15 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -39,6 +39,7 @@ #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "scene/main/viewport.h" +#include "scene/resources/visual_shader_nodes.h" Control *VisualShaderNodePlugin::create_editor(const Ref<VisualShaderNode> &p_node) { @@ -107,16 +108,174 @@ void VisualShaderEditor::remove_custom_type(const Ref<Script> &p_script) { _update_options_menu(); } +bool VisualShaderEditor::_is_available(int p_mode) { + + int current_mode = edit_type->get_selected(); + + if (p_mode != -1) { + + switch (current_mode) { + case VisualShader::TYPE_VERTEX: + current_mode = 1; + break; + case VisualShader::TYPE_FRAGMENT: + current_mode = 2; + break; + case VisualShader::TYPE_LIGHT: + current_mode = 4; + break; + default: + break; + } + + int temp_mode = 0; + + if (p_mode & VisualShader::TYPE_FRAGMENT) { + temp_mode |= 2; + } + + if (p_mode & VisualShader::TYPE_LIGHT) { + temp_mode |= 4; + } + + if (temp_mode == 0) { + temp_mode |= 1; + } + + p_mode = temp_mode; + } + + if (p_mode != -1 && ((p_mode & current_mode) == 0)) { + return false; + } + return true; +} + void VisualShaderEditor::_update_options_menu() { + node_desc->set_text(""); + members_dialog->get_ok()->set_disabled(true); + String prev_category; - add_node->get_popup()->clear(); - for (int i = 0; i < add_options.size(); i++) { - if (prev_category != add_options[i].category) { - add_node->get_popup()->add_separator(add_options[i].category); + String prev_sub_category; + + members->clear(); + TreeItem *root = members->create_item(); + TreeItem *category = NULL; + TreeItem *sub_category = NULL; + + String filter = node_filter->get_text().strip_edges(); + bool use_filter = !filter.empty(); + + Vector<String> categories; + Vector<String> sub_categories; + + int item_count = 0; + int item_count2 = 0; + + for (int i = 0; i < add_options.size() + 1; i++) { + + if (i == add_options.size()) { + if (sub_category != NULL && item_count2 == 0) { + memdelete(sub_category); + --item_count; + } + if (category != NULL && item_count == 0) { + memdelete(category); + } + break; + } + + if (!use_filter || add_options[i].name.findn(filter) != -1) { + + if (prev_category != add_options[i].category) { + if (category != NULL && item_count == 0) { + memdelete(category); + } + + item_count = 0; + prev_sub_category = ""; + category = members->create_item(root); + category->set_text(0, add_options[i].category); + if (!use_filter) + category->set_collapsed(true); + } + + if (add_options[i].sub_category != "") { + if (prev_sub_category != add_options[i].sub_category) { + if (category != NULL) { + if (sub_category != NULL && item_count2 == 0) { + memdelete(sub_category); + --item_count; + } + ++item_count; + item_count2 = 0; + sub_category = members->create_item(category); + sub_category->set_text(0, add_options[i].sub_category); + if (!use_filter) + sub_category->set_collapsed(true); + } + } + if (sub_category != NULL) { + if (_is_available(add_options[i].mode)) { + ++item_count2; + TreeItem *item = members->create_item(sub_category); + item->set_text(0, add_options[i].name); + switch (add_options[i].return_type) { + case VisualShaderNode::PORT_TYPE_SCALAR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_VECTOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_BOOLEAN: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_TRANSFORM: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_COLOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Color", "EditorIcons")); + break; + default: + break; + } + item->set_meta("id", i); + } + } + } else { + if (category != NULL) { + if (_is_available(add_options[i].mode)) { + ++item_count; + TreeItem *item = members->create_item(category); + item->set_text(0, add_options[i].name); + switch (add_options[i].return_type) { + case VisualShaderNode::PORT_TYPE_SCALAR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_VECTOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_BOOLEAN: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_TRANSFORM: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_COLOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Color", "EditorIcons")); + break; + default: + break; + } + item->set_meta("id", i); + } + } + } + + prev_sub_category = add_options[i].sub_category; + prev_category = add_options[i].category; } - add_node->get_popup()->add_item(add_options[i].name, i); - prev_category = add_options[i].category; } } @@ -165,10 +324,11 @@ void VisualShaderEditor::_update_graph() { } } - static const Color type_color[3] = { - Color::html("#61daf4"), - Color::html("#d67dee"), - Color::html("#f6a86e") + static const Color type_color[4] = { + Color::html("#61daf4"), // scalar + Color::html("#d67dee"), // vector + Color::html("#8da6f0"), // boolean + Color::html("#f6a86e") // transform }; List<VisualShader::Connection> connections; @@ -293,7 +453,8 @@ void VisualShaderEditor::_update_graph() { Vector3 v = default_value; button->set_text(String::num(v.x, 3) + "," + String::num(v.y, 3) + "," + String::num(v.z, 3)); } break; - default: {} + default: { + } } } @@ -344,7 +505,7 @@ void VisualShaderEditor::_update_graph() { if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { Ref<StyleBoxFlat> sb = node->get_stylebox("frame", "GraphNode"); - Color c = sb->get_border_color(MARGIN_TOP); + Color c = sb->get_border_color(); Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); mono_color.a = 0.85; c = mono_color; @@ -462,7 +623,7 @@ void VisualShaderEditor::_edit_port_default_input(Object *p_button, int p_node, editing_port = p_port; } -void VisualShaderEditor::_add_node(int p_idx) { +void VisualShaderEditor::_add_node(int p_idx, int p_op_idx) { ERR_FAIL_INDEX(p_idx, add_options.size()); @@ -471,6 +632,70 @@ void VisualShaderEditor::_add_node(int p_idx) { if (add_options[p_idx].type != String()) { VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(add_options[p_idx].type)); ERR_FAIL_COND(!vsn); + + if (p_op_idx != -1) { + + VisualShaderNodeInput *input = Object::cast_to<VisualShaderNodeInput>(vsn); + + if (input) { + input->set_input_name(add_options[p_idx].sub_func_str); + } + + VisualShaderNodeColorOp *colorOp = Object::cast_to<VisualShaderNodeColorOp>(vsn); + + if (colorOp) { + colorOp->set_operator((VisualShaderNodeColorOp::Operator)p_op_idx); + } + + VisualShaderNodeColorFunc *colorFunc = Object::cast_to<VisualShaderNodeColorFunc>(vsn); + + if (colorFunc) { + colorFunc->set_function((VisualShaderNodeColorFunc::Function)p_op_idx); + } + + VisualShaderNodeScalarOp *scalarOp = Object::cast_to<VisualShaderNodeScalarOp>(vsn); + + if (scalarOp) { + scalarOp->set_operator((VisualShaderNodeScalarOp::Operator)p_op_idx); + } + + VisualShaderNodeScalarFunc *scalarFunc = Object::cast_to<VisualShaderNodeScalarFunc>(vsn); + + if (scalarFunc) { + scalarFunc->set_function((VisualShaderNodeScalarFunc::Function)p_op_idx); + } + + VisualShaderNodeVectorOp *vecOp = Object::cast_to<VisualShaderNodeVectorOp>(vsn); + + if (vecOp) { + vecOp->set_operator((VisualShaderNodeVectorOp::Operator)p_op_idx); + } + + VisualShaderNodeVectorFunc *vecFunc = Object::cast_to<VisualShaderNodeVectorFunc>(vsn); + + if (vecFunc) { + vecFunc->set_function((VisualShaderNodeVectorFunc::Function)p_op_idx); + } + + VisualShaderNodeTransformFunc *matFunc = Object::cast_to<VisualShaderNodeTransformFunc>(vsn); + + if (matFunc) { + matFunc->set_function((VisualShaderNodeTransformFunc::Function)p_op_idx); + } + + VisualShaderNodeScalarDerivativeFunc *sderFunc = Object::cast_to<VisualShaderNodeScalarDerivativeFunc>(vsn); + + if (sderFunc) { + sderFunc->set_function((VisualShaderNodeScalarDerivativeFunc::Function)p_op_idx); + } + + VisualShaderNodeVectorDerivativeFunc *vderFunc = Object::cast_to<VisualShaderNodeVectorDerivativeFunc>(vsn); + + if (vderFunc) { + vderFunc->set_function((VisualShaderNodeVectorDerivativeFunc::Function)p_op_idx); + } + } + vsnode = Ref<VisualShaderNode>(vsn); } else { ERR_FAIL_COND(add_options[p_idx].script.is_null()); @@ -481,7 +706,15 @@ void VisualShaderEditor::_add_node(int p_idx) { vsnode->set_script(add_options[p_idx].script.get_ref_ptr()); } - Point2 position = (graph->get_scroll_ofs() + graph->get_size() * 0.5) / EDSCALE; + Point2 position = graph->get_scroll_ofs(); + + if (saved_node_pos_dirty) { + position += saved_node_pos; + } else { + position += graph->get_size() * 0.5; + position /= EDSCALE; + } + saved_node_pos_dirty = false; VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); @@ -600,29 +833,82 @@ void VisualShaderEditor::_node_selected(Object *p_node) { //EditorNode::get_singleton()->push_item(vsnode.ptr(), "", true); } +void VisualShaderEditor::_member_gui_input(const Ref<InputEvent> p_event) { + Ref<InputEventMouseButton> mb = p_event; + Ref<InputEventKey> key = p_event; + + if (mb.is_valid()) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && mb->is_doubleclick()) { + _member_create(); + } + } else if (key.is_valid()) { + if (key->is_pressed() && key->get_scancode() == KEY_ENTER) { + _member_create(); + } + } +} + void VisualShaderEditor::_input(const Ref<InputEvent> p_event) { if (graph->has_focus()) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) { - add_node->get_popup()->set_position(get_viewport()->get_mouse_position()); - add_node->get_popup()->show_modal(); + saved_node_pos_dirty = true; + saved_node_pos = graph->get_local_mouse_position(); + + Point2 gpos = Input::get_singleton()->get_mouse_position(); + members_dialog->popup(); + members_dialog->set_position(gpos); } } } +void VisualShaderEditor::_show_members_dialog() { + saved_node_pos_dirty = false; + members_dialog->popup(); + members_dialog->set_position(graph->get_global_position() + Point2(5 * EDSCALE, 65 * EDSCALE)); +} + void VisualShaderEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + + node_filter->set_clear_button_enabled(true); + + // collapse tree by default + + TreeItem *category = members->get_root()->get_children(); + while (category) { + category->set_collapsed(true); + TreeItem *sub_category = category->get_children(); + while (sub_category) { + sub_category->set_collapsed(true); + sub_category = sub_category->get_next(); + } + category = category->get_next(); + } + } + + if (p_what == NOTIFICATION_DRAG_BEGIN) { + Dictionary dd = get_viewport()->gui_get_drag_data(); + if (members->is_visible_in_tree() && dd.has("id")) { + members->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM); + } + } else if (p_what == NOTIFICATION_DRAG_END) { + members->set_drop_mode_flags(0); + } if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); error_label->add_color_override("font_color", get_color("error_color", "Editor")); + node_filter->set_right_icon(Control::get_icon("Search", "EditorIcons")); + + tools->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Tools", "EditorIcons")); + if (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree()) _update_graph(); - } - - if (p_what == NOTIFICATION_PROCESS) { + } else if (p_what == NOTIFICATION_PROCESS) { } } @@ -718,7 +1004,60 @@ void VisualShaderEditor::_duplicate_nodes() { } } +void VisualShaderEditor::_on_nodes_delete() { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + List<int> to_erase; + + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + if (gn->is_selected() && gn->is_close_button_visible()) { + to_erase.push_back(gn->get_name().operator String().to_int()); + } + } + } + + if (to_erase.empty()) + return; + + undo_redo->create_action(TTR("Delete Nodes")); + + for (List<int>::Element *F = to_erase.front(); F; F = F->next()) { + undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, F->get()); + undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, F->get()), visual_shader->get_node_position(type, F->get()), F->get()); + } + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + + List<VisualShader::Connection> used_conns; + for (List<int>::Element *F = to_erase.front(); F; F = F->next()) { + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + if (E->get().from_node == F->get() || E->get().to_node == F->get()) { + + bool cancel = false; + for (List<VisualShader::Connection>::Element *R = used_conns.front(); R; R = R->next()) { + if (R->get().from_node == E->get().from_node && R->get().from_port == E->get().from_port && R->get().to_node == E->get().to_node && R->get().to_port == E->get().to_port) { + cancel = true; // to avoid ERR_ALREADY_EXISTS warning + break; + } + } + if (!cancel) { + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + used_conns.push_back(E->get()); + } + } + } + } + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); +} + void VisualShaderEditor::_mode_selected(int p_id) { + _update_options_menu(); _update_graph(); } @@ -756,6 +1095,128 @@ void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> input, St undo_redo->commit_action(); } +void VisualShaderEditor::_member_filter_changed(const String &p_text) { + _update_options_menu(); +} + +void VisualShaderEditor::_member_selected() { + TreeItem *item = members->get_selected(); + + if (item != NULL && item->has_meta("id")) { + members_dialog->get_ok()->set_disabled(false); + node_desc->set_text(add_options[item->get_meta("id")].description); + } else { + members_dialog->get_ok()->set_disabled(true); + node_desc->set_text(""); + } +} + +void VisualShaderEditor::_member_unselected() { +} + +void VisualShaderEditor::_member_create() { + TreeItem *item = members->get_selected(); + if (item != NULL && item->has_meta("id")) { + int idx = members->get_selected()->get_meta("id"); + _add_node(idx, add_options[idx].sub_func); + members_dialog->hide(); + } +} + +void VisualShaderEditor::_tools_menu_option(int p_idx) { + + TreeItem *category = members->get_root()->get_children(); + + switch (p_idx) { + case EXPAND_ALL: + + while (category) { + category->set_collapsed(false); + TreeItem *sub_category = category->get_children(); + while (sub_category) { + sub_category->set_collapsed(false); + sub_category = sub_category->get_next(); + } + category = category->get_next(); + } + + break; + + case COLLAPSE_ALL: + + while (category) { + category->set_collapsed(true); + TreeItem *sub_category = category->get_children(); + while (sub_category) { + sub_category->set_collapsed(true); + sub_category = sub_category->get_next(); + } + category = category->get_next(); + } + + break; + default: + break; + } +} + +Variant VisualShaderEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + + if (p_from == members) { + TreeItem *it = members->get_item_at_position(p_point); + if (!it) + return Variant(); + if (!it->has_meta("id")) + return Variant(); + + int id = it->get_meta("id"); + AddOption op = add_options[id]; + + Dictionary d; + d["id"] = id; + if (op.sub_func == -1) { + d["sub_func"] = op.sub_func_str; + } else { + d["sub_func"] = op.sub_func; + } + + Label *label = memnew(Label); + label->set_text(it->get_text(0)); + set_drag_preview(label); + return d; + } + return Variant(); +} + +bool VisualShaderEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + + if (p_from == graph) { + + Dictionary d = p_data; + + if (d.has("id")) { + return true; + } + } + + return false; +} + +void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + + if (p_from == graph) { + + Dictionary d = p_data; + + if (d.has("id")) { + int idx = d["id"]; + saved_node_pos = p_point; + saved_node_pos_dirty = true; + _add_node(idx, add_options[idx].sub_func); + } + } +} + void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_update_graph", &VisualShaderEditor::_update_graph); @@ -766,6 +1227,7 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_node_selected", &VisualShaderEditor::_node_selected); ClassDB::bind_method("_scroll_changed", &VisualShaderEditor::_scroll_changed); ClassDB::bind_method("_delete_request", &VisualShaderEditor::_delete_request); + ClassDB::bind_method("_on_nodes_delete", &VisualShaderEditor::_on_nodes_delete); ClassDB::bind_method("_node_changed", &VisualShaderEditor::_node_changed); ClassDB::bind_method("_edit_port_default_input", &VisualShaderEditor::_edit_port_default_input); ClassDB::bind_method("_port_edited", &VisualShaderEditor::_port_edited); @@ -777,6 +1239,19 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item); ClassDB::bind_method("_preview_select_port", &VisualShaderEditor::_preview_select_port); ClassDB::bind_method("_input", &VisualShaderEditor::_input); + + ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &VisualShaderEditor::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &VisualShaderEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("drop_data_fw"), &VisualShaderEditor::drop_data_fw); + + ClassDB::bind_method("_is_available", &VisualShaderEditor::_is_available); + ClassDB::bind_method("_tools_menu_option", &VisualShaderEditor::_tools_menu_option); + ClassDB::bind_method("_show_members_dialog", &VisualShaderEditor::_show_members_dialog); + ClassDB::bind_method("_member_gui_input", &VisualShaderEditor::_member_gui_input); + ClassDB::bind_method("_member_filter_changed", &VisualShaderEditor::_member_filter_changed); + ClassDB::bind_method("_member_selected", &VisualShaderEditor::_member_selected); + ClassDB::bind_method("_member_unselected", &VisualShaderEditor::_member_unselected); + ClassDB::bind_method("_member_create", &VisualShaderEditor::_member_create); } VisualShaderEditor *VisualShaderEditor::singleton = NULL; @@ -785,10 +1260,14 @@ VisualShaderEditor::VisualShaderEditor() { singleton = this; updating = false; + saved_node_pos_dirty = false; + saved_node_pos = Point2(0, 0); graph = memnew(GraphEdit); add_child(graph); + graph->set_drag_forwarding(this); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR); + graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_BOOLEAN); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_TRANSFORM); //graph->add_valid_left_disconnect_type(0); @@ -798,10 +1277,16 @@ VisualShaderEditor::VisualShaderEditor() { graph->connect("node_selected", this, "_node_selected"); graph->connect("scroll_offset_changed", this, "_scroll_changed"); graph->connect("duplicate_nodes_request", this, "_duplicate_nodes"); + graph->connect("delete_nodes_request", this, "_on_nodes_delete"); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_BOOLEAN); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_BOOLEAN); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_SCALAR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_VECTOR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_BOOLEAN); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM); VSeparator *vs = memnew(VSeparator); @@ -817,40 +1302,316 @@ VisualShaderEditor::VisualShaderEditor() { graph->get_zoom_hbox()->add_child(edit_type); graph->get_zoom_hbox()->move_child(edit_type, 0); - add_node = memnew(MenuButton); + add_node = memnew(ToolButton); graph->get_zoom_hbox()->add_child(add_node); add_node->set_text(TTR("Add Node...")); graph->get_zoom_hbox()->move_child(add_node, 0); - add_node->get_popup()->connect("id_pressed", this, "_add_node"); - - add_options.push_back(AddOption("Scalar", "Constants", "VisualShaderNodeScalarConstant")); - add_options.push_back(AddOption("Vector", "Constants", "VisualShaderNodeVec3Constant")); - add_options.push_back(AddOption("Color", "Constants", "VisualShaderNodeColorConstant")); - add_options.push_back(AddOption("Transform", "Constants", "VisualShaderNodeTransformConstant")); - add_options.push_back(AddOption("Texture", "Constants", "VisualShaderNodeTexture")); - add_options.push_back(AddOption("CubeMap", "Constants", "VisualShaderNodeCubeMap")); - add_options.push_back(AddOption("ScalarOp", "Operators", "VisualShaderNodeScalarOp")); - add_options.push_back(AddOption("VectorOp", "Operators", "VisualShaderNodeVectorOp")); - add_options.push_back(AddOption("ColorOp", "Operators", "VisualShaderNodeColorOp")); - add_options.push_back(AddOption("TransformMult", "Operators", "VisualShaderNodeTransformMult")); - add_options.push_back(AddOption("TransformVectorMult", "Operators", "VisualShaderNodeTransformVecMult")); - add_options.push_back(AddOption("ScalarFunc", "Functions", "VisualShaderNodeScalarFunc")); - add_options.push_back(AddOption("VectorFunc", "Functions", "VisualShaderNodeVectorFunc")); - add_options.push_back(AddOption("DotProduct", "Functions", "VisualShaderNodeDotProduct")); - add_options.push_back(AddOption("VectorLen", "Functions", "VisualShaderNodeVectorLen")); - add_options.push_back(AddOption("ScalarInterp", "Interpolation", "VisualShaderNodeScalarInterp")); - add_options.push_back(AddOption("VectorInterp", "Interpolation", "VisualShaderNodeVectorInterp")); - add_options.push_back(AddOption("VectorCompose", "Compose", "VisualShaderNodeVectorCompose")); - add_options.push_back(AddOption("TransformCompose", "Compose", "VisualShaderNodeTransformCompose")); - add_options.push_back(AddOption("VectorDecompose", "Decompose", "VisualShaderNodeVectorDecompose")); - add_options.push_back(AddOption("TransformDecompose", "Decompose", "VisualShaderNodeTransformDecompose")); - add_options.push_back(AddOption("Scalar", "Uniforms", "VisualShaderNodeScalarUniform")); - add_options.push_back(AddOption("Vector", "Uniforms", "VisualShaderNodeVec3Uniform")); - add_options.push_back(AddOption("Color", "Uniforms", "VisualShaderNodeColorUniform")); - add_options.push_back(AddOption("Transform", "Uniforms", "VisualShaderNodeTransformUniform")); - add_options.push_back(AddOption("Texture", "Uniforms", "VisualShaderNodeTextureUniform")); - add_options.push_back(AddOption("CubeMap", "Uniforms", "VisualShaderNodeCubeMapUniform")); - add_options.push_back(AddOption("Input", "Inputs", "VisualShaderNodeInput")); + add_node->connect("pressed", this, "_show_members_dialog"); + + /////////////////////////////////////// + // SHADER NODES TREE + /////////////////////////////////////// + + VBoxContainer *members_vb = memnew(VBoxContainer); + members_vb->set_v_size_flags(SIZE_EXPAND_FILL); + + HBoxContainer *filter_hb = memnew(HBoxContainer); + members_vb->add_child(filter_hb); + + node_filter = memnew(LineEdit); + filter_hb->add_child(node_filter); + node_filter->connect("text_changed", this, "_member_filter_changed"); + node_filter->set_h_size_flags(SIZE_EXPAND_FILL); + node_filter->set_placeholder(TTR("Search")); + + tools = memnew(MenuButton); + filter_hb->add_child(tools); + tools->set_tooltip(TTR("Options")); + tools->get_popup()->connect("id_pressed", this, "_tools_menu_option"); + tools->get_popup()->add_item(TTR("Expand All"), EXPAND_ALL); + tools->get_popup()->add_item(TTR("Collapse All"), COLLAPSE_ALL); + + members = memnew(Tree); + members_vb->add_child(members); + members->set_drag_forwarding(this); + members->set_h_size_flags(SIZE_EXPAND_FILL); + members->set_v_size_flags(SIZE_EXPAND_FILL); + members->set_hide_root(true); + members->set_allow_reselect(true); + members->set_hide_folding(false); + members->set_custom_minimum_size(Size2(180 * EDSCALE, 200 * EDSCALE)); + members->connect("item_selected", this, "_member_selected"); + members->connect("nothing_selected", this, "_member_unselected"); + members->connect("gui_input", this, "_member_gui_input"); + + Label *desc_label = memnew(Label); + members_vb->add_child(desc_label); + desc_label->set_text(TTR("Description:")); + + node_desc = memnew(RichTextLabel); + members_vb->add_child(node_desc); + node_desc->set_h_size_flags(SIZE_EXPAND_FILL); + node_desc->set_v_size_flags(SIZE_FILL); + node_desc->set_custom_minimum_size(Size2(0, 70 * EDSCALE)); + + members_dialog = memnew(ConfirmationDialog); + members_dialog->set_title(TTR("Create Shader Node")); + members_dialog->add_child(members_vb); + members_dialog->get_ok()->set_text(TTR("Create")); + members_dialog->get_ok()->connect("pressed", this, "_member_create"); + members_dialog->get_ok()->set_disabled(true); + members_dialog->set_resizable(true); + members_dialog->set_as_minsize(); + add_child(members_dialog); + + alert = memnew(AcceptDialog); + alert->set_as_minsize(); + alert->get_label()->set_autowrap(true); + alert->get_label()->set_align(Label::ALIGN_CENTER); + alert->get_label()->set_valign(Label::VALIGN_CENTER); + alert->get_label()->set_custom_minimum_size(Size2(400, 60) * EDSCALE); + add_child(alert); + + /////////////////////////////////////// + // SHADER NODES TREE OPTIONS + /////////////////////////////////////// + + // COLOR + + add_options.push_back(AddOption("ColorFunc", "Color", "Common", "VisualShaderNodeColorFunc", TTR("Color function."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ColorOp", "Color", "Common", "VisualShaderNodeColorOp", TTR("Color operator."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("Grayscale", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Grayscale function."), VisualShaderNodeColorFunc::FUNC_GRAYSCALE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("HSV2RGB", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts HSV vector to RGB equivalent."), VisualShaderNodeVectorFunc::FUNC_HSV2RGB, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("RGB2HSV", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts RGB vector to HSV equivalent."), VisualShaderNodeVectorFunc::FUNC_RGB2HSV, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sepia", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Sepia function."), VisualShaderNodeColorFunc::FUNC_SEPIA, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("Burn", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Burn operator."), VisualShaderNodeColorOp::OP_BURN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Darken", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Darken operator."), VisualShaderNodeColorOp::OP_DARKEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Difference", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Difference operator."), VisualShaderNodeColorOp::OP_DIFFERENCE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Dodge", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Dodge operator."), VisualShaderNodeColorOp::OP_DODGE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("HardLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("HardLight operator"), VisualShaderNodeColorOp::OP_HARD_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Lighten", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Lighten operator."), VisualShaderNodeColorOp::OP_LIGHTEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Overlay", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Overlay operator."), VisualShaderNodeColorOp::OP_OVERLAY, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Screen", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Screen operator."), VisualShaderNodeColorOp::OP_SCREEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SoftLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("SoftLight operator."), VisualShaderNodeColorOp::OP_SOFT_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("ColorConstant", "Color", "Variables", "VisualShaderNodeColorConstant", TTR("Color constant."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + + // BOOLEAN + add_options.push_back(AddOption("If", "Conditional", "Functions", "VisualShaderNodeIf", TTR("Returns an associated vector if the provided scalars are equal, greater or less."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Switch", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated vector if the provided boolean value is true or false."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("BooleanConstant", "Conditional", "Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("BooleanUniform", "Conditional", "Variables", "VisualShaderNodeBooleanUniform", TTR("Boolean uniform."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN)); + + // INPUT + + add_options.push_back(AddOption("Camera", "Input", "All", "VisualShaderNodeInput", TTR("'camera' input parameter for all shader modes."), "camera", VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("InvCamera", "Input", "All", "VisualShaderNodeInput", TTR("'inv_camera' input parameter for all shader modes."), "inv_camera", VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("InvProjection", "Input", "All", "VisualShaderNodeInput", TTR("'inv_projection' input parameter for all shader modes."), "inv_projection", VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("Normal", "Input", "All", "VisualShaderNodeInput", TTR("'normal' input parameter for all shader modes."), "normal", VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Projection", "Input", "All", "VisualShaderNodeInput", TTR("'projection' input parameter for all shader modes."), "projection", VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", TTR("'time' input parameter for all shader modes."), "time", VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ViewportSize", "Input", "All", "VisualShaderNodeInput", TTR("'viewport_size' input parameter for all shader modes."), "viewport_size", VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("World", "Input", "All", "VisualShaderNodeInput", TTR("'world' input parameter for all shader modes."), "world", VisualShaderNode::PORT_TYPE_TRANSFORM)); + + add_options.push_back(AddOption("Input", "Input", "Common", "VisualShaderNodeInput", TTR("Input parameter."))); + + add_options.push_back(AddOption("Alpha", "Input", "Fragment", "VisualShaderNodeInput", TTR("'alpha' input parameter for fragment shader mode."), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT)); + add_options.push_back(AddOption("Binormal", "Input", "Fragment", "VisualShaderNodeInput", TTR("'binormal' input parameter for fragment shader mode."), "binormal", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT)); + add_options.push_back(AddOption("Color", "Input", "Fragment", "VisualShaderNodeInput", TTR("'color' input parameter for fragment shader mode."), "color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT)); + add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", TTR("'fragcoord' input parameter for fragment shader mode."), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT)); + add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", TTR("'point_coord' input parameter for fragment shader mode."), "point_coord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT)); + add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", TTR("'screen_uv' input parameter for fragment shader mode."), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT)); + add_options.push_back(AddOption("Side", "Input", "Fragment", "VisualShaderNodeInput", TTR("'side' input parameter for fragment shader mode."), "side", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT)); + add_options.push_back(AddOption("Tangent", "Input", "Fragment", "VisualShaderNodeInput", TTR("'tangent' input parameter for fragment shader mode."), "tangent", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT)); + add_options.push_back(AddOption("UV", "Input", "Fragment", "VisualShaderNodeInput", TTR("'uv' input parameter for fragment shader mode."), "uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT)); + add_options.push_back(AddOption("UV2", "Input", "Fragment", "VisualShaderNodeInput", TTR("'uv2' input parameter for fragment shader mode."), "uv2", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT)); + add_options.push_back(AddOption("Vertex", "Input", "Fragment", "VisualShaderNodeInput", TTR("'vertex' input parameter for fragment shader mode."), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT)); + add_options.push_back(AddOption("View", "Input", "Fragment", "VisualShaderNodeInput", TTR("'view' input parameter for fragment shader mode."), "view", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT)); + + add_options.push_back(AddOption("Albedo", "Input", "Light", "VisualShaderNodeInput", TTR("'albedo' input parameter for light shader mode."), "albedo", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("Attenuation", "Input", "Light", "VisualShaderNodeInput", TTR("'attenuation' input parameter for light shader mode."), "attenuation", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("Diffuse", "Input", "Light", "VisualShaderNodeInput", TTR("'diffuse' input parameter for light shader mode."), "diffuse", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", TTR("'fragcoord' input parameter for light shader mode."), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", TTR("'light' input parameter for light shader mode."), "light", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", TTR("'light_color' input parameter for light shader mode."), "light_color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("Roughness", "Input", "Light", "VisualShaderNodeInput", TTR("'roughness' input parameter for light shader mode."), "roughness", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("Specular", "Input", "Light", "VisualShaderNodeInput", TTR("'specular' input parameter for light shader mode."), "specular", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("Transmission", "Input", "Light", "VisualShaderNodeInput", TTR("'transmission' input parameter for light shader mode."), "transmission", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("View", "Input", "Light", "VisualShaderNodeInput", TTR("'view' input parameter for light shader mode."), "view", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT)); + + add_options.push_back(AddOption("Alpha", "Input", "Vertex", "VisualShaderNodeInput", TTR("'alpha' input parameter for vertex shader mode."), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX)); + add_options.push_back(AddOption("Binormal", "Input", "Vertex", "VisualShaderNodeInput", TTR("'binormal' input parameter for vertex shader mode."), "binormal", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX)); + add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", TTR("'color' input parameter for vertex shader mode."), "color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX)); + add_options.push_back(AddOption("ModelView", "Input", "Vertex", "VisualShaderNodeInput", TTR("'modelview' input parameter for vertex shader mode."), "modelview", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX)); + add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", TTR("'point_size' input parameter for vertex shader mode."), "point_size", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX)); + add_options.push_back(AddOption("Tangent", "Input", "Vertex", "VisualShaderNodeInput", TTR("'tangent' input parameter for vertex shader mode."), "tangent", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX)); + add_options.push_back(AddOption("UV", "Input", "Vertex", "VisualShaderNodeInput", TTR("'uv' input parameter for vertex shader mode."), "uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX)); + add_options.push_back(AddOption("UV2", "Input", "Vertex", "VisualShaderNodeInput", TTR("'uv2' input parameter for vertex shader mode."), "uv2", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX)); + add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", TTR("'vertex' input parameter for vertex shader mode."), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX)); + + // SCALAR + + add_options.push_back(AddOption("ScalarFunc", "Scalar", "Common", "VisualShaderNodeScalarFunc", TTR("Scalar function."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ScalarOp", "Scalar", "Common", "VisualShaderNodeScalarOp", TTR("Scalar operator."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + + add_options.push_back(AddOption("Abs", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the absolute value of the parameter."), VisualShaderNodeScalarFunc::FUNC_ABS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ACos", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the arc-cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ACOS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ACosH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the inverse hyperbolic cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ACOSH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ASin", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the arc-sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ASIN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ASinH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the inverse hyperbolic sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ASINH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ATan", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the arc-tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_ATAN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ATan2", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the arc-tangent of the parameters."), VisualShaderNodeScalarOp::OP_ATAN2, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ATanH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the inverse hyperbolic tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_ATANH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Ceil", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), VisualShaderNodeScalarFunc::FUNC_CEIL, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Clamp", "Scalar", "Functions", "VisualShaderNodeScalarClamp", TTR("Constrains a value to lie between two further values."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Cos", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_COS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("CosH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the hyperbolic cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_COSH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Degrees", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Converts a quantity in radians to degrees."), VisualShaderNodeScalarFunc::FUNC_DEGREES, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Exp", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Base-e Exponential."), VisualShaderNodeScalarFunc::FUNC_EXP, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Exp2", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Base-2 Exponential."), VisualShaderNodeScalarFunc::FUNC_EXP2, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Floor", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Finds the nearest integer less than or equal to the parameter."), VisualShaderNodeScalarFunc::FUNC_FLOOR, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Fract", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Computes the fractional part of the argument."), VisualShaderNodeScalarFunc::FUNC_FRAC, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("InverseSqrt", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the inverse of the square root of the parameter."), VisualShaderNodeScalarFunc::FUNC_INVERSE_SQRT, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Log", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Natural logarithm."), VisualShaderNodeScalarFunc::FUNC_LOG, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Log2", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Base-2 logarithm."), VisualShaderNodeScalarFunc::FUNC_LOG2, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Max", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the greater of two values."), VisualShaderNodeScalarOp::OP_MAX, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Min", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the lesser of two values."), VisualShaderNodeScalarOp::OP_MIN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Mix", "Scalar", "Functions", "VisualShaderNodeScalarInterp", TTR("Linear interpolation between two scalars."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Negate", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeScalarFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Pow", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeScalarOp::OP_POW, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Radians", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeScalarFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Reciprocal", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("1.0 / scalar"), VisualShaderNodeScalarFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Round", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Finds the nearest integer to the parameter."), VisualShaderNodeScalarFunc::FUNC_ROUND, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("RoundEven", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Finds the nearest even integer to the parameter."), VisualShaderNodeScalarFunc::FUNC_ROUNDEVEN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Saturate", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Clamps the value between 0.0 and 1.0."), VisualShaderNodeScalarFunc::FUNC_SATURATE, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Sign", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Extracts the sign of the parameter."), VisualShaderNodeScalarFunc::FUNC_SIGN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Sin", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_SIN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("SinH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the hyperbolic sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_SINH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Sqrt", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the square root of the parameter."), VisualShaderNodeScalarFunc::FUNC_SQRT, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("SmoothStep", "Scalar", "Functions", "VisualShaderNodeScalarSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Step", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), VisualShaderNodeScalarOp::OP_STEP, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Tan", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_TAN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("TanH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the hyperbolic tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_TANH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Trunc", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Finds the truncated value of the parameter."), VisualShaderNodeScalarFunc::FUNC_TRUNC, VisualShaderNode::PORT_TYPE_SCALAR)); + + add_options.push_back(AddOption("Add", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Adds scalar to scalar."), VisualShaderNodeScalarOp::OP_ADD, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Divide", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Divides scalar by scalar."), VisualShaderNodeScalarOp::OP_DIV, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Multiply", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Multiplies scalar by scalar."), VisualShaderNodeScalarOp::OP_MUL, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Remainder", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Returns the remainder of the two scalars."), VisualShaderNodeScalarOp::OP_MOD, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Subtract", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Subtracts scalar from scalar."), VisualShaderNodeScalarOp::OP_SUB, VisualShaderNode::PORT_TYPE_SCALAR)); + + add_options.push_back(AddOption("ScalarConstant", "Scalar", "Variables", "VisualShaderNodeScalarConstant", TTR("Scalar constant."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ScalarUniform", "Scalar", "Variables", "VisualShaderNodeScalarUniform", TTR("Scalar uniform."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + + // TEXTURES + + add_options.push_back(AddOption("CubeMap", "Textures", "Functions", "VisualShaderNodeCubeMap", TTR("Perform the cubic texture lookup."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + add_options.push_back(AddOption("Texture", "Textures", "Functions", "VisualShaderNodeTexture", TTR("Perform the texture lookup."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + + add_options.push_back(AddOption("CubeMapUniform", "Textures", "Variables", "VisualShaderNodeCubeMapUniform", TTR("Cubic texture uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + add_options.push_back(AddOption("TextureUniform", "Textures", "Variables", "VisualShaderNodeTextureUniform", TTR("2D texture uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + + // TRANSFORM + + add_options.push_back(AddOption("TransformFunc", "Transform", "Common", "VisualShaderNodeTransformFunc", TTR("Transform function."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + + add_options.push_back(AddOption("OuterProduct", "Transform", "Composition", "VisualShaderNodeOuterProduct", TTR("(GLES3 only) Calculate the outer product of a pair of vectors.\n\nOuterProduct treats the first parameter 'c' as a column vector (matrix with one column) and the second parameter 'r' as a row vector (matrix with one row) and does a linear algebraic matrix multiply 'c * r', yielding a matrix whose number of rows is the number of components in 'c' and whose number of columns is the number of components in 'r'."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformCompose", "Transform", "Composition", "VisualShaderNodeTransformCompose", TTR("Composes transform from four vectors."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformDecompose", "Transform", "Composition", "VisualShaderNodeTransformDecompose", TTR("Decomposes transform to four vectors."))); + + add_options.push_back(AddOption("Determinant", "Transform", "Functions", "VisualShaderNodeDeterminant", TTR("(GLES3 only) Calculates the determinant of a transform."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Inverse", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("(GLES3 only) Calculates the inverse of a transform."), VisualShaderNodeTransformFunc::FUNC_INVERSE, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("Transpose", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("(GLES3 only) Calculates the transpose of a transform."), VisualShaderNodeTransformFunc::FUNC_TRANSPOSE, VisualShaderNode::PORT_TYPE_TRANSFORM)); + + add_options.push_back(AddOption("TransformMult", "Transform", "Operators", "VisualShaderNodeTransformMult", TTR("Multiplies transform by transform."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformVectorMult", "Transform", "Operators", "VisualShaderNodeTransformVecMult", TTR("Multiplies vector by transform."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("TransformConstant", "Transform", "Variables", "VisualShaderNodeTransformConstant", TTR("Transform constant."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformUniform", "Transform", "Variables", "VisualShaderNodeTransformUniform", TTR("Transform uniform."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + + // VECTOR + + add_options.push_back(AddOption("VectorFunc", "Vector", "Common", "VisualShaderNodeVectorFunc", TTR("Vector function."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("VectorOp", "Vector", "Common", "VisualShaderNodeVectorOp", TTR("Vector operator."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("VectorCompose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes vector from three scalars."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("VectorDecompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes vector to three scalars."))); + + add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the inverse hyperbolic cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the inverse hyperbolic sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the inverse hyperbolic tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), VisualShaderNodeVectorFunc::FUNC_CEIL, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeVectorClamp", TTR("Constrains a value to lie between two further values."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the hyperbolic cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Cross", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Calculates the cross product of two vectors."), VisualShaderNodeVectorOp::OP_CROSS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Distance", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Dot", "Vector", "Functions", "VisualShaderNodeDotProduct", TTR("Calculates the dot product of two vectors."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns a vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), VisualShaderNodeVectorOp::OP_MAX, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), VisualShaderNodeVectorOp::OP_MIN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeVectorInterp", TTR("Linear interpolation between two vectors."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeVectorOp::OP_POW, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Reflect", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns a vector that points in the direction of reflection ( a : incident vector, b : normal vector )."), VisualShaderNodeVectorOp::OP_REFLECT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Refract", "Vector", "Functions", "VisualShaderNodeVectorRefract", TTR("Returns a vector that points in the direction of refraction."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Finds the nearest integer to the parameter."), VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Finds the nearest even integer to the parameter."), VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the hyperbolic sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeVectorSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeVectorScalarSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), VisualShaderNodeVectorOp::OP_STEP, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeVectorScalarStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the hyperbolic tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Finds the truncated value of the parameter."), VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds vector to vector."), VisualShaderNodeVectorOp::OP_ADD, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides vector by vector."), VisualShaderNodeVectorOp::OP_DIV, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies vector by vector."), VisualShaderNodeVectorOp::OP_MUL, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two vectors."), VisualShaderNodeVectorOp::OP_MOD, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts vector from vector."), VisualShaderNodeVectorOp::OP_SUB, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("VectorConstant", "Vector", "Variables", "VisualShaderNodeVec3Constant", TTR("Vector constant."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("VectorUniform", "Vector", "Variables", "VisualShaderNodeVec3Uniform", TTR("Vector uniform."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + // SPECIAL + + add_options.push_back(AddOption("ScalarDerivativeFunc", "Special", "Common", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Scalar derivative function."), -1, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("VectorDerivativeFunc", "Special", "Common", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Vector derivative function."), -1, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + + add_options.push_back(AddOption("DdX", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), VisualShaderNodeVectorDerivativeFunc::FUNC_X, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("DdXS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Scalar) Derivative in 'x' using local differencing."), VisualShaderNodeScalarDerivativeFunc::FUNC_X, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("DdY", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), VisualShaderNodeVectorDerivativeFunc::FUNC_Y, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("DdYS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Scalar) Derivative in 'y' using local differencing."), VisualShaderNodeScalarDerivativeFunc::FUNC_Y, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("Sum", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), VisualShaderNodeVectorDerivativeFunc::FUNC_SUM, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("SumS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Scalar) Sum of absolute derivative in 'x' and 'y'."), VisualShaderNodeScalarDerivativeFunc::FUNC_SUM, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + + ///////////////////////////////////////////////////////////////////// _update_options_menu(); @@ -940,9 +1701,10 @@ public: void setup(const Ref<VisualShaderNodeInput> &p_input) { input = p_input; - Ref<Texture> type_icon[3] = { + Ref<Texture> type_icon[4] = { EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons"), EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons"), + EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons"), EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons"), }; diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 2fb8a85d5b..4b0b48ad92 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -60,7 +60,7 @@ class VisualShaderEditor : public VBoxContainer { Ref<VisualShader> visual_shader; GraphEdit *graph; - MenuButton *add_node; + ToolButton *add_node; OptionButton *edit_type; @@ -68,18 +68,59 @@ class VisualShaderEditor : public VBoxContainer { Label *error_label; UndoRedo *undo_redo; + Point2 saved_node_pos; + bool saved_node_pos_dirty; + + ConfirmationDialog *members_dialog; + MenuButton *tools; + + enum ToolsMenuOptions { + EXPAND_ALL, + COLLAPSE_ALL + }; + + Tree *members; + AcceptDialog *alert; + LineEdit *node_filter; + RichTextLabel *node_desc; + + void _tools_menu_option(int p_idx); + void _show_members_dialog(); void _update_graph(); struct AddOption { String name; String category; + String sub_category; String type; + String description; + int sub_func; + String sub_func_str; Ref<Script> script; - AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_type = String()) { + int mode; + int return_type; + + AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_sub_category = String(), const String &p_type = String(), const String &p_description = String(), int p_sub_func = -1, int p_return_type = -1, int p_mode = -1) { + name = p_name; + type = p_type; + category = p_category; + sub_category = p_sub_category; + description = p_description; + sub_func = p_sub_func; + return_type = p_return_type; + mode = p_mode; + } + + AddOption(const String &p_name, const String &p_category, const String &p_sub_category, const String &p_type, const String &p_description, const String &p_sub_func, int p_return_type = -1, int p_mode = -1) { name = p_name; type = p_type; category = p_category; + sub_category = p_sub_category; + description = p_description; + sub_func_str = p_sub_func; + return_type = p_return_type; + mode = p_mode; } }; @@ -87,7 +128,7 @@ class VisualShaderEditor : public VBoxContainer { void _draw_color_over_button(Object *obj, Color p_color); - void _add_node(int p_idx); + void _add_node(int p_idx, int p_op_idx = -1); void _update_options_menu(); static VisualShaderEditor *singleton; @@ -102,6 +143,7 @@ class VisualShaderEditor : public VBoxContainer { void _node_selected(Object *p_node); void _delete_request(int); + void _on_nodes_delete(); void _removed_from_graph(); @@ -126,6 +168,18 @@ class VisualShaderEditor : public VBoxContainer { void _preview_select_port(int p_node, int p_port); void _input(const Ref<InputEvent> p_event); + void _member_gui_input(const Ref<InputEvent> p_event); + void _member_filter_changed(const String &p_text); + void _member_selected(); + void _member_unselected(); + void _member_create(); + + Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); + bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; + void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + + bool _is_available(int p_flags); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index a3bcfa6e70..095ec891cd 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -1136,7 +1136,7 @@ void ProjectManager::_unhandled_input(const Ref<InputEvent> &p_ev) { break; } - // else fallthrough to key_down + FALLTHROUGH; } case KEY_DOWN: { diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 21ba6efb82..2d7ad8bc04 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -111,7 +111,7 @@ void ProjectSettingsEditor::_notification(int p_what) { restart_close_button->set_icon(get_icon("Close", "EditorIcons")); restart_container->add_style_override("panel", get_stylebox("bg", "Tree")); restart_icon->set_texture(get_icon("StatusWarning", "EditorIcons")); - restart_label->add_color_override("font_color", get_color("error_color", "Editor")); + restart_label->add_color_override("font_color", get_color("warning_color", "Editor")); } break; case NOTIFICATION_POPUP_HIDE: { @@ -294,7 +294,8 @@ void ProjectSettingsEditor::_device_input_add() { ie = jb; } break; - default: {} + default: { + } } if (idx < 0 || idx >= events.size()) { @@ -519,7 +520,8 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even } } break; - default: {} + default: { + } } } diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index c45a97800c..4fa1bd74fe 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -310,7 +310,8 @@ void CustomPropertyEditor::_menu_option(int p_which) { } } break; - default: {} + default: { + } } } @@ -1049,7 +1050,8 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant:: case Variant::POOL_COLOR_ARRAY: { } break; - default: {} + default: { + } } updating = false; @@ -1091,7 +1093,8 @@ void CustomPropertyEditor::_file_selected(String p_file) { emit_signal("variant_changed"); hide(); } break; - default: {} + default: { + } } } @@ -1432,7 +1435,8 @@ void CustomPropertyEditor::_action_pressed(int p_which) { } break; - default: {}; + default: { + }; } } @@ -1699,7 +1703,8 @@ void CustomPropertyEditor::_modified(String p_string) { case Variant::POOL_COLOR_ARRAY: { } break; - default: {} + default: { + } } updating = false; @@ -1753,7 +1758,8 @@ void CustomPropertyEditor::_focus_enter() { } } } break; - default: {} + default: { + } } } @@ -1774,7 +1780,8 @@ void CustomPropertyEditor::_focus_exit() { value_editor[i]->select(0, 0); } } break; - default: {} + default: { + } } } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 7e4861cd09..bd245d5da9 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -290,12 +290,18 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { switch (p_tool) { case TOOL_BATCH_RENAME: { + if (!profile_allow_editing) { + break; + } Tree *tree = scene_tree->get_scene_tree(); if (tree->is_anything_selected()) { rename_dialog->popup_centered(); } } break; case TOOL_RENAME: { + if (!profile_allow_editing) { + break; + } Tree *tree = scene_tree->get_scene_tree(); if (tree->is_anything_selected()) { tree->grab_focus(); @@ -304,6 +310,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_NEW: { + if (!profile_allow_editing) { + break; + } String preferred = ""; Node *current_edited_scene_root = EditorNode::get_singleton()->get_edited_scene(); @@ -319,6 +328,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_INSTANCE: { + if (!profile_allow_editing) { + break; + } Node *scene = edited_scene; if (!scene) { @@ -332,10 +344,17 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_REPLACE: { + if (!profile_allow_editing) { + break; + } create_dialog->popup_create(false, true); } break; case TOOL_ATTACH_SCRIPT: { + if (!profile_allow_script_editing) { + break; + } + List<Node *> selection = editor_selection->get_selected_node_list(); if (selection.empty()) break; @@ -377,6 +396,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_CLEAR_SCRIPT: { + if (!profile_allow_script_editing) { + break; + } + Array selection = editor_selection->get_selected_nodes(); if (selection.empty()) @@ -404,6 +427,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { case TOOL_MOVE_UP: case TOOL_MOVE_DOWN: { + if (!profile_allow_editing) { + break; + } + if (!scene_tree->get_selected()) break; @@ -464,6 +491,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_DUPLICATE: { + if (!profile_allow_editing) { + break; + } + if (!edited_scene) break; @@ -533,6 +564,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_REPARENT: { + if (!profile_allow_editing) { + break; + } + if (!scene_tree->get_selected()) break; @@ -559,6 +594,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_MAKE_ROOT: { + if (!profile_allow_editing) { + break; + } + List<Node *> nodes = editor_selection->get_selected_node_list(); ERR_FAIL_COND(nodes.size() != 1); @@ -616,6 +655,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_MULTI_EDIT: { + if (!profile_allow_editing) { + break; + } + Node *root = EditorNode::get_singleton()->get_edited_scene(); if (!root) break; @@ -630,6 +673,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { case TOOL_ERASE: { + if (!profile_allow_editing) { + break; + } + List<Node *> remove_list = editor_selection->get_selected_node_list(); if (remove_list.empty()) @@ -649,10 +696,18 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_MERGE_FROM_SCENE: { + if (!profile_allow_editing) { + break; + } + EditorNode::get_singleton()->merge_from_scene(); } break; case TOOL_NEW_SCENE_FROM: { + if (!profile_allow_editing) { + break; + } + Node *scene = editor_data->get_edited_scene_root(); if (!scene) { @@ -704,6 +759,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { new_scene_from_dialog->set_title(TTR("Save New Scene As...")); } break; case TOOL_COPY_NODE_PATH: { + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -723,6 +779,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); } break; case TOOL_SCENE_EDITABLE_CHILDREN: { + + if (!profile_allow_editing) { + break; + } + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -740,6 +801,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } break; case TOOL_SCENE_USE_PLACEHOLDER: { + + if (!profile_allow_editing) { + break; + } + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -760,6 +826,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } break; case TOOL_SCENE_MAKE_LOCAL: { + + if (!profile_allow_editing) { + break; + } + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -782,6 +853,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } break; case TOOL_SCENE_OPEN: { + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -792,9 +864,17 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } break; case TOOL_SCENE_CLEAR_INHERITANCE: { + if (!profile_allow_editing) { + break; + } + clear_inherit_confirm->popup_centered_minsize(); } break; case TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM: { + if (!profile_allow_editing) { + break; + } + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -807,6 +887,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } break; case TOOL_SCENE_OPEN_INHERITED: { + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -894,6 +975,8 @@ void SceneTreeDock::_notification(int p_what) { break; first_enter = false; + EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", this, "_feature_profile_changed"); + CanvasItemEditorPlugin *canvas_item_plugin = Object::cast_to<CanvasItemEditorPlugin>(editor_data->get_editor("2D")); if (canvas_item_plugin) { canvas_item_plugin->get_canvas_item_editor()->connect("item_lock_status_changed", scene_tree, "_update_tree"); @@ -1640,7 +1723,12 @@ void SceneTreeDock::_delete_confirm() { } void SceneTreeDock::_update_script_button() { - if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 0) { + + if (!profile_allow_script_editing) { + + button_create_script->hide(); + button_clear_script->hide(); + } else if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 0) { button_create_script->hide(); button_clear_script->hide(); } else if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 1) { @@ -1653,7 +1741,7 @@ void SceneTreeDock::_update_script_button() { button_clear_script->show(); } } else { - button_create_script->show(); + button_create_script->hide(); Array selection = editor_selection->get_selected_nodes(); for (int i = 0; i < selection.size(); i++) { Node *n = Object::cast_to<Node>(selection[i]); @@ -1760,6 +1848,8 @@ void SceneTreeDock::_create() { replace_node(n, newnode); } } + + scene_tree->get_scene_tree()->call_deferred("grab_focus"); } void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties) { @@ -2074,11 +2164,14 @@ void SceneTreeDock::_add_children_to_popup(Object *p_obj, int p_depth) { } void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { + if (!EditorNode::get_singleton()->get_edited_scene()) { menu->clear(); - menu->add_icon_shortcut(get_icon("Add", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_child_node"), TOOL_NEW); - menu->add_icon_shortcut(get_icon("Instance", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/instance_scene"), TOOL_INSTANCE); + if (profile_allow_editing) { + menu->add_icon_shortcut(get_icon("Add", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_child_node"), TOOL_NEW); + menu->add_icon_shortcut(get_icon("Instance", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/instance_scene"), TOOL_INSTANCE); + } menu->set_size(Size2(1, 1)); menu->set_position(p_menu_pos); @@ -2098,80 +2191,101 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { Node *selected = selection[0]; - subresources.clear(); - menu_subresources->clear(); - menu_subresources->set_size(Size2(1, 1)); - _add_children_to_popup(selection.front()->get(), 0); - if (menu->get_item_count() > 0) - menu->add_separator(); + if (profile_allow_editing) { + subresources.clear(); + menu_subresources->clear(); + menu_subresources->set_size(Size2(1, 1)); + _add_children_to_popup(selection.front()->get(), 0); + if (menu->get_item_count() > 0) + menu->add_separator(); - menu->add_icon_shortcut(get_icon("Add", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_child_node"), TOOL_NEW); - menu->add_icon_shortcut(get_icon("Instance", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/instance_scene"), TOOL_INSTANCE); - menu->add_separator(); + menu->add_icon_shortcut(get_icon("Add", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_child_node"), TOOL_NEW); + menu->add_icon_shortcut(get_icon("Instance", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/instance_scene"), TOOL_INSTANCE); + menu->add_separator(); + } existing_script = selected->get_script(); } - if (!existing_script.is_valid()) { - menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); - } - if (selection.size() > 1 || existing_script.is_valid()) { - menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); - menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT); - } + if (profile_allow_script_editing) { - menu->add_separator(); - if (selection.size() == 1) { - menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME); + if (selection.size() == 1) { + if (!existing_script.is_valid()) { + menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); + } else { + menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT); + } + } + if (selection.size() > 1 || existing_script.is_valid()) { + menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); + } + menu->add_separator(); } - menu->add_icon_shortcut(get_icon("Reload", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE); - if (scene_tree->get_selected() != edited_scene) { - menu->add_separator(); - menu->add_icon_shortcut(get_icon("MoveUp", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_up"), TOOL_MOVE_UP); - menu->add_icon_shortcut(get_icon("MoveDown", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_down"), TOOL_MOVE_DOWN); - menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE); - menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT); + if (profile_allow_editing) { + if (selection.size() == 1) { + menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME); + } + menu->add_icon_shortcut(get_icon("Reload", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE); + + if (scene_tree->get_selected() != edited_scene) { + menu->add_separator(); + menu->add_icon_shortcut(get_icon("MoveUp", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_up"), TOOL_MOVE_UP); + menu->add_icon_shortcut(get_icon("MoveDown", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_down"), TOOL_MOVE_DOWN); + menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE); + menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT); + menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT); + } } if (selection.size() == 1) { - menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT); - menu->add_separator(); - menu->add_icon_shortcut(get_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE); - menu->add_icon_shortcut(get_icon("CreateNewSceneFrom", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/save_branch_as_scene"), TOOL_NEW_SCENE_FROM); - menu->add_separator(); + if (profile_allow_editing) { + menu->add_separator(); + menu->add_icon_shortcut(get_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE); + menu->add_icon_shortcut(get_icon("CreateNewSceneFrom", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/save_branch_as_scene"), TOOL_NEW_SCENE_FROM); + menu->add_separator(); + } menu->add_icon_shortcut(get_icon("CopyNodePath", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/copy_node_path"), TOOL_COPY_NODE_PATH); + bool is_external = (selection[0]->get_filename() != ""); if (is_external) { bool is_inherited = selection[0]->get_scene_inherited_state() != NULL; bool is_top_level = selection[0]->get_owner() == NULL; if (is_inherited && is_top_level) { menu->add_separator(); - menu->add_item(TTR("Clear Inheritance"), TOOL_SCENE_CLEAR_INHERITANCE); + if (profile_allow_editing) { + menu->add_item(TTR("Clear Inheritance"), TOOL_SCENE_CLEAR_INHERITANCE); + } menu->add_icon_item(get_icon("Load", "EditorIcons"), TTR("Open in Editor"), TOOL_SCENE_OPEN_INHERITED); } else if (!is_top_level) { menu->add_separator(); bool editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(selection[0]); bool placeholder = selection[0]->get_scene_instance_load_placeholder(); - menu->add_check_item(TTR("Editable Children"), TOOL_SCENE_EDITABLE_CHILDREN); - menu->add_check_item(TTR("Load As Placeholder"), TOOL_SCENE_USE_PLACEHOLDER); - menu->add_item(TTR("Make Local"), TOOL_SCENE_MAKE_LOCAL); + if (profile_allow_editing) { + menu->add_check_item(TTR("Editable Children"), TOOL_SCENE_EDITABLE_CHILDREN); + menu->add_check_item(TTR("Load As Placeholder"), TOOL_SCENE_USE_PLACEHOLDER); + menu->add_item(TTR("Make Local"), TOOL_SCENE_MAKE_LOCAL); + } menu->add_icon_item(get_icon("Load", "EditorIcons"), TTR("Open in Editor"), TOOL_SCENE_OPEN); - menu->set_item_checked(menu->get_item_idx_from_text(TTR("Editable Children")), editable); - menu->set_item_checked(menu->get_item_idx_from_text(TTR("Load As Placeholder")), placeholder); + if (profile_allow_editing) { + menu->set_item_checked(menu->get_item_idx_from_text(TTR("Editable Children")), editable); + menu->set_item_checked(menu->get_item_idx_from_text(TTR("Load As Placeholder")), placeholder); + } } } } - if (selection.size() > 1) { + if (profile_allow_editing && selection.size() > 1) { //this is not a commonly used action, it makes no sense for it to be where it was nor always present. menu->add_separator(); menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/batch_rename"), TOOL_BATCH_RENAME); } menu->add_separator(); - menu->add_icon_item(get_icon("Help", "EditorIcons"), TTR("Open documentation"), TOOL_OPEN_DOCUMENTATION); + menu->add_icon_item(get_icon("Help", "EditorIcons"), TTR("Open Documentation"), TOOL_OPEN_DOCUMENTATION); - menu->add_separator(); - menu->add_icon_shortcut(get_icon("Remove", "EditorIcons"), ED_SHORTCUT("scene_tree/delete", TTR("Delete Node(s)"), KEY_DELETE), TOOL_ERASE); + if (profile_allow_editing) { + menu->add_separator(); + menu->add_icon_shortcut(get_icon("Remove", "EditorIcons"), ED_SHORTCUT("scene_tree/delete", TTR("Delete Node(s)"), KEY_DELETE), TOOL_ERASE); + } menu->set_size(Size2(1, 1)); menu->set_position(p_menu_pos); menu->popup(); @@ -2321,6 +2435,30 @@ void SceneTreeDock::_favorite_root_selected(const String &p_class) { _tool_selected(TOOL_CREATE_FAVORITE, false); } +void SceneTreeDock::_feature_profile_changed() { + + Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); + + if (profile.is_valid()) { + + profile_allow_editing = !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCENE_TREE); + profile_allow_script_editing = !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT); + + button_add->set_visible(profile_allow_editing); + button_instance->set_visible(profile_allow_editing); + scene_tree->set_can_rename(profile_allow_editing); + + } else { + button_add->set_visible(true); + button_instance->set_visible(true); + scene_tree->set_can_rename(true); + profile_allow_editing = true; + profile_allow_script_editing = true; + } + + _update_script_button(); +} + void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_tool_selected"), &SceneTreeDock::_tool_selected, DEFVAL(false)); @@ -2353,6 +2491,7 @@ void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_script_button"), &SceneTreeDock::_update_script_button); ClassDB::bind_method(D_METHOD("_favorite_root_selected"), &SceneTreeDock::_favorite_root_selected); ClassDB::bind_method(D_METHOD("_update_create_root_dialog"), &SceneTreeDock::_update_create_root_dialog); + ClassDB::bind_method(D_METHOD("_feature_profile_changed"), &SceneTreeDock::_feature_profile_changed); ClassDB::bind_method(D_METHOD("instance"), &SceneTreeDock::instance); @@ -2372,7 +2511,6 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel HBoxContainer *filter_hbc = memnew(HBoxContainer); filter_hbc->add_constant_override("separate", 0); - ToolButton *tb; ED_SHORTCUT("scene_tree/rename", TTR("Rename")); ED_SHORTCUT("scene_tree/batch_rename", TTR("Batch Rename"), KEY_MASK_CMD | KEY_F2); @@ -2393,19 +2531,17 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel ED_SHORTCUT("scene_tree/delete_no_confirm", TTR("Delete (No Confirm)"), KEY_MASK_SHIFT | KEY_DELETE); ED_SHORTCUT("scene_tree/delete", TTR("Delete"), KEY_DELETE); - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false)); - tb->set_tooltip(TTR("Add/Create a New Node")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_child_node")); - filter_hbc->add_child(tb); - button_add = tb; + button_add = memnew(ToolButton); + button_add->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false)); + button_add->set_tooltip(TTR("Add/Create a New Node")); + button_add->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_child_node")); + filter_hbc->add_child(button_add); - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_INSTANCE, false)); - tb->set_tooltip(TTR("Instance a scene file as a Node. Creates an inherited scene if no root node exists.")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/instance_scene")); - filter_hbc->add_child(tb); - button_instance = tb; + button_instance = memnew(ToolButton); + button_instance->connect("pressed", this, "_tool_selected", make_binds(TOOL_INSTANCE, false)); + button_instance->set_tooltip(TTR("Instance a scene file as a Node. Creates an inherited scene if no root node exists.")); + button_instance->set_shortcut(ED_GET_SHORTCUT("scene_tree/instance_scene")); + filter_hbc->add_child(button_instance); vbc->add_child(filter_hbc); filter = memnew(LineEdit); @@ -2415,21 +2551,19 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel filter->add_constant_override("minimum_spaces", 0); filter->connect("text_changed", this, "_filter_changed"); - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_ATTACH_SCRIPT, false)); - tb->set_tooltip(TTR("Attach a new or existing script for the selected node.")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/attach_script")); - filter_hbc->add_child(tb); - tb->hide(); - button_create_script = tb; - - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_CLEAR_SCRIPT, false)); - tb->set_tooltip(TTR("Clear a script for the selected node.")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/clear_script")); - filter_hbc->add_child(tb); - button_clear_script = tb; - tb->hide(); + button_create_script = memnew(ToolButton); + button_create_script->connect("pressed", this, "_tool_selected", make_binds(TOOL_ATTACH_SCRIPT, false)); + button_create_script->set_tooltip(TTR("Attach a new or existing script for the selected node.")); + button_create_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/attach_script")); + filter_hbc->add_child(button_create_script); + button_create_script->hide(); + + button_clear_script = memnew(ToolButton); + button_clear_script->connect("pressed", this, "_tool_selected", make_binds(TOOL_CLEAR_SCRIPT, false)); + button_clear_script->set_tooltip(TTR("Clear a script for the selected node.")); + button_clear_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/clear_script")); + filter_hbc->add_child(button_clear_script); + button_clear_script->hide(); button_hb = memnew(HBoxContainer); vbc->add_child(button_hb); @@ -2537,6 +2671,9 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel set_process_input(true); set_process(true); + profile_allow_editing = true; + profile_allow_script_editing = true; + EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true); EDITOR_DEF("_use_favorites_root_selection", false); } diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 653d0a4eca..e66525d721 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -212,6 +212,11 @@ class SceneTreeDock : public VBoxContainer { void _update_create_root_dialog(); void _favorite_root_selected(const String &p_class); + void _feature_profile_changed(); + + bool profile_allow_editing; + bool profile_allow_script_editing; + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 8cfc6d4e8e..f2d11c2753 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -48,6 +48,9 @@ Node *SceneTreeEditor::get_scene_node() { void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_id) { + if (connect_to_script_mode) { + return; //dont do anything in this mode + } TreeItem *item = Object::cast_to<TreeItem>(p_item); ERR_FAIL_COND(!item); @@ -190,7 +193,25 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { item->set_icon(0, icon); item->set_metadata(0, p_node->get_path()); - if (part_of_subscene) { + if (connect_to_script_mode) { + Color accent = get_color("accent_color", "Editor"); + + if (!p_node->get_script().is_null()) { + //has script + item->add_button(0, get_icon("Script", "EditorIcons"), BUTTON_SCRIPT); + } else { + //has no script + item->set_custom_color(0, get_color("disabled_font_color", "Editor")); + item->set_selectable(0, false); + accent.a *= 0.7; + } + + if (marked.has(p_node)) { + item->set_text(0, String(p_node->get_name()) + " " + TTR("(Connecting From)")); + + item->set_custom_color(0, accent); + } + } else if (part_of_subscene) { //item->set_selectable(0,marked_selectable); if (valid_types.size() == 0) { @@ -199,7 +220,9 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { } else if (marked.has(p_node)) { - item->set_selectable(0, marked_selectable); + if (!connect_to_script_mode) { + item->set_selectable(0, marked_selectable); + } item->set_custom_color(0, get_color("error_color", "Editor")); } else if (!marked_selectable && !marked_children_selectable) { @@ -244,7 +267,7 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { item->set_tooltip(0, String(p_node->get_name()) + "\n" + TTR("Type:") + " " + p_node->get_class()); } - if (can_open_instance) { + if (can_open_instance && undo_redo) { //Show buttons only when necessary(SceneTreeDock) to avoid crashes if (!p_node->is_connected("script_changed", this, "_node_script_changed")) p_node->connect("script_changed", this, "_node_script_changed", varray(p_node)); @@ -620,6 +643,7 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) { item->set_as_cursor(0); selected = p_node; tree->ensure_cursor_is_visible(); + } else { if (!p_node) selected = NULL; @@ -974,6 +998,11 @@ void SceneTreeEditor::_warning_changed(Node *p_for_node) { update_timer->start(); } +void SceneTreeEditor::set_connect_to_script_mode(bool p_enable) { + connect_to_script_mode = p_enable; + update_tree(); +} + void SceneTreeEditor::_bind_methods() { ClassDB::bind_method("_tree_changed", &SceneTreeEditor::_tree_changed); @@ -1016,6 +1045,7 @@ void SceneTreeEditor::_bind_methods() { SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_open_instance) { + connect_to_script_mode = false; undo_redo = NULL; tree_dirty = true; selected = NULL; @@ -1091,22 +1121,17 @@ SceneTreeEditor::~SceneTreeEditor() { void SceneTreeDialog::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - connect("confirmed", this, "_select"); - } - - if (p_what == NOTIFICATION_EXIT_TREE) { - disconnect("confirmed", this, "_select"); - } - if (p_what == NOTIFICATION_DRAW) { - - RID ci = get_canvas_item(); - get_stylebox("panel", "PopupMenu")->draw(ci, Rect2(Point2(), get_size())); - } - - if (p_what == NOTIFICATION_VISIBILITY_CHANGED && is_visible_in_tree()) { - - tree->update_tree(); + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + connect("confirmed", this, "_select"); + } break; + case NOTIFICATION_EXIT_TREE: { + disconnect("confirmed", this, "_select"); + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (is_visible_in_tree()) + tree->update_tree(); + } break; } } @@ -1135,8 +1160,6 @@ SceneTreeDialog::SceneTreeDialog() { tree = memnew(SceneTreeEditor(false, false, true)); add_child(tree); - //set_child_rect(tree); - tree->get_scene_tree()->connect("item_activated", this, "_select"); } diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index aa4d4dd58a..9158c4aa48 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -67,6 +67,8 @@ class SceneTreeEditor : public Control { AcceptDialog *error; AcceptDialog *warning; + bool connect_to_script_mode; + int blocked; void _compute_hash(Node *p_node, uint64_t &hash); @@ -151,6 +153,8 @@ public: void update_tree() { _update_tree(); } + void set_connect_to_script_mode(bool p_enable); + Tree *get_scene_tree() { return tree; } SceneTreeEditor(bool p_label = true, bool p_can_rename = false, bool p_can_open_instance = false); diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index dda46d2414..717d6bc8f6 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -303,12 +303,49 @@ void ScriptEditorDebugger::_scene_tree_rmb_selected(const Vector2 &p_position) { } void ScriptEditorDebugger::_file_selected(const String &p_file) { - if (file_dialog->get_mode() == EditorFileDialog::MODE_SAVE_FILE) { + if (file_dialog_mode == SAVE_NODE) { + Array msg; msg.push_back("save_node"); msg.push_back(inspected_object_id); msg.push_back(p_file); ppeer->put_var(msg); + } else if (file_dialog_mode == SAVE_CSV) { + + Error err; + FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err); + + if (err != OK) { + ERR_PRINTS("Failed to open " + p_file); + return; + } + Vector<String> line; + line.resize(Performance::MONITOR_MAX); + + // signatures + for (int i = 0; i < Performance::MONITOR_MAX; i++) { + line.write[i] = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)); + } + file->store_csv_line(line); + + // values + List<Vector<float> >::Element *E = perf_history.back(); + while (E) { + + Vector<float> &perf_data = E->get(); + for (int i = 0; i < perf_data.size(); i++) { + + line.write[i] = String::num_real(perf_data[i]); + } + file->store_csv_line(line); + E = E->prev(); + } + file->store_string("\n"); + + Vector<Vector<String> > profiler_data = profiler->get_data_as_csv(); + for (int i = 0; i < profiler_data.size(); i++) { + file->store_csv_line(profiler_data[i]); + } } } @@ -1374,6 +1411,13 @@ void ScriptEditorDebugger::_output_clear() { //output->push_color(Color(0,0,0)); } +void ScriptEditorDebugger::_export_csv() { + + file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); + file_dialog_mode = SAVE_CSV; + file_dialog->popup_centered_ratio(); +} + String ScriptEditorDebugger::get_var_value(const String &p_var) const { if (!breaked) return String(); @@ -1859,6 +1903,7 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) { file_dialog->set_access(EditorFileDialog::ACCESS_RESOURCES); file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); + file_dialog_mode = SAVE_NODE; List<String> extensions; Ref<PackedScene> sd = memnew(PackedScene); @@ -1884,6 +1929,7 @@ void ScriptEditorDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("debug_break"), &ScriptEditorDebugger::debug_break); ClassDB::bind_method(D_METHOD("debug_continue"), &ScriptEditorDebugger::debug_continue); ClassDB::bind_method(D_METHOD("_output_clear"), &ScriptEditorDebugger::_output_clear); + ClassDB::bind_method(D_METHOD("_export_csv"), &ScriptEditorDebugger::_export_csv); ClassDB::bind_method(D_METHOD("_performance_draw"), &ScriptEditorDebugger::_performance_draw); ClassDB::bind_method(D_METHOD("_performance_select"), &ScriptEditorDebugger::_performance_select); ClassDB::bind_method(D_METHOD("_scene_tree_request"), &ScriptEditorDebugger::_scene_tree_request); @@ -2205,10 +2251,13 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { } { // misc + VBoxContainer *misc = memnew(VBoxContainer); + misc->set_name(TTR("Misc")); + tabs->add_child(misc); + GridContainer *info_left = memnew(GridContainer); info_left->set_columns(2); - info_left->set_name(TTR("Misc")); - tabs->add_child(info_left); + misc->add_child(info_left); clicked_ctrl = memnew(LineEdit); clicked_ctrl->set_h_size_flags(SIZE_EXPAND_FILL); info_left->add_child(memnew(Label(TTR("Clicked Control:")))); @@ -2233,6 +2282,16 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { le_set->set_disabled(true); le_clear->set_disabled(true); } + + misc->add_child(memnew(VSeparator)); + + HBoxContainer *buttons = memnew(HBoxContainer); + + export_csv = memnew(Button(TTR("Export measures as CSV"))); + export_csv->connect("pressed", this, "_export_csv"); + buttons->add_child(export_csv); + + misc->add_child(buttons); } msgdialog = memnew(AcceptDialog); diff --git a/editor/script_editor_debugger.h b/editor/script_editor_debugger.h index fb1545559c..5f21602579 100644 --- a/editor/script_editor_debugger.h +++ b/editor/script_editor_debugger.h @@ -77,6 +77,7 @@ class ScriptEditorDebugger : public Control { LineEdit *live_edit_root; Button *le_set; Button *le_clear; + Button *export_csv; bool updating_scene_tree; float inspect_scene_tree_timeout; @@ -92,7 +93,13 @@ class ScriptEditorDebugger : public Control { Tree *inspect_scene_tree; Button *clearbutton; PopupMenu *item_menu; + EditorFileDialog *file_dialog; + enum FileDialogMode { + SAVE_CSV, + SAVE_NODE, + }; + FileDialogMode file_dialog_mode; int error_count; int warning_count; @@ -196,6 +203,8 @@ class ScriptEditorDebugger : public Control { void _error_tree_item_rmb_selected(const Vector2 &p_pos); void _item_menu_id_pressed(int p_option); + void _export_csv(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index 611067214d..d6756b2bb3 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -187,7 +187,7 @@ void EditorSettingsDialog::_update_icons() { restart_close_button->set_icon(get_icon("Close", "EditorIcons")); restart_container->add_style_override("panel", get_stylebox("bg", "Tree")); restart_icon->set_texture(get_icon("StatusWarning", "EditorIcons")); - restart_label->add_color_override("font_color", get_color("error_color", "Editor")); + restart_label->add_color_override("font_color", get_color("warning_color", "Editor")); } void EditorSettingsDialog::_update_shortcuts() { diff --git a/editor/translations/ca.po b/editor/translations/ca.po index 27524d5417..75f6c53145 100644 --- a/editor/translations/ca.po +++ b/editor/translations/ca.po @@ -6,12 +6,13 @@ # Javier Ocampos <xavier.ocampos@gmail.com>, 2018. # Roger Blanco Ribera <roger.blancoribera@gmail.com>, 2016-2018. # Rubén Moreno <ruben.moreno.romero@gmail.com>, 2018. +# roger <616steam@gmail.com>, 2019. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-12-13 14:38+0100\n" -"Last-Translator: Roger Blanco Ribera <roger.blancoribera@gmail.com>\n" +"PO-Revision-Date: 2019-04-08 10:35+0000\n" +"Last-Translator: roger <616steam@gmail.com>\n" "Language-Team: Catalan <https://hosted.weblate.org/projects/godot-engine/" "godot/ca/>\n" "Language: ca\n" @@ -19,7 +20,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Poedit 2.2\n" +"X-Generator: Weblate 3.6-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -86,9 +87,8 @@ msgid "Delete Selected Key(s)" msgstr "Elimina les Claus seleccionades" #: editor/animation_bezier_editor.cpp -#, fuzzy msgid "Add Bezier Point" -msgstr "Afegeix un punt" +msgstr "Afegir punt Bezier" #: editor/animation_bezier_editor.cpp #, fuzzy @@ -329,9 +329,8 @@ msgid "Change Animation Step" msgstr "Modifica els FPS de l'Animació" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Rearrange Tracks" -msgstr "Reorganitza AutoCà rregues" +msgstr "Reorganitza pistes" #: editor/animation_track_editor.cpp msgid "Transform tracks only apply to Spatial-based nodes." @@ -383,9 +382,8 @@ msgid "Add Transform Track Key" msgstr "Pista de Transformació 3D" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Add Track Key" -msgstr "Afegeix una Pista" +msgstr "Afegir Clau de Pista" #: editor/animation_track_editor.cpp msgid "Track path is invalid, so can't add a method key." @@ -393,9 +391,8 @@ msgstr "" "No s'hi pot afegit cap clau de mètode. El camà de la pista no és và lid." #: editor/animation_track_editor.cpp -#, fuzzy msgid "Add Method Track Key" -msgstr "Pista de Crida de Mètodes" +msgstr "Afegir Clau de Pista de Mètode" #: editor/animation_track_editor.cpp msgid "Method not found in object: " @@ -1379,11 +1376,8 @@ msgid "Storing File:" msgstr "Emmagatzemant Fitxer:" #: editor/editor_export.cpp -#, fuzzy msgid "No export template found at the expected path:" -msgstr "" -"No s'ha trobat cap plantilla.\n" -"Descarregueu i instal·leu alguna plantilla d'exportació." +msgstr "No s'ha trobat cap plantilla d'exportació en la ruta esperada:" #: editor/editor_export.cpp msgid "Packing" @@ -1412,9 +1406,8 @@ msgstr "" #: editor/editor_export.cpp platform/android/export/export.cpp #: platform/iphone/export/export.cpp platform/javascript/export/export.cpp #: platform/osx/export/export.cpp platform/uwp/export/export.cpp -#, fuzzy msgid "Custom debug template not found." -msgstr "No s'ha trobat cap paquet de depuració personalitzat." +msgstr "No s'ha trobat cap plantilla de depuració personalitzada." #: editor/editor_export.cpp platform/android/export/export.cpp #: platform/iphone/export/export.cpp platform/javascript/export/export.cpp @@ -1535,14 +1528,12 @@ msgid "Move Favorite Down" msgstr "Mou Favorit Avall" #: editor/editor_file_dialog.cpp -#, fuzzy msgid "Previous Folder" -msgstr "Planta Anterior" +msgstr "Directori Anterior" #: editor/editor_file_dialog.cpp -#, fuzzy msgid "Next Folder" -msgstr "Planta Següent" +msgstr "Directori Següent" #: editor/editor_file_dialog.cpp msgid "Go to parent folder" @@ -1619,19 +1610,16 @@ msgid "Methods" msgstr "Mètodes" #: editor/editor_help.cpp -#, fuzzy msgid "Methods:" -msgstr "Mètodes" +msgstr "Mètodes:" #: editor/editor_help.cpp -#, fuzzy msgid "Theme Properties" -msgstr "Propietats" +msgstr "Propietats del tema" #: editor/editor_help.cpp -#, fuzzy msgid "Theme Properties:" -msgstr "Propietats:" +msgstr "Propietats del tema:" #: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp msgid "Signals:" @@ -1658,14 +1646,12 @@ msgid "Constants:" msgstr "Constants:" #: editor/editor_help.cpp -#, fuzzy msgid "Class Description" -msgstr "Descripció" +msgstr "Descripció de la classe" #: editor/editor_help.cpp -#, fuzzy msgid "Class Description:" -msgstr "Descripció:" +msgstr "Descripció de la classe:" #: editor/editor_help.cpp msgid "Online Tutorials:" @@ -1728,34 +1714,28 @@ msgid "Display All" msgstr "Mostra les Normals" #: editor/editor_help_search.cpp -#, fuzzy msgid "Classes Only" -msgstr "Classes" +msgstr "Només Classes" #: editor/editor_help_search.cpp -#, fuzzy msgid "Methods Only" -msgstr "Mètodes" +msgstr "Només Mètodes" #: editor/editor_help_search.cpp -#, fuzzy msgid "Signals Only" -msgstr "Senyals" +msgstr "Només Senyals" #: editor/editor_help_search.cpp -#, fuzzy msgid "Constants Only" -msgstr "Constants" +msgstr "Només Constants" #: editor/editor_help_search.cpp -#, fuzzy msgid "Properties Only" -msgstr "Propietats" +msgstr "Només Propietats" #: editor/editor_help_search.cpp -#, fuzzy msgid "Theme Properties Only" -msgstr "Propietats" +msgstr "Només Propietats del Tema" #: editor/editor_help_search.cpp #, fuzzy @@ -1763,9 +1743,8 @@ msgid "Member Type" msgstr "Membres" #: editor/editor_help_search.cpp -#, fuzzy msgid "Class" -msgstr "Classe:" +msgstr "Classe" #: editor/editor_inspector.cpp editor/project_settings_editor.cpp msgid "Property:" @@ -1803,7 +1782,7 @@ msgstr "L'exportació del projecte ha fallat amb el codi d'error %d." #: editor/editor_node.cpp msgid "Imported resources can't be saved." -msgstr "" +msgstr "Els recursos importats no es poden guardar." #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp #: scene/gui/dialogs.cpp @@ -2043,7 +2022,7 @@ msgstr "No s'ha pogut carregar el recurs." #: editor/editor_node.cpp msgid "A root node is required to save the scene." -msgstr "" +msgstr "Es requereix un node arrel per a guardar l'escena." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -2797,6 +2776,8 @@ msgid "" "The selected resource (%s) does not match any type expected for this " "property (%s)." msgstr "" +"El recurs seleccionat (%s) no coincideix amb cap tipus esperat per aquesta " +"propietat (%s)." #: editor/editor_properties.cpp msgid "" @@ -3126,9 +3107,8 @@ msgstr "" "tipus de fitxers!" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Favorites" -msgstr "Favorits:" +msgstr "Preferits" #: editor/filesystem_dock.cpp msgid "Cannot navigate to '%s' as it has not been found in the file system!" @@ -3199,14 +3179,12 @@ msgid "Instance" msgstr "Instà ncia" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Add to favorites" -msgstr "Favorits:" +msgstr "Afegir a preferits" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Remove from favorites" -msgstr "Treu del Grup" +msgstr "Eliminar dels preferits" #: editor/filesystem_dock.cpp msgid "Edit Dependencies..." @@ -3307,14 +3285,12 @@ msgid "Find:" msgstr "Cerca: " #: editor/find_in_files.cpp -#, fuzzy msgid "Folder:" -msgstr "Directori : " +msgstr "Directori: " #: editor/find_in_files.cpp -#, fuzzy msgid "Filters:" -msgstr "Filtres" +msgstr "Filtres:" #: editor/find_in_files.cpp editor/plugins/script_editor_plugin.cpp #: editor/plugins/script_text_editor.cpp @@ -3492,14 +3468,16 @@ msgid "Save scenes, re-import and restart" msgstr "" #: editor/import_dock.cpp -#, fuzzy msgid "Changing the type of an imported file requires editor restart." -msgstr "Canviar el controlador de vÃdeo requereix reiniciar l'editor." +msgstr "Canviar el tipus d'un fitxer importat requereix reiniciar l'editor." #: editor/import_dock.cpp +#, fuzzy msgid "" "WARNING: Assets exist that use this resource, they may stop loading properly." msgstr "" +"ADVERTIMENT: Existeixen actius que utilitzen aquest recurs, es possible que " +"deixin de carregar-se correctament." #: editor/inspector_dock.cpp msgid "Failed to load resource." @@ -3622,9 +3600,8 @@ msgstr "Activar ara?" #: editor/plugins/abstract_polygon_2d_editor.cpp #: editor/plugins/polygon_2d_editor_plugin.cpp -#, fuzzy msgid "Create Polygon" -msgstr "Crea PolÃgon" +msgstr "Crear PolÃgon" #: editor/plugins/abstract_polygon_2d_editor.cpp #: editor/plugins/animation_blend_space_1d_editor.cpp @@ -3639,10 +3616,9 @@ msgid "" "LMB: Move Point\n" "RMB: Erase Point" msgstr "" -"Edita un PolÃgon existent:\n" -"Clic Esquerra: Mou un Punt.\n" -"Ctrl+Clic Esquerra: Divideix un Segment.\n" -"Clic Dreta: Elimina un Punt." +"Editar punts:\n" +"Clic Esquerra: Moure Punt.\n" +"Clic Dreta: Eliminar Punt." #: editor/plugins/abstract_polygon_2d_editor.cpp #: editor/plugins/animation_blend_space_1d_editor.cpp @@ -3650,9 +3626,8 @@ msgid "Erase points." msgstr "Elimina un Punt." #: editor/plugins/abstract_polygon_2d_editor.cpp -#, fuzzy msgid "Edit Polygon" -msgstr "Edita PolÃgon" +msgstr "Editar PolÃgon" #: editor/plugins/abstract_polygon_2d_editor.cpp msgid "Insert Point" @@ -3772,9 +3747,8 @@ msgid "Triangle already exists" msgstr "El triangle ja existeix" #: editor/plugins/animation_blend_space_2d_editor.cpp -#, fuzzy msgid "Add Triangle" -msgstr "Afegeix una Variable" +msgstr "Afegir Triangle" #: editor/plugins/animation_blend_space_2d_editor.cpp #, fuzzy @@ -3827,9 +3801,8 @@ msgid "Blend:" msgstr "Mescla:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed" -msgstr "Canvis de Material" +msgstr "Parà metre Canviat" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -3847,9 +3820,8 @@ msgstr "Afegeix Nodes des d'Arbre" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Node Moved" -msgstr "Mode de moviment" +msgstr "Node mogut" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp @@ -3860,7 +3832,7 @@ msgstr "No es pot connectar. El port és en ús o la connexió no és và lida." #: editor/plugins/visual_shader_editor_plugin.cpp #, fuzzy msgid "Nodes Connected" -msgstr "Connectat" +msgstr "Nodes Connectats" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp @@ -4171,11 +4143,11 @@ msgstr "Final/s" #: editor/plugins/animation_state_machine_editor.cpp msgid "Immediate" -msgstr "" +msgstr "Immediat" #: editor/plugins/animation_state_machine_editor.cpp msgid "Sync" -msgstr "" +msgstr "Sincronitzar" #: editor/plugins/animation_state_machine_editor.cpp msgid "At End" @@ -4484,7 +4456,7 @@ msgstr "Següent" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Last" -msgstr "" +msgstr "Últim" #: editor/plugins/asset_library_editor_plugin.cpp #: modules/gdnative/gdnative_library_editor_plugin.cpp @@ -4946,9 +4918,8 @@ msgstr "" "Arrossegar i deixar anar + Maj: Canvia el tipus del node" #: editor/plugins/collision_polygon_editor_plugin.cpp -#, fuzzy msgid "Create Polygon3D" -msgstr "Crea PolÃgon" +msgstr "Crear PolÃgon3D" #: editor/plugins/collision_polygon_editor_plugin.cpp msgid "Edit Poly" @@ -5697,7 +5668,7 @@ msgstr "" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Radius:" -msgstr "" +msgstr "Radi:" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Polygon->UV" @@ -5832,9 +5803,8 @@ msgid "Error writing TextFile:" msgstr "Error en desar TileSet!" #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Error: could not load file." -msgstr "Error - No s'ha pogut crea l'Script en el sistema de fitxers." +msgstr "Error: No s'ha pogut carregar el fitxer." #: editor/plugins/script_editor_plugin.cpp #, fuzzy @@ -5842,14 +5812,12 @@ msgid "Error could not load file." msgstr "Error - No s'ha pogut crea l'Script en el sistema de fitxers." #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Error saving file!" -msgstr "Error en desar el TileSet!" +msgstr "Error en desar el fitxer!" #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Error while saving theme." -msgstr "Error en desar el tema" +msgstr "Error en desar el tema." #: editor/plugins/script_editor_plugin.cpp #, fuzzy @@ -6082,18 +6050,16 @@ msgid "Search Results" msgstr "Cerca Ajuda" #: editor/plugins/script_text_editor.cpp -#, fuzzy msgid "Line" -msgstr "LÃnia:" +msgstr "LÃnia" #: editor/plugins/script_text_editor.cpp msgid "(ignore)" msgstr "" #: editor/plugins/script_text_editor.cpp -#, fuzzy msgid "Go to Function" -msgstr "Vés a la Funció..." +msgstr "Vés a la Funció" #: editor/plugins/script_text_editor.cpp editor/plugins/text_editor.cpp msgid "Standard" @@ -6183,9 +6149,8 @@ msgid "Trim Trailing Whitespace" msgstr "Retalla els espais en blanc al final" #: editor/plugins/script_text_editor.cpp -#, fuzzy msgid "Convert Indent to Spaces" -msgstr "Converteix la Sagnia en espais" +msgstr "Converteix la Sagnia en Espais" #: editor/plugins/script_text_editor.cpp #, fuzzy @@ -6790,9 +6755,8 @@ msgid "Update Preview" msgstr "Previsualització" #: editor/plugins/sprite_editor_plugin.cpp -#, fuzzy msgid "Settings:" -msgstr "Configuració" +msgstr "Configuració:" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "ERROR: Couldn't load frame resource!" @@ -6823,14 +6787,12 @@ msgid "(empty)" msgstr "(buit)" #: editor/plugins/sprite_frames_editor_plugin.cpp -#, fuzzy msgid "Animations:" -msgstr "Animacions" +msgstr "Animacions:" #: editor/plugins/sprite_frames_editor_plugin.cpp -#, fuzzy msgid "New Animation" -msgstr "Animació" +msgstr "Nova Animació" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Speed (FPS):" @@ -6841,9 +6803,8 @@ msgid "Loop" msgstr "Bucle" #: editor/plugins/sprite_frames_editor_plugin.cpp -#, fuzzy msgid "Animation Frames:" -msgstr "Fotogrames d'Animació" +msgstr "Fotogrames d'Animació:" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Insert Empty (Before)" @@ -7175,9 +7136,8 @@ msgid "Erase bitmask." msgstr "Elimina un Punt." #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Create a new rectangle." -msgstr "Crea Nou %s" +msgstr "Crear un Nou Rectangle." #: editor/plugins/tile_set_editor_plugin.cpp #, fuzzy @@ -7299,9 +7259,8 @@ msgid "Edit Tile Bitmask" msgstr "Edita Filtres" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Edit Collision Polygon" -msgstr "Edita un polÃgon existent:" +msgstr "Editar PolÃgon de Col·lisió" #: editor/plugins/tile_set_editor_plugin.cpp #, fuzzy @@ -7309,9 +7268,8 @@ msgid "Edit Occlusion Polygon" msgstr "Edita PolÃgon" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Edit Navigation Polygon" -msgstr "Crea un PolÃgon de Navegació" +msgstr "Editar PolÃgon de Navegació" #: editor/plugins/tile_set_editor_plugin.cpp #, fuzzy @@ -7338,19 +7296,16 @@ msgid "Remove Tile" msgstr "Elimina la Plantilla" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Remove Collision Polygon" -msgstr "Elimina el PolÃgon i el Punt" +msgstr "Eliminar PolÃgon de Col·lisió" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Remove Occlusion Polygon" -msgstr "Crea un PolÃgon Oclusor" +msgstr "Eliminar PolÃgon d'Oclusió" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Remove Navigation Polygon" -msgstr "Crea un PolÃgon de Navegació" +msgstr "Eliminar PolÃgon de Navegació" #: editor/plugins/tile_set_editor_plugin.cpp #, fuzzy @@ -7367,14 +7322,12 @@ msgid "Create Collision Polygon" msgstr "Crea un PolÃgon de Navegació" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Create Occlusion Polygon" -msgstr "Crea un PolÃgon Oclusor" +msgstr "Crear PolÃgon d'Oclusió" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "This property can't be changed." -msgstr "Aquesta operació no pot dur-se a terme sense cap escena." +msgstr "Aquesta propietat no es pot canviar." #: editor/plugins/tile_set_editor_plugin.cpp #, fuzzy @@ -8151,9 +8104,8 @@ msgid "Action:" msgstr "Acció:" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Action" -msgstr "Acció:" +msgstr "Acció" #: editor/project_settings_editor.cpp msgid "Deadzone" @@ -8301,18 +8253,16 @@ msgid "Suffix" msgstr "" #: editor/rename_dialog.cpp -#, fuzzy msgid "Advanced options" -msgstr "Opcions d'Alineament" +msgstr "Opcions Avançades" #: editor/rename_dialog.cpp msgid "Substitute" msgstr "" #: editor/rename_dialog.cpp -#, fuzzy msgid "Node name" -msgstr "Nom del node:" +msgstr "Nom del node" #: editor/rename_dialog.cpp msgid "Node's parent name, if available" diff --git a/editor/translations/de.po b/editor/translations/de.po index f14b72da05..adae79c0e7 100644 --- a/editor/translations/de.po +++ b/editor/translations/de.po @@ -43,7 +43,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-12 15:26+0000\n" +"PO-Revision-Date: 2019-03-30 20:04+0000\n" "Last-Translator: So Wieso <sowieso@dukun.de>\n" "Language-Team: German <https://hosted.weblate.org/projects/godot-engine/" "godot/de/>\n" @@ -52,7 +52,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 3.5.1\n" +"X-Generator: Weblate 3.6-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -5957,11 +5957,11 @@ msgstr "Öffne Godot-Referenzdokumentation" #: editor/plugins/script_editor_plugin.cpp msgid "Request Docs" -msgstr "" +msgstr "Dokumentation anfragen" #: editor/plugins/script_editor_plugin.cpp msgid "Help improve the Godot documentation by giving feedback" -msgstr "" +msgstr "Die Godot-Dokumentation durch Meinungsäußerung verbessern" #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." diff --git a/editor/translations/es.po b/editor/translations/es.po index a8b37f2b4f..22907ff2ea 100644 --- a/editor/translations/es.po +++ b/editor/translations/es.po @@ -42,7 +42,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-24 17:38+0000\n" +"PO-Revision-Date: 2019-04-05 13:04+0000\n" "Last-Translator: Javier Ocampos <xavier.ocampos@gmail.com>\n" "Language-Team: Spanish <https://hosted.weblate.org/projects/godot-engine/" "godot/es/>\n" @@ -1434,13 +1434,13 @@ msgstr "" #: platform/iphone/export/export.cpp platform/javascript/export/export.cpp #: platform/osx/export/export.cpp platform/uwp/export/export.cpp msgid "Custom debug template not found." -msgstr "No se ha encontrado la plantilla de depuración personalizada." +msgstr "Plantilla debug personalizada no encontrada." #: editor/editor_export.cpp platform/android/export/export.cpp #: platform/iphone/export/export.cpp platform/javascript/export/export.cpp #: platform/osx/export/export.cpp platform/uwp/export/export.cpp msgid "Custom release template not found." -msgstr "No se ha encontrado ninguna plantilla de lanzamineto personalizada." +msgstr "Plantilla release personalizada no encontrada." #: editor/editor_export.cpp platform/javascript/export/export.cpp msgid "Template file not found:" @@ -5307,7 +5307,7 @@ msgstr "Tiempo de generación (seg):" #: editor/plugins/particles_2d_editor_plugin.cpp msgid "Emission Mask" -msgstr "Máscara de emisión" +msgstr "Máscara de Emisión" #: editor/plugins/particles_2d_editor_plugin.cpp msgid "Capture from Pixel" @@ -5315,7 +5315,7 @@ msgstr "Capturar desde pÃxel" #: editor/plugins/particles_2d_editor_plugin.cpp msgid "Emission Colors" -msgstr "Colores de emisión" +msgstr "Colores de Emisión" #: editor/plugins/particles_editor_plugin.cpp msgid "Faces contain no area!" @@ -5960,11 +5960,11 @@ msgstr "Abrir documentación online de Godot" #: editor/plugins/script_editor_plugin.cpp msgid "Request Docs" -msgstr "" +msgstr "Solicitar Documentos" #: editor/plugins/script_editor_plugin.cpp msgid "Help improve the Godot documentation by giving feedback" -msgstr "" +msgstr "Ayuda a mejorar la documentación de Godot dando feedback" #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." diff --git a/editor/translations/es_AR.po b/editor/translations/es_AR.po index 4d794dfdda..da204947b0 100644 --- a/editor/translations/es_AR.po +++ b/editor/translations/es_AR.po @@ -14,8 +14,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-24 16:57+0000\n" -"Last-Translator: Javier Ocampos <xavier.ocampos@gmail.com>\n" +"PO-Revision-Date: 2019-03-30 20:04+0000\n" +"Last-Translator: Lisandro Lorea <lisandrolorea@gmail.com>\n" "Language-Team: Spanish (Argentina) <https://hosted.weblate.org/projects/" "godot-engine/godot/es_AR/>\n" "Language: es_AR\n" @@ -5920,11 +5920,11 @@ msgstr "Abrir la documentación online de Godot" #: editor/plugins/script_editor_plugin.cpp msgid "Request Docs" -msgstr "" +msgstr "Solicitar Docum." #: editor/plugins/script_editor_plugin.cpp msgid "Help improve the Godot documentation by giving feedback" -msgstr "" +msgstr "Ayudá a mejorar la documentación de Godot dando feedback" #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." diff --git a/editor/translations/extract.py b/editor/translations/extract.py index fd9b1183c4..2075bd5f3c 100755 --- a/editor/translations/extract.py +++ b/editor/translations/extract.py @@ -60,7 +60,7 @@ def process_file(f, fname): lc = 1 while (l): - patterns = ['RTR(\"', 'TTR(\"'] + patterns = ['RTR(\"', 'TTR(\"','TTRC(\"'] idx = 0 pos = 0 while (pos >= 0): diff --git a/editor/translations/fi.po b/editor/translations/fi.po index 5e0b28db44..7924d22ba0 100644 --- a/editor/translations/fi.po +++ b/editor/translations/fi.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-12 15:25+0000\n" +"PO-Revision-Date: 2019-04-03 22:06+0000\n" "Last-Translator: Tapani Niemi <tapani.niemi@kapsi.fi>\n" "Language-Team: Finnish <https://hosted.weblate.org/projects/godot-engine/" "godot/fi/>\n" @@ -22,7 +22,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 3.5.1\n" +"X-Generator: Weblate 3.6-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -5891,11 +5891,11 @@ msgstr "Avaa Godotin online-dokumentaatio" #: editor/plugins/script_editor_plugin.cpp msgid "Request Docs" -msgstr "" +msgstr "Pyydä dokumentaatiota" #: editor/plugins/script_editor_plugin.cpp msgid "Help improve the Godot documentation by giving feedback" -msgstr "" +msgstr "Auta parantamaan Godotin dokumentaatiota antamalla palautetta" #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." diff --git a/editor/translations/fil.po b/editor/translations/fil.po new file mode 100644 index 0000000000..5160c5b3bc --- /dev/null +++ b/editor/translations/fil.po @@ -0,0 +1,9873 @@ +# Filipino translation of the Godot Engine editor +# Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. +# Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) +# This file is distributed under the same license as the Godot source code. +# Marco Santos <enum.scima@gmail.com>, 2019. +# Amado Wilkins <epicalert68@gmail.com>, 2019. +msgid "" +msgstr "" +"Project-Id-Version: Godot Engine editor\n" +"PO-Revision-Date: 2019-04-07 08:04+0000\n" +"Last-Translator: Amado Wilkins <epicalert68@gmail.com>\n" +"Language-Team: Filipino <https://hosted.weblate.org/projects/godot-engine/" +"godot/fil/>\n" +"Language: fil\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8-bit\n" +"Plural-Forms: nplurals=2; plural=n != 1 && n != 2 && n != 3 && (n % 10 == 4 " +"|| n % 10 == 6 || n % 10 == 9);\n" +"X-Generator: Weblate 3.6-dev\n" + +#: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp +#: modules/visual_script/visual_script_builtin_funcs.cpp +msgid "Invalid type argument to convert(), use TYPE_* constants." +msgstr "" + +#: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp +#: modules/mono/glue/gd_glue.cpp +#: modules/visual_script/visual_script_builtin_funcs.cpp +msgid "Not enough bytes for decoding bytes, or invalid format." +msgstr "" + +#: core/math/expression.cpp +msgid "Invalid input %i (not passed) in expression" +msgstr "" + +#: core/math/expression.cpp +msgid "self can't be used because instance is null (not passed)" +msgstr "" + +#: core/math/expression.cpp +msgid "Invalid operands to operator %s, %s and %s." +msgstr "" + +#: core/math/expression.cpp +msgid "Invalid index of type %s for base type %s" +msgstr "" + +#: core/math/expression.cpp +msgid "Invalid named index '%s' for base type %s" +msgstr "" + +#: core/math/expression.cpp +msgid "Invalid arguments to construct '%s'" +msgstr "" + +#: core/math/expression.cpp +msgid "On call to '%s':" +msgstr "" + +#: editor/animation_bezier_editor.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Free" +msgstr "" + +#: editor/animation_bezier_editor.cpp +msgid "Balanced" +msgstr "" + +#: editor/animation_bezier_editor.cpp +msgid "Mirror" +msgstr "" + +#: editor/animation_bezier_editor.cpp +msgid "Insert Key Here" +msgstr "" + +#: editor/animation_bezier_editor.cpp +msgid "Duplicate Selected Key(s)" +msgstr "" + +#: editor/animation_bezier_editor.cpp +msgid "Delete Selected Key(s)" +msgstr "" + +#: editor/animation_bezier_editor.cpp +msgid "Add Bezier Point" +msgstr "Idagdag Ang Bezier Point" + +#: editor/animation_bezier_editor.cpp +msgid "Move Bezier Points" +msgstr "Ilipat Ang Mga Bezier Points" + +#: editor/animation_bezier_editor.cpp editor/animation_track_editor.cpp +msgid "Anim Duplicate Keys" +msgstr "" + +#: editor/animation_bezier_editor.cpp editor/animation_track_editor.cpp +msgid "Anim Delete Keys" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Change Keyframe Time" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Change Transition" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Change Transform" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Change Keyframe Value" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Change Call" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Change Animation Length" +msgstr "" + +#: editor/animation_track_editor.cpp +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Change Animation Loop" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Property Track" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "3D Transform Track" +msgstr "3D Transform Track" + +#: editor/animation_track_editor.cpp +msgid "Call Method Track" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Bezier Curve Track" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Audio Playback Track" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Animation Playback Track" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Add Track" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Animation Length Time (seconds)" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Animation Looping" +msgstr "" + +#: editor/animation_track_editor.cpp +#: modules/visual_script/visual_script_editor.cpp +msgid "Functions:" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Audio Clips:" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Clips:" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Change Track Path" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Toggle this track on/off." +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Update Mode (How this property is set)" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Interpolation Mode" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Loop Wrap Mode (Interpolate end with beginning on loop)" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Remove this track." +msgstr "Tanggalin ang track na ito." + +#: editor/animation_track_editor.cpp +msgid "Time (s): " +msgstr "Oras (s): " + +#: editor/animation_track_editor.cpp +msgid "Toggle Track Enabled" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Continuous" +msgstr "Tuloy-tuloy" + +#: editor/animation_track_editor.cpp +msgid "Discrete" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Trigger" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Capture" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Nearest" +msgstr "Pinakamalapit" + +#: editor/animation_track_editor.cpp editor/plugins/curve_editor_plugin.cpp +#: editor/property_editor.cpp +msgid "Linear" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Cubic" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Clamp Loop Interp" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Wrap Loop Interp" +msgstr "" + +#: editor/animation_track_editor.cpp +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Insert Key" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Duplicate Key(s)" +msgstr "Kopyahin Ang (Mga) Key(s)" + +#: editor/animation_track_editor.cpp +msgid "Delete Key(s)" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Change Animation Update Mode" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Change Animation Interpolation Mode" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Change Animation Loop Mode" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Remove Anim Track" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Create NEW track for %s and insert key?" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Create %d NEW tracks and insert keys?" +msgstr "" + +#: editor/animation_track_editor.cpp editor/create_dialog.cpp +#: editor/editor_audio_buses.cpp editor/editor_plugin_settings.cpp +#: editor/plugin_config_dialog.cpp +#: editor/plugins/abstract_polygon_2d_editor.cpp +#: editor/plugins/mesh_instance_editor_plugin.cpp +#: editor/plugins/particles_editor_plugin.cpp editor/script_create_dialog.cpp +msgid "Create" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Insert" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "AnimationPlayer can't animate itself, only other players." +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Create & Insert" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Insert Track & Key" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Insert Key" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Change Animation Step" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Rearrange Tracks" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Transform tracks only apply to Spatial-based nodes." +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "" +"Audio tracks can only point to nodes of type:\n" +"-AudioStreamPlayer\n" +"-AudioStreamPlayer2D\n" +"-AudioStreamPlayer3D" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Animation tracks can only point to AnimationPlayer nodes." +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "An animation player can't animate itself, only other players." +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Not possible to add a new track without a root" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Add Bezier Track" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Track path is invalid, so can't add a key." +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Track is not of type Spatial, can't insert key" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Add Transform Track Key" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Add Track Key" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Track path is invalid, so can't add a method key." +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Add Method Track Key" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Method not found in object: " +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Move Keys" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Clipboard is empty" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Paste Tracks" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim Scale Keys" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "" +"This option does not work for Bezier editing, as it's only a single track." +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Only show tracks from nodes selected in tree." +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Group tracks by node or display them as plain list." +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Snap (s): " +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Animation step value." +msgstr "" + +#: editor/animation_track_editor.cpp editor/editor_properties.cpp +#: editor/plugins/polygon_2d_editor_plugin.cpp +#: editor/plugins/script_text_editor.cpp +#: editor/plugins/shader_editor_plugin.cpp editor/plugins/text_editor.cpp +#: editor/project_manager.cpp editor/project_settings_editor.cpp +#: editor/property_editor.cpp modules/visual_script/visual_script_editor.cpp +msgid "Edit" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Animation properties." +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Copy Tracks" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Scale Selection" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Scale From Cursor" +msgstr "" + +#: editor/animation_track_editor.cpp modules/gridmap/grid_map_editor_plugin.cpp +msgid "Duplicate Selection" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Duplicate Transposed" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Delete Selection" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Go to Next Step" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Go to Previous Step" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Optimize Animation" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Clean-Up Animation" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Pick the node that will be animated:" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Use Bezier Curves" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Anim. Optimizer" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Max. Linear Error:" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Max. Angular Error:" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Max Optimizable Angle:" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Optimize" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Remove invalid keys" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Remove unresolved and empty tracks" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Clean-up all animations" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Clean-Up Animation(s) (NO UNDO!)" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Clean-Up" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Scale Ratio:" +msgstr "" + +#: editor/animation_track_editor.cpp +msgid "Select tracks to copy:" +msgstr "" + +#: editor/animation_track_editor.cpp editor/editor_properties.cpp +#: editor/plugins/animation_player_editor_plugin.cpp +#: editor/plugins/script_text_editor.cpp +#: editor/plugins/sprite_frames_editor_plugin.cpp editor/property_editor.cpp +#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp +msgid "Copy" +msgstr "" + +#: editor/animation_track_editor_plugins.cpp +msgid "Add Audio Track Clip" +msgstr "" + +#: editor/animation_track_editor_plugins.cpp +msgid "Change Audio Track Clip Start Offset" +msgstr "" + +#: editor/animation_track_editor_plugins.cpp +msgid "Change Audio Track Clip End Offset" +msgstr "" + +#: editor/array_property_edit.cpp +msgid "Resize Array" +msgstr "" + +#: editor/array_property_edit.cpp +msgid "Change Array Value Type" +msgstr "" + +#: editor/array_property_edit.cpp +msgid "Change Array Value" +msgstr "" + +#: editor/code_editor.cpp +msgid "Go to Line" +msgstr "" + +#: editor/code_editor.cpp +msgid "Line Number:" +msgstr "" + +#: editor/code_editor.cpp editor/editor_help.cpp +msgid "No Matches" +msgstr "" + +#: editor/code_editor.cpp +msgid "Replaced %d occurrence(s)." +msgstr "" + +#: editor/code_editor.cpp editor/find_in_files.cpp +msgid "Match Case" +msgstr "" + +#: editor/code_editor.cpp editor/find_in_files.cpp +msgid "Whole Words" +msgstr "" + +#: editor/code_editor.cpp editor/rename_dialog.cpp +msgid "Replace" +msgstr "" + +#: editor/code_editor.cpp +msgid "Replace All" +msgstr "" + +#: editor/code_editor.cpp +msgid "Selection Only" +msgstr "" + +#: editor/code_editor.cpp editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/texture_region_editor_plugin.cpp +#: editor/plugins/tile_set_editor_plugin.cpp scene/gui/graph_edit.cpp +msgid "Zoom In" +msgstr "" + +#: editor/code_editor.cpp editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/texture_region_editor_plugin.cpp +#: editor/plugins/tile_set_editor_plugin.cpp scene/gui/graph_edit.cpp +msgid "Zoom Out" +msgstr "" + +#: editor/code_editor.cpp +msgid "Reset Zoom" +msgstr "" + +#: editor/code_editor.cpp modules/mono/editor/mono_bottom_panel.cpp +msgid "Warnings" +msgstr "" + +#: editor/code_editor.cpp +msgid "Line and column numbers." +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Method in target Node must be specified!" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "" +"Target method not found! Specify a valid method or attach a script to target " +"Node." +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Connect To Node:" +msgstr "" + +#: editor/connections_dialog.cpp editor/editor_autoload_settings.cpp +#: editor/groups_editor.cpp editor/plugins/item_list_editor_plugin.cpp +#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp +msgid "Add" +msgstr "" + +#: editor/connections_dialog.cpp editor/dependency_editor.cpp +#: editor/groups_editor.cpp editor/plugins/animation_player_editor_plugin.cpp +#: editor/plugins/animation_tree_player_editor_plugin.cpp +#: editor/plugins/resource_preloader_editor_plugin.cpp +#: editor/plugins/theme_editor_plugin.cpp editor/project_manager.cpp +#: editor/project_settings_editor.cpp +msgid "Remove" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Add Extra Call Argument:" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Extra Call Arguments:" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Path to Node:" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Make Function" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Deferred" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Oneshot" +msgstr "" + +#: editor/connections_dialog.cpp editor/dependency_editor.cpp +#: editor/export_template_manager.cpp editor/groups_editor.cpp +#: editor/plugins/animation_player_editor_plugin.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +#: editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/resource_preloader_editor_plugin.cpp +#: editor/plugins/script_editor_plugin.cpp +#: editor/plugins/sprite_frames_editor_plugin.cpp editor/project_export.cpp +#: editor/project_settings_editor.cpp editor/property_editor.cpp +#: editor/run_settings_dialog.cpp editor/settings_config_dialog.cpp +#: modules/visual_script/visual_script_editor.cpp +msgid "Close" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Connect" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Connect '%s' to '%s'" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Disconnect '%s' from '%s'" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Disconnect all from signal: '%s'" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Connect..." +msgstr "" + +#: editor/connections_dialog.cpp +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Disconnect" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Connect Signal: " +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Edit Connection: " +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Are you sure you want to remove all connections from the \"%s\" signal?" +msgstr "" + +#: editor/connections_dialog.cpp editor/editor_help.cpp editor/node_dock.cpp +msgid "Signals" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Are you sure you want to remove all connections from this signal?" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Disconnect All" +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Edit..." +msgstr "" + +#: editor/connections_dialog.cpp +msgid "Go To Method" +msgstr "" + +#: editor/create_dialog.cpp +msgid "Change %s Type" +msgstr "" + +#: editor/create_dialog.cpp editor/project_settings_editor.cpp +#: modules/visual_script/visual_script_editor.cpp +msgid "Change" +msgstr "" + +#: editor/create_dialog.cpp +msgid "Create New %s" +msgstr "" + +#: editor/create_dialog.cpp editor/editor_file_dialog.cpp +#: editor/filesystem_dock.cpp +msgid "Favorites:" +msgstr "" + +#: editor/create_dialog.cpp editor/editor_file_dialog.cpp +msgid "Recent:" +msgstr "" + +#: editor/create_dialog.cpp editor/plugins/asset_library_editor_plugin.cpp +#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp +#: editor/property_selector.cpp editor/quick_open.cpp +#: modules/visual_script/visual_script_property_selector.cpp +msgid "Search:" +msgstr "" + +#: editor/create_dialog.cpp editor/plugins/script_editor_plugin.cpp +#: editor/property_selector.cpp editor/quick_open.cpp +#: modules/visual_script/visual_script_property_selector.cpp +msgid "Matches:" +msgstr "" + +#: editor/create_dialog.cpp editor/plugin_config_dialog.cpp +#: editor/plugins/asset_library_editor_plugin.cpp editor/property_selector.cpp +#: modules/visual_script/visual_script_property_selector.cpp +msgid "Description:" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Search Replacement For:" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Dependencies For:" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "" +"Scene '%s' is currently being edited.\n" +"Changes will not take effect unless reloaded." +msgstr "" + +#: editor/dependency_editor.cpp +msgid "" +"Resource '%s' is in use.\n" +"Changes will take effect when reloaded." +msgstr "" + +#: editor/dependency_editor.cpp +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "Dependencies" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Resource" +msgstr "" + +#: editor/dependency_editor.cpp editor/editor_autoload_settings.cpp +#: editor/project_settings_editor.cpp editor/script_create_dialog.cpp +msgid "Path" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Dependencies:" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Fix Broken" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Dependency Editor" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Search Replacement Resource:" +msgstr "" + +#: editor/dependency_editor.cpp editor/editor_file_dialog.cpp +#: editor/editor_help_search.cpp editor/editor_node.cpp +#: editor/filesystem_dock.cpp editor/plugins/script_editor_plugin.cpp +#: editor/property_selector.cpp editor/quick_open.cpp +#: editor/script_create_dialog.cpp +#: modules/visual_script/visual_script_property_selector.cpp +#: scene/gui/file_dialog.cpp +msgid "Open" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Owners Of:" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Remove selected files from the project? (no undo)" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "" +"The files being removed are required by other resources in order for them to " +"work.\n" +"Remove them anyway? (no undo)" +msgstr "" + +#: editor/dependency_editor.cpp editor/export_template_manager.cpp +msgid "Cannot remove:" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Error loading:" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Load failed due to missing dependencies:" +msgstr "" + +#: editor/dependency_editor.cpp editor/editor_node.cpp +msgid "Open Anyway" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Which action should be taken?" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Fix Dependencies" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Errors loading!" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Permanently delete %d item(s)? (No undo!)" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Owns" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Resources Without Explicit Ownership:" +msgstr "" + +#: editor/dependency_editor.cpp editor/editor_node.cpp +msgid "Orphan Resource Explorer" +msgstr "" + +#: editor/dependency_editor.cpp +msgid "Delete selected files?" +msgstr "" + +#: editor/dependency_editor.cpp editor/editor_audio_buses.cpp +#: editor/editor_file_dialog.cpp editor/editor_node.cpp +#: editor/filesystem_dock.cpp editor/plugins/item_list_editor_plugin.cpp +#: editor/plugins/sprite_frames_editor_plugin.cpp editor/project_export.cpp +#: editor/project_settings_editor.cpp editor/scene_tree_dock.cpp +msgid "Delete" +msgstr "" + +#: editor/dictionary_property_edit.cpp +msgid "Change Dictionary Key" +msgstr "" + +#: editor/dictionary_property_edit.cpp +msgid "Change Dictionary Value" +msgstr "" + +#: editor/editor_about.cpp +msgid "Thanks from the Godot community!" +msgstr "" + +#: editor/editor_about.cpp +msgid "Godot Engine contributors" +msgstr "" + +#: editor/editor_about.cpp +msgid "Project Founders" +msgstr "" + +#: editor/editor_about.cpp +msgid "Lead Developer" +msgstr "" + +#: editor/editor_about.cpp +msgid "Project Manager " +msgstr "" + +#: editor/editor_about.cpp +msgid "Developers" +msgstr "" + +#: editor/editor_about.cpp +msgid "Authors" +msgstr "" + +#: editor/editor_about.cpp +msgid "Platinum Sponsors" +msgstr "" + +#: editor/editor_about.cpp +msgid "Gold Sponsors" +msgstr "" + +#: editor/editor_about.cpp +msgid "Mini Sponsors" +msgstr "" + +#: editor/editor_about.cpp +msgid "Gold Donors" +msgstr "" + +#: editor/editor_about.cpp +msgid "Silver Donors" +msgstr "" + +#: editor/editor_about.cpp +msgid "Bronze Donors" +msgstr "" + +#: editor/editor_about.cpp +msgid "Donors" +msgstr "" + +#: editor/editor_about.cpp +msgid "License" +msgstr "" + +#: editor/editor_about.cpp +msgid "Thirdparty License" +msgstr "" + +#: editor/editor_about.cpp +msgid "" +"Godot Engine relies on a number of thirdparty free and open source " +"libraries, all compatible with the terms of its MIT license. The following " +"is an exhaustive list of all such thirdparty components with their " +"respective copyright statements and license terms." +msgstr "" + +#: editor/editor_about.cpp +msgid "All Components" +msgstr "" + +#: editor/editor_about.cpp +msgid "Components" +msgstr "" + +#: editor/editor_about.cpp +msgid "Licenses" +msgstr "" + +#: editor/editor_asset_installer.cpp editor/project_manager.cpp +msgid "Error opening package file, not in zip format." +msgstr "" + +#: editor/editor_asset_installer.cpp +msgid "Uncompressing Assets" +msgstr "" + +#: editor/editor_asset_installer.cpp editor/project_manager.cpp +msgid "Package installed successfully!" +msgstr "" + +#: editor/editor_asset_installer.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Success!" +msgstr "" + +#: editor/editor_asset_installer.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Install" +msgstr "" + +#: editor/editor_asset_installer.cpp +msgid "Package Installer" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Speakers" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Add Effect" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Rename Audio Bus" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Change Audio Bus Volume" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Toggle Audio Bus Solo" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Toggle Audio Bus Mute" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Toggle Audio Bus Bypass Effects" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Select Audio Bus Send" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Add Audio Bus Effect" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Move Bus Effect" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Delete Bus Effect" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Audio Bus, Drag and Drop to rearrange." +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Solo" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Mute" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Bypass" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Bus options" +msgstr "" + +#: editor/editor_audio_buses.cpp editor/filesystem_dock.cpp +#: editor/plugins/animation_player_editor_plugin.cpp editor/scene_tree_dock.cpp +msgid "Duplicate" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Reset Volume" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Delete Effect" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Audio" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Add Audio Bus" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Master bus can't be deleted!" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Delete Audio Bus" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Duplicate Audio Bus" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Reset Bus Volume" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Move Audio Bus" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Save Audio Bus Layout As..." +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Location for New Layout..." +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Open Audio Bus Layout" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "There is no 'res://default_bus_layout.tres' file." +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Invalid file, not an audio bus layout." +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Add Bus" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Add a new Audio Bus to this layout." +msgstr "" + +#: editor/editor_audio_buses.cpp editor/editor_properties.cpp +#: editor/plugins/animation_player_editor_plugin.cpp editor/property_editor.cpp +#: editor/script_create_dialog.cpp +msgid "Load" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Load an existing Bus Layout." +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Save As" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Save this Bus Layout to a file." +msgstr "" + +#: editor/editor_audio_buses.cpp editor/import_dock.cpp +msgid "Load Default" +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Load the default Bus Layout." +msgstr "" + +#: editor/editor_audio_buses.cpp +msgid "Create a new Bus Layout." +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Invalid name." +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Valid characters:" +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Invalid name. Must not collide with an existing engine class name." +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Invalid name. Must not collide with an existing buit-in type name." +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Invalid name. Must not collide with an existing global constant name." +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Autoload '%s' already exists!" +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Rename Autoload" +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Toggle AutoLoad Globals" +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Move Autoload" +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Remove Autoload" +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Enable" +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Rearrange Autoloads" +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Invalid Path." +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "File does not exist." +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Not in resource path." +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Add AutoLoad" +msgstr "" + +#: editor/editor_autoload_settings.cpp editor/editor_file_dialog.cpp +#: editor/plugins/animation_tree_editor_plugin.cpp scene/gui/file_dialog.cpp +msgid "Path:" +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Node Name:" +msgstr "" + +#: editor/editor_autoload_settings.cpp editor/editor_help_search.cpp +#: editor/editor_profiler.cpp editor/settings_config_dialog.cpp +msgid "Name" +msgstr "" + +#: editor/editor_autoload_settings.cpp +msgid "Singleton" +msgstr "" + +#: editor/editor_data.cpp +msgid "Updating Scene" +msgstr "" + +#: editor/editor_data.cpp +msgid "Storing local changes..." +msgstr "" + +#: editor/editor_data.cpp +msgid "Updating scene..." +msgstr "" + +#: editor/editor_data.cpp editor/editor_properties.cpp +msgid "[empty]" +msgstr "" + +#: editor/editor_data.cpp +msgid "[unsaved]" +msgstr "" + +#: editor/editor_dir_dialog.cpp +msgid "Please select a base directory first" +msgstr "" + +#: editor/editor_dir_dialog.cpp +msgid "Choose a Directory" +msgstr "" + +#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp +#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp +msgid "Create Folder" +msgstr "" + +#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp +#: editor/editor_plugin_settings.cpp editor/filesystem_dock.cpp +#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp +#: scene/gui/file_dialog.cpp +msgid "Name:" +msgstr "" + +#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp +#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp +msgid "Could not create folder." +msgstr "" + +#: editor/editor_dir_dialog.cpp +msgid "Choose" +msgstr "" + +#: editor/editor_export.cpp +msgid "Storing File:" +msgstr "" + +#: editor/editor_export.cpp +msgid "No export template found at the expected path:" +msgstr "" + +#: editor/editor_export.cpp +msgid "Packing" +msgstr "" + +#: editor/editor_export.cpp +msgid "" +"Target platform requires 'ETC' texture compression for GLES2. Enable 'Import " +"Etc' in Project Settings." +msgstr "" + +#: editor/editor_export.cpp +msgid "" +"Target platform requires 'ETC2' texture compression for GLES3. Enable " +"'Import Etc 2' in Project Settings." +msgstr "" + +#: editor/editor_export.cpp +msgid "" +"Target platform requires 'ETC' texture compression for the driver fallback " +"to GLES2.\n" +"Enable 'Import Etc' in Project Settings, or disable 'Driver Fallback " +"Enabled'." +msgstr "" + +#: editor/editor_export.cpp platform/android/export/export.cpp +#: platform/iphone/export/export.cpp platform/javascript/export/export.cpp +#: platform/osx/export/export.cpp platform/uwp/export/export.cpp +msgid "Custom debug template not found." +msgstr "" + +#: editor/editor_export.cpp platform/android/export/export.cpp +#: platform/iphone/export/export.cpp platform/javascript/export/export.cpp +#: platform/osx/export/export.cpp platform/uwp/export/export.cpp +msgid "Custom release template not found." +msgstr "" + +#: editor/editor_export.cpp platform/javascript/export/export.cpp +msgid "Template file not found:" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "Select Current Folder" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "File Exists, Overwrite?" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "Select This Folder" +msgstr "" + +#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp +msgid "Copy Path" +msgstr "" + +#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp +msgid "Open in File Manager" +msgstr "" + +#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp +#: editor/project_manager.cpp +msgid "Show in File Manager" +msgstr "" + +#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp +msgid "New Folder..." +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "Refresh" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "All Recognized" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "All Files (*)" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "Open a File" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "Open File(s)" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "Open a Directory" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "Open a File or Directory" +msgstr "" + +#: editor/editor_file_dialog.cpp editor/editor_node.cpp +#: editor/editor_properties.cpp editor/inspector_dock.cpp +#: editor/plugins/animation_player_editor_plugin.cpp +#: editor/plugins/script_editor_plugin.cpp scene/gui/file_dialog.cpp +msgid "Save" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "Save a File" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Go Back" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Go Forward" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Go Up" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Toggle Hidden Files" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Toggle Favorite" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Toggle Mode" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Focus Path" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Move Favorite Up" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Move Favorite Down" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Previous Folder" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Next Folder" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "Go to parent folder" +msgstr "" + +#: editor/editor_file_dialog.cpp +msgid "(Un)favorite current folder." +msgstr "" + +#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp +msgid "View items as a grid of thumbnails." +msgstr "" + +#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp +msgid "View items as a list." +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "Directories & Files:" +msgstr "" + +#: editor/editor_file_dialog.cpp editor/plugins/sprite_editor_plugin.cpp +#: editor/plugins/style_box_editor_plugin.cpp +msgid "Preview:" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "File:" +msgstr "" + +#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +msgid "Must use a valid extension." +msgstr "" + +#: editor/editor_file_system.cpp +msgid "ScanSources" +msgstr "" + +#: editor/editor_file_system.cpp +msgid "(Re)Importing Assets" +msgstr "" + +#: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp +msgid "Top" +msgstr "" + +#: editor/editor_help.cpp +msgid "Class:" +msgstr "" + +#: editor/editor_help.cpp editor/scene_tree_editor.cpp +msgid "Inherits:" +msgstr "" + +#: editor/editor_help.cpp +msgid "Inherited by:" +msgstr "" + +#: editor/editor_help.cpp +msgid "Brief Description:" +msgstr "" + +#: editor/editor_help.cpp +msgid "Properties" +msgstr "" + +#: editor/editor_help.cpp +msgid "Properties:" +msgstr "" + +#: editor/editor_help.cpp +msgid "Methods" +msgstr "" + +#: editor/editor_help.cpp +msgid "Methods:" +msgstr "" + +#: editor/editor_help.cpp +msgid "Theme Properties" +msgstr "" + +#: editor/editor_help.cpp +msgid "Theme Properties:" +msgstr "" + +#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp +msgid "Signals:" +msgstr "" + +#: editor/editor_help.cpp +msgid "Enumerations" +msgstr "" + +#: editor/editor_help.cpp +msgid "Enumerations:" +msgstr "" + +#: editor/editor_help.cpp +msgid "enum " +msgstr "" + +#: editor/editor_help.cpp +msgid "Constants" +msgstr "" + +#: editor/editor_help.cpp +msgid "Constants:" +msgstr "" + +#: editor/editor_help.cpp +msgid "Class Description" +msgstr "" + +#: editor/editor_help.cpp +msgid "Class Description:" +msgstr "" + +#: editor/editor_help.cpp +msgid "Online Tutorials:" +msgstr "" + +#: editor/editor_help.cpp +msgid "" +"There are currently no tutorials for this class, you can [color=$color][url=" +"$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" +"url][/color]." +msgstr "" + +#: editor/editor_help.cpp +msgid "Property Descriptions" +msgstr "" + +#: editor/editor_help.cpp +msgid "Property Descriptions:" +msgstr "" + +#: editor/editor_help.cpp +msgid "" +"There is currently no description for this property. Please help us by " +"[color=$color][url=$url]contributing one[/url][/color]!" +msgstr "" + +#: editor/editor_help.cpp +msgid "Method Descriptions" +msgstr "" + +#: editor/editor_help.cpp +msgid "Method Descriptions:" +msgstr "" + +#: editor/editor_help.cpp +msgid "" +"There is currently no description for this method. Please help us by [color=" +"$color][url=$url]contributing one[/url][/color]!" +msgstr "" + +#: editor/editor_help_search.cpp editor/editor_node.cpp +#: editor/plugins/script_editor_plugin.cpp +msgid "Search Help" +msgstr "" + +#: editor/editor_help_search.cpp +msgid "Display All" +msgstr "" + +#: editor/editor_help_search.cpp +msgid "Classes Only" +msgstr "" + +#: editor/editor_help_search.cpp +msgid "Methods Only" +msgstr "" + +#: editor/editor_help_search.cpp +msgid "Signals Only" +msgstr "" + +#: editor/editor_help_search.cpp +msgid "Constants Only" +msgstr "" + +#: editor/editor_help_search.cpp +msgid "Properties Only" +msgstr "" + +#: editor/editor_help_search.cpp +msgid "Theme Properties Only" +msgstr "" + +#: editor/editor_help_search.cpp +msgid "Member Type" +msgstr "" + +#: editor/editor_help_search.cpp +msgid "Class" +msgstr "" + +#: editor/editor_inspector.cpp editor/project_settings_editor.cpp +msgid "Property:" +msgstr "" + +#: editor/editor_inspector.cpp +msgid "Set" +msgstr "" + +#: editor/editor_inspector.cpp +msgid "Set Multiple:" +msgstr "" + +#: editor/editor_log.cpp +msgid "Output:" +msgstr "" + +#: editor/editor_log.cpp editor/editor_profiler.cpp +#: editor/editor_properties.cpp +#: editor/plugins/animation_tree_player_editor_plugin.cpp +#: editor/property_editor.cpp editor/scene_tree_dock.cpp +#: editor/script_editor_debugger.cpp +#: modules/gdnative/gdnative_library_editor_plugin.cpp scene/gui/line_edit.cpp +#: scene/gui/text_edit.cpp +msgid "Clear" +msgstr "" + +#: editor/editor_log.cpp +msgid "Clear Output" +msgstr "" + +#: editor/editor_node.cpp +msgid "Project export failed with error code %d." +msgstr "" + +#: editor/editor_node.cpp +msgid "Imported resources can't be saved." +msgstr "" + +#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp +#: scene/gui/dialogs.cpp +msgid "OK" +msgstr "" + +#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp +msgid "Error saving resource!" +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"This resource can't be saved because it does not belong to the edited scene. " +"Make it unique first." +msgstr "" + +#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp +msgid "Save Resource As..." +msgstr "" + +#: editor/editor_node.cpp +msgid "Can't open file for writing:" +msgstr "" + +#: editor/editor_node.cpp +msgid "Requested file format unknown:" +msgstr "" + +#: editor/editor_node.cpp +msgid "Error while saving." +msgstr "" + +#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp +msgid "Can't open '%s'. The file could have been moved or deleted." +msgstr "" + +#: editor/editor_node.cpp +msgid "Error while parsing '%s'." +msgstr "" + +#: editor/editor_node.cpp +msgid "Unexpected end of file '%s'." +msgstr "" + +#: editor/editor_node.cpp +msgid "Missing '%s' or its dependencies." +msgstr "" + +#: editor/editor_node.cpp +msgid "Error while loading '%s'." +msgstr "" + +#: editor/editor_node.cpp +msgid "Saving Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Analyzing" +msgstr "" + +#: editor/editor_node.cpp +msgid "Creating Thumbnail" +msgstr "" + +#: editor/editor_node.cpp +msgid "This operation can't be done without a tree root." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"This scene can't be saved because there is a cyclic instancing inclusion.\n" +"Please resolve it and then attempt to save again." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"Couldn't save scene. Likely dependencies (instances or inheritance) couldn't " +"be satisfied." +msgstr "" + +#: editor/editor_node.cpp editor/scene_tree_dock.cpp +msgid "Can't overwrite scene that is still open!" +msgstr "" + +#: editor/editor_node.cpp +msgid "Can't load MeshLibrary for merging!" +msgstr "" + +#: editor/editor_node.cpp +msgid "Error saving MeshLibrary!" +msgstr "" + +#: editor/editor_node.cpp +msgid "Can't load TileSet for merging!" +msgstr "" + +#: editor/editor_node.cpp +msgid "Error saving TileSet!" +msgstr "" + +#: editor/editor_node.cpp +msgid "Error trying to save layout!" +msgstr "" + +#: editor/editor_node.cpp +msgid "Default editor layout overridden." +msgstr "" + +#: editor/editor_node.cpp +msgid "Layout name not found!" +msgstr "" + +#: editor/editor_node.cpp +msgid "Restored default layout to base settings." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"This resource belongs to a scene that was imported, so it's not editable.\n" +"Please read the documentation relevant to importing scenes to better " +"understand this workflow." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"This resource belongs to a scene that was instanced or inherited.\n" +"Changes to it will not be kept when saving the current scene." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"This resource was imported, so it's not editable. Change its settings in the " +"import panel and then re-import." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"This scene was imported, so changes to it will not be kept.\n" +"Instancing it or inheriting will allow making changes to it.\n" +"Please read the documentation relevant to importing scenes to better " +"understand this workflow." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"This is a remote object so changes to it will not be kept.\n" +"Please read the documentation relevant to debugging to better understand " +"this workflow." +msgstr "" + +#: editor/editor_node.cpp +msgid "There is no defined scene to run." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"No main scene has ever been defined, select one?\n" +"You can change it later in \"Project Settings\" under the 'application' " +"category." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"Selected scene '%s' does not exist, select a valid one?\n" +"You can change it later in \"Project Settings\" under the 'application' " +"category." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"Selected scene '%s' is not a scene file, select a valid one?\n" +"You can change it later in \"Project Settings\" under the 'application' " +"category." +msgstr "" + +#: editor/editor_node.cpp +msgid "Current scene was never saved, please save it prior to running." +msgstr "" + +#: editor/editor_node.cpp +msgid "Could not start subprocess!" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open Base Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Quick Open Scene..." +msgstr "" + +#: editor/editor_node.cpp +msgid "Quick Open Script..." +msgstr "" + +#: editor/editor_node.cpp +msgid "Save & Close" +msgstr "" + +#: editor/editor_node.cpp +msgid "Save changes to '%s' before closing?" +msgstr "" + +#: editor/editor_node.cpp +msgid "Saved %s modified resource(s)." +msgstr "" + +#: editor/editor_node.cpp +msgid "A root node is required to save the scene." +msgstr "" + +#: editor/editor_node.cpp +msgid "Save Scene As..." +msgstr "" + +#: editor/editor_node.cpp +msgid "No" +msgstr "" + +#: editor/editor_node.cpp +msgid "Yes" +msgstr "" + +#: editor/editor_node.cpp +msgid "This scene has never been saved. Save before running?" +msgstr "" + +#: editor/editor_node.cpp editor/scene_tree_dock.cpp +msgid "This operation can't be done without a scene." +msgstr "" + +#: editor/editor_node.cpp +msgid "Export Mesh Library" +msgstr "" + +#: editor/editor_node.cpp +msgid "This operation can't be done without a root node." +msgstr "" + +#: editor/editor_node.cpp +msgid "Export Tile Set" +msgstr "" + +#: editor/editor_node.cpp +msgid "This operation can't be done without a selected node." +msgstr "" + +#: editor/editor_node.cpp +msgid "Current scene not saved. Open anyway?" +msgstr "" + +#: editor/editor_node.cpp +msgid "Can't reload a scene that was never saved." +msgstr "" + +#: editor/editor_node.cpp +msgid "Revert" +msgstr "" + +#: editor/editor_node.cpp +msgid "This action cannot be undone. Revert anyway?" +msgstr "" + +#: editor/editor_node.cpp +msgid "Quick Run Scene..." +msgstr "" + +#: editor/editor_node.cpp +msgid "Quit" +msgstr "" + +#: editor/editor_node.cpp +msgid "Exit the editor?" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open Project Manager?" +msgstr "" + +#: editor/editor_node.cpp +msgid "Save & Quit" +msgstr "" + +#: editor/editor_node.cpp +msgid "Save changes to the following scene(s) before quitting?" +msgstr "" + +#: editor/editor_node.cpp +msgid "Save changes the following scene(s) before opening Project Manager?" +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"This option is deprecated. Situations where refresh must be forced are now " +"considered a bug. Please report." +msgstr "" + +#: editor/editor_node.cpp +msgid "Pick a Main Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Unable to enable addon plugin at: '%s' parsing of config failed." +msgstr "" + +#: editor/editor_node.cpp +msgid "Unable to find script field for addon plugin at: 'res://addons/%s'." +msgstr "" + +#: editor/editor_node.cpp +msgid "Unable to load addon script from path: '%s'." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"Unable to load addon script from path: '%s' There seems to be an error in " +"the code, please check the syntax." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"Unable to load addon script from path: '%s' Base type is not EditorPlugin." +msgstr "" + +#: editor/editor_node.cpp +msgid "Unable to load addon script from path: '%s' Script is not in tool mode." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"Scene '%s' was automatically imported, so it can't be modified.\n" +"To make changes to it, a new inherited scene can be created." +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"Error loading scene, it must be inside the project path. Use 'Import' to " +"open the scene, then save it inside the project path." +msgstr "" + +#: editor/editor_node.cpp +msgid "Scene '%s' has broken dependencies:" +msgstr "" + +#: editor/editor_node.cpp +msgid "Clear Recent Scenes" +msgstr "" + +#: editor/editor_node.cpp +msgid "Save Layout" +msgstr "" + +#: editor/editor_node.cpp +msgid "Delete Layout" +msgstr "" + +#: editor/editor_node.cpp editor/import_dock.cpp +#: editor/script_create_dialog.cpp +msgid "Default" +msgstr "" + +#: editor/editor_node.cpp editor/editor_properties.cpp +#: editor/plugins/script_editor_plugin.cpp editor/property_editor.cpp +msgid "Show in FileSystem" +msgstr "" + +#: editor/editor_node.cpp +msgid "Play This Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Close Tab" +msgstr "" + +#: editor/editor_node.cpp +msgid "Switch Scene Tab" +msgstr "" + +#: editor/editor_node.cpp +msgid "%d more files or folders" +msgstr "" + +#: editor/editor_node.cpp +msgid "%d more folders" +msgstr "" + +#: editor/editor_node.cpp +msgid "%d more files" +msgstr "" + +#: editor/editor_node.cpp +msgid "Dock Position" +msgstr "" + +#: editor/editor_node.cpp +msgid "Distraction Free Mode" +msgstr "" + +#: editor/editor_node.cpp +msgid "Toggle distraction-free mode." +msgstr "" + +#: editor/editor_node.cpp +msgid "Add a new scene." +msgstr "" + +#: editor/editor_node.cpp +msgid "Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Go to previously opened scene." +msgstr "" + +#: editor/editor_node.cpp +msgid "Next tab" +msgstr "" + +#: editor/editor_node.cpp +msgid "Previous tab" +msgstr "" + +#: editor/editor_node.cpp +msgid "Filter Files..." +msgstr "" + +#: editor/editor_node.cpp +msgid "Operations with scene files." +msgstr "" + +#: editor/editor_node.cpp +msgid "New Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "New Inherited Scene..." +msgstr "" + +#: editor/editor_node.cpp +msgid "Open Scene..." +msgstr "" + +#: editor/editor_node.cpp +msgid "Save Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Save All Scenes" +msgstr "" + +#: editor/editor_node.cpp +msgid "Close Scene" +msgstr "" + +#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp +msgid "Open Recent" +msgstr "" + +#: editor/editor_node.cpp +msgid "Convert To..." +msgstr "" + +#: editor/editor_node.cpp +msgid "MeshLibrary..." +msgstr "" + +#: editor/editor_node.cpp +msgid "TileSet..." +msgstr "" + +#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp +#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp +msgid "Undo" +msgstr "" + +#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp +#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp +msgid "Redo" +msgstr "" + +#: editor/editor_node.cpp +msgid "Revert Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Miscellaneous project or scene-wide tools." +msgstr "" + +#: editor/editor_node.cpp +msgid "Project" +msgstr "" + +#: editor/editor_node.cpp +msgid "Project Settings" +msgstr "" + +#: editor/editor_node.cpp editor/project_export.cpp +msgid "Export" +msgstr "" + +#: editor/editor_node.cpp editor/plugins/tile_set_editor_plugin.cpp +msgid "Tools" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open Project Data Folder" +msgstr "" + +#: editor/editor_node.cpp +msgid "Quit to Project List" +msgstr "" + +#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp +#: editor/project_export.cpp +msgid "Debug" +msgstr "" + +#: editor/editor_node.cpp +msgid "Deploy with Remote Debug" +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"When exporting or deploying, the resulting executable will attempt to " +"connect to the IP of this computer in order to be debugged." +msgstr "" + +#: editor/editor_node.cpp +msgid "Small Deploy with Network FS" +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"When this option is enabled, export or deploy will produce a minimal " +"executable.\n" +"The filesystem will be provided from the project by the editor over the " +"network.\n" +"On Android, deploy will use the USB cable for faster performance. This " +"option speeds up testing for games with a large footprint." +msgstr "" + +#: editor/editor_node.cpp +msgid "Visible Collision Shapes" +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"Collision shapes and raycast nodes (for 2D and 3D) will be visible on the " +"running game if this option is turned on." +msgstr "" + +#: editor/editor_node.cpp +msgid "Visible Navigation" +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"Navigation meshes and polygons will be visible on the running game if this " +"option is turned on." +msgstr "" + +#: editor/editor_node.cpp +msgid "Sync Scene Changes" +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"When this option is turned on, any changes made to the scene in the editor " +"will be replicated in the running game.\n" +"When used remotely on a device, this is more efficient with network " +"filesystem." +msgstr "" + +#: editor/editor_node.cpp +msgid "Sync Script Changes" +msgstr "" + +#: editor/editor_node.cpp +msgid "" +"When this option is turned on, any script that is saved will be reloaded on " +"the running game.\n" +"When used remotely on a device, this is more efficient with network " +"filesystem." +msgstr "" + +#: editor/editor_node.cpp +msgid "Editor" +msgstr "" + +#: editor/editor_node.cpp editor/settings_config_dialog.cpp +msgid "Editor Settings" +msgstr "" + +#: editor/editor_node.cpp +msgid "Editor Layout" +msgstr "" + +#: editor/editor_node.cpp +msgid "Toggle Fullscreen" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open Editor Data/Settings Folder" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open Editor Data Folder" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open Editor Settings Folder" +msgstr "" + +#: editor/editor_node.cpp editor/project_export.cpp +msgid "Manage Export Templates" +msgstr "" + +#: editor/editor_node.cpp +msgid "Help" +msgstr "" + +#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp +#: editor/plugins/script_editor_plugin.cpp +#: editor/plugins/script_text_editor.cpp +#: editor/plugins/shader_editor_plugin.cpp editor/plugins/text_editor.cpp +#: editor/project_settings_editor.cpp editor/rename_dialog.cpp +msgid "Search" +msgstr "" + +#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp +msgid "Online Docs" +msgstr "" + +#: editor/editor_node.cpp +msgid "Q&A" +msgstr "" + +#: editor/editor_node.cpp +msgid "Issue Tracker" +msgstr "" + +#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp +msgid "Community" +msgstr "" + +#: editor/editor_node.cpp +msgid "About" +msgstr "" + +#: editor/editor_node.cpp +msgid "Play the project." +msgstr "" + +#: editor/editor_node.cpp +msgid "Play" +msgstr "" + +#: editor/editor_node.cpp +msgid "Pause the scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Pause Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Stop the scene." +msgstr "" + +#: editor/editor_node.cpp editor/editor_profiler.cpp +msgid "Stop" +msgstr "" + +#: editor/editor_node.cpp +msgid "Play the edited scene." +msgstr "" + +#: editor/editor_node.cpp +msgid "Play Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Play custom scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Play Custom Scene" +msgstr "" + +#: editor/editor_node.cpp +msgid "Changing the video driver requires restarting the editor." +msgstr "" + +#: editor/editor_node.cpp editor/project_settings_editor.cpp +#: editor/settings_config_dialog.cpp +msgid "Save & Restart" +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp +msgid "Update Always" +msgstr "" + +#: editor/editor_node.cpp +msgid "Update Changes" +msgstr "" + +#: editor/editor_node.cpp +msgid "Disable Update Spinner" +msgstr "" + +#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp +#: editor/project_manager.cpp +msgid "Import" +msgstr "" + +#: editor/editor_node.cpp +msgid "FileSystem" +msgstr "" + +#: editor/editor_node.cpp +msgid "Inspector" +msgstr "" + +#: editor/editor_node.cpp +msgid "Node" +msgstr "" + +#: editor/editor_node.cpp +msgid "Expand Bottom Panel" +msgstr "" + +#: editor/editor_node.cpp scene/resources/visual_shader.cpp +msgid "Output" +msgstr "" + +#: editor/editor_node.cpp +msgid "Don't Save" +msgstr "" + +#: editor/editor_node.cpp +msgid "Import Templates From ZIP File" +msgstr "" + +#: editor/editor_node.cpp editor/project_export.cpp +msgid "Export Project" +msgstr "" + +#: editor/editor_node.cpp +msgid "Export Library" +msgstr "" + +#: editor/editor_node.cpp +msgid "Merge With Existing" +msgstr "" + +#: editor/editor_node.cpp +msgid "Password:" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open & Run a Script" +msgstr "" + +#: editor/editor_node.cpp +msgid "New Inherited" +msgstr "" + +#: editor/editor_node.cpp +msgid "Load Errors" +msgstr "" + +#: editor/editor_node.cpp editor/plugins/tile_map_editor_plugin.cpp +msgid "Select" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open 2D Editor" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open 3D Editor" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open Script Editor" +msgstr "" + +#: editor/editor_node.cpp editor/project_manager.cpp +msgid "Open Asset Library" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open the next Editor" +msgstr "" + +#: editor/editor_node.cpp +msgid "Open the previous Editor" +msgstr "" + +#: editor/editor_plugin.cpp +msgid "Creating Mesh Previews" +msgstr "" + +#: editor/editor_plugin.cpp +msgid "Thumbnail..." +msgstr "" + +#: editor/editor_plugin_settings.cpp +msgid "Edit Plugin" +msgstr "" + +#: editor/editor_plugin_settings.cpp +msgid "Installed Plugins:" +msgstr "" + +#: editor/editor_plugin_settings.cpp editor/plugin_config_dialog.cpp +msgid "Update" +msgstr "" + +#: editor/editor_plugin_settings.cpp editor/plugin_config_dialog.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Version:" +msgstr "" + +#: editor/editor_plugin_settings.cpp editor/plugin_config_dialog.cpp +msgid "Author:" +msgstr "" + +#: editor/editor_plugin_settings.cpp +msgid "Status:" +msgstr "" + +#: editor/editor_plugin_settings.cpp +msgid "Edit:" +msgstr "" + +#: editor/editor_profiler.cpp editor/plugins/animation_state_machine_editor.cpp +#: editor/rename_dialog.cpp +msgid "Start" +msgstr "" + +#: editor/editor_profiler.cpp +msgid "Measure:" +msgstr "" + +#: editor/editor_profiler.cpp +msgid "Frame Time (sec)" +msgstr "" + +#: editor/editor_profiler.cpp +msgid "Average Time (sec)" +msgstr "" + +#: editor/editor_profiler.cpp +msgid "Frame %" +msgstr "" + +#: editor/editor_profiler.cpp +msgid "Physics Frame %" +msgstr "" + +#: editor/editor_profiler.cpp +msgid "Time:" +msgstr "" + +#: editor/editor_profiler.cpp +msgid "Inclusive" +msgstr "" + +#: editor/editor_profiler.cpp +msgid "Self" +msgstr "" + +#: editor/editor_profiler.cpp +msgid "Frame #:" +msgstr "" + +#: editor/editor_profiler.cpp +msgid "Time" +msgstr "" + +#: editor/editor_profiler.cpp +msgid "Calls" +msgstr "" + +#: editor/editor_properties.cpp +msgid "On" +msgstr "" + +#: editor/editor_properties.cpp +msgid "Layer" +msgstr "" + +#: editor/editor_properties.cpp +msgid "Bit %d, value %d" +msgstr "" + +#: editor/editor_properties.cpp +msgid "[Empty]" +msgstr "" + +#: editor/editor_properties.cpp editor/plugins/root_motion_editor_plugin.cpp +msgid "Assign..." +msgstr "" + +#: editor/editor_properties.cpp +msgid "Invalid RID" +msgstr "" + +#: editor/editor_properties.cpp +msgid "" +"The selected resource (%s) does not match any type expected for this " +"property (%s)." +msgstr "" + +#: editor/editor_properties.cpp +msgid "" +"Can't create a ViewportTexture on resources saved as a file.\n" +"Resource needs to belong to a scene." +msgstr "" + +#: editor/editor_properties.cpp +msgid "" +"Can't create a ViewportTexture on this resource because it's not set as " +"local to scene.\n" +"Please switch on the 'local to scene' property on it (and all resources " +"containing it up to a node)." +msgstr "" + +#: editor/editor_properties.cpp editor/property_editor.cpp +msgid "Pick a Viewport" +msgstr "" + +#: editor/editor_properties.cpp editor/property_editor.cpp +msgid "New Script" +msgstr "" + +#: editor/editor_properties.cpp editor/property_editor.cpp +msgid "New %s" +msgstr "" + +#: editor/editor_properties.cpp editor/property_editor.cpp +msgid "Make Unique" +msgstr "" + +#: editor/editor_properties.cpp +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/animation_player_editor_plugin.cpp +#: editor/plugins/animation_state_machine_editor.cpp +#: editor/plugins/resource_preloader_editor_plugin.cpp +#: editor/plugins/script_text_editor.cpp +#: editor/plugins/sprite_frames_editor_plugin.cpp +#: editor/plugins/tile_map_editor_plugin.cpp editor/property_editor.cpp +#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp +msgid "Paste" +msgstr "" + +#: editor/editor_properties.cpp editor/property_editor.cpp +msgid "Convert To %s" +msgstr "" + +#: editor/editor_properties.cpp +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +msgid "Open Editor" +msgstr "" + +#: editor/editor_properties.cpp editor/property_editor.cpp +msgid "Selected node is not a Viewport!" +msgstr "" + +#: editor/editor_properties_array_dict.cpp +msgid "Size: " +msgstr "" + +#: editor/editor_properties_array_dict.cpp +msgid "Page: " +msgstr "" + +#: editor/editor_properties_array_dict.cpp +msgid "New Key:" +msgstr "" + +#: editor/editor_properties_array_dict.cpp +msgid "New Value:" +msgstr "" + +#: editor/editor_properties_array_dict.cpp +msgid "Add Key/Value Pair" +msgstr "" + +#: editor/editor_properties_array_dict.cpp +#: editor/plugins/theme_editor_plugin.cpp +msgid "Remove Item" +msgstr "" + +#: editor/editor_run_native.cpp +msgid "Select device from the list" +msgstr "" + +#: editor/editor_run_native.cpp +msgid "" +"No runnable export preset found for this platform.\n" +"Please add a runnable preset in the export menu." +msgstr "" + +#: editor/editor_run_script.cpp +msgid "Write your logic in the _run() method." +msgstr "" + +#: editor/editor_run_script.cpp +msgid "There is an edited scene already." +msgstr "" + +#: editor/editor_run_script.cpp +msgid "Couldn't instance script:" +msgstr "" + +#: editor/editor_run_script.cpp +msgid "Did you forget the 'tool' keyword?" +msgstr "" + +#: editor/editor_run_script.cpp +msgid "Couldn't run script:" +msgstr "" + +#: editor/editor_run_script.cpp +msgid "Did you forget the '_run' method?" +msgstr "" + +#: editor/editor_sub_scene.cpp +msgid "Select Node(s) to Import" +msgstr "" + +#: editor/editor_sub_scene.cpp +msgid "Scene Path:" +msgstr "" + +#: editor/editor_sub_scene.cpp +msgid "Import From Node:" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Re-Download" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Uninstall" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "(Installed)" +msgstr "" + +#: editor/export_template_manager.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Download" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "(Missing)" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "(Current)" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Retrieving mirrors, please wait..." +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Remove template version '%s'?" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Can't open export templates zip." +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Invalid version.txt format inside templates: %s." +msgstr "" + +#: editor/export_template_manager.cpp +msgid "No version.txt found inside templates." +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Error creating path for templates:" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Extracting Export Templates" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Importing:" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "" +"No download links found for this version. Direct download is only available " +"for official releases." +msgstr "" + +#: editor/export_template_manager.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Can't resolve." +msgstr "" + +#: editor/export_template_manager.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Can't connect." +msgstr "" + +#: editor/export_template_manager.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "No response." +msgstr "" + +#: editor/export_template_manager.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Request Failed." +msgstr "" + +#: editor/export_template_manager.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Redirect Loop." +msgstr "" + +#: editor/export_template_manager.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Failed:" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Download Complete." +msgstr "" + +#: editor/export_template_manager.cpp +msgid "" +"Templates installation failed. The problematic templates archives can be " +"found at '%s'." +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Error requesting url: " +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Connecting to Mirror..." +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Disconnected" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Resolving" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Can't Resolve" +msgstr "" + +#: editor/export_template_manager.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Connecting..." +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Can't Connect" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Connected" +msgstr "" + +#: editor/export_template_manager.cpp +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Requesting..." +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Downloading" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Connection Error" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "SSL Handshake Error" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Current Version:" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Installed Versions:" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Install From File" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Remove Template" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Select template file" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Export Template Manager" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Download Templates" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Select mirror from list: (Shift+Click: Open in Browser)" +msgstr "" + +#: editor/file_type_cache.cpp +msgid "Can't open file_type_cache.cch for writing, not saving file type cache!" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Favorites" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Cannot navigate to '%s' as it has not been found in the file system!" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Status: Import of file failed. Please fix file and reimport manually." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Cannot move/rename resources root." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Cannot move a folder into itself." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Error moving:" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Error duplicating:" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Unable to update dependencies:" +msgstr "" + +#: editor/filesystem_dock.cpp editor/scene_tree_editor.cpp +msgid "No name provided." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Provided name contains invalid characters" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Name contains invalid characters." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "A file or folder with this name already exists." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Renaming file:" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Renaming folder:" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Duplicating file:" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Duplicating folder:" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Open Scene(s)" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Instance" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Add to favorites" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Remove from favorites" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Edit Dependencies..." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "View Owners..." +msgstr "" + +#: editor/filesystem_dock.cpp editor/plugins/animation_player_editor_plugin.cpp +msgid "Rename..." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Duplicate..." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Move To..." +msgstr "" + +#: editor/filesystem_dock.cpp editor/plugins/script_editor_plugin.cpp +msgid "New Script..." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "New Resource..." +msgstr "" + +#: editor/filesystem_dock.cpp editor/script_editor_debugger.cpp +msgid "Expand All" +msgstr "" + +#: editor/filesystem_dock.cpp editor/script_editor_debugger.cpp +msgid "Collapse All" +msgstr "" + +#: editor/filesystem_dock.cpp +#: editor/plugins/animation_tree_player_editor_plugin.cpp +#: editor/project_manager.cpp editor/rename_dialog.cpp +#: editor/scene_tree_dock.cpp +msgid "Rename" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Previous Directory" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Next Directory" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Re-Scan Filesystem" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Toggle split mode" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Search files" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "" +"Scanning Files,\n" +"Please Wait..." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Move" +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "There is already file or folder with the same name in this location." +msgstr "" + +#: editor/filesystem_dock.cpp +msgid "Overwrite" +msgstr "" + +#: editor/filesystem_dock.cpp editor/plugins/script_editor_plugin.cpp +msgid "Create Script" +msgstr "" + +#: editor/find_in_files.cpp +msgid "Find in Files" +msgstr "" + +#: editor/find_in_files.cpp +msgid "Find:" +msgstr "" + +#: editor/find_in_files.cpp +msgid "Folder:" +msgstr "" + +#: editor/find_in_files.cpp +msgid "Filters:" +msgstr "" + +#: editor/find_in_files.cpp editor/plugins/script_editor_plugin.cpp +#: editor/plugins/script_text_editor.cpp +msgid "Find..." +msgstr "" + +#: editor/find_in_files.cpp editor/plugins/script_text_editor.cpp +msgid "Replace..." +msgstr "" + +#: editor/find_in_files.cpp editor/progress_dialog.cpp scene/gui/dialogs.cpp +msgid "Cancel" +msgstr "" + +#: editor/find_in_files.cpp +msgid "Find: " +msgstr "" + +#: editor/find_in_files.cpp +msgid "Replace: " +msgstr "" + +#: editor/find_in_files.cpp +msgid "Replace all (no undo)" +msgstr "" + +#: editor/find_in_files.cpp +msgid "Searching..." +msgstr "" + +#: editor/find_in_files.cpp +msgid "Search complete" +msgstr "" + +#: editor/groups_editor.cpp +msgid "Group name already exists." +msgstr "" + +#: editor/groups_editor.cpp +msgid "Invalid group name." +msgstr "" + +#: editor/groups_editor.cpp editor/node_dock.cpp +msgid "Groups" +msgstr "" + +#: editor/groups_editor.cpp +msgid "Nodes not in Group" +msgstr "" + +#: editor/groups_editor.cpp editor/scene_tree_dock.cpp +msgid "Filter nodes" +msgstr "" + +#: editor/groups_editor.cpp +msgid "Nodes in Group" +msgstr "" + +#: editor/groups_editor.cpp +msgid "Add to Group" +msgstr "" + +#: editor/groups_editor.cpp +msgid "Remove from Group" +msgstr "" + +#: editor/groups_editor.cpp +msgid "Manage Groups" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Import as Single Scene" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Import with Separate Animations" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Import with Separate Materials" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Import with Separate Objects" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Import with Separate Objects+Materials" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Import with Separate Objects+Animations" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Import with Separate Materials+Animations" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Import with Separate Objects+Materials+Animations" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Import as Multiple Scenes" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Import as Multiple Scenes+Materials" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +#: editor/plugins/mesh_library_editor_plugin.cpp +msgid "Import Scene" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Importing Scene..." +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Generating Lightmaps" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Generating for Mesh: " +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Running Custom Script..." +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Couldn't load post-import script:" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Invalid/broken script for post-import (check console):" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Error running post-import script:" +msgstr "" + +#: editor/import/resource_importer_scene.cpp +msgid "Saving..." +msgstr "" + +#: editor/import_dock.cpp +msgid "Set as Default for '%s'" +msgstr "" + +#: editor/import_dock.cpp +msgid "Clear Default for '%s'" +msgstr "" + +#: editor/import_dock.cpp +msgid " Files" +msgstr "" + +#: editor/import_dock.cpp +msgid "Import As:" +msgstr "" + +#: editor/import_dock.cpp editor/property_editor.cpp +msgid "Preset..." +msgstr "" + +#: editor/import_dock.cpp +msgid "Reimport" +msgstr "" + +#: editor/import_dock.cpp +msgid "Save scenes, re-import and restart" +msgstr "" + +#: editor/import_dock.cpp +msgid "Changing the type of an imported file requires editor restart." +msgstr "" + +#: editor/import_dock.cpp +msgid "" +"WARNING: Assets exist that use this resource, they may stop loading properly." +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Failed to load resource." +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Expand All Properties" +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Collapse All Properties" +msgstr "" + +#: editor/inspector_dock.cpp editor/plugins/animation_player_editor_plugin.cpp +#: editor/plugins/script_editor_plugin.cpp +msgid "Save As..." +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Copy Params" +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Paste Params" +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Edit Resource Clipboard" +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Copy Resource" +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Make Built-In" +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Make Sub-Resources Unique" +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Open in Help" +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Create a new resource in memory and edit it." +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Load an existing resource from disk and edit it." +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Save the currently edited resource." +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Go to the previous edited object in history." +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Go to the next edited object in history." +msgstr "" + +#: editor/inspector_dock.cpp +msgid "History of recently edited objects." +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Object properties." +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Filter properties" +msgstr "" + +#: editor/inspector_dock.cpp +msgid "Changes may be lost!" +msgstr "" + +#: editor/multi_node_edit.cpp +msgid "MultiNode Set" +msgstr "" + +#: editor/node_dock.cpp +msgid "Select a Node to edit Signals and Groups." +msgstr "" + +#: editor/plugin_config_dialog.cpp +msgid "Edit a Plugin" +msgstr "" + +#: editor/plugin_config_dialog.cpp +msgid "Create a Plugin" +msgstr "" + +#: editor/plugin_config_dialog.cpp +msgid "Plugin Name:" +msgstr "" + +#: editor/plugin_config_dialog.cpp +msgid "Subfolder:" +msgstr "" + +#: editor/plugin_config_dialog.cpp +msgid "Language:" +msgstr "" + +#: editor/plugin_config_dialog.cpp +msgid "Script Name:" +msgstr "" + +#: editor/plugin_config_dialog.cpp +msgid "Activate now?" +msgstr "" + +#: editor/plugins/abstract_polygon_2d_editor.cpp +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Create Polygon" +msgstr "" + +#: editor/plugins/abstract_polygon_2d_editor.cpp +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Create points." +msgstr "" + +#: editor/plugins/abstract_polygon_2d_editor.cpp +msgid "" +"Edit points.\n" +"LMB: Move Point\n" +"RMB: Erase Point" +msgstr "" + +#: editor/plugins/abstract_polygon_2d_editor.cpp +#: editor/plugins/animation_blend_space_1d_editor.cpp +msgid "Erase points." +msgstr "" + +#: editor/plugins/abstract_polygon_2d_editor.cpp +msgid "Edit Polygon" +msgstr "" + +#: editor/plugins/abstract_polygon_2d_editor.cpp +msgid "Insert Point" +msgstr "" + +#: editor/plugins/abstract_polygon_2d_editor.cpp +msgid "Edit Polygon (Remove Point)" +msgstr "" + +#: editor/plugins/abstract_polygon_2d_editor.cpp +msgid "Remove Polygon And Point" +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +#: editor/plugins/animation_player_editor_plugin.cpp +#: editor/plugins/animation_state_machine_editor.cpp +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Add Animation" +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Load..." +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Move Node Point" +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +msgid "Change BlendSpace1D Limits" +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +msgid "Change BlendSpace1D Labels" +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +#: editor/plugins/animation_state_machine_editor.cpp +msgid "This type of node can't be used. Only root nodes are allowed." +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Add Node Point" +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Add Animation Point" +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +msgid "Remove BlendSpace1D Point" +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +msgid "Move BlendSpace1D Node Point" +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/animation_state_machine_editor.cpp +msgid "" +"AnimationTree is inactive.\n" +"Activate to enable playback, check node warnings if activation fails." +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Set the blending position within the space" +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Select and move points, create points with RMB." +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp scene/gui/graph_edit.cpp +msgid "Enable snap and show grid." +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Point" +msgstr "" + +#: editor/plugins/animation_blend_space_1d_editor.cpp +#: editor/plugins/animation_blend_space_2d_editor.cpp +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Open Animation Node" +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Triangle already exists" +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Add Triangle" +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Change BlendSpace2D Limits" +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Change BlendSpace2D Labels" +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Remove BlendSpace2D Point" +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Remove BlendSpace2D Triangle" +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "BlendSpace2D does not belong to an AnimationTree node." +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "No triangles exist, so no blending can take place." +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Toggle Auto Triangles" +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Create triangles by connecting points." +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Erase points and triangles." +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +msgid "Generate blend triangles automatically (instead of manually)" +msgstr "" + +#: editor/plugins/animation_blend_space_2d_editor.cpp +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Blend:" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +msgid "Parameter Changed" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Edit Filters" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +msgid "Output node can't be added to the blend tree." +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +msgid "Add Node to BlendTree" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Node Moved" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Unable to connect, port may be in use or connection may be invalid." +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Nodes Connected" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Nodes Disconnected" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +msgid "Set Animation" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Delete Node" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +msgid "Toggle Filter On/Off" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +msgid "Change Filter" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +msgid "No animation player set, so unable to retrieve track names." +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +msgid "Player path set is invalid, so unable to retrieve track names." +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/root_motion_editor_plugin.cpp +msgid "" +"Animation player has no valid root node path, so unable to retrieve track " +"names." +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Node Renamed" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Add Node..." +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +#: editor/plugins/root_motion_editor_plugin.cpp +msgid "Edit Filtered Tracks:" +msgstr "" + +#: editor/plugins/animation_blend_tree_editor_plugin.cpp +msgid "Enable filtering" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Toggle Autoplay" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "New Animation Name:" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "New Anim" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Change Animation Name:" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Delete Animation?" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Remove Animation" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Invalid animation name!" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Animation name already exists!" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Rename Animation" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Blend Next Changed" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Change Blend Time" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Load Animation" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Duplicate Animation" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "No animation to copy!" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "No animation resource on clipboard!" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Pasted Animation" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Paste Animation" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "No animation to edit!" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Play selected animation backwards from current pos. (A)" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Play selected animation backwards from end. (Shift+A)" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Stop animation playback. (S)" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Play selected animation from start. (Shift+D)" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Play selected animation from current pos. (D)" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Animation position (in seconds)." +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Scale animation playback globally for the node." +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Animation Tools" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Animation" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "New" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Edit Transitions..." +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Open in Inspector" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Display list of animations in player." +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Autoplay on Load" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Onion Skinning" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Enable Onion Skinning" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Directions" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Past" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Future" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Depth" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "1 step" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "2 steps" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "3 steps" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Differences Only" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Force White Modulate" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Include Gizmos (3D)" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Pin AnimationPlayer" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Create New Animation" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Animation Name:" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +#: editor/plugins/resource_preloader_editor_plugin.cpp +#: editor/plugins/script_editor_plugin.cpp +#: editor/plugins/sprite_frames_editor_plugin.cpp editor/property_editor.cpp +#: editor/script_create_dialog.cpp +msgid "Error!" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Blend Times:" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Next (Auto Queue):" +msgstr "" + +#: editor/plugins/animation_player_editor_plugin.cpp +msgid "Cross-Animation Blend Times" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Move Node" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Add Transition" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +#: modules/visual_script/visual_script_editor.cpp +msgid "Add Node" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "End" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Immediate" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Sync" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "At End" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Travel" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Start and end nodes are needed for a sub-transition." +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "No playback resource set at path: %s." +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Node Removed" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Transition Removed" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Set Start Node (Autoplay)" +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "" +"Select and move nodes.\n" +"RMB to add new nodes.\n" +"Shift+LMB to create connections." +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Create new nodes." +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Connect nodes." +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Remove selected node or transition." +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Toggle autoplay this animation on start, restart or seek to zero." +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Set the end animation. This is useful for sub-transitions." +msgstr "" + +#: editor/plugins/animation_state_machine_editor.cpp +msgid "Transition: " +msgstr "" + +#: editor/plugins/animation_tree_editor_plugin.cpp +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "AnimationTree" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "New name:" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Scale:" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Fade In (s):" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Fade Out (s):" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Blend" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Mix" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Auto Restart:" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Restart (s):" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Random Restart (s):" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Start!" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Amount:" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Blend 0:" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Blend 1:" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "X-Fade Time (s):" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Current:" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Add Input" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Clear Auto-Advance" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Set Auto-Advance" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Delete Input" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Animation tree is valid." +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Animation tree is invalid." +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Animation Node" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "OneShot Node" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Mix Node" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Blend2 Node" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Blend3 Node" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Blend4 Node" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "TimeScale Node" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "TimeSeek Node" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Transition Node" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Import Animations..." +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Edit Node Filters" +msgstr "" + +#: editor/plugins/animation_tree_player_editor_plugin.cpp +msgid "Filters..." +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Contents:" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "View Files" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Can't resolve hostname:" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Connection error, please try again." +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Can't connect to host:" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "No response from host:" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Request failed, return code:" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Request failed, too many redirects" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Bad download hash, assuming file has been tampered with." +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Expected:" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Got:" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Failed sha256 hash check" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Asset Download Error:" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Downloading (%s / %s)..." +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Downloading..." +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Resolving..." +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Error making request" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Idle" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Retry" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Download Error" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Download for this asset is already in progress!" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "First" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Previous" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Next" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Last" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "All" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +#: editor/project_settings_editor.cpp +msgid "Plugins" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp editor/project_manager.cpp +msgid "Sort:" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Reverse" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +#: editor/project_settings_editor.cpp +msgid "Category:" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Site:" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Support..." +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Official" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Testing" +msgstr "" + +#: editor/plugins/asset_library_editor_plugin.cpp +msgid "Assets ZIP File" +msgstr "" + +#: editor/plugins/baked_lightmap_editor_plugin.cpp +msgid "" +"Can't determine a save path for lightmap images.\n" +"Save your scene (for images to be saved in the same dir), or pick a save " +"path from the BakedLightmap properties." +msgstr "" + +#: editor/plugins/baked_lightmap_editor_plugin.cpp +msgid "" +"No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake " +"Light' flag is on." +msgstr "" + +#: editor/plugins/baked_lightmap_editor_plugin.cpp +msgid "Failed creating lightmap images, make sure path is writable." +msgstr "" + +#: editor/plugins/baked_lightmap_editor_plugin.cpp +msgid "Bake Lightmaps" +msgstr "" + +#: editor/plugins/camera_editor_plugin.cpp +#: editor/plugins/spatial_editor_plugin.cpp editor/rename_dialog.cpp +msgid "Preview" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Configure Snap" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Grid Offset:" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Grid Step:" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Rotation Offset:" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Rotation Step:" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Move vertical guide" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Create new vertical guide" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Remove vertical guide" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Move horizontal guide" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Create new horizontal guide" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Remove horizontal guide" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Create new horizontal and vertical guides" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Move pivot" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Rotate CanvasItem" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Move anchor" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Resize CanvasItem" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Scale CanvasItem" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Move CanvasItem" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Presets for the anchors and margins values of a Control node." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "" +"Children of containers have their anchors and margins values overridden by " +"their parent." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Anchors only" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Change Anchors and Margins" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Change Anchors" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Paste Pose" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "" +"Warning: Children of a container get their position and size determined only " +"by their parent." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/texture_region_editor_plugin.cpp +#: editor/plugins/tile_set_editor_plugin.cpp scene/gui/graph_edit.cpp +msgid "Zoom Reset" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Select Mode" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Drag: Rotate" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Alt+Drag: Move" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving)." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Alt+RMB: Depth list selection" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Move Mode" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Rotate Mode" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Scale Mode" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/spatial_editor_plugin.cpp +msgid "" +"Show a list of all objects at the position clicked\n" +"(same as Alt+RMB in select mode)." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Click to change object's rotation pivot." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Pan Mode" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Toggle snapping." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Use Snap" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Snapping Options" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Snap to grid" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Use Rotation Snap" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Configure Snap..." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Snap Relative" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Use Pixel Snap" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Smart snapping" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Snap to parent" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Snap to node anchor" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Snap to node sides" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Snap to node center" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Snap to other nodes" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Snap to guides" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Lock the selected object in place (can't be moved)." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Unlock the selected object (can be moved)." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Makes sure the object's children are not selectable." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Restores the object's children's ability to be selected." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Skeleton Options" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Show Bones" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Make IK Chain" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Clear IK Chain" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Make Custom Bone(s) from Node(s)" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Clear Custom Bones" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/spatial_editor_plugin.cpp +msgid "View" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Show Grid" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Show Helpers" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Show Rulers" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Show Guides" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Show Origin" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Show Viewport" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Show Group And Lock Icons" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Center Selection" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Frame Selection" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Layout" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Insert keys." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Insert Key (Existing Tracks)" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Copy Pose" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Clear Pose" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Multiply grid step by 2" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Divide grid step by 2" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Add %s" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Adding %s..." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Cannot instantiate multiple nodes without root." +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp +msgid "Create Node" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp +msgid "Error instancing scene from %s" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "Change default type" +msgstr "" + +#: editor/plugins/canvas_item_editor_plugin.cpp +msgid "" +"Drag & drop + Shift : Add node as sibling\n" +"Drag & drop + Alt : Change node type" +msgstr "" + +#: editor/plugins/collision_polygon_editor_plugin.cpp +msgid "Create Polygon3D" +msgstr "" + +#: editor/plugins/collision_polygon_editor_plugin.cpp +msgid "Edit Poly" +msgstr "" + +#: editor/plugins/collision_polygon_editor_plugin.cpp +msgid "Edit Poly (Remove Point)" +msgstr "" + +#: editor/plugins/collision_shape_2d_editor_plugin.cpp +msgid "Set Handle" +msgstr "" + +#: editor/plugins/cpu_particles_editor_plugin.cpp +msgid "CPUParticles" +msgstr "" + +#: editor/plugins/cpu_particles_editor_plugin.cpp +#: editor/plugins/particles_editor_plugin.cpp +msgid "Create Emission Points From Mesh" +msgstr "" + +#: editor/plugins/cpu_particles_editor_plugin.cpp +#: editor/plugins/particles_editor_plugin.cpp +msgid "Create Emission Points From Node" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Flat0" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Flat1" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Ease in" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Ease out" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Smoothstep" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Modify Curve Point" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Modify Curve Tangent" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Load Curve Preset" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Add point" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Remove point" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Left linear" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Right linear" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Load preset" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Remove Curve Point" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Toggle Curve Linear Tangent" +msgstr "" + +#: editor/plugins/curve_editor_plugin.cpp +msgid "Hold Shift to edit tangents individually" +msgstr "" + +#: editor/plugins/gi_probe_editor_plugin.cpp +msgid "Bake GI Probe" +msgstr "" + +#: editor/plugins/gradient_editor_plugin.cpp +msgid "Gradient Edited" +msgstr "" + +#: editor/plugins/item_list_editor_plugin.cpp +msgid "Item %d" +msgstr "" + +#: editor/plugins/item_list_editor_plugin.cpp +msgid "Items" +msgstr "" + +#: editor/plugins/item_list_editor_plugin.cpp +msgid "Item List Editor" +msgstr "" + +#: editor/plugins/light_occluder_2d_editor_plugin.cpp +msgid "Create Occluder Polygon" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Mesh is empty!" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Static Trimesh Body" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Static Convex Body" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "This doesn't work on scene root!" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Trimesh Shape" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Convex Shape" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Navigation Mesh" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Contained Mesh is not of type ArrayMesh." +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "UV Unwrap failed, mesh may not be manifold?" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "No mesh to debug." +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +#: editor/plugins/sprite_editor_plugin.cpp +msgid "Model has no UV in this layer" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "MeshInstance lacks a Mesh!" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Mesh has not surface to create outlines from!" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Could not create outline!" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Outline" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Mesh" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Trimesh Static Body" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Convex Static Body" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Trimesh Collision Sibling" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Convex Collision Sibling" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Outline Mesh..." +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "View UV1" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "View UV2" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Unwrap UV2 for Lightmap/AO" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Create Outline Mesh" +msgstr "" + +#: editor/plugins/mesh_instance_editor_plugin.cpp +msgid "Outline Size:" +msgstr "" + +#: editor/plugins/mesh_library_editor_plugin.cpp +msgid "Remove item %d?" +msgstr "" + +#: editor/plugins/mesh_library_editor_plugin.cpp +#: editor/plugins/theme_editor_plugin.cpp +msgid "Add Item" +msgstr "" + +#: editor/plugins/mesh_library_editor_plugin.cpp +msgid "Remove Selected Item" +msgstr "" + +#: editor/plugins/mesh_library_editor_plugin.cpp +msgid "Import from Scene" +msgstr "" + +#: editor/plugins/mesh_library_editor_plugin.cpp +msgid "Update from Scene" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "No mesh source specified (and no MultiMesh set in node)." +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "No mesh source specified (and MultiMesh contains no Mesh)." +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Mesh source is invalid (invalid path)." +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Mesh source is invalid (not a MeshInstance)." +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Mesh source is invalid (contains no Mesh resource)." +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "No surface source specified." +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Surface source is invalid (invalid path)." +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Surface source is invalid (no geometry)." +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Surface source is invalid (no faces)." +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Parent has no solid faces to populate." +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Couldn't map area." +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Select a Source Mesh:" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Select a Target Surface:" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Populate Surface" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Populate MultiMesh" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Target Surface:" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Source Mesh:" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "X-Axis" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Y-Axis" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Z-Axis" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Mesh Up Axis:" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Random Rotation:" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Random Tilt:" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Random Scale:" +msgstr "" + +#: editor/plugins/multimesh_editor_plugin.cpp +msgid "Populate" +msgstr "" + +#: editor/plugins/navigation_polygon_editor_plugin.cpp +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Create Navigation Polygon" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +msgid "Generating Visibility Rect" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +msgid "Generate Visibility Rect" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +msgid "Can only set point into a ParticlesMaterial process material" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +msgid "Error loading image:" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +msgid "No pixels with transparency > 128 in image..." +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +msgid "Load Emission Mask" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +msgid "Clear Emission Mask" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +#: editor/plugins/particles_editor_plugin.cpp +msgid "Convert to CPUParticles" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +#: editor/plugins/particles_editor_plugin.cpp +msgid "Particles" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +msgid "Generated Point Count:" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +#: editor/plugins/particles_editor_plugin.cpp +msgid "Generation Time (sec):" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +msgid "Emission Mask" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +msgid "Capture from Pixel" +msgstr "" + +#: editor/plugins/particles_2d_editor_plugin.cpp +msgid "Emission Colors" +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Faces contain no area!" +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "No faces!" +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Node does not contain geometry." +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Node does not contain geometry (faces)." +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Create Emitter" +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Emission Points:" +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Surface Points" +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Surface Points+Normal (Directed)" +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Volume" +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Emission Source: " +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "A processor material of type 'ParticlesMaterial' is required." +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Generating AABB" +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Generate Visibility AABB" +msgstr "" + +#: editor/plugins/particles_editor_plugin.cpp +msgid "Generate AABB" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +msgid "Remove Point from Curve" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +msgid "Remove Out-Control from Curve" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +msgid "Remove In-Control from Curve" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +#: editor/plugins/path_editor_plugin.cpp +msgid "Add Point to Curve" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +msgid "Split Curve" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +msgid "Move Point in Curve" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +msgid "Move In-Control in Curve" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +msgid "Move Out-Control in Curve" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +#: editor/plugins/path_editor_plugin.cpp +msgid "Select Points" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +#: editor/plugins/path_editor_plugin.cpp +msgid "Shift+Drag: Select Control Points" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +#: editor/plugins/path_editor_plugin.cpp +msgid "Click: Add Point" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +msgid "Left Click: Split Segment (in curve)" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +#: editor/plugins/path_editor_plugin.cpp +msgid "Right Click: Delete Point" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +msgid "Select Control Points (Shift+Drag)" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +#: editor/plugins/path_editor_plugin.cpp +msgid "Add Point (in empty space)" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +#: editor/plugins/path_editor_plugin.cpp +msgid "Delete Point" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +#: editor/plugins/path_editor_plugin.cpp +msgid "Close Curve" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +#: editor/plugins/path_editor_plugin.cpp editor/plugins/theme_editor_plugin.cpp +#: editor/project_export.cpp +msgid "Options" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +#: editor/plugins/path_editor_plugin.cpp +msgid "Mirror Handle Angles" +msgstr "" + +#: editor/plugins/path_2d_editor_plugin.cpp +#: editor/plugins/path_editor_plugin.cpp +msgid "Mirror Handle Lengths" +msgstr "" + +#: editor/plugins/path_editor_plugin.cpp +msgid "Curve Point #" +msgstr "" + +#: editor/plugins/path_editor_plugin.cpp +msgid "Set Curve Point Position" +msgstr "" + +#: editor/plugins/path_editor_plugin.cpp +msgid "Set Curve In Position" +msgstr "" + +#: editor/plugins/path_editor_plugin.cpp +msgid "Set Curve Out Position" +msgstr "" + +#: editor/plugins/path_editor_plugin.cpp +msgid "Split Path" +msgstr "" + +#: editor/plugins/path_editor_plugin.cpp +msgid "Remove Path Point" +msgstr "" + +#: editor/plugins/path_editor_plugin.cpp +msgid "Remove Out-Control Point" +msgstr "" + +#: editor/plugins/path_editor_plugin.cpp +msgid "Remove In-Control Point" +msgstr "" + +#: editor/plugins/path_editor_plugin.cpp +msgid "Split Segment (in curve)" +msgstr "" + +#: editor/plugins/physical_bone_plugin.cpp +msgid "Move joint" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "" +"The skeleton property of the Polygon2D does not point to a Skeleton2D node" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Sync Bones" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "" +"No texture in this polygon.\n" +"Set a texture to be able to edit UV." +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Create UV Map" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "" +"Polygon 2D has internal vertices, so it can no longer be edited in the " +"viewport." +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Create Polygon & UV" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Create Internal Vertex" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Remove Internal Vertex" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Invalid Polygon (need 3 different vertices)" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Add Custom Polygon" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Remove Custom Polygon" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Transform UV Map" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Transform Polygon" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Paint Bone Weights" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Open Polygon 2D UV editor." +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Polygon 2D UV Editor" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "UV" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Points" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Polygons" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Bones" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Move Points" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Ctrl: Rotate" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Shift: Move All" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Shift+Ctrl: Scale" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Move Polygon" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Rotate Polygon" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Scale Polygon" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Create a custom polygon. Enables custom polygon rendering." +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "" +"Remove a custom polygon. If none remain, custom polygon rendering is " +"disabled." +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Paint weights with specified intensity." +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Unpaint weights with specified intensity." +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Radius:" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Polygon->UV" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "UV->Polygon" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Clear UV" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Grid Settings" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Snap" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Enable Snap" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Grid" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Configure Grid:" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Grid Offset X:" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Grid Offset Y:" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Grid Step X:" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Grid Step Y:" +msgstr "" + +#: editor/plugins/polygon_2d_editor_plugin.cpp +msgid "Sync Bones to Polygon" +msgstr "" + +#: editor/plugins/resource_preloader_editor_plugin.cpp +msgid "ERROR: Couldn't load resource!" +msgstr "" + +#: editor/plugins/resource_preloader_editor_plugin.cpp +msgid "Add Resource" +msgstr "" + +#: editor/plugins/resource_preloader_editor_plugin.cpp +msgid "Rename Resource" +msgstr "" + +#: editor/plugins/resource_preloader_editor_plugin.cpp +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Delete Resource" +msgstr "" + +#: editor/plugins/resource_preloader_editor_plugin.cpp +msgid "Resource clipboard is empty!" +msgstr "" + +#: editor/plugins/resource_preloader_editor_plugin.cpp +msgid "Paste Resource" +msgstr "" + +#: editor/plugins/resource_preloader_editor_plugin.cpp +#: editor/scene_tree_editor.cpp +msgid "Instance:" +msgstr "" + +#: editor/plugins/resource_preloader_editor_plugin.cpp +#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp +#: editor/scene_tree_editor.cpp +msgid "Type:" +msgstr "" + +#: editor/plugins/resource_preloader_editor_plugin.cpp +#: editor/scene_tree_dock.cpp editor/scene_tree_editor.cpp +msgid "Open in Editor" +msgstr "" + +#: editor/plugins/resource_preloader_editor_plugin.cpp +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Load Resource" +msgstr "" + +#: editor/plugins/resource_preloader_editor_plugin.cpp +msgid "ResourcePreloader" +msgstr "" + +#: editor/plugins/root_motion_editor_plugin.cpp +msgid "AnimationTree has no path set to an AnimationPlayer" +msgstr "" + +#: editor/plugins/root_motion_editor_plugin.cpp +msgid "Path to AnimationPlayer is invalid" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Clear Recent Files" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Close and save changes?" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Error writing TextFile:" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Error: could not load file." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Error could not load file." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Error saving file!" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Error while saving theme." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Error Saving" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Error importing theme." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Error Importing" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "New TextFile..." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Open File" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Save File As..." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Import Theme" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Error while saving theme" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Error saving" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Save Theme As..." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid " Class Reference" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Toggle alphabetical sorting of the method list." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Sort" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +#: editor/plugins/script_text_editor.cpp editor/scene_tree_dock.cpp +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "Move Up" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +#: editor/plugins/script_text_editor.cpp editor/scene_tree_dock.cpp +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "Move Down" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Next script" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Previous script" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "File" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Open..." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Save All" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Soft Reload Script" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Copy Script Path" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "History Previous" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "History Next" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +#: editor/plugins/theme_editor_plugin.cpp +msgid "Theme" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Import Theme..." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Reload Theme" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Save Theme" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Close Docs" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Close All" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Close Other Tabs" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp +msgid "Run" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Toggle Scripts Panel" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +#: editor/plugins/script_text_editor.cpp +msgid "Find Next" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp +msgid "Step Over" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp +msgid "Step Into" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp +msgid "Break" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp +#: editor/script_editor_debugger.cpp +msgid "Continue" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Keep Debugger Open" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Debug with External Editor" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Open Godot online documentation" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Request Docs" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Help improve the Godot documentation by giving feedback" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Search the reference documentation." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Go to previous edited document." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Go to next edited document." +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Discard" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "" +"The following files are newer on disk.\n" +"What action should be taken?:" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Reload" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Resave" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp +msgid "Debugger" +msgstr "" + +#: editor/plugins/script_editor_plugin.cpp +msgid "Search Results" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Line" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "(ignore)" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Go to Function" +msgstr "" + +#: editor/plugins/script_text_editor.cpp editor/plugins/text_editor.cpp +msgid "Standard" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Only resources from filesystem can be dropped." +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Lookup Symbol" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Pick Color" +msgstr "" + +#: editor/plugins/script_text_editor.cpp editor/plugins/text_editor.cpp +msgid "Convert Case" +msgstr "" + +#: editor/plugins/script_text_editor.cpp editor/plugins/text_editor.cpp +msgid "Uppercase" +msgstr "" + +#: editor/plugins/script_text_editor.cpp editor/plugins/text_editor.cpp +msgid "Lowercase" +msgstr "" + +#: editor/plugins/script_text_editor.cpp editor/plugins/text_editor.cpp +msgid "Capitalize" +msgstr "" + +#: editor/plugins/script_text_editor.cpp editor/plugins/text_editor.cpp +msgid "Syntax Highlighter" +msgstr "" + +#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp +#: scene/gui/text_edit.cpp +msgid "Cut" +msgstr "" + +#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp +#: scene/gui/text_edit.cpp +msgid "Select All" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Delete Line" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Indent Left" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Indent Right" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Toggle Comment" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Fold/Unfold Line" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Fold All Lines" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Unfold All Lines" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Clone Down" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Complete Symbol" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Trim Trailing Whitespace" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Convert Indent to Spaces" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Convert Indent to Tabs" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Auto Indent" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +#: modules/visual_script/visual_script_editor.cpp +msgid "Toggle Breakpoint" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Remove All Breakpoints" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Go to Next Breakpoint" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Go to Previous Breakpoint" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Find Previous" +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Find in Files..." +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Go to Function..." +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Go to Line..." +msgstr "" + +#: editor/plugins/script_text_editor.cpp +msgid "Contextual Help" +msgstr "" + +#: editor/plugins/shader_editor_plugin.cpp +msgid "Shader" +msgstr "" + +#: editor/plugins/skeleton_2d_editor_plugin.cpp +msgid "This skeleton has no bones, create some children Bone2D nodes." +msgstr "" + +#: editor/plugins/skeleton_2d_editor_plugin.cpp +msgid "Create Rest Pose from Bones" +msgstr "" + +#: editor/plugins/skeleton_2d_editor_plugin.cpp +msgid "Set Rest Pose to Bones" +msgstr "" + +#: editor/plugins/skeleton_2d_editor_plugin.cpp +msgid "Skeleton2D" +msgstr "" + +#: editor/plugins/skeleton_2d_editor_plugin.cpp +msgid "Make Rest Pose (From Bones)" +msgstr "" + +#: editor/plugins/skeleton_2d_editor_plugin.cpp +msgid "Set Bones to Rest Pose" +msgstr "" + +#: editor/plugins/skeleton_editor_plugin.cpp +msgid "Create physical bones" +msgstr "" + +#: editor/plugins/skeleton_editor_plugin.cpp +msgid "Skeleton" +msgstr "" + +#: editor/plugins/skeleton_editor_plugin.cpp +msgid "Create physical skeleton" +msgstr "" + +#: editor/plugins/skeleton_ik_editor_plugin.cpp +msgid "Play IK" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Orthogonal" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Perspective" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Transform Aborted." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "X-Axis Transform." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Y-Axis Transform." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Z-Axis Transform." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "View Plane Transform." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Scaling: " +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Translating: " +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Rotating %s degrees." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Keying is disabled (no key inserted)." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Animation Key Inserted." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Pitch" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Yaw" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Objects Drawn" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Material Changes" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Shader Changes" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Surface Changes" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Draw Calls" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Vertices" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Top View." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Bottom View." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Bottom" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Left View." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Left" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Right View." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Right" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Front View." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Front" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Rear View." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Rear" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Align with View" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp +msgid "No parent to instance a child at." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp +msgid "This operation requires a single selected node." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Lock View Rotation" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Display Normal" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Display Wireframe" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Display Overdraw" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Display Unshaded" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "View Environment" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "View Gizmos" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "View Information" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "View FPS" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Half Resolution" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Audio Listener" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Doppler Enable" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Cinematic Preview" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Freelook Left" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Freelook Right" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Freelook Forward" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Freelook Backwards" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Freelook Up" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Freelook Down" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Freelook Speed Modifier" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "" +"Note: The FPS value displayed is the editor's framerate.\n" +"It cannot be used as a reliable indication of in-game performance." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "View Rotation Locked" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "XForm Dialog" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Snap Nodes To Floor" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Select Mode (Q)" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "" +"Drag: Rotate\n" +"Alt+Drag: Move\n" +"Alt+RMB: Depth list selection" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Move Mode (W)" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Rotate Mode (E)" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Scale Mode (R)" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Local Coords" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Local Space Mode (%s)" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Snap Mode (%s)" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Bottom View" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Top View" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Rear View" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Front View" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Left View" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Right View" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Switch Perspective/Orthogonal view" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Insert Animation Key" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Focus Origin" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Focus Selection" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Align Selection With View" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Tool Select" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Tool Move" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Tool Rotate" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Tool Scale" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Toggle Freelook" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Transform" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Snap object to floor" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Transform Dialog..." +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "1 Viewport" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "2 Viewports" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "2 Viewports (Alt)" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "3 Viewports" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "3 Viewports (Alt)" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "4 Viewports" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Gizmos" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "View Origin" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "View Grid" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Settings" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Snap Settings" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Translate Snap:" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Rotate Snap (deg.):" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Scale Snap (%):" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Viewport Settings" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Perspective FOV (deg.):" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "View Z-Near:" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "View Z-Far:" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Transform Change" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Translate:" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Rotate (deg.):" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Scale (ratio):" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Transform Type" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Pre" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Post" +msgstr "" + +#: editor/plugins/spatial_editor_plugin.cpp +msgid "Nameless gizmo" +msgstr "" + +#: editor/plugins/sprite_editor_plugin.cpp +msgid "Sprite is empty!" +msgstr "" + +#: editor/plugins/sprite_editor_plugin.cpp +msgid "Can't convert a sprite using animation frames to mesh." +msgstr "" + +#: editor/plugins/sprite_editor_plugin.cpp +msgid "Invalid geometry, can't replace by mesh." +msgstr "" + +#: editor/plugins/sprite_editor_plugin.cpp +msgid "Sprite" +msgstr "" + +#: editor/plugins/sprite_editor_plugin.cpp +msgid "Convert to 2D Mesh" +msgstr "" + +#: editor/plugins/sprite_editor_plugin.cpp +msgid "Create 2D Mesh" +msgstr "" + +#: editor/plugins/sprite_editor_plugin.cpp +msgid "Simplification: " +msgstr "" + +#: editor/plugins/sprite_editor_plugin.cpp +msgid "Grow (Pixels): " +msgstr "" + +#: editor/plugins/sprite_editor_plugin.cpp +msgid "Update Preview" +msgstr "" + +#: editor/plugins/sprite_editor_plugin.cpp +msgid "Settings:" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "ERROR: Couldn't load frame resource!" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Add Frame" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Resource clipboard is empty or not a texture!" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Paste Frame" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Add Empty" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Change Animation FPS" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "(empty)" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Animations:" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "New Animation" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Speed (FPS):" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Loop" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Animation Frames:" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Insert Empty (Before)" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Insert Empty (After)" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Move (Before)" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "Move (After)" +msgstr "" + +#: editor/plugins/sprite_frames_editor_plugin.cpp +msgid "SpriteFrames" +msgstr "" + +#: editor/plugins/texture_region_editor_plugin.cpp +msgid "Set Region Rect" +msgstr "" + +#: editor/plugins/texture_region_editor_plugin.cpp +msgid "Set Margin" +msgstr "" + +#: editor/plugins/texture_region_editor_plugin.cpp +msgid "Snap Mode:" +msgstr "" + +#: editor/plugins/texture_region_editor_plugin.cpp +#: scene/resources/visual_shader.cpp +msgid "None" +msgstr "" + +#: editor/plugins/texture_region_editor_plugin.cpp +msgid "Pixel Snap" +msgstr "" + +#: editor/plugins/texture_region_editor_plugin.cpp +msgid "Grid Snap" +msgstr "" + +#: editor/plugins/texture_region_editor_plugin.cpp +msgid "Auto Slice" +msgstr "" + +#: editor/plugins/texture_region_editor_plugin.cpp +msgid "Offset:" +msgstr "" + +#: editor/plugins/texture_region_editor_plugin.cpp +msgid "Step:" +msgstr "" + +#: editor/plugins/texture_region_editor_plugin.cpp +msgid "Sep.:" +msgstr "" + +#: editor/plugins/texture_region_editor_plugin.cpp +msgid "TextureRegion" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Can't save theme to file:" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Add All Items" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Add All" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Remove All Items" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Remove All" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Edit theme..." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Theme editing menu." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Add Class Items" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Remove Class Items" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Create Empty Template" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Create Empty Editor Template" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Create From Current Editor Theme" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "CheckBox Radio1" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "CheckBox Radio2" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Item" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Check Item" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Checked Item" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Radio Item" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Checked Radio Item" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Has" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Many" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Has,Many,Options" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Tab 1" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Tab 2" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Tab 3" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Data Type:" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Icon" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp editor/rename_dialog.cpp +msgid "Style" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Font" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Color" +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "Constant" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Erase Selection" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Fix Invalid Tiles" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Cut Selection" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Paint TileMap" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Line Draw" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Rectangle Paint" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Bucket Fill" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Erase TileMap" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Find Tile" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Transpose" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Mirror X" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Mirror Y" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Paint Tile" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Pick Tile" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Copy Selection" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Rotate left" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Rotate right" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Flip horizontally" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Flip vertically" +msgstr "" + +#: editor/plugins/tile_map_editor_plugin.cpp +msgid "Clear transform" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Add Texture(s) to TileSet." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Remove selected Texture from TileSet." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Create from Scene" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Merge from Scene" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Next Coordinate" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Select the next shape, subtile, or Tile." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Previous Coordinate" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Select the previous shape, subtile, or Tile." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Copy bitmask." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Paste bitmask." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Erase bitmask." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Create a new rectangle." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Create a new polygon." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Keep polygon inside region Rect." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Enable snap and show grid (configurable via the Inspector)." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Display Tile Names (Hold Alt Key)" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Remove selected texture? This will remove all tiles which use it." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "You haven't selected a texture to remove." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Create from scene? This will overwrite all current tiles." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Merge from scene?" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Remove Texture" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "%s file(s) were not added because was already on the list." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "" +"Drag handles to edit Rect.\n" +"Click on another Tile to edit it." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Delete selected Rect." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "" +"Select current edited sub-tile.\n" +"Click on another Tile to edit it." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Delete polygon." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "" +"LMB: Set bit on.\n" +"RMB: Set bit off.\n" +"Click on another Tile to edit it." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "" +"Select sub-tile to use as icon, this will be also used on invalid autotile " +"bindings.\n" +"Click on another Tile to edit it." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "" +"Select sub-tile to change its priority.\n" +"Click on another Tile to edit it." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "" +"Select sub-tile to change its z index.\n" +"Click on another Tile to edit it." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Set Tile Region" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Create Tile" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Set Tile Icon" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Edit Tile Bitmask" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Edit Collision Polygon" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Edit Occlusion Polygon" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Edit Navigation Polygon" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Paste Tile Bitmask" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Clear Tile Bitmask" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Make Polygon Concave" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Make Polygon Convex" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Remove Tile" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Remove Collision Polygon" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Remove Occlusion Polygon" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Remove Navigation Polygon" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Edit Tile Priority" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Edit Tile Z Index" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Create Collision Polygon" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "Create Occlusion Polygon" +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "This property can't be changed." +msgstr "" + +#: editor/plugins/tile_set_editor_plugin.cpp +msgid "TileSet" +msgstr "" + +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Set Uniform Name" +msgstr "" + +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Set Input Default Port" +msgstr "" + +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Add Node to Visual Shader" +msgstr "" + +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Duplicate Nodes" +msgstr "" + +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Visual Shader Input Type Changed" +msgstr "" + +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Vertex" +msgstr "" + +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Fragment" +msgstr "" + +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Light" +msgstr "" + +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "VisualShader" +msgstr "" + +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Edit Visual Property" +msgstr "" + +#: editor/plugins/visual_shader_editor_plugin.cpp +msgid "Visual Shader Mode Changed" +msgstr "" + +#: editor/project_export.cpp +msgid "Runnable" +msgstr "" + +#: editor/project_export.cpp +msgid "Delete patch '%s' from list?" +msgstr "" + +#: editor/project_export.cpp +msgid "Delete preset '%s'?" +msgstr "" + +#: editor/project_export.cpp +msgid "" +"Failed to export the project for platform '%s'.\n" +"Export templates seem to be missing or invalid." +msgstr "" + +#: editor/project_export.cpp +msgid "" +"Failed to export the project for platform '%s'.\n" +"This might be due to a configuration issue in the export preset or your " +"export settings." +msgstr "" + +#: editor/project_export.cpp +msgid "Release" +msgstr "" + +#: editor/project_export.cpp +msgid "Exporting All" +msgstr "" + +#: editor/project_export.cpp +msgid "The given export path doesn't exist:" +msgstr "" + +#: editor/project_export.cpp +msgid "Export templates for this platform are missing/corrupted:" +msgstr "" + +#: editor/project_export.cpp +msgid "Presets" +msgstr "" + +#: editor/project_export.cpp editor/project_settings_editor.cpp +msgid "Add..." +msgstr "" + +#: editor/project_export.cpp +msgid "Export Path" +msgstr "" + +#: editor/project_export.cpp +msgid "Resources" +msgstr "" + +#: editor/project_export.cpp +msgid "Export all resources in the project" +msgstr "" + +#: editor/project_export.cpp +msgid "Export selected scenes (and dependencies)" +msgstr "" + +#: editor/project_export.cpp +msgid "Export selected resources (and dependencies)" +msgstr "" + +#: editor/project_export.cpp +msgid "Export Mode:" +msgstr "" + +#: editor/project_export.cpp +msgid "Resources to export:" +msgstr "" + +#: editor/project_export.cpp +msgid "" +"Filters to export non-resource files (comma separated, e.g: *.json, *.txt)" +msgstr "" + +#: editor/project_export.cpp +msgid "" +"Filters to exclude files from project (comma separated, e.g: *.json, *.txt)" +msgstr "" + +#: editor/project_export.cpp +msgid "Patches" +msgstr "" + +#: editor/project_export.cpp +msgid "Make Patch" +msgstr "" + +#: editor/project_export.cpp +msgid "Features" +msgstr "" + +#: editor/project_export.cpp +msgid "Custom (comma-separated):" +msgstr "" + +#: editor/project_export.cpp +msgid "Feature List:" +msgstr "" + +#: editor/project_export.cpp +msgid "Script" +msgstr "" + +#: editor/project_export.cpp +msgid "Script Export Mode:" +msgstr "" + +#: editor/project_export.cpp +msgid "Text" +msgstr "" + +#: editor/project_export.cpp +msgid "Compiled" +msgstr "" + +#: editor/project_export.cpp +msgid "Encrypted (Provide Key Below)" +msgstr "" + +#: editor/project_export.cpp +msgid "Invalid Encryption Key (must be 64 characters long)" +msgstr "" + +#: editor/project_export.cpp +msgid "Script Encryption Key (256-bits as hex):" +msgstr "" + +#: editor/project_export.cpp +msgid "Export PCK/Zip" +msgstr "" + +#: editor/project_export.cpp +msgid "Export mode?" +msgstr "" + +#: editor/project_export.cpp +msgid "Export All" +msgstr "" + +#: editor/project_export.cpp +msgid "Export templates for this platform are missing:" +msgstr "" + +#: editor/project_export.cpp +msgid "Export With Debug" +msgstr "" + +#: editor/project_manager.cpp +msgid "The path does not exist." +msgstr "" + +#: editor/project_manager.cpp +msgid "Invalid '.zip' project file, does not contain a 'project.godot' file." +msgstr "" + +#: editor/project_manager.cpp +msgid "Please choose an empty folder." +msgstr "" + +#: editor/project_manager.cpp +msgid "Please choose a 'project.godot' or '.zip' file." +msgstr "" + +#: editor/project_manager.cpp +msgid "Directory already contains a Godot project." +msgstr "" + +#: editor/project_manager.cpp +msgid "Imported Project" +msgstr "" + +#: editor/project_manager.cpp +msgid "Invalid Project Name." +msgstr "" + +#: editor/project_manager.cpp +msgid "Couldn't create folder." +msgstr "" + +#: editor/project_manager.cpp +msgid "There is already a folder in this path with the specified name." +msgstr "" + +#: editor/project_manager.cpp +msgid "It would be a good idea to name your project." +msgstr "" + +#: editor/project_manager.cpp +msgid "Invalid project path (changed anything?)." +msgstr "" + +#: editor/project_manager.cpp +msgid "" +"Couldn't load project.godot in project path (error %d). It may be missing or " +"corrupted." +msgstr "" + +#: editor/project_manager.cpp +msgid "Couldn't edit project.godot in project path." +msgstr "" + +#: editor/project_manager.cpp +msgid "Couldn't create project.godot in project path." +msgstr "" + +#: editor/project_manager.cpp +msgid "The following files failed extraction from package:" +msgstr "" + +#: editor/project_manager.cpp +msgid "Rename Project" +msgstr "" + +#: editor/project_manager.cpp +msgid "New Game Project" +msgstr "" + +#: editor/project_manager.cpp +msgid "Import Existing Project" +msgstr "" + +#: editor/project_manager.cpp +msgid "Import & Edit" +msgstr "" + +#: editor/project_manager.cpp +msgid "Create New Project" +msgstr "" + +#: editor/project_manager.cpp +msgid "Create & Edit" +msgstr "" + +#: editor/project_manager.cpp +msgid "Install Project:" +msgstr "" + +#: editor/project_manager.cpp +msgid "Install & Edit" +msgstr "" + +#: editor/project_manager.cpp +msgid "Project Name:" +msgstr "" + +#: editor/project_manager.cpp +msgid "Create folder" +msgstr "" + +#: editor/project_manager.cpp +msgid "Project Path:" +msgstr "" + +#: editor/project_manager.cpp +msgid "Project Installation Path:" +msgstr "" + +#: editor/project_manager.cpp +msgid "Browse" +msgstr "" + +#: editor/project_manager.cpp +msgid "Renderer:" +msgstr "" + +#: editor/project_manager.cpp +msgid "OpenGL ES 3.0" +msgstr "" + +#: editor/project_manager.cpp +msgid "" +"Higher visual quality\n" +"All features available\n" +"Incompatible with older hardware\n" +"Not recommended for web games" +msgstr "" + +#: editor/project_manager.cpp +msgid "OpenGL ES 2.0" +msgstr "" + +#: editor/project_manager.cpp +msgid "" +"Lower visual quality\n" +"Some features not available\n" +"Works on most hardware\n" +"Recommended for web games" +msgstr "" + +#: editor/project_manager.cpp +msgid "Renderer can be changed later, but scenes may need to be adjusted." +msgstr "" + +#: editor/project_manager.cpp +msgid "Unnamed Project" +msgstr "" + +#: editor/project_manager.cpp +msgid "Can't open project at '%s'." +msgstr "" + +#: editor/project_manager.cpp +msgid "Are you sure to open more than one project?" +msgstr "" + +#: editor/project_manager.cpp +msgid "" +"The following project settings file does not specify the version of Godot " +"through which it was created.\n" +"\n" +"%s\n" +"\n" +"If you proceed with opening it, it will be converted to Godot's current " +"configuration file format.\n" +"Warning: You will not be able to open the project with previous versions of " +"the engine anymore." +msgstr "" + +#: editor/project_manager.cpp +msgid "" +"The following project settings file was generated by an older engine " +"version, and needs to be converted for this version:\n" +"\n" +"%s\n" +"\n" +"Do you want to convert it?\n" +"Warning: You will not be able to open the project with previous versions of " +"the engine anymore." +msgstr "" + +#: editor/project_manager.cpp +msgid "" +"The project settings were created by a newer engine version, whose settings " +"are not compatible with this version." +msgstr "" + +#: editor/project_manager.cpp +msgid "" +"Can't run project: no main scene defined.\n" +"Please edit the project and set the main scene in \"Project Settings\" under " +"the \"Application\" category." +msgstr "" + +#: editor/project_manager.cpp +msgid "" +"Can't run project: Assets need to be imported.\n" +"Please edit the project to trigger the initial import." +msgstr "" + +#: editor/project_manager.cpp +msgid "Are you sure to run more than one project?" +msgstr "" + +#: editor/project_manager.cpp +msgid "Remove project from the list? (Folder contents will not be modified)" +msgstr "" + +#: editor/project_manager.cpp +msgid "" +"Language changed.\n" +"The UI will update next time the editor or project manager starts." +msgstr "" + +#: editor/project_manager.cpp +msgid "" +"You are about the scan %s folders for existing Godot projects. Do you " +"confirm?" +msgstr "" + +#: editor/project_manager.cpp +msgid "Project Manager" +msgstr "" + +#: editor/project_manager.cpp +msgid "Project List" +msgstr "" + +#: editor/project_manager.cpp +msgid "Scan" +msgstr "" + +#: editor/project_manager.cpp +msgid "Select a Folder to Scan" +msgstr "" + +#: editor/project_manager.cpp +msgid "New Project" +msgstr "" + +#: editor/project_manager.cpp +msgid "Templates" +msgstr "" + +#: editor/project_manager.cpp +msgid "Exit" +msgstr "" + +#: editor/project_manager.cpp +msgid "Restart Now" +msgstr "" + +#: editor/project_manager.cpp +msgid "Can't run project" +msgstr "" + +#: editor/project_manager.cpp +msgid "" +"You don't currently have any projects.\n" +"Would you like to explore the official example projects in the Asset Library?" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Key " +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Joy Button" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Joy Axis" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Mouse Button" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "" +"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or " +"'\"'" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Action '%s' already exists!" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Rename Input Action Event" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Change Action deadzone" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Add Input Action Event" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "All Devices" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Device" +msgstr "" + +#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp +msgid "Shift+" +msgstr "" + +#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp +msgid "Alt+" +msgstr "" + +#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp +msgid "Control+" +msgstr "" + +#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp +msgid "Press a Key..." +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Mouse Button Index:" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Left Button" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Right Button" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Middle Button" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Wheel Up Button" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Wheel Down Button" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Wheel Left Button" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Wheel Right Button" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "X Button 1" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "X Button 2" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Joypad Axis Index:" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Axis" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Joypad Button Index:" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Erase Input Action" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Erase Input Action Event" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Add Event" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Button" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Left Button." +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Right Button." +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Middle Button." +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Wheel Up." +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Wheel Down." +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Add Global Property" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Select a setting item first!" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "No property '%s' exists." +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Setting '%s' is internal, and it can't be deleted." +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Delete Item" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "" +"Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or " +"'\"'." +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Already existing" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Add Input Action" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Error saving settings." +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Settings saved OK." +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Override for Feature" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Add Translation" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Remove Translation" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Add Remapped Path" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Resource Remap Add Remap" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Change Resource Remap Language" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Remove Resource Remap" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Remove Resource Remap Option" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Changed Locale Filter" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Changed Locale Filter Mode" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Project Settings (project.godot)" +msgstr "" + +#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp +msgid "General" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Override For..." +msgstr "" + +#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp +msgid "Editor must be restarted for changes to take effect" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Input Map" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Action:" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Action" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Deadzone" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Device:" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Index:" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Localization" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Translations" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Translations:" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Remaps" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Resources:" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Remaps by Locale:" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Locale" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Locales Filter" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Show all locales" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Show only selected locales" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Filter mode:" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "Locales:" +msgstr "" + +#: editor/project_settings_editor.cpp +msgid "AutoLoad" +msgstr "" + +#: editor/property_editor.cpp +msgid "Ease In" +msgstr "" + +#: editor/property_editor.cpp +msgid "Ease Out" +msgstr "" + +#: editor/property_editor.cpp +msgid "Zero" +msgstr "" + +#: editor/property_editor.cpp +msgid "Easing In-Out" +msgstr "" + +#: editor/property_editor.cpp +msgid "Easing Out-In" +msgstr "" + +#: editor/property_editor.cpp +msgid "File..." +msgstr "" + +#: editor/property_editor.cpp +msgid "Dir..." +msgstr "" + +#: editor/property_editor.cpp +msgid "Assign" +msgstr "" + +#: editor/property_editor.cpp +msgid "Select Node" +msgstr "" + +#: editor/property_editor.cpp +msgid "Error loading file: Not a resource!" +msgstr "" + +#: editor/property_editor.cpp +msgid "Pick a Node" +msgstr "" + +#: editor/property_editor.cpp +msgid "Bit %d, val %d." +msgstr "" + +#: editor/property_selector.cpp +msgid "Select Property" +msgstr "" + +#: editor/property_selector.cpp +msgid "Select Virtual Method" +msgstr "" + +#: editor/property_selector.cpp +msgid "Select Method" +msgstr "" + +#: editor/pvrtc_compress.cpp +msgid "Could not execute PVRTC tool:" +msgstr "" + +#: editor/pvrtc_compress.cpp +msgid "Can't load back converted image using PVRTC tool:" +msgstr "" + +#: editor/rename_dialog.cpp editor/scene_tree_dock.cpp +msgid "Batch Rename" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Prefix" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Suffix" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Advanced options" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Substitute" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Node name" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Node's parent name, if available" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Node type" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Current scene name" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Root node name" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "" +"Sequential integer counter.\n" +"Compare counter options." +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Per Level counter" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "If set the counter restarts for each group of child nodes" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Initial value for the counter" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Step" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Amount by which counter is incremented for each node" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Padding" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "" +"Minimum number of digits for the counter.\n" +"Missing digits are padded with leading zeros." +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Regular Expressions" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Post-Process" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Keep" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "CamelCase to under_scored" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "under_scored to CamelCase" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Case" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "To Lowercase" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "To Uppercase" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Reset" +msgstr "" + +#: editor/rename_dialog.cpp +msgid "Error" +msgstr "" + +#: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp +msgid "Reparent Node" +msgstr "" + +#: editor/reparent_dialog.cpp +msgid "Reparent Location (Select new Parent):" +msgstr "" + +#: editor/reparent_dialog.cpp +msgid "Keep Global Transform" +msgstr "" + +#: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp +msgid "Reparent" +msgstr "" + +#: editor/run_settings_dialog.cpp +msgid "Run Mode:" +msgstr "" + +#: editor/run_settings_dialog.cpp +msgid "Current Scene" +msgstr "" + +#: editor/run_settings_dialog.cpp +msgid "Main Scene" +msgstr "" + +#: editor/run_settings_dialog.cpp +msgid "Main Scene Arguments:" +msgstr "" + +#: editor/run_settings_dialog.cpp +msgid "Scene Run Settings" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "No parent to instance the scenes at." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Error loading scene from %s" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "" +"Cannot instance the scene '%s' because the current scene exists within one " +"of its nodes." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Instance Scene(s)" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Instance Child Scene" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Clear Script" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "This operation can't be done on the tree root." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Move Node In Parent" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Move Nodes In Parent" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Duplicate Node(s)" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Can't reparent nodes in inherited scenes, order of nodes can't change." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Node must belong to the edited scene to become root." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Instantiated scenes can't become root" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Make node as Root" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Delete Node(s)?" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Can not perform with the root node." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "This operation can't be done on instanced scenes." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Save New Scene As..." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "" +"Disabling \"editable_instance\" will cause all properties of the node to be " +"reverted to their default." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Editable Children" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Load As Placeholder" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Make Local" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "New Scene Root" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Create Root Node:" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "2D Scene" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "3D Scene" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "User Interface" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Custom Node" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Can't operate on nodes from a foreign scene!" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Can't operate on nodes the current scene inherits from!" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Attach Script" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Remove Node(s)" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "" +"Couldn't save new scene. Likely dependencies (instances) couldn't be " +"satisfied." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Error saving scene." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Error duplicating scene to save it." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Sub-Resources" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Clear Inheritance" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Open documentation" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Delete Node(s)" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Add Child Node" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Change Type" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Extend Script" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Make Scene Root" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Merge From Scene" +msgstr "" + +#: editor/scene_tree_dock.cpp editor/script_editor_debugger.cpp +msgid "Save Branch as Scene" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Copy Node Path" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Delete (No Confirm)" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Add/Create a New Node" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "" +"Instance a scene file as a Node. Creates an inherited scene if no root node " +"exists." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Attach a new or existing script for the selected node." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Clear a script for the selected node." +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Remote" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Local" +msgstr "" + +#: editor/scene_tree_dock.cpp +msgid "Clear Inheritance? (No Undo!)" +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "Toggle Visible" +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "Node configuration warning:" +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "" +"Node has connection(s) and group(s).\n" +"Click to show signals dock." +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "" +"Node has connections.\n" +"Click to show signals dock." +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "" +"Node is in group(s).\n" +"Click to show groups dock." +msgstr "" + +#: editor/scene_tree_editor.cpp editor/script_create_dialog.cpp +msgid "Open Script" +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "" +"Node is locked.\n" +"Click to unlock it." +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "" +"Children are not selectable.\n" +"Click to make selectable." +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "Toggle Visibility" +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "" +"AnimationPlayer is pinned.\n" +"Click to unpin." +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "Invalid node name, the following characters are not allowed:" +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "Rename Node" +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "Scene Tree (Nodes):" +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "Node Configuration Warning!" +msgstr "" + +#: editor/scene_tree_editor.cpp +msgid "Select a Node" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Error loading template '%s'" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Error - Could not create script in filesystem." +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Error loading script from %s" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "N/A" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Open Script/Choose Location" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Path is empty" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Filename is empty" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Path is not local" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Invalid base path" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Directory of the same name exists" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "File exists, will be reused" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Invalid extension" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Wrong extension chosen" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Invalid Path" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Invalid class name" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Invalid inherited parent name or path" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Script valid" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Allowed: a-z, A-Z, 0-9 and _" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Built-in script (into scene file)" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Create new script file" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Load existing script file" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Language" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Inherits" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Class Name" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Template" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Built-in Script" +msgstr "" + +#: editor/script_create_dialog.cpp +msgid "Attach Node Script" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Remote " +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Bytes:" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Stack Trace" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Pick one or more items from the list to display the graph." +msgstr "" + +#: editor/script_editor_debugger.cpp modules/mono/editor/mono_bottom_panel.cpp +msgid "Errors" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Child Process Connected" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Copy Error" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Inspect Previous Instance" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Inspect Next Instance" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Stack Frames" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Profiler" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Monitor" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Value" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Monitors" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "List of Video Memory Usage by Resource:" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Total:" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Video Mem" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Resource Path" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Type" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Format" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Usage" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Misc" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Clicked Control:" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Clicked Control Type:" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Live Edit Root:" +msgstr "" + +#: editor/script_editor_debugger.cpp +msgid "Set From Tree" +msgstr "" + +#: editor/settings_config_dialog.cpp +msgid "Erase Shortcut" +msgstr "" + +#: editor/settings_config_dialog.cpp +msgid "Restore Shortcut" +msgstr "" + +#: editor/settings_config_dialog.cpp +msgid "Change Shortcut" +msgstr "" + +#: editor/settings_config_dialog.cpp +msgid "Shortcuts" +msgstr "" + +#: editor/settings_config_dialog.cpp +msgid "Binding" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change Light Radius" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change AudioStreamPlayer3D Emission Angle" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change Camera FOV" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change Camera Size" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change Notifier AABB" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change Particles AABB" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change Probe Extents" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp modules/csg/csg_gizmos.cpp +msgid "Change Sphere Shape Radius" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp modules/csg/csg_gizmos.cpp +msgid "Change Box Shape Extents" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change Capsule Shape Radius" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change Capsule Shape Height" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change Cylinder Shape Radius" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change Cylinder Shape Height" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Change Ray Shape Length" +msgstr "" + +#: modules/csg/csg_gizmos.cpp +msgid "Change Cylinder Radius" +msgstr "" + +#: modules/csg/csg_gizmos.cpp +msgid "Change Cylinder Height" +msgstr "" + +#: modules/csg/csg_gizmos.cpp +msgid "Change Torus Inner Radius" +msgstr "" + +#: modules/csg/csg_gizmos.cpp +msgid "Change Torus Outer Radius" +msgstr "" + +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "Select the dynamic library for this entry" +msgstr "" + +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "Select dependencies of the library for this entry" +msgstr "" + +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "Remove current entry" +msgstr "" + +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "Double click to create a new entry" +msgstr "" + +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "Platform:" +msgstr "" + +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "Platform" +msgstr "" + +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "Dynamic Library" +msgstr "" + +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "Add an architecture entry" +msgstr "" + +#: modules/gdnative/gdnative_library_editor_plugin.cpp +msgid "GDNativeLibrary" +msgstr "" + +#: modules/gdnative/gdnative_library_singleton_editor.cpp +msgid "Library" +msgstr "" + +#: modules/gdnative/gdnative_library_singleton_editor.cpp +msgid "Status" +msgstr "" + +#: modules/gdnative/gdnative_library_singleton_editor.cpp +msgid "Libraries: " +msgstr "" + +#: modules/gdnative/register_types.cpp +msgid "GDNative" +msgstr "" + +#: modules/gdscript/gdscript_functions.cpp +msgid "Step argument is zero!" +msgstr "" + +#: modules/gdscript/gdscript_functions.cpp +msgid "Not a script with an instance" +msgstr "" + +#: modules/gdscript/gdscript_functions.cpp +msgid "Not based on a script" +msgstr "" + +#: modules/gdscript/gdscript_functions.cpp +msgid "Not based on a resource file" +msgstr "" + +#: modules/gdscript/gdscript_functions.cpp +msgid "Invalid instance dictionary format (missing @path)" +msgstr "" + +#: modules/gdscript/gdscript_functions.cpp +msgid "Invalid instance dictionary format (can't load script at @path)" +msgstr "" + +#: modules/gdscript/gdscript_functions.cpp +msgid "Invalid instance dictionary format (invalid script at @path)" +msgstr "" + +#: modules/gdscript/gdscript_functions.cpp +msgid "Invalid instance dictionary (invalid subclasses)" +msgstr "" + +#: modules/gdscript/gdscript_functions.cpp +msgid "Object can't provide a length." +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Next Plane" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Previous Plane" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Plane:" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Next Floor" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Previous Floor" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Floor:" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "GridMap Delete Selection" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "GridMap Fill Selection" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "GridMap Duplicate Selection" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "GridMap Paint" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Grid Map" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Snap View" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Clip Disabled" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Clip Above" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Clip Below" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Edit X Axis" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Edit Y Axis" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Edit Z Axis" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Cursor Rotate X" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Cursor Rotate Y" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Cursor Rotate Z" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Cursor Back Rotate X" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Cursor Back Rotate Y" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Cursor Back Rotate Z" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Cursor Clear Rotation" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Create Area" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Create Exterior Connector" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Erase Area" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Clear Selection" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Fill Selection" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "GridMap Settings" +msgstr "" + +#: modules/gridmap/grid_map_editor_plugin.cpp +msgid "Pick Distance:" +msgstr "" + +#: modules/mono/csharp_script.cpp +msgid "Class name can't be a reserved keyword" +msgstr "" + +#: modules/mono/editor/godotsharp_editor.cpp +msgid "Generating solution..." +msgstr "" + +#: modules/mono/editor/godotsharp_editor.cpp +msgid "Generating C# project..." +msgstr "" + +#: modules/mono/editor/godotsharp_editor.cpp +msgid "Failed to create solution." +msgstr "" + +#: modules/mono/editor/godotsharp_editor.cpp +msgid "Failed to save solution." +msgstr "" + +#: modules/mono/editor/godotsharp_editor.cpp +msgid "Done" +msgstr "" + +#: modules/mono/editor/godotsharp_editor.cpp +msgid "Failed to create C# project." +msgstr "" + +#: modules/mono/editor/godotsharp_editor.cpp +msgid "Mono" +msgstr "" + +#: modules/mono/editor/godotsharp_editor.cpp +msgid "About C# support" +msgstr "" + +#: modules/mono/editor/godotsharp_editor.cpp +msgid "Create C# solution" +msgstr "" + +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "Builds" +msgstr "" + +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "Build Project" +msgstr "" + +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + +#: modules/mono/mono_gd/gd_mono_utils.cpp +msgid "End of inner exception stack trace" +msgstr "" + +#: modules/recast/navigation_mesh_editor_plugin.cpp +msgid "Bake NavMesh" +msgstr "" + +#: modules/recast/navigation_mesh_editor_plugin.cpp +msgid "Clear the navigation mesh." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Setting up Configuration..." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Calculating grid size..." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Creating heightfield..." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Marking walkable triangles..." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Constructing compact heightfield..." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Eroding walkable area..." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Partitioning..." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Creating contours..." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Creating polymesh..." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Converting to native navigation mesh..." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Navigation Mesh Generator Setup:" +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Parsing Geometry..." +msgstr "" + +#: modules/recast/navigation_mesh_generator.cpp +msgid "Done!" +msgstr "" + +#: modules/visual_script/visual_script.cpp +msgid "" +"A node yielded without working memory, please read the docs on how to yield " +"properly!" +msgstr "" + +#: modules/visual_script/visual_script.cpp +msgid "" +"Node yielded, but did not return a function state in the first working " +"memory." +msgstr "" + +#: modules/visual_script/visual_script.cpp +msgid "" +"Return value must be assigned to first element of node working memory! Fix " +"your node please." +msgstr "" + +#: modules/visual_script/visual_script.cpp +msgid "Node returned an invalid sequence output: " +msgstr "" + +#: modules/visual_script/visual_script.cpp +msgid "Found sequence bit but not the node in the stack, report bug!" +msgstr "" + +#: modules/visual_script/visual_script.cpp +msgid "Stack overflow with stack depth: " +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Change Signal Arguments" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Change Argument Type" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Change Argument name" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Set Variable Default Value" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Set Variable Type" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Variables:" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Name is not a valid identifier:" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Name already in use by another func/var/signal:" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Rename Function" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Rename Variable" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Rename Signal" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Add Function" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Add Variable" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Add Signal" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Change Expression" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Remove VisualScript Nodes" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Duplicate VisualScript Nodes" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Hold %s to drop a Getter. Hold Shift to drop a generic signature." +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature." +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Hold %s to drop a simple reference to the node." +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Hold Ctrl to drop a simple reference to the node." +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Hold %s to drop a Variable Setter." +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Hold Ctrl to drop a Variable Setter." +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Add Preload Node" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Add Node(s) From Tree" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Add Getter Property" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Add Setter Property" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Change Base Type" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Move Node(s)" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Remove VisualScript Node" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Connect Nodes" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Connect Node Data" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Connect Node Sequence" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Script already has function '%s'" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Change Input Value" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Resize Comment" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Can't copy the function node." +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Clipboard is empty!" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Paste VisualScript Nodes" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Remove Function" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Remove Variable" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Editing Variable:" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Remove Signal" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Editing Signal:" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Base Type:" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Members:" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Available Nodes:" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Select or create a function to edit graph" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Edit Signal Arguments:" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Edit Variable:" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Delete Selected" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Find Node Type" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Copy Nodes" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Cut Nodes" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Paste Nodes" +msgstr "" + +#: modules/visual_script/visual_script_editor.cpp +msgid "Edit Member" +msgstr "" + +#: modules/visual_script/visual_script_flow_control.cpp +msgid "Input type not iterable: " +msgstr "" + +#: modules/visual_script/visual_script_flow_control.cpp +msgid "Iterator became invalid" +msgstr "" + +#: modules/visual_script/visual_script_flow_control.cpp +msgid "Iterator became invalid: " +msgstr "" + +#: modules/visual_script/visual_script_func_nodes.cpp +msgid "Invalid index property name." +msgstr "" + +#: modules/visual_script/visual_script_func_nodes.cpp +msgid "Base object is not a Node!" +msgstr "" + +#: modules/visual_script/visual_script_func_nodes.cpp +msgid "Path does not lead Node!" +msgstr "" + +#: modules/visual_script/visual_script_func_nodes.cpp +msgid "Invalid index property name '%s' in node %s." +msgstr "" + +#: modules/visual_script/visual_script_nodes.cpp +msgid ": Invalid argument of type: " +msgstr "" + +#: modules/visual_script/visual_script_nodes.cpp +msgid ": Invalid arguments: " +msgstr "" + +#: modules/visual_script/visual_script_nodes.cpp +msgid "VariableGet not found in script: " +msgstr "" + +#: modules/visual_script/visual_script_nodes.cpp +msgid "VariableSet not found in script: " +msgstr "" + +#: modules/visual_script/visual_script_nodes.cpp +msgid "Custom node has no _step() method, can't process graph." +msgstr "" + +#: modules/visual_script/visual_script_nodes.cpp +msgid "" +"Invalid return value from _step(), must be integer (seq out), or string " +"(error)." +msgstr "" + +#: modules/visual_script/visual_script_property_selector.cpp +msgid "Search VisualScript" +msgstr "" + +#: modules/visual_script/visual_script_property_selector.cpp +msgid "Get %s" +msgstr "" + +#: modules/visual_script/visual_script_property_selector.cpp +msgid "Set %s" +msgstr "" + +#: platform/android/export/export.cpp +msgid "Package name is missing." +msgstr "" + +#: platform/android/export/export.cpp +msgid "Package segments must be of non-zero length." +msgstr "" + +#: platform/android/export/export.cpp +msgid "The character '%s' is not allowed in Android application package names." +msgstr "" + +#: platform/android/export/export.cpp +msgid "A digit cannot be the first character in a package segment." +msgstr "" + +#: platform/android/export/export.cpp +msgid "The character '%s' cannot be the first character in a package segment." +msgstr "" + +#: platform/android/export/export.cpp +msgid "The package must have at least one '.' separator." +msgstr "" + +#: platform/android/export/export.cpp +msgid "ADB executable not configured in the Editor Settings." +msgstr "" + +#: platform/android/export/export.cpp +msgid "OpenJDK jarsigner not configured in the Editor Settings." +msgstr "" + +#: platform/android/export/export.cpp +msgid "Debug keystore not configured in the Editor Settings nor in the preset." +msgstr "" + +#: platform/android/export/export.cpp +msgid "Invalid public key for APK expansion." +msgstr "" + +#: platform/android/export/export.cpp +msgid "Invalid package name:" +msgstr "" + +#: platform/iphone/export/export.cpp +msgid "Identifier is missing." +msgstr "" + +#: platform/iphone/export/export.cpp +msgid "Identifier segments must be of non-zero length." +msgstr "" + +#: platform/iphone/export/export.cpp +msgid "The character '%s' is not allowed in Identifier." +msgstr "" + +#: platform/iphone/export/export.cpp +msgid "A digit cannot be the first character in a Identifier segment." +msgstr "" + +#: platform/iphone/export/export.cpp +msgid "" +"The character '%s' cannot be the first character in a Identifier segment." +msgstr "" + +#: platform/iphone/export/export.cpp +msgid "The Identifier must have at least one '.' separator." +msgstr "" + +#: platform/iphone/export/export.cpp +msgid "App Store Team ID not specified - cannot configure the project." +msgstr "" + +#: platform/iphone/export/export.cpp +msgid "Invalid Identifier:" +msgstr "" + +#: platform/iphone/export/export.cpp +msgid "Required icon is not specified in the preset." +msgstr "" + +#: platform/javascript/export/export.cpp +msgid "Run in Browser" +msgstr "" + +#: platform/javascript/export/export.cpp +msgid "Run exported HTML in the system's default browser." +msgstr "" + +#: platform/javascript/export/export.cpp +msgid "Could not write file:" +msgstr "" + +#: platform/javascript/export/export.cpp +msgid "Could not open template for export:" +msgstr "" + +#: platform/javascript/export/export.cpp +msgid "Invalid export template:" +msgstr "" + +#: platform/javascript/export/export.cpp +msgid "Could not read custom HTML shell:" +msgstr "" + +#: platform/javascript/export/export.cpp +msgid "Could not read boot splash image file:" +msgstr "" + +#: platform/javascript/export/export.cpp +msgid "Using default boot splash image." +msgstr "" + +#: platform/uwp/export/export.cpp +msgid "Invalid package unique name." +msgstr "" + +#: platform/uwp/export/export.cpp +msgid "Invalid product GUID." +msgstr "" + +#: platform/uwp/export/export.cpp +msgid "Invalid publisher GUID." +msgstr "" + +#: platform/uwp/export/export.cpp +msgid "Invalid background color." +msgstr "" + +#: platform/uwp/export/export.cpp +msgid "Invalid Store Logo image dimensions (should be 50x50)." +msgstr "" + +#: platform/uwp/export/export.cpp +msgid "Invalid square 44x44 logo image dimensions (should be 44x44)." +msgstr "" + +#: platform/uwp/export/export.cpp +msgid "Invalid square 71x71 logo image dimensions (should be 71x71)." +msgstr "" + +#: platform/uwp/export/export.cpp +msgid "Invalid square 150x150 logo image dimensions (should be 150x150)." +msgstr "" + +#: platform/uwp/export/export.cpp +msgid "Invalid square 310x310 logo image dimensions (should be 310x310)." +msgstr "" + +#: platform/uwp/export/export.cpp +msgid "Invalid wide 310x150 logo image dimensions (should be 310x150)." +msgstr "" + +#: platform/uwp/export/export.cpp +msgid "Invalid splash screen image dimensions (should be 620x300)." +msgstr "" + +#: scene/2d/animated_sprite.cpp +msgid "" +"A SpriteFrames resource must be created or set in the 'Frames' property in " +"order for AnimatedSprite to display frames." +msgstr "" + +#: scene/2d/canvas_modulate.cpp +msgid "" +"Only one visible CanvasModulate is allowed per scene (or set of instanced " +"scenes). The first created one will work, while the rest will be ignored." +msgstr "" + +#: scene/2d/collision_object_2d.cpp +msgid "" +"This node has no shape, so it can't collide or interact with other objects.\n" +"Consider adding a CollisionShape2D or CollisionPolygon2D as a child to " +"define its shape." +msgstr "" + +#: scene/2d/collision_polygon_2d.cpp +msgid "" +"CollisionPolygon2D only serves to provide a collision shape to a " +"CollisionObject2D derived node. Please only use it as a child of Area2D, " +"StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape." +msgstr "" + +#: scene/2d/collision_polygon_2d.cpp +msgid "An empty CollisionPolygon2D has no effect on collision." +msgstr "" + +#: scene/2d/collision_shape_2d.cpp +msgid "" +"CollisionShape2D only serves to provide a collision shape to a " +"CollisionObject2D derived node. Please only use it as a child of Area2D, " +"StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape." +msgstr "" + +#: scene/2d/collision_shape_2d.cpp +msgid "" +"A shape must be provided for CollisionShape2D to function. Please create a " +"shape resource for it!" +msgstr "" + +#: scene/2d/cpu_particles_2d.cpp +msgid "" +"CPUParticles2D animation requires the usage of a CanvasItemMaterial with " +"\"Particles Animation\" enabled." +msgstr "" + +#: scene/2d/light_2d.cpp +msgid "" +"A texture with the shape of the light must be supplied to the 'texture' " +"property." +msgstr "" + +#: scene/2d/light_occluder_2d.cpp +msgid "" +"An occluder polygon must be set (or drawn) for this occluder to take effect." +msgstr "" + +#: scene/2d/light_occluder_2d.cpp +msgid "The occluder polygon for this occluder is empty. Please draw a polygon!" +msgstr "" + +#: scene/2d/navigation_polygon.cpp +msgid "" +"A NavigationPolygon resource must be set or created for this node to work. " +"Please set a property or draw a polygon." +msgstr "" + +#: scene/2d/navigation_polygon.cpp +msgid "" +"NavigationPolygonInstance must be a child or grandchild to a Navigation2D " +"node. It only provides navigation data." +msgstr "" + +#: scene/2d/parallax_layer.cpp +msgid "" +"ParallaxLayer node only works when set as child of a ParallaxBackground node." +msgstr "" + +#: scene/2d/particles_2d.cpp +msgid "" +"GPU-based particles are not supported by the GLES2 video driver.\n" +"Use the CPUParticles2D node instead. You can use the \"Convert to " +"CPUParticles\" option for this purpose." +msgstr "" + +#: scene/2d/particles_2d.cpp scene/3d/particles.cpp +msgid "" +"A material to process the particles is not assigned, so no behavior is " +"imprinted." +msgstr "" + +#: scene/2d/particles_2d.cpp +msgid "" +"Particles2D animation requires the usage of a CanvasItemMaterial with " +"\"Particles Animation\" enabled." +msgstr "" + +#: scene/2d/path_2d.cpp +msgid "PathFollow2D only works when set as a child of a Path2D node." +msgstr "" + +#: scene/2d/physics_body_2d.cpp +msgid "" +"Size changes to RigidBody2D (in character or rigid modes) will be overridden " +"by the physics engine when running.\n" +"Change the size in children collision shapes instead." +msgstr "" + +#: scene/2d/remote_transform_2d.cpp +msgid "Path property must point to a valid Node2D node to work." +msgstr "" + +#: scene/2d/skeleton_2d.cpp +msgid "This Bone2D chain should end at a Skeleton2D node." +msgstr "" + +#: scene/2d/skeleton_2d.cpp +msgid "A Bone2D only works with a Skeleton2D or another Bone2D as parent node." +msgstr "" + +#: scene/2d/skeleton_2d.cpp +msgid "" +"This bone lacks a proper REST pose. Go to the Skeleton2D node and set one." +msgstr "" + +#: scene/2d/visibility_notifier_2d.cpp +msgid "" +"VisibilityEnable2D works best when used with the edited scene root directly " +"as parent." +msgstr "" + +#: scene/3d/arvr_nodes.cpp +msgid "ARVRCamera must have an ARVROrigin node as its parent" +msgstr "" + +#: scene/3d/arvr_nodes.cpp +msgid "ARVRController must have an ARVROrigin node as its parent" +msgstr "" + +#: scene/3d/arvr_nodes.cpp +msgid "" +"The controller id must not be 0 or this controller will not be bound to an " +"actual controller" +msgstr "" + +#: scene/3d/arvr_nodes.cpp +msgid "ARVRAnchor must have an ARVROrigin node as its parent" +msgstr "" + +#: scene/3d/arvr_nodes.cpp +msgid "" +"The anchor id must not be 0 or this anchor will not be bound to an actual " +"anchor" +msgstr "" + +#: scene/3d/arvr_nodes.cpp +msgid "ARVROrigin requires an ARVRCamera child node" +msgstr "" + +#: scene/3d/baked_lightmap.cpp +msgid "%d%%" +msgstr "" + +#: scene/3d/baked_lightmap.cpp +msgid "(Time Left: %d:%02d s)" +msgstr "" + +#: scene/3d/baked_lightmap.cpp +msgid "Plotting Meshes: " +msgstr "" + +#: scene/3d/baked_lightmap.cpp +msgid "Plotting Lights:" +msgstr "" + +#: scene/3d/baked_lightmap.cpp scene/3d/gi_probe.cpp +msgid "Finishing Plot" +msgstr "" + +#: scene/3d/baked_lightmap.cpp +msgid "Lighting Meshes: " +msgstr "" + +#: scene/3d/collision_object.cpp +msgid "" +"This node has no shape, so it can't collide or interact with other objects.\n" +"Consider adding a CollisionShape or CollisionPolygon as a child to define " +"its shape." +msgstr "" + +#: scene/3d/collision_polygon.cpp +msgid "" +"CollisionPolygon only serves to provide a collision shape to a " +"CollisionObject derived node. Please only use it as a child of Area, " +"StaticBody, RigidBody, KinematicBody, etc. to give them a shape." +msgstr "" + +#: scene/3d/collision_polygon.cpp +msgid "An empty CollisionPolygon has no effect on collision." +msgstr "" + +#: scene/3d/collision_shape.cpp +msgid "" +"CollisionShape only serves to provide a collision shape to a CollisionObject " +"derived node. Please only use it as a child of Area, StaticBody, RigidBody, " +"KinematicBody, etc. to give them a shape." +msgstr "" + +#: scene/3d/collision_shape.cpp +msgid "" +"A shape must be provided for CollisionShape to function. Please create a " +"shape resource for it!" +msgstr "" + +#: scene/3d/collision_shape.cpp +msgid "" +"Plane shapes don't work well and will be removed in future versions. Please " +"don't use them." +msgstr "" + +#: scene/3d/cpu_particles.cpp +msgid "Nothing is visible because no mesh has been assigned." +msgstr "" + +#: scene/3d/cpu_particles.cpp +msgid "" +"CPUParticles animation requires the usage of a SpatialMaterial with " +"\"Billboard Particles\" enabled." +msgstr "" + +#: scene/3d/gi_probe.cpp +msgid "Plotting Meshes" +msgstr "" + +#: scene/3d/gi_probe.cpp +msgid "" +"GIProbes are not supported by the GLES2 video driver.\n" +"Use a BakedLightmap instead." +msgstr "" + +#: scene/3d/navigation_mesh.cpp +msgid "A NavigationMesh resource must be set or created for this node to work." +msgstr "" + +#: scene/3d/navigation_mesh.cpp +msgid "" +"NavigationMeshInstance must be a child or grandchild to a Navigation node. " +"It only provides navigation data." +msgstr "" + +#: scene/3d/particles.cpp +msgid "" +"GPU-based particles are not supported by the GLES2 video driver.\n" +"Use the CPUParticles node instead. You can use the \"Convert to CPUParticles" +"\" option for this purpose." +msgstr "" + +#: scene/3d/particles.cpp +msgid "" +"Nothing is visible because meshes have not been assigned to draw passes." +msgstr "" + +#: scene/3d/particles.cpp +msgid "" +"Particles animation requires the usage of a SpatialMaterial with \"Billboard " +"Particles\" enabled." +msgstr "" + +#: scene/3d/path.cpp +msgid "PathFollow only works when set as a child of a Path node." +msgstr "" + +#: scene/3d/path.cpp +msgid "" +"PathFollow ROTATION_ORIENTED requires \"Up Vector\" enabled in its parent " +"Path's Curve resource." +msgstr "" + +#: scene/3d/physics_body.cpp +msgid "" +"Size changes to RigidBody (in character or rigid modes) will be overridden " +"by the physics engine when running.\n" +"Change the size in children collision shapes instead." +msgstr "" + +#: scene/3d/remote_transform.cpp +msgid "Path property must point to a valid Spatial node to work." +msgstr "" + +#: scene/3d/soft_body.cpp +msgid "This body will be ignored until you set a mesh" +msgstr "" + +#: scene/3d/soft_body.cpp +msgid "" +"Size changes to SoftBody will be overridden by the physics engine when " +"running.\n" +"Change the size in children collision shapes instead." +msgstr "" + +#: scene/3d/sprite_3d.cpp +msgid "" +"A SpriteFrames resource must be created or set in the 'Frames' property in " +"order for AnimatedSprite3D to display frames." +msgstr "" + +#: scene/3d/vehicle_body.cpp +msgid "" +"VehicleWheel serves to provide a wheel system to a VehicleBody. Please use " +"it as a child of a VehicleBody." +msgstr "" + +#: scene/3d/world_environment.cpp +msgid "WorldEnvironment needs an Environment resource." +msgstr "" + +#: scene/3d/world_environment.cpp +msgid "" +"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)." +msgstr "" + +#: scene/3d/world_environment.cpp +msgid "" +"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set " +"this environment's Background Mode to Canvas (for 2D scenes)." +msgstr "" + +#: scene/animation/animation_blend_tree.cpp +msgid "On BlendTree node '%s', animation not found: '%s'" +msgstr "" + +#: scene/animation/animation_blend_tree.cpp +msgid "Animation not found: '%s'" +msgstr "" + +#: scene/animation/animation_tree.cpp +msgid "In node '%s', invalid animation: '%s'." +msgstr "" + +#: scene/animation/animation_tree.cpp +msgid "Invalid animation: '%s'." +msgstr "" + +#: scene/animation/animation_tree.cpp +msgid "Nothing connected to input '%s' of node '%s'." +msgstr "" + +#: scene/animation/animation_tree.cpp +msgid "A root AnimationNode for the graph is not set." +msgstr "" + +#: scene/animation/animation_tree.cpp +msgid "Path to an AnimationPlayer node containing animations is not set." +msgstr "" + +#: scene/animation/animation_tree.cpp +msgid "Path set for AnimationPlayer does not lead to an AnimationPlayer node." +msgstr "" + +#: scene/animation/animation_tree.cpp +msgid "AnimationPlayer root is not a valid node." +msgstr "" + +#: scene/animation/animation_tree_player.cpp +msgid "This node has been deprecated. Use AnimationTree instead." +msgstr "" + +#: scene/gui/color_picker.cpp +msgid "Pick a color from the screen." +msgstr "" + +#: scene/gui/color_picker.cpp +msgid "Raw Mode" +msgstr "" + +#: scene/gui/color_picker.cpp +msgid "Switch between hexadecimal and code values." +msgstr "" + +#: scene/gui/color_picker.cpp +msgid "Add current color as a preset." +msgstr "" + +#: scene/gui/container.cpp +msgid "" +"Container by itself serves no purpose unless a script configures it's " +"children placement behavior.\n" +"If you dont't intend to add a script, then please use a plain 'Control' node " +"instead." +msgstr "" + +#: scene/gui/dialogs.cpp +msgid "Alert!" +msgstr "" + +#: scene/gui/dialogs.cpp +msgid "Please Confirm..." +msgstr "" + +#: scene/gui/file_dialog.cpp +msgid "Go to parent folder." +msgstr "" + +#: scene/gui/popup.cpp +msgid "" +"Popups will hide by default unless you call popup() or any of the popup*() " +"functions. Making them visible for editing is fine though, but they will " +"hide upon running." +msgstr "" + +#: scene/gui/range.cpp +msgid "If exp_edit is true min_value must be > 0." +msgstr "" + +#: scene/gui/scroll_container.cpp +msgid "" +"ScrollContainer is intended to work with a single child control.\n" +"Use a container as child (VBox,HBox,etc), or a Control and set the custom " +"minimum size manually." +msgstr "" + +#: scene/gui/tree.cpp +msgid "(Other)" +msgstr "" + +#: scene/main/scene_tree.cpp +msgid "" +"Default Environment as specified in Project Settings (Rendering -> " +"Environment -> Default Environment) could not be loaded." +msgstr "" + +#: scene/main/viewport.cpp +msgid "" +"This viewport is not set as render target. If you intend for it to display " +"its contents directly to the screen, make it a child of a Control so it can " +"obtain a size. Otherwise, make it a RenderTarget and assign its internal " +"texture to some node for display." +msgstr "" + +#: scene/resources/dynamic_font.cpp +msgid "Error initializing FreeType." +msgstr "" + +#: scene/resources/dynamic_font.cpp +msgid "Unknown font format." +msgstr "" + +#: scene/resources/dynamic_font.cpp +msgid "Error loading font." +msgstr "" + +#: scene/resources/dynamic_font.cpp +msgid "Invalid font size." +msgstr "" + +#: scene/resources/visual_shader.cpp +msgid "Input" +msgstr "" + +#: scene/resources/visual_shader_nodes.cpp +msgid "Invalid source for shader." +msgstr "" + +#: servers/visual/shader_language.cpp +msgid "Assignment to function." +msgstr "" + +#: servers/visual/shader_language.cpp +msgid "Assignment to uniform." +msgstr "" + +#: servers/visual/shader_language.cpp +msgid "Varyings can only be assigned in vertex function." +msgstr "" diff --git a/editor/translations/fr.po b/editor/translations/fr.po index 67a11b2224..a91f78a912 100644 --- a/editor/translations/fr.po +++ b/editor/translations/fr.po @@ -59,7 +59,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-12 15:25+0000\n" +"PO-Revision-Date: 2019-04-05 13:04+0000\n" "Last-Translator: Caye Pierre <pierrecaye@laposte.net>\n" "Language-Team: French <https://hosted.weblate.org/projects/godot-engine/" "godot/fr/>\n" @@ -68,7 +68,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 3.5.1\n" +"X-Generator: Weblate 3.6-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -2539,7 +2539,7 @@ msgstr "Documentation en ligne" #: editor/editor_node.cpp msgid "Q&A" -msgstr "Questions et Réponses" +msgstr "Q & R" #: editor/editor_node.cpp msgid "Issue Tracker" @@ -5349,7 +5349,7 @@ msgstr "Capturer depuis Pixel" #: editor/plugins/particles_2d_editor_plugin.cpp msgid "Emission Colors" -msgstr "Couleurs d'Émission" +msgstr "Couleurs d'émission" #: editor/plugins/particles_editor_plugin.cpp msgid "Faces contain no area!" @@ -5994,11 +5994,11 @@ msgstr "Ouvrir la documentation Godot en ligne" #: editor/plugins/script_editor_plugin.cpp msgid "Request Docs" -msgstr "" +msgstr "Demande de documentation" #: editor/plugins/script_editor_plugin.cpp msgid "Help improve the Godot documentation by giving feedback" -msgstr "" +msgstr "Aider à améliorer la documentation de Godot en donnant vos réactions" #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." diff --git a/editor/translations/id.po b/editor/translations/id.po index a9346d9af1..a277a3b3e2 100644 --- a/editor/translations/id.po +++ b/editor/translations/id.po @@ -18,12 +18,13 @@ # Evan Hyacinth <muhammad.ivan669@gmail.com>, 2018, 2019. # Guntur Sarwohadi <gsarwohadi@gmail.com>, 2019. # Alphin Albukhari <alphinalbukhari5@gmail.com>, 2019. +# I Dewa Agung Adhinata <agungnata2003@gmail.com>, 2019. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-08 15:04+0000\n" -"Last-Translator: Evan Hyacinth <muhammad.ivan669@gmail.com>\n" +"PO-Revision-Date: 2019-04-05 13:04+0000\n" +"Last-Translator: I Dewa Agung Adhinata <agungnata2003@gmail.com>\n" "Language-Team: Indonesian <https://hosted.weblate.org/projects/godot-engine/" "godot/id/>\n" "Language: id\n" @@ -31,7 +32,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 3.5.1-dev\n" +"X-Generator: Weblate 3.6-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -1388,7 +1389,7 @@ msgid "" "Target platform requires 'ETC2' texture compression for GLES3. Enable " "'Import Etc 2' in Project Settings." msgstr "" -"Platform target membutuhkan kompressi tekstur 'ETC' untuk GLES2. Aktifkan " +"Platform target membutuhkan kompressi tekstur 'ETC2' untuk GLES3. Aktifkan " "dukungan di Pengaturan Proyek." #: editor/editor_export.cpp @@ -1528,9 +1529,8 @@ msgid "Previous Folder" msgstr "Tab sebelumnya" #: editor/editor_file_dialog.cpp -#, fuzzy msgid "Next Folder" -msgstr "Buat Folder" +msgstr "Folder Berikutnya" #: editor/editor_file_dialog.cpp msgid "Go to parent folder" @@ -1542,14 +1542,12 @@ msgid "(Un)favorite current folder." msgstr "Tidak dapat membuat folder." #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp -#, fuzzy msgid "View items as a grid of thumbnails." msgstr "Tampilkan item sebagai grid thumbnail" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp -#, fuzzy msgid "View items as a list." -msgstr "Tampilkan item sebagai daftar" +msgstr "Tampilkan item sebagai daftar." #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Directories & Files:" @@ -1774,8 +1772,9 @@ msgid "Project export failed with error code %d." msgstr "Ekspor proyek gagal dengan kode kesalahan% d." #: editor/editor_node.cpp +#, fuzzy msgid "Imported resources can't be saved." -msgstr "" +msgstr "Resource yang telah diimpor tidak dapat disimpan." #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp #: scene/gui/dialogs.cpp @@ -1787,10 +1786,13 @@ msgid "Error saving resource!" msgstr "Error menyimpan resource!" #: editor/editor_node.cpp +#, fuzzy msgid "" "This resource can't be saved because it does not belong to the edited scene. " "Make it unique first." msgstr "" +"Resourse ini tidak dapat disimpan karena bukan di scene yang diedit. Buat " +"unik dahulu." #: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp msgid "Save Resource As..." @@ -2330,9 +2332,8 @@ msgid "Tools" msgstr "Alat-alat" #: editor/editor_node.cpp -#, fuzzy msgid "Open Project Data Folder" -msgstr "Buka Project Manager?" +msgstr "Buka Project Data Manager" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -2449,7 +2450,7 @@ msgstr "Mode Layar Penuh" #: editor/editor_node.cpp #, fuzzy msgid "Open Editor Data/Settings Folder" -msgstr "Pengaturan Editor" +msgstr "Buka Editor Data/Folder Pengaturan" #: editor/editor_node.cpp msgid "Open Editor Data Folder" @@ -2458,7 +2459,7 @@ msgstr "Buka Folder Data Editor" #: editor/editor_node.cpp #, fuzzy msgid "Open Editor Settings Folder" -msgstr "Pengaturan Editor" +msgstr "Buka Folder Pengaturan Editor" #: editor/editor_node.cpp editor/project_export.cpp msgid "Manage Export Templates" @@ -2542,9 +2543,8 @@ msgstr "Mengubah driver video harus memulai ulang editor." #: editor/editor_node.cpp editor/project_settings_editor.cpp #: editor/settings_config_dialog.cpp -#, fuzzy msgid "Save & Restart" -msgstr "Simpan & Keluar" +msgstr "Simpan & Mulai Ulang" #: editor/editor_node.cpp #, fuzzy @@ -2581,9 +2581,8 @@ msgid "Node" msgstr "Node" #: editor/editor_node.cpp -#, fuzzy msgid "Expand Bottom Panel" -msgstr "Perluas semua" +msgstr "Perluas Panel Bawah" #: editor/editor_node.cpp scene/resources/visual_shader.cpp msgid "Output" @@ -2662,9 +2661,8 @@ msgid "Thumbnail..." msgstr "Gambar Kecil..." #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Edit Plugin" -msgstr "Sunting Bidang" +msgstr "Sunting Plug-in" #: editor/editor_plugin_settings.cpp msgid "Installed Plugins:" @@ -2688,15 +2686,13 @@ msgid "Status:" msgstr "Status:" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Edit:" -msgstr "Sunting" +msgstr "Sunting:" #: editor/editor_profiler.cpp editor/plugins/animation_state_machine_editor.cpp #: editor/rename_dialog.cpp -#, fuzzy msgid "Start" -msgstr "Mulai!" +msgstr "Mulai" #: editor/editor_profiler.cpp msgid "Measure:" @@ -2769,13 +2765,12 @@ msgid "Invalid RID" msgstr "Path Tidak Sah." #: editor/editor_properties.cpp -#, fuzzy msgid "" "The selected resource (%s) does not match any type expected for this " "property (%s)." msgstr "" -"Resource terpilih (%s) tidak sesuai dengan tipe yang diharapkan untuk " -"properti ini (%s)." +"Resource yang terpilih (%s) tidak sesuai dengan tipe apapun yang diharapkan " +"untuk properti ini (%s)." #: editor/editor_properties.cpp msgid "" @@ -2803,9 +2798,8 @@ msgid "Pick a Viewport" msgstr "Pilih Viewport" #: editor/editor_properties.cpp editor/property_editor.cpp -#, fuzzy msgid "New Script" -msgstr "Scene Baru" +msgstr "Skrip Baru" #: editor/editor_properties.cpp editor/property_editor.cpp msgid "New %s" @@ -2841,11 +2835,11 @@ msgstr "Sambungkan Ke Node:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #, fuzzy msgid "Open Editor" -msgstr "Buka dalam Penyunting" +msgstr "Buka Editor" #: editor/editor_properties.cpp editor/property_editor.cpp msgid "Selected node is not a Viewport!" -msgstr "Node terpilih bukanlah Viewport!" +msgstr "Node yang terpilih bukanlah Viewport!" #: editor/editor_properties_array_dict.cpp msgid "Size: " diff --git a/editor/translations/it.po b/editor/translations/it.po index 45ebfd0b3c..30c4d6a3b9 100644 --- a/editor/translations/it.po +++ b/editor/translations/it.po @@ -37,8 +37,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-28 09:36+0000\n" -"Last-Translator: MassiminoilTrace <omino.gis@gmail.com>\n" +"PO-Revision-Date: 2019-03-30 20:04+0000\n" +"Last-Translator: Marco Melorio <m.melorio@icloud.com>\n" "Language-Team: Italian <https://hosted.weblate.org/projects/godot-engine/" "godot/it/>\n" "Language: it\n" @@ -132,7 +132,7 @@ msgstr "Elimina chiavi d'animazione" #: editor/animation_track_editor.cpp msgid "Anim Change Keyframe Time" -msgstr "Cambia istante della chiave d'animazione" +msgstr "Anim Cambia Tempo Keyframe" #: editor/animation_track_editor.cpp msgid "Anim Change Transition" @@ -194,7 +194,7 @@ msgstr "Durata Animazione (in secondi)" #: editor/animation_track_editor.cpp msgid "Animation Looping" -msgstr "Ciclicità animazione" +msgstr "Ciclicità Animazione" #: editor/animation_track_editor.cpp #: modules/visual_script/visual_script_editor.cpp diff --git a/editor/translations/ko.po b/editor/translations/ko.po index 903a54b102..c50d35d69b 100644 --- a/editor/translations/ko.po +++ b/editor/translations/ko.po @@ -12,12 +12,13 @@ # ì†¡íƒœì„ <xotjq237@gmail.com>, 2018, 2019. # JY <yimjisoo@mailfence.com>, 2018. # Ch. <ccwpc@hanmail.net>, 2018. +# moolow <copyhyeon@gmail.com>, 2019. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-19 15:04+0000\n" -"Last-Translator: ì†¡íƒœì„ <xotjq237@gmail.com>\n" +"PO-Revision-Date: 2019-04-05 13:04+0000\n" +"Last-Translator: moolow <copyhyeon@gmail.com>\n" "Language-Team: Korean <https://hosted.weblate.org/projects/godot-engine/" "godot/ko/>\n" "Language: ko\n" @@ -5216,7 +5217,7 @@ msgstr "ìƒì„± 시간 (ì´ˆ):" #: editor/plugins/particles_2d_editor_plugin.cpp msgid "Emission Mask" -msgstr "ì—미션 마스í¬" +msgstr "ì—미션 마스í¬(Emission Mask)" #: editor/plugins/particles_2d_editor_plugin.cpp msgid "Capture from Pixel" @@ -5867,11 +5868,11 @@ msgstr "Godot 온ë¼ì¸ 문서 열기" #: editor/plugins/script_editor_plugin.cpp msgid "Request Docs" -msgstr "" +msgstr "문서 ìš”ì²" #: editor/plugins/script_editor_plugin.cpp msgid "Help improve the Godot documentation by giving feedback" -msgstr "" +msgstr "피드백으로 Godot 문서를 ê°œì„ í•˜ëŠ”ë° ë„ì›€ì„ ì£¼ì„¸ìš”" #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." diff --git a/editor/translations/pl.po b/editor/translations/pl.po index a2e25fba35..530417f63c 100644 --- a/editor/translations/pl.po +++ b/editor/translations/pl.po @@ -32,12 +32,13 @@ # Sebastian Hojka <sibibibi1@gmail.com>, 2019. # Robert <vizz0@onet.pl>, 2019. # MichaÅ‚ Topa <moonchasered@gmail.com>, 2019. +# PrzemysÅ‚aw Pierzga <przemyslawpierzga@gmail.com>, 2019. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-28 09:36+0000\n" -"Last-Translator: Tomek <kobewi4e@gmail.com>\n" +"PO-Revision-Date: 2019-04-05 13:04+0000\n" +"Last-Translator: PrzemysÅ‚aw Pierzga <przemyslawpierzga@gmail.com>\n" "Language-Team: Polish <https://hosted.weblate.org/projects/godot-engine/" "godot/pl/>\n" "Language: pl\n" @@ -2296,7 +2297,7 @@ msgstr "Konwertuj na..." #: editor/editor_node.cpp msgid "MeshLibrary..." -msgstr "MeshLibrary..." +msgstr "Biblioteka Meshów..." #: editor/editor_node.cpp msgid "TileSet..." @@ -5916,11 +5917,11 @@ msgstr "Otwórz dokumentacjÄ™ online" #: editor/plugins/script_editor_plugin.cpp msgid "Request Docs" -msgstr "" +msgstr "PoproÅ› o dokumentacjÄ™" #: editor/plugins/script_editor_plugin.cpp msgid "Help improve the Godot documentation by giving feedback" -msgstr "" +msgstr "Pomóż polepszyć dokumentacjÄ™ Godota przesyÅ‚ajÄ…c opiniÄ™" #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." diff --git a/editor/translations/pt_BR.po b/editor/translations/pt_BR.po index f546b8928f..0deb9619d0 100644 --- a/editor/translations/pt_BR.po +++ b/editor/translations/pt_BR.po @@ -55,12 +55,13 @@ # Dimenicius <vinicius.costa.92@gmail.com>, 2019. # Davi <wokep.ma.wavid@gmail.com>, 2019. # Endrick Gustavo <endrickgb@hotmail.com>, 2019. +# Hans M. Boron <hansmateusboron@gmail.com>, 2019. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: 2016-05-30\n" -"PO-Revision-Date: 2019-03-28 09:36+0000\n" -"Last-Translator: Endrick Gustavo <endrickgb@hotmail.com>\n" +"PO-Revision-Date: 2019-04-08 10:35+0000\n" +"Last-Translator: Hans M. Boron <hansmateusboron@gmail.com>\n" "Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/" "godot-engine/godot/pt_BR/>\n" "Language: pt_BR\n" @@ -257,9 +258,8 @@ msgid "Time (s): " msgstr "Tempo (s): " #: editor/animation_track_editor.cpp -#, fuzzy msgid "Toggle Track Enabled" -msgstr "Habilitar Doppler" +msgstr "Ligar/Desligar trilha" #: editor/animation_track_editor.cpp msgid "Continuous" @@ -365,14 +365,13 @@ msgid "Anim Insert Key" msgstr "Inserir Chave na Anim" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Change Animation Step" msgstr "Alterar FPS da Animação" #: editor/animation_track_editor.cpp #, fuzzy msgid "Rearrange Tracks" -msgstr "Reordenar Autoloads" +msgstr "Reordenar Faixas" #: editor/animation_track_editor.cpp msgid "Transform tracks only apply to Spatial-based nodes." @@ -405,9 +404,8 @@ msgid "Not possible to add a new track without a root" msgstr "Não é possÃvel adicionar uma nova trilha sem uma raiz" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Add Bezier Track" -msgstr "Adicionar Trilha" +msgstr "Adicionar Trilha Bezier" #: editor/animation_track_editor.cpp msgid "Track path is invalid, so can't add a key." @@ -423,7 +421,6 @@ msgid "Add Transform Track Key" msgstr "Trilha de transformação 3D" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Add Track Key" msgstr "Adicionar Trilha" @@ -3924,7 +3921,7 @@ msgstr "Editar trilhas filtradas:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp msgid "Enable filtering" -msgstr "Filhos editáveis" +msgstr "Habilitar filtragem" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Toggle Autoplay" diff --git a/editor/translations/pt_PT.po b/editor/translations/pt_PT.po index 2eabc658ba..0c8ad8dce9 100644 --- a/editor/translations/pt_PT.po +++ b/editor/translations/pt_PT.po @@ -18,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-19 15:04+0000\n" +"PO-Revision-Date: 2019-03-30 20:04+0000\n" "Last-Translator: João Lopes <linux-man@hotmail.com>\n" "Language-Team: Portuguese (Portugal) <https://hosted.weblate.org/projects/" "godot-engine/godot/pt_PT/>\n" @@ -5899,11 +5899,11 @@ msgstr "Abrir documentação online do Godot" #: editor/plugins/script_editor_plugin.cpp msgid "Request Docs" -msgstr "" +msgstr "Requisitar Docs" #: editor/plugins/script_editor_plugin.cpp msgid "Help improve the Godot documentation by giving feedback" -msgstr "" +msgstr "Dê a sua opinião para ajudar a melhorar a documentação Godot" #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." diff --git a/editor/translations/ru.po b/editor/translations/ru.po index 19896638b8..f950d99eec 100644 --- a/editor/translations/ru.po +++ b/editor/translations/ru.po @@ -5,7 +5,7 @@ # Ðркадий ÐÐ²Ð°Ñ <savvot@gmail.com>, 2018. # Artem Varaksa <aymfst@gmail.com>, 2018. # B10nicMachine <shumik1337@gmail.com>, 2017. -# Chaosus89 <chaosus89@gmail.com>, 2018. +# Chaosus89 <chaosus89@gmail.com>, 2018, 2019. # DimOkGamer <dimokgamer@gmail.com>, 2016-2017. # Forest Swamp <sample1989@mail.ru>, 2018. # Igor S <scorched@bk.ru>, 2017. @@ -41,12 +41,13 @@ # Nikita <iron-nikita@yandex.ru>, 2019. # LeLuCh B0й <alekx@alumni.nottingham.ac.uk>, 2019. # ÐÑ€Ñений Солодков <arsen332211@gmail.com>, 2019. +# Nikita <yakrobat@protonmail.com>, 2019. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-28 09:36+0000\n" -"Last-Translator: ÐÑ€Ñений Солодков <arsen332211@gmail.com>\n" +"PO-Revision-Date: 2019-04-07 08:04+0000\n" +"Last-Translator: Chaosus89 <chaosus89@gmail.com>\n" "Language-Team: Russian <https://hosted.weblate.org/projects/godot-engine/" "godot/ru/>\n" "Language: ru\n" @@ -210,7 +211,7 @@ msgstr "Функции:" #: editor/animation_track_editor.cpp msgid "Audio Clips:" -msgstr "Ðудио Дорожки:" +msgstr "Ðудиодорожки:" #: editor/animation_track_editor.cpp msgid "Anim Clips:" @@ -246,9 +247,8 @@ msgid "Time (s): " msgstr "Ð’Ñ€ÐµÐ¼Ñ (Ñек.): " #: editor/animation_track_editor.cpp -#, fuzzy msgid "Toggle Track Enabled" -msgstr "ДоплеровÑкий режим" +msgstr "Переключатель дорожки включен" #: editor/animation_track_editor.cpp msgid "Continuous" @@ -302,11 +302,11 @@ msgstr "Удалить ключ(ключи)" #: editor/animation_track_editor.cpp msgid "Change Animation Update Mode" -msgstr "Изменить Режим ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ðнимации" +msgstr "Изменить ÑпоÑоб Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð°Ð½Ð¸Ð¼Ð°Ñ†Ð¸Ð¸" #: editor/animation_track_editor.cpp msgid "Change Animation Interpolation Mode" -msgstr "Изменить Режим ИнтерполÑции Ðнимации" +msgstr "Изменить метод интерполÑции анимации" #: editor/animation_track_editor.cpp msgid "Change Animation Loop Mode" @@ -414,9 +414,8 @@ msgid "Track path is invalid, so can't add a method key." msgstr "Путь трека некорректен, потому Ð½ÐµÐ»ÑŒÐ·Ñ Ð´Ð¾Ð±Ð°Ð²Ð¸Ñ‚ÑŒ ключ метода." #: editor/animation_track_editor.cpp -#, fuzzy msgid "Add Method Track Key" -msgstr "Трек Вызова Метода" +msgstr "Добавить ключ отÑÐ»ÐµÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð¼ÐµÑ‚Ð¾Ð´Ð°" #: editor/animation_track_editor.cpp msgid "Method not found in object: " @@ -579,17 +578,16 @@ msgid "Copy" msgstr "Копировать" #: editor/animation_track_editor_plugins.cpp -#, fuzzy msgid "Add Audio Track Clip" -msgstr "Ðудио Дорожки:" +msgstr "Добавить звуковую дорожку" #: editor/animation_track_editor_plugins.cpp msgid "Change Audio Track Clip Start Offset" -msgstr "Изменить Ñмещение начала в Audio Track клипе" +msgstr "Изменение Ðачального Сдвига Ðудио Дорожки" #: editor/animation_track_editor_plugins.cpp msgid "Change Audio Track Clip End Offset" -msgstr "Изменить Ñмещение начала в Audio Track клипе" +msgstr "Изменение Конечного Сдвига Ðудио Дорожки" #: editor/array_property_edit.cpp msgid "Resize Array" @@ -2038,9 +2036,8 @@ msgid "Save changes to '%s' before closing?" msgstr "Сохранить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² «%s» перед закрытием?" #: editor/editor_node.cpp -#, fuzzy msgid "Saved %s modified resource(s)." -msgstr "Ðе удалоÑÑŒ загрузить реÑурÑ." +msgstr "%s измененных реÑурÑов Ñохранено." #: editor/editor_node.cpp msgid "A root node is required to save the scene." @@ -3227,7 +3224,7 @@ msgstr "ПеремеÑтить в..." #: editor/filesystem_dock.cpp editor/plugins/script_editor_plugin.cpp msgid "New Script..." -msgstr "Ðовый Ñкрипт." +msgstr "Ðовый Ñкрипт..." #: editor/filesystem_dock.cpp msgid "New Resource..." @@ -3674,14 +3671,12 @@ msgid "Move Node Point" msgstr "Передвинуть Точку" #: editor/plugins/animation_blend_space_1d_editor.cpp -#, fuzzy msgid "Change BlendSpace1D Limits" -msgstr "Изменить Ð²Ñ€ÐµÐ¼Ñ \"ÑмешиваниÑ\"" +msgstr "Изменить границы BlendSpace1D" #: editor/plugins/animation_blend_space_1d_editor.cpp -#, fuzzy msgid "Change BlendSpace1D Labels" -msgstr "Изменить Ð²Ñ€ÐµÐ¼Ñ \"ÑмешиваниÑ\"" +msgstr "Изменить метки BlendSpace1D" #: editor/plugins/animation_blend_space_1d_editor.cpp #: editor/plugins/animation_blend_space_2d_editor.cpp @@ -3692,20 +3687,17 @@ msgstr "" #: editor/plugins/animation_blend_space_1d_editor.cpp #: editor/plugins/animation_blend_space_2d_editor.cpp -#, fuzzy msgid "Add Node Point" -msgstr "Добавить узел" +msgstr "Добавить точку узла" #: editor/plugins/animation_blend_space_1d_editor.cpp #: editor/plugins/animation_blend_space_2d_editor.cpp -#, fuzzy msgid "Add Animation Point" -msgstr "Добавить анимацию" +msgstr "Добавить точку анимации" #: editor/plugins/animation_blend_space_1d_editor.cpp -#, fuzzy msgid "Remove BlendSpace1D Point" -msgstr "Удалить точку пути" +msgstr "Удалить точку BlendSpace1D" #: editor/plugins/animation_blend_space_1d_editor.cpp msgid "Move BlendSpace1D Node Point" @@ -3755,29 +3747,24 @@ msgid "Triangle already exists" msgstr "Треугольник уже ÑущеÑтвует" #: editor/plugins/animation_blend_space_2d_editor.cpp -#, fuzzy msgid "Add Triangle" -msgstr "Добавить переменную" +msgstr "Добавить треугольник" #: editor/plugins/animation_blend_space_2d_editor.cpp -#, fuzzy msgid "Change BlendSpace2D Limits" -msgstr "Изменить Ð²Ñ€ÐµÐ¼Ñ \"ÑмешиваниÑ\"" +msgstr "Изменить границы BlendSpace2D" #: editor/plugins/animation_blend_space_2d_editor.cpp -#, fuzzy msgid "Change BlendSpace2D Labels" -msgstr "Изменить Ð²Ñ€ÐµÐ¼Ñ \"ÑмешиваниÑ\"" +msgstr "Изменить метки BlendSpace2D" #: editor/plugins/animation_blend_space_2d_editor.cpp -#, fuzzy msgid "Remove BlendSpace2D Point" -msgstr "Удалить точку пути" +msgstr "Удалить точку BlendSpace2D" #: editor/plugins/animation_blend_space_2d_editor.cpp -#, fuzzy msgid "Remove BlendSpace2D Triangle" -msgstr "Удалить переменную" +msgstr "Удалить треугольник BlendSpace2D" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "BlendSpace2D does not belong to an AnimationTree node." @@ -3788,9 +3775,8 @@ msgid "No triangles exist, so no blending can take place." msgstr "Ðевозможно Ñмешивать, поÑкольку отÑутÑтвуют треугольники." #: editor/plugins/animation_blend_space_2d_editor.cpp -#, fuzzy msgid "Toggle Auto Triangles" -msgstr "Переключить автозагрузку глобальных Ñкриптов" +msgstr "Переключить автоматичеÑкие треугольники" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Create triangles by connecting points." @@ -3810,9 +3796,8 @@ msgid "Blend:" msgstr "Смешивание:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed" -msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¼Ð°Ñ‚ÐµÑ€Ð¸Ð°Ð»Ð°" +msgstr "Параметр изменён" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -3824,9 +3809,8 @@ msgid "Output node can't be added to the blend tree." msgstr "Узел вывода не может быть добавлен в дерево ÑмешиваниÑ." #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Add Node to BlendTree" -msgstr "Добавить узел(узлы) из дерева" +msgstr "Добавить узел к BlendTree" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp @@ -3842,15 +3826,13 @@ msgstr "" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Nodes Connected" -msgstr "Подключен" +msgstr "Узлы Ñоединены" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Nodes Disconnected" -msgstr "Отключен" +msgstr "Узлы разъединены" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #, fuzzy @@ -3859,19 +3841,16 @@ msgstr "ÐнимациÑ" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Delete Node" -msgstr "Удалить узел(узлы)" +msgstr "Удалить узел" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Toggle Filter On/Off" -msgstr "Переключить Ñтот трек вкл/выкл." +msgstr "Переключить фильтр вкл/выкл" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Change Filter" -msgstr "Изменен фильтр Ñзыков" +msgstr "Изменить фильтр" #: editor/plugins/animation_blend_tree_editor_plugin.cpp msgid "No animation player set, so unable to retrieve track names." @@ -3892,9 +3871,8 @@ msgstr "" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_state_machine_editor.cpp -#, fuzzy msgid "Node Renamed" -msgstr "Ð˜Ð¼Ñ ÑƒÐ·Ð»Ð°" +msgstr "Узел переименован" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp @@ -4125,14 +4103,12 @@ msgid "Cross-Animation Blend Times" msgstr "Межанимационный инÑтрумент ÑмешиваниÑ" #: editor/plugins/animation_state_machine_editor.cpp -#, fuzzy msgid "Move Node" -msgstr "Режим перемещениÑ" +msgstr "ПеремеÑтить узел" #: editor/plugins/animation_state_machine_editor.cpp -#, fuzzy msgid "Add Transition" -msgstr "Добавить перевод" +msgstr "Добавить переход" #: editor/plugins/animation_state_machine_editor.cpp #: modules/visual_script/visual_script_editor.cpp @@ -4168,14 +4144,12 @@ msgid "No playback resource set at path: %s." msgstr "Ð’ пути нет реÑурÑов воÑпроизведениÑ: %s." #: editor/plugins/animation_state_machine_editor.cpp -#, fuzzy msgid "Node Removed" -msgstr "Удалено:" +msgstr "Узел удалён" #: editor/plugins/animation_state_machine_editor.cpp -#, fuzzy msgid "Transition Removed" -msgstr "Transition узел" +msgstr "Переход удалён" #: editor/plugins/animation_state_machine_editor.cpp msgid "Set Start Node (Autoplay)" @@ -4608,13 +4582,10 @@ msgid "Presets for the anchors and margins values of a Control node." msgstr "ПредуÑтановки Ð´Ð»Ñ Ñкорей и Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¾Ñ‚Ñтупов контрольного узла." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "" "Children of containers have their anchors and margins values overridden by " "their parent." -msgstr "" -"Внимание: Положение и размер детей контейнера определÑетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ их " -"родителÑми." +msgstr "Ð¯ÐºÐ¾Ñ€Ñ Ð¸ отÑтупы дочерних контейнеров переопределÑÑŽÑ‚ÑÑ Ð¸Ñ… родителÑми." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Anchors only" @@ -5958,11 +5929,11 @@ msgstr "Открыть онлайн документацию Godot" #: editor/plugins/script_editor_plugin.cpp msgid "Request Docs" -msgstr "" +msgstr "Запрашиваемые Документы" #: editor/plugins/script_editor_plugin.cpp msgid "Help improve the Godot documentation by giving feedback" -msgstr "" +msgstr "Помогите улучшить документацию Godot, предоÑтавив обратную ÑвÑзь" #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." @@ -6726,14 +6697,12 @@ msgid "(empty)" msgstr "(пуÑто)" #: editor/plugins/sprite_frames_editor_plugin.cpp -#, fuzzy msgid "Animations:" -msgstr "Ðнимации" +msgstr "Ðнимации:" #: editor/plugins/sprite_frames_editor_plugin.cpp -#, fuzzy msgid "New Animation" -msgstr "ÐнимациÑ" +msgstr "ÐÐ¾Ð²Ð°Ñ Ð°Ð½Ð¸Ð¼Ð°Ñ†Ð¸Ñ" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Speed (FPS):" @@ -6744,9 +6713,8 @@ msgid "Loop" msgstr "Зациклить" #: editor/plugins/sprite_frames_editor_plugin.cpp -#, fuzzy msgid "Animation Frames:" -msgstr "Кадры анимации" +msgstr "Кадры анимации:" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Insert Empty (Before)" diff --git a/editor/translations/uk.po b/editor/translations/uk.po index 95f9d87aae..0929e66b29 100644 --- a/editor/translations/uk.po +++ b/editor/translations/uk.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: Ukrainian (Godot Engine)\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-12 15:26+0000\n" +"PO-Revision-Date: 2019-03-30 20:04+0000\n" "Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n" "Language-Team: Ukrainian <https://hosted.weblate.org/projects/godot-engine/" "godot/uk/>\n" @@ -25,7 +25,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 3.5.1\n" +"X-Generator: Weblate 3.6-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -5909,11 +5909,11 @@ msgstr "Відкрити онлайнову документацію Godot" #: editor/plugins/script_editor_plugin.cpp msgid "Request Docs" -msgstr "" +msgstr "Запит щодо документації" #: editor/plugins/script_editor_plugin.cpp msgid "Help improve the Godot documentation by giving feedback" -msgstr "" +msgstr "Допоможіть у поліпшенні документації до Godot, надіÑлавши Ñвій відгук" #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." diff --git a/editor/translations/zh_CN.po b/editor/translations/zh_CN.po index c1d2f61af9..e79ca6a36d 100644 --- a/editor/translations/zh_CN.po +++ b/editor/translations/zh_CN.po @@ -42,12 +42,13 @@ # ws00010203 <ws00010203@hotmail.com>, 2019. # Song DongHui <14729626293@163.com>, 2019. # simano clio <sim2cle@gmail.com>, 2019. +# ByonkoGalilei <byonko@qq.com>, 2019. msgid "" msgstr "" "Project-Id-Version: Chinese (Simplified) (Godot Engine)\n" "POT-Creation-Date: 2018-01-20 12:15+0200\n" -"PO-Revision-Date: 2019-03-24 04:06+0000\n" -"Last-Translator: simano clio <sim2cle@gmail.com>\n" +"PO-Revision-Date: 2019-04-08 10:35+0000\n" +"Last-Translator: ByonkoGalilei <byonko@qq.com>\n" "Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/" "godot-engine/godot/zh_Hans/>\n" "Language: zh_CN\n" @@ -122,14 +123,12 @@ msgid "Delete Selected Key(s)" msgstr "åˆ é™¤å·²é€‰å¸§" #: editor/animation_bezier_editor.cpp -#, fuzzy msgid "Add Bezier Point" -msgstr "æ·»åŠ é¡¶ç‚¹" +msgstr "æ·»åŠ è´å¡žå°”顶点" #: editor/animation_bezier_editor.cpp -#, fuzzy msgid "Move Bezier Points" -msgstr "移动点" +msgstr "移动è´å¡žå°”顶点" #: editor/animation_bezier_editor.cpp editor/animation_track_editor.cpp msgid "Anim Duplicate Keys" @@ -218,9 +217,8 @@ msgid "Anim Clips:" msgstr "动画剪辑:" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Change Track Path" -msgstr "修改数组值" +msgstr "改å˜è½¨è¿¹è·¯å¾„" #: editor/animation_track_editor.cpp msgid "Toggle this track on/off." @@ -247,9 +245,8 @@ msgid "Time (s): " msgstr "时间(秒): " #: editor/animation_track_editor.cpp -#, fuzzy msgid "Toggle Track Enabled" -msgstr "å¯ç”¨å¤šæ™®å‹’效应" +msgstr "å¯ç”¨è½¨é“切æ¢" #: editor/animation_track_editor.cpp msgid "Continuous" @@ -302,19 +299,16 @@ msgid "Delete Key(s)" msgstr "åˆ é™¤å¸§" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Change Animation Update Mode" -msgstr "é‡å‘½å动画:" +msgstr "更改动画更新模å¼" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Change Animation Interpolation Mode" -msgstr "æ’值模å¼" +msgstr "更改动画æ’值模å¼" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Change Animation Loop Mode" -msgstr "修改循环" +msgstr "更改动画循环模å¼" #: editor/animation_track_editor.cpp msgid "Remove Anim Track" @@ -358,14 +352,12 @@ msgid "Anim Insert Key" msgstr "æ’入关键帧" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Change Animation Step" -msgstr "修改FPS" +msgstr "更改动画æ¥é•¿" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Rearrange Tracks" -msgstr "釿ޒåºAutoload" +msgstr "釿–°æŽ’列轨é“" #: editor/animation_track_editor.cpp msgid "Transform tracks only apply to Spatial-based nodes." @@ -396,9 +388,8 @@ msgid "Not possible to add a new track without a root" msgstr "æ— æ³•åœ¨æ²¡æœ‰rootçš„æƒ…å†µä¸‹æ·»åŠ æ–°è½¨é“" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Add Bezier Track" -msgstr "æ·»åŠ è½¨é“" +msgstr "æ·»åŠ è´å¡žå°”轨é“" #: editor/animation_track_editor.cpp msgid "Track path is invalid, so can't add a key." @@ -409,14 +400,12 @@ msgid "Track is not of type Spatial, can't insert key" msgstr "Track䏿˜¯Spatial类型,ä¸èƒ½ä½œä¸ºé”®å€¼æ’å…¥" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Add Transform Track Key" -msgstr "3Då˜æ¢è½¨é“" +msgstr "æ·»åŠ è½¬æ¢è½¨é“é”®" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Add Track Key" -msgstr "æ·»åŠ è½¨é“" +msgstr "æ·»åŠ è½¨é“é”®" #: editor/animation_track_editor.cpp msgid "Track path is invalid, so can't add a method key." @@ -586,17 +575,16 @@ msgid "Copy" msgstr "å¤åˆ¶" #: editor/animation_track_editor_plugins.cpp -#, fuzzy msgid "Add Audio Track Clip" -msgstr "音频剪辑:" +msgstr "æ·»åŠ éŸ³é¢‘è½¨é“剪辑" #: editor/animation_track_editor_plugins.cpp msgid "Change Audio Track Clip Start Offset" -msgstr "" +msgstr "更改音频轨é“剪辑起始åç§»" #: editor/animation_track_editor_plugins.cpp msgid "Change Audio Track Clip End Offset" -msgstr "" +msgstr "更改音频轨é“剪辑结æŸåç§»" #: editor/array_property_edit.cpp msgid "Resize Array" @@ -1396,13 +1384,13 @@ msgstr "打包ä¸" msgid "" "Target platform requires 'ETC' texture compression for GLES2. Enable 'Import " "Etc' in Project Settings." -msgstr "" +msgstr "ç›®æ ‡å¹³å°éœ€è¦GLES2的“ETCâ€çº¹ç†åŽ‹ç¼©ã€‚åœ¨é¡¹ç›®è®¾ç½®ä¸å¯ç”¨â€œå¯¼å…¥Etcâ€ã€‚" #: editor/editor_export.cpp msgid "" "Target platform requires 'ETC2' texture compression for GLES3. Enable " "'Import Etc 2' in Project Settings." -msgstr "" +msgstr "ç›®æ ‡å¹³å°éœ€è¦GLES3的“ETC2â€çº¹ç†åŽ‹ç¼©ã€‚åœ¨é¡¹ç›®è®¾ç½®ä¸å¯ç”¨â€œå¯¼å…¥Etc 2â€ã€‚" #: editor/editor_export.cpp msgid "" @@ -1411,6 +1399,8 @@ msgid "" "Enable 'Import Etc' in Project Settings, or disable 'Driver Fallback " "Enabled'." msgstr "" +"ç›®æ ‡å¹³å°éœ€è¦'ETC'纹ç†åŽ‹ç¼©ï¼Œä»¥ä¾¿é©±åŠ¨ç¨‹åºå›žé€€åˆ°GLES2。\n" +"在项目设置ä¸å¯ç”¨â€œå¯¼å…¥Etcâ€ï¼Œæˆ–ç¦ç”¨â€œå¯ç”¨é©±åŠ¨ç¨‹åºå›žé€€â€ã€‚" #: editor/editor_export.cpp platform/android/export/export.cpp #: platform/iphone/export/export.cpp platform/javascript/export/export.cpp @@ -1533,23 +1523,20 @@ msgid "Move Favorite Down" msgstr "å‘下移动收è—" #: editor/editor_file_dialog.cpp -#, fuzzy msgid "Previous Folder" -msgstr "上一个层" +msgstr "上一个文件夹" #: editor/editor_file_dialog.cpp -#, fuzzy msgid "Next Folder" -msgstr "下一层" +msgstr "下一个文件夹" #: editor/editor_file_dialog.cpp msgid "Go to parent folder" msgstr "转到上层文件夹" #: editor/editor_file_dialog.cpp -#, fuzzy msgid "(Un)favorite current folder." -msgstr "æ— æ³•åˆ›å»ºç›®å½•ã€‚" +msgstr "ï¼ˆå–æ¶ˆï¼‰æ”¶è—当剿–‡ä»¶å¤¹ã€‚" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "View items as a grid of thumbnails." @@ -1781,9 +1768,8 @@ msgid "Project export failed with error code %d." msgstr "项目导出失败,错误代ç %d。" #: editor/editor_node.cpp -#, fuzzy msgid "Imported resources can't be saved." -msgstr "已导入的资æº" +msgstr "å¯¼å…¥çš„èµ„æºæ— 法ä¿å˜ã€‚" #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp #: scene/gui/dialogs.cpp @@ -1798,7 +1784,7 @@ msgstr "ä¿å˜èµ„æºå‡ºé”™ï¼" msgid "" "This resource can't be saved because it does not belong to the edited scene. " "Make it unique first." -msgstr "" +msgstr "æ— æ³•ä¿å˜æ¤èµ„æºï¼Œå› 为它ä¸å±žäºŽå·²ç¼–辑的场景。首先使它唯一化。" #: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp msgid "Save Resource As..." @@ -2010,14 +1996,12 @@ msgid "Save changes to '%s' before closing?" msgstr "在关é—å‰ä¿å˜æ›´æ”¹åˆ° %s å—?" #: editor/editor_node.cpp -#, fuzzy msgid "Saved %s modified resource(s)." -msgstr "åŠ è½½èµ„æºå¤±è´¥ã€‚" +msgstr "å·²ä¿å˜ï¼…s修改åŽçš„资æºã€‚" #: editor/editor_node.cpp -#, fuzzy msgid "A root node is required to save the scene." -msgstr "大图导入仅支æŒä¸€ä¸ªè¾“入文件。" +msgstr "ä¿å˜åœºæ™¯éœ€è¦æ ¹èŠ‚ç‚¹ã€‚" #: editor/editor_node.cpp msgid "Save Scene As..." @@ -3690,9 +3674,8 @@ msgid "Triangle already exists" msgstr "三角形已ç»å˜åœ¨" #: editor/plugins/animation_blend_space_2d_editor.cpp -#, fuzzy msgid "Add Triangle" -msgstr "æ·»åŠ å˜é‡" +msgstr "æ·»åŠ ä¸‰è§’é¢" #: editor/plugins/animation_blend_space_2d_editor.cpp #, fuzzy @@ -3723,9 +3706,8 @@ msgid "No triangles exist, so no blending can take place." msgstr "ä¸å˜åœ¨ä»»ä½•ä¸‰è§’å½¢ï¼Œå› æ¤ä¸ä¼šæœ‰ä»»ä½•混效果åˆäº§ç”Ÿã€‚" #: editor/plugins/animation_blend_space_2d_editor.cpp -#, fuzzy msgid "Toggle Auto Triangles" -msgstr "切æ¢å…¨å±€AutoLoad" +msgstr "切æ¢è‡ªåŠ¨ä¸‰è§’å½¢" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Create triangles by connecting points." @@ -3745,9 +3727,8 @@ msgid "Blend:" msgstr "æ··åˆ:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed" -msgstr "æè´¨å˜æ›´" +msgstr "傿•°å·²æ›´æ”¹" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -3765,9 +3746,8 @@ msgstr "ä»Žæ ‘ä¸æ·»åŠ èŠ‚ç‚¹" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Node Moved" -msgstr "移动模å¼" +msgstr "节点已移动" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp @@ -3776,20 +3756,17 @@ msgstr "æ— æ³•è¿žæŽ¥ï¼Œç«¯å£å¯èƒ½è¢«å ç”¨æˆ–è€…è¿žæŽ¥æ— æ•ˆã€‚" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Nodes Connected" -msgstr "已连接" +msgstr "节点已连接" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Nodes Disconnected" -msgstr "å·²æ–å¼€" +msgstr "节点æ–开连接" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Set Animation" -msgstr "新建动画" +msgstr "设置动画" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp @@ -3798,14 +3775,12 @@ msgid "Delete Node" msgstr "åˆ é™¤èŠ‚ç‚¹" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Toggle Filter On/Off" -msgstr "切æ¢å½“å‰è½¨é“开关。" +msgstr "打开/å…³é—过滤器" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Change Filter" -msgstr "修改区域设置ç›é€‰æ¨¡å¼" +msgstr "更改过滤器" #: editor/plugins/animation_blend_tree_editor_plugin.cpp msgid "No animation player set, so unable to retrieve track names." @@ -3824,9 +3799,8 @@ msgstr "åŠ¨ç”»æ’æ”¾å™¨æ²¡æœ‰åˆæ³•çš„æ ¹èŠ‚ç‚¹è·¯å¾„ï¼Œå› æ¤æ— 法获å–è½¨é“ #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_state_machine_editor.cpp -#, fuzzy msgid "Node Renamed" -msgstr "节点åç§°" +msgstr "节点已é‡å‘½å" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp @@ -4054,14 +4028,12 @@ msgid "Cross-Animation Blend Times" msgstr "跨动画时间混åˆ" #: editor/plugins/animation_state_machine_editor.cpp -#, fuzzy msgid "Move Node" -msgstr "移动模å¼" +msgstr "移动节点" #: editor/plugins/animation_state_machine_editor.cpp -#, fuzzy msgid "Add Transition" -msgstr "æ·»åŠ è¯è¨€" +msgstr "æ·»åŠ è½¬æ¢" #: editor/plugins/animation_state_machine_editor.cpp #: modules/visual_script/visual_script_editor.cpp @@ -4097,18 +4069,16 @@ msgid "No playback resource set at path: %s." msgstr "在路径: %s ä¸‹æ²¡æœ‰ä»»ä½•æ’æ”¾èµ„æºã€‚" #: editor/plugins/animation_state_machine_editor.cpp -#, fuzzy msgid "Node Removed" -msgstr "已移除:" +msgstr "节点已移除" #: editor/plugins/animation_state_machine_editor.cpp -#, fuzzy msgid "Transition Removed" -msgstr "过渡节点" +msgstr "转æ¢å·²ç§»é™¤" #: editor/plugins/animation_state_machine_editor.cpp msgid "Set Start Node (Autoplay)" -msgstr "" +msgstr "è®¾ç½®å¼€å§‹èŠ‚ç‚¹ï¼ˆè‡ªåŠ¨æ’æ”¾ï¼‰" #: editor/plugins/animation_state_machine_editor.cpp msgid "" @@ -4919,7 +4889,7 @@ msgstr "渲染GI Probe" #: editor/plugins/gradient_editor_plugin.cpp msgid "Gradient Edited" -msgstr "" +msgstr "æ¸å˜ç¼–辑" #: editor/plugins/item_list_editor_plugin.cpp msgid "Item %d" @@ -5866,11 +5836,11 @@ msgstr "打开Godot在线文档" #: editor/plugins/script_editor_plugin.cpp msgid "Request Docs" -msgstr "" +msgstr "请求文档" #: editor/plugins/script_editor_plugin.cpp msgid "Help improve the Godot documentation by giving feedback" -msgstr "" +msgstr "通过æä¾›å馈å助改进Godot文档" #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." @@ -6068,14 +6038,12 @@ msgid "This skeleton has no bones, create some children Bone2D nodes." msgstr "该骨架没有骨骼绑定,请创建一些 Bone2D 骨骼å节点。" #: editor/plugins/skeleton_2d_editor_plugin.cpp -#, fuzzy msgid "Create Rest Pose from Bones" -msgstr "制作放æ¾å§¿åŠ¿ï¼ˆä»Žéª¨éª¼ï¼‰" +msgstr "从骨骼创建休闲姿势" #: editor/plugins/skeleton_2d_editor_plugin.cpp -#, fuzzy msgid "Set Rest Pose to Bones" -msgstr "制作放æ¾å§¿åŠ¿ï¼ˆä»Žéª¨éª¼ï¼‰" +msgstr "将休闲姿势设置为骨骼" #: editor/plugins/skeleton_2d_editor_plugin.cpp msgid "Skeleton2D" @@ -6327,6 +6295,8 @@ msgid "" "Note: The FPS value displayed is the editor's framerate.\n" "It cannot be used as a reliable indication of in-game performance." msgstr "" +"注æ„:显示的FPS值是编辑器的帧速率。\n" +"它ä¸èƒ½ç”¨äºŽè¡¨çŽ°æ¸¸æˆä¸çš„实际性能。" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -6337,9 +6307,8 @@ msgid "XForm Dialog" msgstr "XFormå¯¹è¯æ¡†" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes To Floor" -msgstr "å¸é™„到地é¢" +msgstr "将节点å¸é™„至地é¢" #: editor/plugins/spatial_editor_plugin.cpp msgid "Select Mode (Q)" @@ -6934,18 +6903,16 @@ msgid "Merge from Scene" msgstr "从场景ä¸åˆå¹¶" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Next Coordinate" -msgstr "下一层" +msgstr "ä¸‹ä¸€ä¸ªåæ ‡" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Select the next shape, subtile, or Tile." -msgstr "" +msgstr "选择下一个形状,åç –å—ï¼Œæˆ–ç –å—。" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Previous Coordinate" -msgstr "上一个层" +msgstr "ä¸Šä¸€ä¸ªåæ ‡" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Select the previous shape, subtile, or Tile." @@ -6964,9 +6931,8 @@ msgid "Erase bitmask." msgstr "æ“¦é™¤ä½æŽ©ç 。" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Create a new rectangle." -msgstr "创建新节点。" +msgstr "新建一个四边形。" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Create a new polygon." @@ -7106,14 +7072,12 @@ msgid "Clear Tile Bitmask" msgstr "æ¸…é™¤ä½æŽ©ç " #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Make Polygon Concave" -msgstr "移动多边形" +msgstr "使多边形塌陷" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Make Polygon Convex" -msgstr "移动多边形" +msgstr "使多边形凸起" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Remove Tile" @@ -7160,17 +7124,14 @@ msgid "Set Uniform Name" msgstr "" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Set Input Default Port" -msgstr "将默认设置为 '%s'" +msgstr "设置输入默认端å£" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Add Node to Visual Shader" -msgstr "å¯è§†ç€è‰²å™¨" +msgstr "å°†èŠ‚ç‚¹æ·»åŠ åˆ°å¯è§†ç€è‰²å™¨" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Duplicate Nodes" msgstr "å¤åˆ¶èŠ‚ç‚¹" @@ -7195,14 +7156,12 @@ msgid "VisualShader" msgstr "å¯è§†ç€è‰²å™¨" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property" -msgstr "编辑ç£è´´ä¼˜å…ˆçº§" +msgstr "编辑å¯è§†å±žæ€§" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Visual Shader Mode Changed" -msgstr "ç€è‰²å™¨å˜æ›´" +msgstr "å¯è§†ç€è‰²å™¨æ¨¡å¼å·²æ›´æ”¹" #: editor/project_export.cpp msgid "Runnable" @@ -7238,9 +7197,8 @@ msgid "Exporting All" msgstr "全部导出" #: editor/project_export.cpp -#, fuzzy msgid "The given export path doesn't exist:" -msgstr "路径ä¸å˜åœ¨ã€‚" +msgstr "指定导出路径ä¸å˜åœ¨ï¼š" #: editor/project_export.cpp msgid "Export templates for this platform are missing/corrupted:" @@ -8255,9 +8213,8 @@ msgid "Instantiated scenes can't become root" msgstr "实例化的场景ä¸èƒ½æˆä¸ºæ ¹èŠ‚ç‚¹" #: editor/scene_tree_dock.cpp -#, fuzzy msgid "Make node as Root" -msgstr "åˆ›å»ºåœºæ™¯æ ¹èŠ‚ç‚¹" +msgstr "å°†èŠ‚ç‚¹è®¾ç½®ä¸ºæ ¹èŠ‚ç‚¹" #: editor/scene_tree_dock.cpp msgid "Delete Node(s)?" @@ -8294,9 +8251,8 @@ msgid "Make Local" msgstr "使用本地" #: editor/scene_tree_dock.cpp -#, fuzzy msgid "New Scene Root" -msgstr "åˆ›å»ºåœºæ™¯æ ¹èŠ‚ç‚¹" +msgstr "æ–°åœºæ™¯æ ¹" #: editor/scene_tree_dock.cpp msgid "Create Root Node:" @@ -8723,19 +8679,16 @@ msgid "Set From Tree" msgstr "ä»Žåœºæ™¯æ ‘è®¾ç½®" #: editor/settings_config_dialog.cpp -#, fuzzy msgid "Erase Shortcut" -msgstr "æ¸å‡º" +msgstr "æ¸…é™¤å¿«æ·æ–¹å¼" #: editor/settings_config_dialog.cpp -#, fuzzy msgid "Restore Shortcut" -msgstr "å¿«æ·é”®" +msgstr "æ¢å¤å¿«æ·æ–¹å¼" #: editor/settings_config_dialog.cpp -#, fuzzy msgid "Change Shortcut" -msgstr "编辑锚点" +msgstr "æ›´æ”¹å¿«æ·æ–¹å¼" #: editor/settings_config_dialog.cpp msgid "Shortcuts" @@ -9321,9 +9274,8 @@ msgid "Change Input Value" msgstr "更改输入值" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Resize Comment" -msgstr "调整 CanvasItem 尺寸" +msgstr "调整注释尺寸" #: modules/visual_script/visual_script_editor.cpp msgid "Can't copy the function node." @@ -10075,9 +10027,8 @@ msgid "Please Confirm..." msgstr "请确认..." #: scene/gui/file_dialog.cpp -#, fuzzy msgid "Go to parent folder." -msgstr "转到上层文件夹" +msgstr "转到父文件夹。" #: scene/gui/popup.cpp msgid "" diff --git a/editor/translations/zh_TW.po b/editor/translations/zh_TW.po index b04a702467..8fdb7e8db0 100644 --- a/editor/translations/zh_TW.po +++ b/editor/translations/zh_TW.po @@ -18,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2019-03-28 09:36+0000\n" +"PO-Revision-Date: 2019-03-30 20:04+0000\n" "Last-Translator: cnieFIT <dtotncq@gmail.com>\n" "Language-Team: Chinese (Traditional) <https://hosted.weblate.org/projects/" "godot-engine/godot/zh_Hant/>\n" @@ -3125,7 +3125,7 @@ msgstr "" #: editor/filesystem_dock.cpp msgid "Cannot move a folder into itself." -msgstr "" +msgstr "無法將資料夾移動到其自身。" #: editor/filesystem_dock.cpp #, fuzzy @@ -3144,19 +3144,19 @@ msgstr "å ´æ™¯ç¼ºå°‘äº†æŸäº›è³‡æºä»¥è‡³æ–¼ç„¡æ³•載入" #: editor/filesystem_dock.cpp editor/scene_tree_editor.cpp msgid "No name provided." -msgstr "" +msgstr "未æä¾›å稱。" #: editor/filesystem_dock.cpp msgid "Provided name contains invalid characters" -msgstr "" +msgstr "æä¾›çš„å稱包å«ç„¡æ•ˆå—å…ƒ" #: editor/filesystem_dock.cpp msgid "Name contains invalid characters." -msgstr "" +msgstr "å稱包å«ç„¡æ•ˆå—元。" #: editor/filesystem_dock.cpp msgid "A file or folder with this name already exists." -msgstr "" +msgstr "具有æ¤å稱的檔或資料夾已å˜åœ¨ã€‚" #: editor/filesystem_dock.cpp #, fuzzy @@ -3165,7 +3165,7 @@ msgstr "載入時發生錯誤:" #: editor/filesystem_dock.cpp msgid "Renaming folder:" -msgstr "" +msgstr "é‡å‘½å資料夾:" #: editor/filesystem_dock.cpp #, fuzzy @@ -3174,7 +3174,7 @@ msgstr "載入時發生錯誤:" #: editor/filesystem_dock.cpp msgid "Duplicating folder:" -msgstr "" +msgstr "複製資料夾:" #: editor/filesystem_dock.cpp #, fuzzy @@ -3183,7 +3183,7 @@ msgstr "é–‹å•Ÿå ´æ™¯" #: editor/filesystem_dock.cpp msgid "Instance" -msgstr "" +msgstr "實例" #: editor/filesystem_dock.cpp #, fuzzy @@ -3201,11 +3201,11 @@ msgstr "" #: editor/filesystem_dock.cpp msgid "View Owners..." -msgstr "" +msgstr "查看所有者…" #: editor/filesystem_dock.cpp editor/plugins/animation_player_editor_plugin.cpp msgid "Rename..." -msgstr "" +msgstr "é‡å‘½å…" #: editor/filesystem_dock.cpp #, fuzzy @@ -3214,7 +3214,7 @@ msgstr "複製動畫關éµç•«æ ¼" #: editor/filesystem_dock.cpp msgid "Move To..." -msgstr "" +msgstr "移動到..。" #: editor/filesystem_dock.cpp editor/plugins/script_editor_plugin.cpp #, fuzzy @@ -3228,7 +3228,7 @@ msgstr "å¦å˜è³‡æºç‚º..." #: editor/filesystem_dock.cpp editor/script_editor_debugger.cpp msgid "Expand All" -msgstr "" +msgstr "展開所有" #: editor/filesystem_dock.cpp editor/script_editor_debugger.cpp #, fuzzy @@ -3240,19 +3240,19 @@ msgstr "å–代全部" #: editor/project_manager.cpp editor/rename_dialog.cpp #: editor/scene_tree_dock.cpp msgid "Rename" -msgstr "" +msgstr "é‡å‘½å" #: editor/filesystem_dock.cpp msgid "Previous Directory" -msgstr "" +msgstr "上一個目錄" #: editor/filesystem_dock.cpp msgid "Next Directory" -msgstr "" +msgstr "下一個目錄" #: editor/filesystem_dock.cpp msgid "Re-Scan Filesystem" -msgstr "" +msgstr "釿–°æŽƒææª”案系統" #: editor/filesystem_dock.cpp #, fuzzy @@ -3268,23 +3268,23 @@ msgstr "æœå°‹ Class" msgid "" "Scanning Files,\n" "Please Wait..." -msgstr "" +msgstr "æ£åœ¨æŽƒææª”, è«‹ç¨å€™..。" #: editor/filesystem_dock.cpp msgid "Move" -msgstr "" +msgstr "移動" #: editor/filesystem_dock.cpp msgid "There is already file or folder with the same name in this location." -msgstr "" +msgstr "æ¤ä½ç½®å·²å˜åœ¨åŒå的檔案或資料夾。" #: editor/filesystem_dock.cpp msgid "Overwrite" -msgstr "" +msgstr "覆蓋" #: editor/filesystem_dock.cpp editor/plugins/script_editor_plugin.cpp msgid "Create Script" -msgstr "" +msgstr "創建腳本" #: editor/find_in_files.cpp #, fuzzy @@ -3309,15 +3309,15 @@ msgstr "éŽæ¿¾å™¨:" #: editor/find_in_files.cpp editor/plugins/script_editor_plugin.cpp #: editor/plugins/script_text_editor.cpp msgid "Find..." -msgstr "" +msgstr "尋找..." #: editor/find_in_files.cpp editor/plugins/script_text_editor.cpp msgid "Replace..." -msgstr "" +msgstr "替æ›â€¦" #: editor/find_in_files.cpp editor/progress_dialog.cpp scene/gui/dialogs.cpp msgid "Cancel" -msgstr "" +msgstr "å–æ¶ˆ" #: editor/find_in_files.cpp #, fuzzy @@ -3356,11 +3356,11 @@ msgstr "ä¸èƒ½ä½¿ç”¨çš„å稱。" #: editor/groups_editor.cpp editor/node_dock.cpp msgid "Groups" -msgstr "" +msgstr "組" #: editor/groups_editor.cpp msgid "Nodes not in Group" -msgstr "" +msgstr "ä¸åœ¨çµ„ä¸çš„節點" #: editor/groups_editor.cpp editor/scene_tree_dock.cpp #, fuzzy @@ -3369,19 +3369,19 @@ msgstr "éŽæ¿¾æª”案..." #: editor/groups_editor.cpp msgid "Nodes in Group" -msgstr "" +msgstr "組ä¸çš„節點" #: editor/groups_editor.cpp msgid "Add to Group" -msgstr "" +msgstr "æ·»åŠ åˆ°çµ„" #: editor/groups_editor.cpp msgid "Remove from Group" -msgstr "" +msgstr "從群組移除" #: editor/groups_editor.cpp msgid "Manage Groups" -msgstr "" +msgstr "管ç†çµ„" #: editor/import/resource_importer_scene.cpp #, fuzzy @@ -3390,48 +3390,48 @@ msgstr "æ›´æ–°å ´æ™¯" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Animations" -msgstr "" +msgstr "使用單ç¨çš„動畫一åŒå°Žå…¥" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Materials" -msgstr "" +msgstr "使用單ç¨çš„æ ä¸€åŒå°Žå…¥" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Objects" -msgstr "" +msgstr "使用單ç¨çš„物件導入" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Objects+Materials" -msgstr "" +msgstr "使用單ç¨çš„å°è±¡+æè³ªå°Žå…¥" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Objects+Animations" -msgstr "" +msgstr "使用單ç¨çš„å°è±¡+å‹•ç•«å°Žå…¥" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Materials+Animations" -msgstr "" +msgstr "使用單ç¨çš„æè³ª+å‹•ç•«å°Žå…¥" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Objects+Materials+Animations" -msgstr "" +msgstr "使用單ç¨çš„å°è±¡+æè³ª+å‹•ç•«å°Žå…¥" #: editor/import/resource_importer_scene.cpp msgid "Import as Multiple Scenes" -msgstr "" +msgstr "ä½œç‚ºå¤šå€‹å ´æ™¯å°Žå…¥" #: editor/import/resource_importer_scene.cpp msgid "Import as Multiple Scenes+Materials" -msgstr "" +msgstr "å°Žå…¥ç‚ºå¤šå€‹å ´æ™¯+æè³ª" #: editor/import/resource_importer_scene.cpp #: editor/plugins/mesh_library_editor_plugin.cpp msgid "Import Scene" -msgstr "" +msgstr "å°Žå…¥å ´æ™¯" #: editor/import/resource_importer_scene.cpp msgid "Importing Scene..." -msgstr "" +msgstr "æ£åœ¨å°Žå…¥å ´æ™¯â€¦" #: editor/import/resource_importer_scene.cpp msgid "Generating Lightmaps" @@ -3443,7 +3443,7 @@ msgstr "" #: editor/import/resource_importer_scene.cpp msgid "Running Custom Script..." -msgstr "" +msgstr "æ£åœ¨é‹è¡Œè‡ªå®šç¾©è…³æœ¬â€¦" #: editor/import/resource_importer_scene.cpp msgid "Couldn't load post-import script:" @@ -3455,39 +3455,39 @@ msgstr "" #: editor/import/resource_importer_scene.cpp msgid "Error running post-import script:" -msgstr "" +msgstr "é‹è¡Œå°Žå…¥å¾Œè…³æœ¬æ™‚出錯:" #: editor/import/resource_importer_scene.cpp msgid "Saving..." -msgstr "" +msgstr "儲å˜ä¸â€¦â€¦" #: editor/import_dock.cpp msgid "Set as Default for '%s'" -msgstr "" +msgstr "è¨å®šç‚ºâ€œ%sâ€çš„é è¨å€¼" #: editor/import_dock.cpp msgid "Clear Default for '%s'" -msgstr "" +msgstr "清除“%sâ€çš„é è¨å€¼" #: editor/import_dock.cpp msgid " Files" -msgstr "" +msgstr " 資料夾" #: editor/import_dock.cpp msgid "Import As:" -msgstr "" +msgstr "導入為:" #: editor/import_dock.cpp editor/property_editor.cpp msgid "Preset..." -msgstr "" +msgstr "é è¨ã€‚。。" #: editor/import_dock.cpp msgid "Reimport" -msgstr "" +msgstr "釿–°å°Žå…¥" #: editor/import_dock.cpp msgid "Save scenes, re-import and restart" -msgstr "" +msgstr "ä¿å˜å ´æ™¯ï¼Œé‡æ–°å°Žå…¥ä¸¦é‡æ–°å•Ÿå‹•" #: editor/import_dock.cpp msgid "Changing the type of an imported file requires editor restart." @@ -3500,7 +3500,7 @@ msgstr "" #: editor/inspector_dock.cpp msgid "Failed to load resource." -msgstr "" +msgstr "載入資æºå¤±æ•—。" #: editor/inspector_dock.cpp #, fuzzy @@ -3515,7 +3515,7 @@ msgstr "展開所有屬性" #: editor/inspector_dock.cpp editor/plugins/animation_player_editor_plugin.cpp #: editor/plugins/script_editor_plugin.cpp msgid "Save As..." -msgstr "" +msgstr "å¦å˜ç‚º..。" #: editor/inspector_dock.cpp msgid "Copy Params" @@ -3536,11 +3536,11 @@ msgstr "複製資æº" #: editor/inspector_dock.cpp msgid "Make Built-In" -msgstr "" +msgstr "å…§ç½®" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" -msgstr "" +msgstr "使å資æºå”¯ä¸€" #: editor/inspector_dock.cpp msgid "Open in Help" @@ -3548,31 +3548,31 @@ msgstr "在幫助界é¢ä¸é–‹å•Ÿ" #: editor/inspector_dock.cpp msgid "Create a new resource in memory and edit it." -msgstr "" +msgstr "在記憶體ä¸å‰µå»ºä¸€å€‹æ–°è³‡æºä¸¦å°å…¶é€²è¡Œç·¨è¼¯ã€‚" #: editor/inspector_dock.cpp msgid "Load an existing resource from disk and edit it." -msgstr "" +msgstr "從ç£ç‰‡åŠ è¼‰ç¾æœ‰è³‡æºä¸¦å°å…¶é€²è¡Œç·¨è¼¯ã€‚" #: editor/inspector_dock.cpp msgid "Save the currently edited resource." -msgstr "" +msgstr "ä¿å˜ç•¶å‰ç·¨è¼¯çš„資æºã€‚" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." -msgstr "" +msgstr "轉到æ·å²è¨˜éŒ„ä¸ä»¥å‰ç·¨è¼¯çš„å°è±¡ã€‚" #: editor/inspector_dock.cpp msgid "Go to the next edited object in history." -msgstr "" +msgstr "轉到æ·å²è¨˜éŒ„ä¸çš„下一個編輯å°è±¡ã€‚" #: editor/inspector_dock.cpp msgid "History of recently edited objects." -msgstr "" +msgstr "最近編輯å°è±¡çš„æ·å²è¨˜éŒ„。" #: editor/inspector_dock.cpp msgid "Object properties." -msgstr "" +msgstr "å°è±¡å†…容。" #: editor/inspector_dock.cpp #, fuzzy @@ -3581,19 +3581,19 @@ msgstr "éŽæ¿¾æª”案..." #: editor/inspector_dock.cpp msgid "Changes may be lost!" -msgstr "" +msgstr "更改å¯èƒ½æœƒä¸Ÿå¤±!" #: editor/multi_node_edit.cpp msgid "MultiNode Set" -msgstr "" +msgstr "多節點集" #: editor/node_dock.cpp msgid "Select a Node to edit Signals and Groups." -msgstr "" +msgstr "鏿“‡è¦ç·¨è¼¯è¨Šè™Ÿå’Œçµ„的節點。" #: editor/plugin_config_dialog.cpp msgid "Edit a Plugin" -msgstr "" +msgstr "編輯挿件" #: editor/plugin_config_dialog.cpp #, fuzzy @@ -3602,23 +3602,23 @@ msgstr "新增" #: editor/plugin_config_dialog.cpp msgid "Plugin Name:" -msgstr "" +msgstr "挿件å稱:" #: editor/plugin_config_dialog.cpp msgid "Subfolder:" -msgstr "" +msgstr "å資料夾:" #: editor/plugin_config_dialog.cpp msgid "Language:" -msgstr "" +msgstr "語言:" #: editor/plugin_config_dialog.cpp msgid "Script Name:" -msgstr "" +msgstr "腳本å稱:" #: editor/plugin_config_dialog.cpp msgid "Activate now?" -msgstr "" +msgstr "ç¾åœ¨å•Ÿå‹•?" #: editor/plugins/abstract_polygon_2d_editor.cpp #: editor/plugins/polygon_2d_editor_plugin.cpp @@ -3653,11 +3653,11 @@ msgstr "新增資料夾" #: editor/plugins/abstract_polygon_2d_editor.cpp msgid "Insert Point" -msgstr "" +msgstr "æ’入點" #: editor/plugins/abstract_polygon_2d_editor.cpp msgid "Edit Polygon (Remove Point)" -msgstr "" +msgstr "編輯多邊形 (刪除點)" #: editor/plugins/abstract_polygon_2d_editor.cpp #, fuzzy @@ -3670,7 +3670,7 @@ msgstr "移除" #: editor/plugins/animation_state_machine_editor.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Add Animation" -msgstr "" +msgstr "æ·»åŠ å‹•ç•«" #: editor/plugins/animation_blend_space_1d_editor.cpp #: editor/plugins/animation_blend_space_2d_editor.cpp @@ -3688,17 +3688,17 @@ msgstr "移除" #: editor/plugins/animation_blend_space_1d_editor.cpp msgid "Change BlendSpace1D Limits" -msgstr "" +msgstr "更改 BlendSpace1D é™åˆ¶" #: editor/plugins/animation_blend_space_1d_editor.cpp msgid "Change BlendSpace1D Labels" -msgstr "" +msgstr "更改Blendspace1d標籤" #: editor/plugins/animation_blend_space_1d_editor.cpp #: editor/plugins/animation_blend_space_2d_editor.cpp #: editor/plugins/animation_state_machine_editor.cpp msgid "This type of node can't be used. Only root nodes are allowed." -msgstr "" +msgstr "無法使用æ¤é¡žåž‹çš„節點。åªå…è¨±æ ¹ç¯€é»žã€‚" #: editor/plugins/animation_blend_space_1d_editor.cpp #: editor/plugins/animation_blend_space_2d_editor.cpp @@ -3733,7 +3733,7 @@ msgstr "" #: editor/plugins/animation_blend_space_1d_editor.cpp #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Set the blending position within the space" -msgstr "" +msgstr "è¨ç½®ç©ºé–“內的混åˆä½ç½®" #: editor/plugins/animation_blend_space_1d_editor.cpp #: editor/plugins/animation_blend_space_2d_editor.cpp @@ -3743,12 +3743,12 @@ msgstr "" #: editor/plugins/animation_blend_space_1d_editor.cpp #: editor/plugins/animation_blend_space_2d_editor.cpp scene/gui/graph_edit.cpp msgid "Enable snap and show grid." -msgstr "" +msgstr "å•Ÿç”¨æ•æ‰ä¸¦é¡¯ç¤ºç¶²æ ¼ã€‚" #: editor/plugins/animation_blend_space_1d_editor.cpp #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Point" -msgstr "" +msgstr "點" #: editor/plugins/animation_blend_space_1d_editor.cpp #: editor/plugins/animation_blend_space_2d_editor.cpp @@ -3770,11 +3770,11 @@ msgstr "æ·»åŠ å‹•ç•«è»Œ" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Change BlendSpace2D Limits" -msgstr "" +msgstr "更改Blendspace2dé™åˆ¶" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Change BlendSpace2D Labels" -msgstr "" +msgstr "更改Blendspace2d標籤" #: editor/plugins/animation_blend_space_2d_editor.cpp #, fuzzy @@ -3783,15 +3783,15 @@ msgstr "移除" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Remove BlendSpace2D Triangle" -msgstr "" +msgstr "åˆ é™¤Blendspace2d三角形" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "BlendSpace2D does not belong to an AnimationTree node." -msgstr "" +msgstr "Blendspace2dä¸å±¬æ–¼AnimationTree節點。" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "No triangles exist, so no blending can take place." -msgstr "" +msgstr "ä¸å˜åœ¨ä¸‰è§’å½¢, å› æ¤ä¸èƒ½é€²è¡Œæ··åˆã€‚" #: editor/plugins/animation_blend_space_2d_editor.cpp #, fuzzy @@ -3800,20 +3800,20 @@ msgstr "åˆ‡æ›æœ€æ„›" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Create triangles by connecting points." -msgstr "" +msgstr "通éŽé€£æŽ¥é»žå‰µå»ºä¸‰è§’形。" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Erase points and triangles." -msgstr "" +msgstr "åˆ é™¤é»žå’Œä¸‰è§’å½¢ã€‚" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Generate blend triangles automatically (instead of manually)" -msgstr "" +msgstr "è‡ªå‹•ç”Ÿæˆæ··åˆä¸‰è§’å½¢ (è€Œä¸æ˜¯æ‰‹å‹•生æˆ)" #: editor/plugins/animation_blend_space_2d_editor.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp msgid "Blend:" -msgstr "" +msgstr "æ··åˆï¼š" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #, fuzzy @@ -3828,11 +3828,11 @@ msgstr "éŽæ¿¾æª”案..." #: editor/plugins/animation_blend_tree_editor_plugin.cpp msgid "Output node can't be added to the blend tree." -msgstr "" +msgstr "ç„¡æ³•å°‡è¼¸å‡ºç¯€é»žæ·»åŠ åˆ°æ··åˆæ¨¹ä¸ã€‚" #: editor/plugins/animation_blend_tree_editor_plugin.cpp msgid "Add Node to BlendTree" -msgstr "" +msgstr "å°‡ç¯€é»žæ·»åŠ åˆ°BlendTree" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp @@ -3843,7 +3843,7 @@ msgstr "節點å稱:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Unable to connect, port may be in use or connection may be invalid." -msgstr "" +msgstr "ç„¡æ³•é€£æŽ¥ï¼ŒåŸ å¯èƒ½æ£åœ¨ä½¿ç”¨ï¼Œæˆ–者連接å¯èƒ½ç„¡æ•ˆã€‚" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/visual_shader_editor_plugin.cpp @@ -3880,7 +3880,7 @@ msgstr "變更é¡é 尺寸" #: editor/plugins/animation_blend_tree_editor_plugin.cpp msgid "No animation player set, so unable to retrieve track names." -msgstr "" +msgstr "沒有è¨å®šå‹•ç•«æ’æ”¾æ©Ÿï¼Œå›™æ¤ç„¡æ³•檢索曲目å稱。" #: editor/plugins/animation_blend_tree_editor_plugin.cpp msgid "Player path set is invalid, so unable to retrieve track names." diff --git a/main/input_default.cpp b/main/input_default.cpp index e9f1eeff6a..6c3252ad1a 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -623,7 +623,8 @@ bool InputDefault::is_emulating_mouse_from_touch() const { return emulate_mouse_from_touch; } -Input::CursorShape InputDefault::get_default_cursor_shape() { +Input::CursorShape InputDefault::get_default_cursor_shape() const { + return default_shape; } @@ -638,6 +639,11 @@ void InputDefault::set_default_cursor_shape(CursorShape p_shape) { parse_input_event(mm); } +Input::CursorShape InputDefault::get_current_cursor_shape() const { + + return (Input::CursorShape)OS::get_singleton()->get_cursor_shape(); +} + void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { if (Engine::get_singleton()->is_editor_hint()) return; @@ -645,21 +651,6 @@ void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_sh OS::get_singleton()->set_custom_mouse_cursor(p_cursor, (OS::CursorShape)p_shape, p_hotspot); } -void InputDefault::set_mouse_in_window(bool p_in_window) { - /* no longer supported, leaving this for reference to anyone who might want to implement hardware cursors - if (custom_cursor.is_valid()) { - - if (p_in_window) { - set_mouse_mode(MOUSE_MODE_HIDDEN); - VisualServer::get_singleton()->cursor_set_visible(true); - } else { - set_mouse_mode(MOUSE_MODE_VISIBLE); - VisualServer::get_singleton()->cursor_set_visible(false); - } - } - */ -} - void InputDefault::accumulate_input_event(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); diff --git a/main/input_default.h b/main/input_default.h index 79a90cc4a4..f9b4cbbd57 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -242,10 +242,10 @@ public: void set_emulate_mouse_from_touch(bool p_emulate); virtual bool is_emulating_mouse_from_touch() const; - virtual CursorShape get_default_cursor_shape(); + virtual CursorShape get_default_cursor_shape() const; virtual void set_default_cursor_shape(CursorShape p_shape); + virtual CursorShape get_current_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); - virtual void set_mouse_in_window(bool p_in_window); void parse_mapping(String p_mapping); void joy_button(int p_device, int p_button, bool p_pressed); diff --git a/main/main.cpp b/main/main.cpp index 0871b12338..fc9ec3b2d9 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1882,7 +1882,9 @@ bool Main::iteration() { uint64_t idle_begin = OS::get_singleton()->get_ticks_usec(); - OS::get_singleton()->get_main_loop()->idle(step * time_scale); + if (OS::get_singleton()->get_main_loop()->idle(step * time_scale)) { + exit = true; + } message_queue->flush(); VisualServer::get_singleton()->sync(); //sync if still drawing from previous frames. diff --git a/main/performance.cpp b/main/performance.cpp index 49a08aa8d1..2f7ebf7656 100644 --- a/main/performance.cpp +++ b/main/performance.cpp @@ -154,7 +154,8 @@ float Performance::get_monitor(Monitor p_monitor) const { case PHYSICS_3D_ISLAND_COUNT: return PhysicsServer::get_singleton()->get_process_info(PhysicsServer::INFO_ISLAND_COUNT); case AUDIO_OUTPUT_LATENCY: return AudioServer::get_singleton()->get_output_latency(); - default: {} + default: { + } } return 0; diff --git a/main/tests/test_gdscript.cpp b/main/tests/test_gdscript.cpp index 27180f84aa..87bd640001 100644 --- a/main/tests/test_gdscript.cpp +++ b/main/tests/test_gdscript.cpp @@ -127,6 +127,7 @@ static String _parser_expr(const GDScriptParser::Node *p_expr) { case GDScriptParser::OperatorNode::OP_PARENT_CALL: txt += "."; + FALLTHROUGH; case GDScriptParser::OperatorNode::OP_CALL: { ERR_FAIL_COND_V(c_node->arguments.size() < 1, ""); @@ -283,10 +284,16 @@ static String _parser_expr(const GDScriptParser::Node *p_expr) { case GDScriptParser::OperatorNode::OP_BIT_XOR: { txt = _parser_expr(c_node->arguments[0]) + "^" + _parser_expr(c_node->arguments[1]); } break; - default: {} + default: { + } } } break; + case GDScriptParser::Node::TYPE_CAST: { + const GDScriptParser::CastNode *cast_node = static_cast<const GDScriptParser::CastNode *>(p_expr); + txt = _parser_expr(cast_node->source_node) + " as " + cast_node->cast_type.to_string(); + + } break; case GDScriptParser::Node::TYPE_NEWLINE: { //skippie @@ -667,6 +674,17 @@ static void _disassemble_class(const Ref<GDScript> &p_class, const Vector<String incr += 2; } break; + case GDScriptFunction::OPCODE_CAST_TO_SCRIPT: { + + txt += " cast "; + txt += DADDR(3); + txt += "="; + txt += DADDR(1); + txt += " as "; + txt += DADDR(2); + incr += 4; + + } break; case GDScriptFunction::OPCODE_CONSTRUCT: { Variant::Type t = Variant::Type(code[ip + 1]); @@ -1016,19 +1034,17 @@ MainLoop *test(TestType p_type) { return NULL; } - GDScript *script = memnew(GDScript); + Ref<GDScript> gds; + gds.instance(); GDScriptCompiler gdc; - err = gdc.compile(&parser, script); + err = gdc.compile(&parser, gds.ptr()); if (err) { print_line("Compile Error:\n" + itos(gdc.get_error_line()) + ":" + itos(gdc.get_error_column()) + ":" + gdc.get_error()); - memdelete(script); return NULL; } - Ref<GDScript> gds = Ref<GDScript>(script); - Ref<GDScript> current = gds; while (current.is_valid()) { diff --git a/main/tests/test_string.cpp b/main/tests/test_string.cpp index 3465fd783e..dcbb930d1b 100644 --- a/main/tests/test_string.cpp +++ b/main/tests/test_string.cpp @@ -1053,6 +1053,19 @@ bool test_33() { return empty.parse_utf8(NULL, -1) == true; } +bool test_34() { + OS::get_singleton()->print("\n\nTest 34: Cyrillic to_lower()\n"); + + String upper = L"ÐБВГДЕÐЖЗИЙКЛМÐОПРСТУФХЦЧШЩЪЫЬÐЮЯ"; + String lower = L"абвгдеёжзийклмнопрÑтуфхцчшщъыьÑÑŽÑ"; + + String test = upper.to_lower(); + + bool state = test == lower; + + return state; +} + typedef bool (*TestFunc)(void); TestFunc test_funcs[] = { @@ -1090,6 +1103,7 @@ TestFunc test_funcs[] = { test_31, test_32, test_33, + test_34, 0 }; diff --git a/methods.py b/methods.py index 7465962187..ec9ecdb17f 100644 --- a/methods.py +++ b/methods.py @@ -4,7 +4,6 @@ import sys import re import glob import string -import datetime import subprocess from compat import iteritems, isbasestring, decode_utf8 @@ -668,5 +667,8 @@ def get_compiler_version(env): else: return None -def use_gcc(env): +def using_gcc(env): return 'gcc' in os.path.basename(env["CC"]) + +def using_clang(env): + return 'clang' in os.path.basename(env["CC"]) diff --git a/modules/assimp/SCsub b/modules/assimp/SCsub new file mode 100644 index 0000000000..61a357809a --- /dev/null +++ b/modules/assimp/SCsub @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_assimp = env_modules.Clone() +env_assimp.Append(CPPPATH=['#thirdparty/assimp']) +env_assimp.Append(CPPPATH=['#thirdparty/assimp/include']) +env_assimp.Append(CPPPATH=['#thirdparty/assimp/code/Importer/IFC']) +env_assimp.Append(CPPPATH=['#thirdparty/misc']) +env_assimp.Append(CPPPATH=['#thirdparty/assimp/code']) +env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/irrXML/']) +env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/unzip/']) +env_assimp.Append(CPPPATH=['#thirdparty/assimp/code/Importer/STEPParser']) +env_assimp.Append(CPPPATH=['#thirdparty/assimp/']) +env_assimp.Append(CPPPATH=['#thirdparty/zlib/']) +env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/openddlparser/include']) +env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/rapidjson/include']) +env_assimp.Append(CPPPATH=['.']) +#env_assimp.Append(CPPFLAGS=['-DASSIMP_DOUBLE_PRECISION']) # TODO default to what godot is compiled with for future double support +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_BOOST_WORKAROUND']) +env_assimp.Append(CPPFLAGS=['-DOPENDDLPARSER_BUILD']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_OWN_ZLIB']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_EXPORT']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_X_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_AMF_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_3DS_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MD3_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MD5_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MDL_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MD2_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_PLY_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_ASE_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_OBJ_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_HMP_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_SMD_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MDC_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MD5_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_STL_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_LWO_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_DXF_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_NFF_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_RAW_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_SIB_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_OFF_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_AC_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_BVH_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_IRRMESH_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_IRR_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_Q3D_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_B3D_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_COLLADA_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_TERRAGEN_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_CSM_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_3D_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_LWS_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_OGRE_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_OPENGEX_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MS3D_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_COB_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_BLEND_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_Q3BSP_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_NDO_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_STEP_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_IFC_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_XGL_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_ASSBIN_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_GLTF_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_C4D_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_3MF_IMPORTER']) +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_X3D_IMPORTER']) + +env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_SINGLETHREADED']) + +if (not env.msvc): + env_assimp.Append(CXXFLAGS=['-std=c++11']) +elif (env.msvc == False and env['platform'] == 'windows'): + env_assimp.Append(LDFLAGS=['-pthread']) + +if(env['platform'] == 'windows'): + env_assimp.Append(CPPFLAGS=['-DPLATFORM_WINDOWS']) + env_assimp.Append(CPPFLAGS=['-DPLATFORM=WINDOWS']) +elif(env['platform'] == 'x11'): + env_assimp.Append(CPPFLAGS=['-DPLATFORM_LINUX']) + env_assimp.Append(CPPFLAGS=['-DPLATFORM=LINUX']) +elif(env['platform'] == 'osx'): + env_assimp.Append(CPPFLAGS=['-DPLATFORM_DARWIN']) + env_assimp.Append(CPPFLAGS=['-DPLATFORM=DARWIN']) + +env_thirdparty = env_assimp.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, Glob('#thirdparty/assimp/code/*.cpp')) + +# Godot's own source files +env_assimp.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/assimp/config.py b/modules/assimp/config.py new file mode 100644 index 0000000000..098f1eafa9 --- /dev/null +++ b/modules/assimp/config.py @@ -0,0 +1,5 @@ +def can_build(env, platform): + return env['tools'] + +def configure(env): + pass diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp new file mode 100644 index 0000000000..0330ab4604 --- /dev/null +++ b/modules/assimp/editor_scene_importer_assimp.cpp @@ -0,0 +1,1766 @@ +/*************************************************************************/ +/* editor_scene_importer_assimp.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "assimp/DefaultLogger.hpp" +#include "assimp/Importer.hpp" +#include "assimp/LogStream.hpp" +#include "assimp/Logger.hpp" +#include "assimp/SceneCombiner.h" +#include "assimp/cexport.h" +#include "assimp/cimport.h" +#include "assimp/matrix4x4.h" +#include "assimp/pbrmaterial.h" +#include "assimp/postprocess.h" +#include "assimp/scene.h" + +#include "core/bind/core_bind.h" +#include "core/io/image_loader.h" +#include "editor/editor_file_system.h" +#include "editor/import/resource_importer_scene.h" +#include "editor_scene_importer_assimp.h" +#include "editor_settings.h" +#include "scene/3d/camera.h" +#include "scene/3d/light.h" +#include "scene/3d/mesh_instance.h" +#include "scene/animation/animation_player.h" +#include "scene/main/node.h" +#include "scene/resources/material.h" +#include "scene/resources/surface_tool.h" +#include "zutil.h" +#include <string> + +void EditorSceneImporterAssimp::get_extensions(List<String> *r_extensions) const { + + const String import_setting_string = "filesystem/import/open_asset_import/"; + + Map<String, ImportFormat> import_format; + { + Vector<String> exts; + exts.push_back("fbx"); + ImportFormat import = { exts, true }; + import_format.insert("fbx", import); + } + { + Vector<String> exts; + exts.push_back("pmx"); + ImportFormat import = { exts, true }; + import_format.insert("mmd", import); + } + for (Map<String, ImportFormat>::Element *E = import_format.front(); E; E = E->next()) { + _register_project_setting_import(E->key(), import_setting_string, E->get().extensions, r_extensions, E->get().is_default); + } +} + +void EditorSceneImporterAssimp::_register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const { + const String use_generic = "use_" + generic; + _GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true); + if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) { + for (int32_t i = 0; i < exts.size(); i++) { + r_extensions->push_back(exts[i]); + } + } +} + +uint32_t EditorSceneImporterAssimp::get_import_flags() const { + return IMPORT_SCENE; +} + +AssimpStream::AssimpStream() { + // empty +} + +AssimpStream::~AssimpStream() { + // empty +} + +void AssimpStream::write(const char *message) { + print_verbose(String("Open Asset Import: ") + String(message).strip_edges()); +} + +void EditorSceneImporterAssimp::_bind_methods() { +} + +Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { + Assimp::Importer importer; + std::wstring w_path = ProjectSettings::get_singleton()->globalize_path(p_path).c_str(); + std::string s_path(w_path.begin(), w_path.end()); + importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true); + // Cannot remove pivot points because the static mesh will be in the wrong place + importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false); + int32_t max_bone_weights = 4; + //if (p_flags & IMPORT_ANIMATION_EIGHT_WEIGHTS) { + // const int eight_bones = 8; + // importer.SetPropertyBool(AI_CONFIG_PP_LBW_MAX_WEIGHTS, eight_bones); + // max_bone_weights = eight_bones; + //} + + importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT); + //importer.SetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD, 1.0f); + int32_t post_process_Steps = aiProcess_CalcTangentSpace | + //aiProcess_FlipUVs | + //aiProcess_FlipWindingOrder | + aiProcess_DropNormals | + aiProcess_GenSmoothNormals | + aiProcess_JoinIdenticalVertices | + aiProcess_ImproveCacheLocality | + aiProcess_LimitBoneWeights | + //aiProcess_RemoveRedundantMaterials | // Causes a crash + aiProcess_SplitLargeMeshes | + aiProcess_Triangulate | + aiProcess_GenUVCoords | + //aiProcess_FindDegenerates | + aiProcess_SortByPType | + aiProcess_FindInvalidData | + aiProcess_TransformUVCoords | + aiProcess_FindInstances | + //aiProcess_FixInfacingNormals | + //aiProcess_ValidateDataStructure | + aiProcess_OptimizeMeshes | + //aiProcess_OptimizeGraph | + //aiProcess_Debone | + aiProcess_EmbedTextures | + aiProcess_SplitByBoneCount | + 0; + const aiScene *scene = importer.ReadFile(s_path.c_str(), + post_process_Steps); + ERR_EXPLAIN(String("Open Asset Import failed to open: ") + String(importer.GetErrorString())); + ERR_FAIL_COND_V(scene == NULL, NULL); + return _generate_scene(p_path, scene, p_flags, p_bake_fps, max_bone_weights); +} + +template <class T> +struct EditorSceneImporterAssetImportInterpolate { + + T lerp(const T &a, const T &b, float c) const { + + return a + (b - a) * c; + } + + T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) { + + float t2 = t * t; + float t3 = t2 * t; + + return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); + } + + T bezier(T start, T control_1, T control_2, T end, float t) { + /* Formula from Wikipedia article on Bezier curves. */ + real_t omt = (1.0 - t); + real_t omt2 = omt * omt; + real_t omt3 = omt2 * omt; + real_t t2 = t * t; + real_t t3 = t2 * t; + + return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3; + } +}; + +//thank you for existing, partial specialization +template <> +struct EditorSceneImporterAssetImportInterpolate<Quat> { + + Quat lerp(const Quat &a, const Quat &b, float c) const { + ERR_FAIL_COND_V(!a.is_normalized(), Quat()); + ERR_FAIL_COND_V(!b.is_normalized(), Quat()); + + return a.slerp(b, c).normalized(); + } + + Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, float c) { + ERR_FAIL_COND_V(!p1.is_normalized(), Quat()); + ERR_FAIL_COND_V(!p2.is_normalized(), Quat()); + + return p1.slerp(p2, c).normalized(); + } + + Quat bezier(Quat start, Quat control_1, Quat control_2, Quat end, float t) { + ERR_FAIL_COND_V(!start.is_normalized(), Quat()); + ERR_FAIL_COND_V(!end.is_normalized(), Quat()); + + return start.slerp(end, t).normalized(); + } +}; + +template <class T> +T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp) { + //could use binary search, worth it? + int idx = -1; + for (int i = 0; i < p_times.size(); i++) { + if (p_times[i] > p_time) + break; + idx++; + } + + EditorSceneImporterAssetImportInterpolate<T> interp; + + switch (p_interp) { + case AssetImportAnimation::INTERP_LINEAR: { + + if (idx == -1) { + return p_values[0]; + } else if (idx >= p_times.size() - 1) { + return p_values[p_times.size() - 1]; + } + + float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + return interp.lerp(p_values[idx], p_values[idx + 1], c); + + } break; + case AssetImportAnimation::INTERP_STEP: { + + if (idx == -1) { + return p_values[0]; + } else if (idx >= p_times.size() - 1) { + return p_values[p_times.size() - 1]; + } + + return p_values[idx]; + + } break; + case AssetImportAnimation::INTERP_CATMULLROMSPLINE: { + + if (idx == -1) { + return p_values[1]; + } else if (idx >= p_times.size() - 1) { + return p_values[1 + p_times.size() - 1]; + } + + float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c); + + } break; + case AssetImportAnimation::INTERP_CUBIC_SPLINE: { + + if (idx == -1) { + return p_values[1]; + } else if (idx >= p_times.size() - 1) { + return p_values[(p_times.size() - 1) * 3 + 1]; + } + + float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + T from = p_values[idx * 3 + 1]; + T c1 = from + p_values[idx * 3 + 2]; + T to = p_values[idx * 3 + 4]; + T c2 = to + p_values[idx * 3 + 3]; + + return interp.bezier(from, c1, c2, to, c); + + } break; + } + + ERR_FAIL_V(p_values[0]); +} + +void EditorSceneImporterAssimp::_generate_bone_groups(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<String, Transform> &bind_xforms) { + + Transform mesh_offset = _get_global_assimp_node_transform(p_assimp_node); + //mesh_offset.basis = Basis(); + for (uint32_t i = 0; i < p_assimp_node->mNumMeshes; i++) { + const aiMesh *mesh = state.assimp_scene->mMeshes[i]; + int owned_by = -1; + for (uint32_t j = 0; j < mesh->mNumBones; j++) { + const aiBone *bone = mesh->mBones[j]; + String name = _assimp_get_string(bone->mName); + + if (ownership.has(name)) { + owned_by = ownership[name]; + break; + } + } + + if (owned_by == -1) { //no owned, create new unique id + owned_by = 1; + for (Map<String, int>::Element *E = ownership.front(); E; E = E->next()) { + owned_by = MAX(E->get() + 1, owned_by); + } + } + + for (uint32_t j = 0; j < mesh->mNumBones; j++) { + const aiBone *bone = mesh->mBones[j]; + String name = _assimp_get_string(bone->mName); + ownership[name] = owned_by; + //store the actuall full path for the bone transform + //when skeleton finds it's place in the tree, it will be restored + bind_xforms[name] = mesh_offset * _assimp_matrix_transform(bone->mOffsetMatrix); + } + } + + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _generate_bone_groups(state, p_assimp_node->mChildren[i], ownership, bind_xforms); + } +} + +void EditorSceneImporterAssimp::_fill_node_relationships(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, int p_skeleton_id, Skeleton *p_skeleton, const String &p_parent_name, int &holecount, const Vector<SkeletonHole> &p_holes, const Map<String, Transform> &bind_xforms) { + + String name = _assimp_get_string(p_assimp_node->mName); + if (name == String()) { + name = "AuxiliaryBone" + itos(holecount++); + } + + Transform pose = _assimp_matrix_transform(p_assimp_node->mTransformation); + + if (!ownership.has(name)) { + //not a bone, it's a hole + Vector<SkeletonHole> holes = p_holes; + SkeletonHole hole; //add a new one + hole.name = name; + hole.pose = pose; + hole.node = p_assimp_node; + hole.parent = p_parent_name; + holes.push_back(hole); + + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _fill_node_relationships(state, p_assimp_node->mChildren[i], ownership, skeleton_map, p_skeleton_id, p_skeleton, name, holecount, holes, bind_xforms); + } + + return; + } else if (ownership[name] != p_skeleton_id) { + //oh, it's from another skeleton? fine.. reparent all bones to this skeleton. + int prev_owner = ownership[name]; + ERR_EXPLAIN("A previous skeleton exists for bone '" + name + "', this type of skeleton layout is unsupported."); + ERR_FAIL_COND(skeleton_map.has(prev_owner)); + for (Map<String, int>::Element *E = ownership.front(); E; E = E->next()) { + if (E->get() == prev_owner) { + E->get() = p_skeleton_id; + } + } + } + + //valid bone, first fill holes if needed + for (int i = 0; i < p_holes.size(); i++) { + + int bone_idx = p_skeleton->get_bone_count(); + p_skeleton->add_bone(p_holes[i].name); + int parent_idx = p_skeleton->find_bone(p_holes[i].parent); + if (parent_idx >= 0) { + p_skeleton->set_bone_parent(bone_idx, parent_idx); + } + + Transform pose_transform = _get_global_assimp_node_transform(p_holes[i].node); + p_skeleton->set_bone_rest(bone_idx, pose_transform); + + state.bone_owners[p_holes[i].name] = skeleton_map[p_skeleton_id]; + } + + //finally fill bone + + int bone_idx = p_skeleton->get_bone_count(); + p_skeleton->add_bone(name); + int parent_idx = p_skeleton->find_bone(p_parent_name); + if (parent_idx >= 0) { + p_skeleton->set_bone_parent(bone_idx, parent_idx); + } + //p_skeleton->set_bone_pose(bone_idx, pose); + if (bind_xforms.has(name)) { + //for now this is the full path to the bone in rest pose + //when skeleton finds it's place in the tree, it will get fixed + p_skeleton->set_bone_rest(bone_idx, bind_xforms[name]); + } + state.bone_owners[name] = skeleton_map[p_skeleton_id]; + //go to children + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _fill_node_relationships(state, p_assimp_node->mChildren[i], ownership, skeleton_map, p_skeleton_id, p_skeleton, name, holecount, Vector<SkeletonHole>(), bind_xforms); + } +} + +void EditorSceneImporterAssimp::_generate_skeletons(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, const Map<String, Transform> &bind_xforms) { + + //find skeletons at this level, there may be multiple root nodes for each + Map<int, List<aiNode *> > skeletons_found; + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + String name = _assimp_get_string(p_assimp_node->mChildren[i]->mName); + if (ownership.has(name)) { + int skeleton = ownership[name]; + if (!skeletons_found.has(skeleton)) { + skeletons_found[skeleton] = List<aiNode *>(); + } + skeletons_found[skeleton].push_back(p_assimp_node->mChildren[i]); + } + } + + //go via the potential skeletons found and generate the actual skeleton + for (Map<int, List<aiNode *> >::Element *E = skeletons_found.front(); E; E = E->next()) { + ERR_CONTINUE(skeleton_map.has(E->key())); //skeleton already exists? this can't be.. skip + Skeleton *skeleton = memnew(Skeleton); + //this the only way to reliably use multiple meshes with one skeleton, at the cost of less precision + skeleton->set_use_bones_in_world_transform(true); + skeleton_map[E->key()] = state.skeletons.size(); + state.skeletons.push_back(skeleton); + int holecount = 1; + //fill the bones and their relationships + for (List<aiNode *>::Element *F = E->get().front(); F; F = F->next()) { + _fill_node_relationships(state, F->get(), ownership, skeleton_map, E->key(), skeleton, "", holecount, Vector<SkeletonHole>(), bind_xforms); + } + } + + //go to the children + for (uint32_t i = 0; i < p_assimp_node->mNumChildren; i++) { + String name = _assimp_get_string(p_assimp_node->mChildren[i]->mName); + if (ownership.has(name)) { + continue; //a bone, so don't bother with this + } + _generate_skeletons(state, p_assimp_node->mChildren[i], ownership, skeleton_map, bind_xforms); + } +} + +Spatial *EditorSceneImporterAssimp::_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights) { + ERR_FAIL_COND_V(scene == NULL, NULL); + + ImportState state; + state.path = p_path; + state.assimp_scene = scene; + state.max_bone_weights = p_max_bone_weights; + state.root = memnew(Spatial); + state.fbx = false; + state.animation_player = NULL; + + real_t scale_factor = 1.0f; + { + //handle scale + String ext = p_path.get_file().get_extension().to_lower(); + if (ext == "fbx") { + if (scene->mMetaData != NULL) { + float factor = 1.0; + scene->mMetaData->Get("UnitScaleFactor", factor); + scale_factor = factor * 0.01f; + } + state.fbx = true; + } + } + + state.root->set_scale(Vector3(scale_factor, scale_factor, scale_factor)); + + //fill light map cache + for (size_t l = 0; l < scene->mNumLights; l++) { + + aiLight *ai_light = scene->mLights[l]; + ERR_CONTINUE(ai_light == NULL); + state.light_cache[_assimp_get_string(ai_light->mName)] = l; + } + + //fill camera cache + for (size_t c = 0; c < scene->mNumCameras; c++) { + aiCamera *ai_camera = scene->mCameras[c]; + ERR_CONTINUE(ai_camera == NULL); + state.camera_cache[_assimp_get_string(ai_camera->mName)] = c; + } + + if (scene->mRootNode) { + Map<String, Transform> bind_xforms; //temporary map to store bind transforms + //guess the skeletons, since assimp does not really support them directly + Map<String, int> ownership; //bone names to groups + //fill this map with bone names and which group where they detected to, going mesh by mesh + _generate_bone_groups(state, state.assimp_scene->mRootNode, ownership, bind_xforms); + Map<int, int> skeleton_map; //maps previously created groups to actual skeletons + //generates the skeletons when bones are found in the hierarchy, and follows them (including gaps/holes). + _generate_skeletons(state, state.assimp_scene->mRootNode, ownership, skeleton_map, bind_xforms); + + //generate nodes + for (uint32_t i = 0; i < scene->mRootNode->mNumChildren; i++) { + _generate_node(state, scene->mRootNode->mChildren[i], state.root); + } + + //assign skeletons to nodes + + for (Map<MeshInstance *, Skeleton *>::Element *E = state.mesh_skeletons.front(); E; E = E->next()) { + MeshInstance *mesh = E->key(); + Skeleton *skeleton = E->get(); + NodePath skeleton_path = mesh->get_path_to(skeleton); + mesh->set_skeleton_path(skeleton_path); + } + } + + if (p_flags & IMPORT_ANIMATION && scene->mNumAnimations) { + + state.animation_player = memnew(AnimationPlayer); + state.root->add_child(state.animation_player); + state.animation_player->set_owner(state.root); + + for (uint32_t i = 0; i < scene->mNumAnimations; i++) { + _import_animation(state, i, p_bake_fps); + } + } + + return state.root; +} + +void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int p_track, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, Skeleton *p_skeleton, const NodePath &p_path, const String &p_name) { + + const aiNodeAnim *assimp_track = assimp_anim->mChannels[p_track]; + //make transform track + int track_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_TRANSFORM); + animation->track_set_path(track_idx, p_path); + //first determine animation length + + float increment = 1.0 / float(p_bake_fps); + float time = 0.0; + + bool last = false; + + int skeleton_bone = -1; + + if (p_skeleton) { + skeleton_bone = p_skeleton->find_bone(p_name); + } + + Vector<Vector3> pos_values; + Vector<float> pos_times; + Vector<Vector3> scale_values; + Vector<float> scale_times; + Vector<Quat> rot_values; + Vector<float> rot_times; + + for (size_t p = 0; p < assimp_track->mNumPositionKeys; p++) { + aiVector3D pos = assimp_track->mPositionKeys[p].mValue; + pos_values.push_back(Vector3(pos.x, pos.y, pos.z)); + pos_times.push_back(assimp_track->mPositionKeys[p].mTime / ticks_per_second); + } + + for (size_t r = 0; r < assimp_track->mNumRotationKeys; r++) { + aiQuaternion quat = assimp_track->mRotationKeys[r].mValue; + rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized()); + rot_times.push_back(assimp_track->mRotationKeys[r].mTime / ticks_per_second); + } + + for (size_t sc = 0; sc < assimp_track->mNumScalingKeys; sc++) { + aiVector3D scale = assimp_track->mScalingKeys[sc].mValue; + scale_values.push_back(Vector3(scale.x, scale.y, scale.z)); + scale_times.push_back(assimp_track->mScalingKeys[sc].mTime / ticks_per_second); + } + while (true) { + Vector3 pos; + Quat rot; + Vector3 scale(1, 1, 1); + + if (pos_values.size()) { + pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR); + } + + if (rot_values.size()) { + rot = _interpolate_track<Quat>(rot_times, rot_values, time, AssetImportAnimation::INTERP_LINEAR).normalized(); + } + + if (scale_values.size()) { + scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR); + } + + if (skeleton_bone >= 0) { + Transform xform; + xform.basis.set_quat_scale(rot, scale); + xform.origin = pos; + + Transform rest_xform = p_skeleton->get_bone_rest(skeleton_bone); + xform = rest_xform.affine_inverse() * xform; + rot = xform.basis.get_rotation_quat(); + scale = xform.basis.get_scale(); + pos = xform.origin; + } + + rot.normalize(); + + animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR); + animation->transform_track_insert_key(track_idx, time, pos, rot, scale); + + if (last) { //done this way so a key is always inserted past the end (for proper interpolation) + break; + } + time += increment; + if (time >= animation->get_length()) { + last = true; + } + } +} + +void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_animation_index, int p_bake_fps) { + + ERR_FAIL_INDEX(p_animation_index, (int)state.assimp_scene->mNumAnimations); + + const aiAnimation *anim = state.assimp_scene->mAnimations[p_animation_index]; + String name = _assimp_anim_string_to_string(anim->mName); + if (name == String()) { + name = "Animation " + itos(p_animation_index + 1); + } + + float ticks_per_second = anim->mTicksPerSecond; + + if (state.assimp_scene->mMetaData != NULL && Math::is_equal_approx(ticks_per_second, 0.0f)) { + int32_t time_mode = 0; + state.assimp_scene->mMetaData->Get("TimeMode", time_mode); + ticks_per_second = _get_fbx_fps(time_mode, state.assimp_scene); + } + + //? + //if ((p_path.get_file().get_extension().to_lower() == "glb" || p_path.get_file().get_extension().to_lower() == "gltf") && Math::is_equal_approx(ticks_per_second, 0.0f)) { + // ticks_per_second = 1000.0f; + //} + + if (Math::is_equal_approx(ticks_per_second, 0.0f)) { + ticks_per_second = 25.0f; + } + + Ref<Animation> animation; + animation.instance(); + animation->set_name(name); + animation->set_length(anim->mDuration / ticks_per_second); + + //regular tracks + + for (size_t i = 0; i < anim->mNumChannels; i++) { + const aiNodeAnim *track = anim->mChannels[i]; + String node_name = _assimp_get_string(track->mNodeName); + /* + if (node_name.find(ASSIMP_FBX_KEY) != -1) { + String p_track_type = node_name.get_slice(ASSIMP_FBX_KEY, 1); + if (p_track_type == "_Translation" || p_track_type == "_Rotation" || p_track_type == "_Scaling") { + continue; + } + } +*/ + if (track->mNumRotationKeys == 0 && track->mNumPositionKeys == 0 && track->mNumScalingKeys == 0) { + continue; //do not bother + } + + bool is_bone = state.bone_owners.has(node_name); + NodePath node_path; + Skeleton *skeleton = NULL; + + if (is_bone) { + skeleton = state.skeletons[state.bone_owners[node_name]]; + String path = state.root->get_path_to(skeleton); + path += ":" + node_name; + node_path = path; + } else { + + ERR_CONTINUE(!state.node_map.has(node_name)); + Node *node = state.node_map[node_name]; + node_path = state.root->get_path_to(node); + } + + _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton, node_path, node_name); + } + + //blend shape tracks + + for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) { + + const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i]; + + const String prop_name = _assimp_get_string(anim_mesh->mName); + const String mesh_name = prop_name.split("*")[0]; + + ERR_CONTINUE(prop_name.split("*").size() != 2); + + ERR_CONTINUE(!state.node_map.has(mesh_name)); + + const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(state.node_map[mesh_name]); + + ERR_CONTINUE(mesh_instance == NULL); + + String base_path = state.root->get_path_to(mesh_instance); + + Ref<Mesh> mesh = mesh_instance->get_mesh(); + ERR_CONTINUE(mesh.is_null()); + + //add the tracks for this mesh + int base_track = animation->get_track_count(); + for (int j = 0; j < mesh->get_blend_shape_count(); j++) { + + animation->add_track(Animation::TYPE_VALUE); + animation->track_set_path(base_track + j, base_path + ":blend_shapes/" + mesh->get_blend_shape_name(j)); + } + + for (size_t k = 0; k < anim_mesh->mNumKeys; k++) { + for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) { + + float t = anim_mesh->mKeys[k].mTime / ticks_per_second; + float w = anim_mesh->mKeys[k].mWeights[j]; + + animation->track_insert_key(base_track + j, t, w); + } + } + } + + if (animation->get_track_count()) { + state.animation_player->add_animation(name, animation); + } +} + +float EditorSceneImporterAssimp::_get_fbx_fps(int32_t time_mode, const aiScene *p_scene) { + switch (time_mode) { + case AssetImportFbx::TIME_MODE_DEFAULT: return 24; //hack + case AssetImportFbx::TIME_MODE_120: return 120; + case AssetImportFbx::TIME_MODE_100: return 100; + case AssetImportFbx::TIME_MODE_60: return 60; + case AssetImportFbx::TIME_MODE_50: return 50; + case AssetImportFbx::TIME_MODE_48: return 48; + case AssetImportFbx::TIME_MODE_30: return 30; + case AssetImportFbx::TIME_MODE_30_DROP: return 30; + case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: return 29.9700262f; + case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: return 29.9700262f; + case AssetImportFbx::TIME_MODE_PAL: return 25; + case AssetImportFbx::TIME_MODE_CINEMA: return 24; + case AssetImportFbx::TIME_MODE_1000: return 1000; + case AssetImportFbx::TIME_MODE_CINEMA_ND: return 23.976f; + case AssetImportFbx::TIME_MODE_CUSTOM: + int32_t frame_rate; + p_scene->mMetaData->Get("FrameRate", frame_rate); + return frame_rate; + } + return 0; +} + +Transform EditorSceneImporterAssimp::_get_global_assimp_node_transform(const aiNode *p_current_node) { + aiNode const *current_node = p_current_node; + Transform xform; + while (current_node != NULL) { + xform = _assimp_matrix_transform(current_node->mTransformation) * xform; + current_node = current_node->mParent; + } + return xform; +} + +Ref<Texture> EditorSceneImporterAssimp::_load_texture(ImportState &state, String p_path) { + Vector<String> split_path = p_path.get_basename().split("*"); + if (split_path.size() == 2) { + size_t texture_idx = split_path[1].to_int(); + ERR_FAIL_COND_V(texture_idx >= state.assimp_scene->mNumTextures, Ref<Texture>()); + aiTexture *tex = state.assimp_scene->mTextures[texture_idx]; + String filename = _assimp_raw_string_to_string(tex->mFilename); + filename = filename.get_file(); + print_verbose("Open Asset Import: Loading embedded texture " + filename); + if (tex->mHeight == 0) { + if (tex->CheckFormat("png")) { + Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); + ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); + + Ref<ImageTexture> t; + t.instance(); + t->create_from_image(img); + t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + return t; + } else if (tex->CheckFormat("jpg")) { + Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); + ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); + Ref<ImageTexture> t; + t.instance(); + t->create_from_image(img); + t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + return t; + } else if (tex->CheckFormat("dds")) { + ERR_EXPLAIN("Open Asset Import: Embedded dds not implemented"); + ERR_FAIL_COND_V(true, Ref<Texture>()); + //Ref<Image> img = Image::_dds_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); + //ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); + //Ref<ImageTexture> t; + //t.instance(); + //t->create_from_image(img); + //t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + //return t; + } + } else { + Ref<Image> img; + img.instance(); + PoolByteArray arr; + uint32_t size = tex->mWidth * tex->mHeight; + arr.resize(size); + memcpy(arr.write().ptr(), tex->pcData, size); + ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Texture>()); + //ARGB8888 to RGBA8888 + for (int32_t i = 0; i < arr.size() / 4; i++) { + arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0]; + arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1]; + arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2]; + arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3]; + } + img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr); + ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); + + Ref<ImageTexture> t; + t.instance(); + t->create_from_image(img); + t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + return t; + } + return Ref<Texture>(); + } + Ref<Texture> p_texture = ResourceLoader::load(p_path, "Texture"); + return p_texture; +} + +Ref<Material> EditorSceneImporterAssimp::_generate_material_from_index(ImportState &state, int p_index, bool p_double_sided) { + + ERR_FAIL_INDEX_V(p_index, (int)state.assimp_scene->mNumMaterials, Ref<Material>()); + + aiMaterial *ai_material = state.assimp_scene->mMaterials[p_index]; + Ref<SpatialMaterial> mat; + mat.instance(); + + int32_t mat_two_sided = 0; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) { + if (mat_two_sided > 0) { + mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); + } + } + + //const String mesh_name = _assimp_get_string(ai_mesh->mName); + aiString mat_name; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) { + mat->set_name(_assimp_get_string(mat_name)); + } + + aiTextureType tex_normal = aiTextureType_NORMALS; + { + aiString ai_filename = aiString(); + String filename = ""; + aiTextureMapMode map_mode[2]; + + if (AI_SUCCESS == ai_material->GetTexture(tex_normal, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + + if (texture != NULL) { + if (map_mode != NULL) { + _set_texture_mapping_mode(map_mode, texture); + } + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + } + } + } + } + + { + aiString ai_filename = aiString(); + String filename = ""; + + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, ai_filename)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + } + } + } + } + + aiTextureType tex_emissive = aiTextureType_EMISSIVE; + + if (ai_material->GetTextureCount(tex_emissive) > 0) { + + aiString ai_filename = aiString(); + String filename = ""; + aiTextureMapMode map_mode[2]; + + if (AI_SUCCESS == ai_material->GetTexture(tex_emissive, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + _set_texture_mapping_mode(map_mode, texture); + mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true); + mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, texture); + } + } + } + } + + aiTextureType tex_albedo = aiTextureType_DIFFUSE; + if (ai_material->GetTextureCount(tex_albedo) > 0) { + + aiString ai_filename = aiString(); + String filename = ""; + aiTextureMapMode map_mode[2]; + if (AI_SUCCESS == ai_material->GetTexture(tex_albedo, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() != Image::ALPHA_NONE) { + _set_texture_mapping_mode(map_mode, texture); + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + } + } + } + } else { + aiColor4D clr_diffuse; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) { + if (Math::is_equal_approx(clr_diffuse.a, 1.0f) == false) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a)); + } + } + + aiString tex_gltf_base_color_path = aiString(); + aiTextureMapMode map_mode[2]; + if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE, &tex_gltf_base_color_path, NULL, NULL, NULL, NULL, map_mode)) { + String filename = _assimp_raw_string_to_string(tex_gltf_base_color_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { + _set_texture_mapping_mode(map_mode, texture); + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + } + } + } else { + aiColor4D pbr_base_color; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, pbr_base_color)) { + if (Math::is_equal_approx(pbr_base_color.a, 1.0f) == false) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a)); + } + } + { + aiString tex_fbx_pbs_base_color_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_base_color_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + } + } + } else { + aiColor4D pbr_base_color; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR, pbr_base_color)) { + mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a)); + } + } + + aiUVTransform pbr_base_color_uv_xform; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM, pbr_base_color_uv_xform)) { + mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f)); + mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f)); + } + } + + { + aiString tex_fbx_pbs_normal_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE, tex_fbx_pbs_normal_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_normal_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + } + } + } + } + + if (p_double_sided) { + mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); + } + + { + aiString tex_fbx_stingray_normal_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE, tex_fbx_stingray_normal_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_stingray_normal_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + } + } + } + } + + { + aiString tex_fbx_pbs_base_color_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_base_color_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + } + } + } else { + aiColor4D pbr_base_color; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR, pbr_base_color)) { + mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a)); + } + } + + aiUVTransform pbr_base_color_uv_xform; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM, pbr_base_color_uv_xform)) { + mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f)); + mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f)); + } + } + + { + aiString tex_fbx_pbs_emissive_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE, tex_fbx_pbs_emissive_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_emissive_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + } + } + } else { + aiColor4D pbr_emmissive_color; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR, pbr_emmissive_color)) { + mat->set_emission(Color(pbr_emmissive_color.r, pbr_emmissive_color.g, pbr_emmissive_color.b, pbr_emmissive_color.a)); + } + } + + real_t pbr_emission_intensity; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR, pbr_emission_intensity)) { + mat->set_emission_energy(pbr_emission_intensity); + } + } + + aiString tex_gltf_pbr_metallicroughness_path; + if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &tex_gltf_pbr_metallicroughness_path)) { + String filename = _assimp_raw_string_to_string(tex_gltf_pbr_metallicroughness_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); + mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_BLUE); + mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); + mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GREEN); + } + } + } else { + float pbr_roughness = 0.0f; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, pbr_roughness)) { + mat->set_roughness(pbr_roughness); + } + float pbr_metallic = 0.0f; + + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, pbr_metallic)) { + mat->set_metallic(pbr_metallic); + } + } + { + aiString tex_fbx_pbs_metallic_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE, tex_fbx_pbs_metallic_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_metallic_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); + mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); + } + } + } else { + float pbr_metallic = 0.0f; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR, pbr_metallic)) { + mat->set_metallic(pbr_metallic); + } + } + + aiString tex_fbx_pbs_rough_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_rough_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); + mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); + } + } + } else { + float pbr_roughness = 0.04f; + + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR, pbr_roughness)) { + mat->set_roughness(pbr_roughness); + } + } + } + + { + aiString tex_fbx_pbs_metallic_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE, tex_fbx_pbs_metallic_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_metallic_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); + mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); + } + } + } else { + float pbr_metallic = 0.0f; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_FACTOR, pbr_metallic)) { + mat->set_metallic(pbr_metallic); + } + } + + aiString tex_fbx_pbs_rough_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_rough_path); + String path = state.path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); + mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); + } + } + } else { + float pbr_roughness = 0.04f; + + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR, pbr_roughness)) { + mat->set_roughness(pbr_roughness); + } + } + } + + return mat; +} + +Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, Skeleton *p_skeleton, bool p_double_sided_material) { + + Ref<ArrayMesh> mesh; + mesh.instance(); + bool has_uvs = false; + + for (int i = 0; i < p_surface_indices.size(); i++) { + const unsigned int mesh_idx = p_surface_indices[i]; + const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx]; + + Map<uint32_t, Vector<BoneInfo> > vertex_weights; + + if (p_skeleton) { + for (size_t b = 0; b < ai_mesh->mNumBones; b++) { + aiBone *bone = ai_mesh->mBones[b]; + String bone_name = _assimp_get_string(bone->mName); + int bone_index = p_skeleton->find_bone(bone_name); + ERR_CONTINUE(bone_index == -1); //bone refers to an unexisting index, wtf. + + for (size_t w = 0; w < bone->mNumWeights; w++) { + + aiVertexWeight ai_weights = bone->mWeights[w]; + + BoneInfo bi; + + uint32_t vertex_index = ai_weights.mVertexId; + bi.bone = bone_index; + bi.weight = ai_weights.mWeight; + ; + + if (!vertex_weights.has(vertex_index)) { + vertex_weights[vertex_index] = Vector<BoneInfo>(); + } + + vertex_weights[vertex_index].push_back(bi); + } + } + } + + Ref<SurfaceTool> st; + st.instance(); + st->begin(Mesh::PRIMITIVE_TRIANGLES); + + for (size_t j = 0; j < ai_mesh->mNumVertices; j++) { + if (ai_mesh->HasTextureCoords(0)) { + has_uvs = true; + st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y)); + } + if (ai_mesh->HasTextureCoords(1)) { + has_uvs = true; + st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y)); + } + if (ai_mesh->HasVertexColors(0)) { + Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b, ai_mesh->mColors[0]->a); + st->add_color(color); + } + if (ai_mesh->mNormals != NULL) { + const aiVector3D normals = ai_mesh->mNormals[j]; + const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z); + st->add_normal(godot_normal); + if (ai_mesh->HasTangentsAndBitangents()) { + const aiVector3D tangents = ai_mesh->mTangents[j]; + const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z); + const aiVector3D bitangent = ai_mesh->mBitangents[j]; + const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z); + float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f; + st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d)); + } + } + + if (vertex_weights.has(j)) { + + Vector<BoneInfo> bone_info = vertex_weights[j]; + Vector<int> bones; + bones.resize(bone_info.size()); + Vector<float> weights; + weights.resize(bone_info.size()); + for (int k = 0; k < bone_info.size(); k++) { + bones.write[k] = bone_info[k].bone; + weights.write[k] = bone_info[k].weight; + } + + st->add_bones(bones); + st->add_weights(weights); + } + + const aiVector3D pos = ai_mesh->mVertices[j]; + Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z); + st->add_vertex(godot_pos); + } + + for (size_t j = 0; j < ai_mesh->mNumFaces; j++) { + const aiFace face = ai_mesh->mFaces[j]; + ERR_CONTINUE(face.mNumIndices != 3); + Vector<size_t> order; + order.push_back(2); + order.push_back(1); + order.push_back(0); + for (int32_t k = 0; k < order.size(); k++) { + st->add_index(face.mIndices[order[k]]); + } + } + if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) { + st->generate_tangents(); + } + + Ref<Material> material; + + if (!state.material_cache.has(ai_mesh->mMaterialIndex)) { + material = _generate_material_from_index(state, ai_mesh->mMaterialIndex, p_double_sided_material); + } + + Array array_mesh = st->commit_to_arrays(); + Array morphs; + morphs.resize(ai_mesh->mNumAnimMeshes); + Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES; + Map<uint32_t, String> morph_mesh_idx_names; + for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) { + + if (i == 0) { + //only do this the first time + String ai_anim_mesh_name = _assimp_get_string(ai_mesh->mAnimMeshes[j]->mName); + mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); + if (ai_anim_mesh_name.empty()) { + ai_anim_mesh_name = String("morph_") + itos(j); + } + mesh->add_blend_shape(ai_anim_mesh_name); + } + + Array array_copy; + array_copy.resize(VisualServer::ARRAY_MAX); + + for (int l = 0; l < VisualServer::ARRAY_MAX; l++) { + array_copy[l] = array_mesh[l].duplicate(true); + } + + const size_t num_vertices = ai_mesh->mAnimMeshes[j]->mNumVertices; + array_copy[Mesh::ARRAY_INDEX] = Variant(); + if (ai_mesh->mAnimMeshes[j]->HasPositions()) { + PoolVector3Array vertices; + vertices.resize(num_vertices); + for (size_t l = 0; l < num_vertices; l++) { + const aiVector3D ai_pos = ai_mesh->mAnimMeshes[j]->mVertices[l]; + Vector3 position = Vector3(ai_pos.x, ai_pos.y, ai_pos.z); + vertices.write()[l] = position; + } + PoolVector3Array new_vertices = array_copy[VisualServer::ARRAY_VERTEX].duplicate(true); + + for (int32_t l = 0; l < vertices.size(); l++) { + PoolVector3Array::Write w = new_vertices.write(); + w[l] = vertices[l]; + } + ERR_CONTINUE(vertices.size() != new_vertices.size()); + array_copy[VisualServer::ARRAY_VERTEX] = new_vertices; + } + + int32_t color_set = 0; + if (ai_mesh->mAnimMeshes[j]->HasVertexColors(color_set)) { + PoolColorArray colors; + colors.resize(num_vertices); + for (size_t l = 0; l < num_vertices; l++) { + const aiColor4D ai_color = ai_mesh->mAnimMeshes[j]->mColors[color_set][l]; + Color color = Color(ai_color.r, ai_color.g, ai_color.b, ai_color.a); + colors.write()[l] = color; + } + PoolColorArray new_colors = array_copy[VisualServer::ARRAY_COLOR].duplicate(true); + + for (int32_t l = 0; l < colors.size(); l++) { + PoolColorArray::Write w = new_colors.write(); + w[l] = colors[l]; + } + array_copy[VisualServer::ARRAY_COLOR] = new_colors; + } + + if (ai_mesh->mAnimMeshes[j]->HasNormals()) { + PoolVector3Array normals; + normals.resize(num_vertices); + for (size_t l = 0; l < num_vertices; l++) { + const aiVector3D ai_normal = ai_mesh->mAnimMeshes[i]->mNormals[l]; + Vector3 normal = Vector3(ai_normal.x, ai_normal.y, ai_normal.z); + normals.write()[l] = normal; + } + PoolVector3Array new_normals = array_copy[VisualServer::ARRAY_NORMAL].duplicate(true); + + for (int l = 0; l < normals.size(); l++) { + PoolVector3Array::Write w = new_normals.write(); + w[l] = normals[l]; + } + array_copy[VisualServer::ARRAY_NORMAL] = new_normals; + } + + if (ai_mesh->mAnimMeshes[j]->HasTangentsAndBitangents()) { + PoolColorArray tangents; + tangents.resize(num_vertices); + PoolColorArray::Write w = tangents.write(); + for (size_t l = 0; l < num_vertices; l++) { + _calc_tangent_from_mesh(ai_mesh, j, l, l, w); + } + PoolRealArray new_tangents = array_copy[VisualServer::ARRAY_TANGENT].duplicate(true); + ERR_CONTINUE(new_tangents.size() != tangents.size() * 4); + for (int32_t l = 0; l < tangents.size(); l++) { + new_tangents.write()[l + 0] = tangents[l].r; + new_tangents.write()[l + 1] = tangents[l].g; + new_tangents.write()[l + 2] = tangents[l].b; + new_tangents.write()[l + 3] = tangents[l].a; + } + + array_copy[VisualServer::ARRAY_TANGENT] = new_tangents; + } + + morphs[j] = array_copy; + } + + mesh->add_surface_from_arrays(primitive, array_mesh, morphs); + mesh->surface_set_material(i, material); + mesh->surface_set_name(i, _assimp_get_string(ai_mesh->mName)); + } + + return mesh; +} + +void EditorSceneImporterAssimp::_generate_node(ImportState &state, const aiNode *p_assimp_node, Node *p_parent) { + + Spatial *new_node = NULL; + String node_name = _assimp_get_string(p_assimp_node->mName); + Transform node_transform = _assimp_matrix_transform(p_assimp_node->mTransformation); + + if (p_assimp_node->mNumMeshes > 0) { + /* MESH NODE */ + Ref<Mesh> mesh; + Skeleton *skeleton = NULL; + { + + //see if we have mesh cache for this. + Vector<int> surface_indices; + for (uint32_t i = 0; i < p_assimp_node->mNumMeshes; i++) { + int mesh_index = p_assimp_node->mMeshes[i]; + surface_indices.push_back(mesh_index); + + //take the chane and attempt to find the skeleton from the bones + if (!skeleton) { + aiMesh *ai_mesh = state.assimp_scene->mMeshes[p_assimp_node->mMeshes[i]]; + for (uint32_t j = 0; j < ai_mesh->mNumBones; j++) { + aiBone *bone = ai_mesh->mBones[j]; + String bone_name = _assimp_get_string(bone->mName); + if (state.bone_owners.has(bone_name)) { + skeleton = state.skeletons[state.bone_owners[bone_name]]; + break; + } + } + } + } + surface_indices.sort(); + String mesh_key; + for (int i = 0; i < surface_indices.size(); i++) { + if (i > 0) { + mesh_key += ":"; + } + mesh_key += itos(surface_indices[i]); + } + + if (!state.mesh_cache.has(mesh_key)) { + //adding cache + aiString cull_mode; //cull is on mesh, which is kind of stupid tbh + bool double_sided_material = false; + if (p_assimp_node->mMetaData) { + p_assimp_node->mMetaData->Get("Culling", cull_mode); + } + if (cull_mode.length != 0 && cull_mode == aiString("CullingOff")) { + double_sided_material = true; + } + + mesh = _generate_mesh_from_surface_indices(state, surface_indices, skeleton, double_sided_material); + state.mesh_cache[mesh_key] = mesh; + } + + mesh = state.mesh_cache[mesh_key]; + } + + MeshInstance *mesh_node = memnew(MeshInstance); + if (skeleton) { + state.mesh_skeletons[mesh_node] = skeleton; + } + mesh_node->set_mesh(mesh); + new_node = mesh_node; + + } else if (state.light_cache.has(node_name)) { + + Light *light = NULL; + aiLight *ai_light = state.assimp_scene->mLights[state.light_cache[node_name]]; + ERR_FAIL_COND(!ai_light); + + if (ai_light->mType == aiLightSource_DIRECTIONAL) { + light = memnew(DirectionalLight); + Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z); + dir.normalize(); + Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); + Vector3 up = Vector3(ai_light->mUp.x, ai_light->mUp.y, ai_light->mUp.z); + up.normalize(); + + Transform light_transform; + light_transform.set_look_at(pos, pos + dir, up); + + node_transform *= light_transform; + + } else if (ai_light->mType == aiLightSource_POINT) { + light = memnew(OmniLight); + Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); + Transform xform; + xform.origin = pos; + + node_transform *= xform; + + light->set_transform(xform); + + //light->set_param(Light::PARAM_ATTENUATION, 1); + } else if (ai_light->mType == aiLightSource_SPOT) { + light = memnew(SpotLight); + + Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z); + dir.normalize(); + Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); + Vector3 up = Vector3(ai_light->mUp.x, ai_light->mUp.y, ai_light->mUp.z); + up.normalize(); + + Transform light_transform; + light_transform.set_look_at(pos, pos + dir, up); + node_transform *= light_transform; + + //light->set_param(Light::PARAM_ATTENUATION, 0.0f); + } + ERR_FAIL_COND(light == NULL); + light->set_color(Color(ai_light->mColorDiffuse.r, ai_light->mColorDiffuse.g, ai_light->mColorDiffuse.b)); + new_node = light; + } else if (state.camera_cache.has(node_name)) { + + aiCamera *ai_camera = state.assimp_scene->mCameras[state.camera_cache[node_name]]; + ERR_FAIL_COND(!ai_camera); + + Camera *camera = memnew(Camera); + + float near = ai_camera->mClipPlaneNear; + if (Math::is_equal_approx(near, 0.0f)) { + near = 0.1f; + } + camera->set_perspective(Math::rad2deg(ai_camera->mHorizontalFOV) * 2.0f, near, ai_camera->mClipPlaneFar); + + Vector3 pos = Vector3(ai_camera->mPosition.x, ai_camera->mPosition.y, ai_camera->mPosition.z); + Vector3 look_at = Vector3(ai_camera->mLookAt.y, ai_camera->mLookAt.x, ai_camera->mLookAt.z).normalized(); + Vector3 up = Vector3(ai_camera->mUp.x, ai_camera->mUp.y, ai_camera->mUp.z); + + Transform xform; + xform.set_look_at(pos, look_at, up); + + new_node = camera; + } else if (state.bone_owners.has(node_name)) { + + //have to actually put the skeleton somewhere, you know. + Skeleton *skeleton = state.skeletons[state.bone_owners[node_name]]; + if (skeleton->get_parent()) { + //a bone for a skeleton already added.. + //could go downwards here to add meshes children of skeleton bones + //but let's not support it for now. + return; + } + //restore rest poses to local, now that we know where the skeleton finally is + Transform skeleton_transform; + if (p_assimp_node->mParent) { + skeleton_transform = _get_global_assimp_node_transform(p_assimp_node->mParent); + } + for (int i = 0; i < skeleton->get_bone_count(); i++) { + Transform rest = skeleton_transform.affine_inverse() * skeleton->get_bone_rest(i); + skeleton->set_bone_rest(i, rest.affine_inverse()); + } + + skeleton->localize_rests(); + node_name = "Skeleton"; //don't use the bone root name + node_transform = Transform(); //dont transform + + new_node = skeleton; + } else { + //generic node + new_node = memnew(Spatial); + } + + { + + new_node->set_name(node_name); + new_node->set_transform(node_transform); + p_parent->add_child(new_node); + new_node->set_owner(state.root); + } + + state.node_map[node_name] = new_node; + + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _generate_node(state, p_assimp_node->mChildren[i], new_node); + } +} + +void EditorSceneImporterAssimp::_calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w) { + const aiVector3D normals = ai_mesh->mAnimMeshes[i]->mNormals[tri_index]; + const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z); + const aiVector3D tangent = ai_mesh->mAnimMeshes[i]->mTangents[tri_index]; + const Vector3 godot_tangent = Vector3(tangent.x, tangent.y, tangent.z); + const aiVector3D bitangent = ai_mesh->mAnimMeshes[i]->mBitangents[tri_index]; + const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z); + float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f; + Color plane_tangent = Color(tangent.x, tangent.y, tangent.z, d); + w[index] = plane_tangent; +} + +void EditorSceneImporterAssimp::_set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<Texture> texture) { + ERR_FAIL_COND(map_mode == NULL); + aiTextureMapMode tex_mode = aiTextureMapMode::aiTextureMapMode_Wrap; + //for (size_t i = 0; i < 3; i++) { + tex_mode = map_mode[0]; + //} + int32_t flags = Texture::FLAGS_DEFAULT; + if (tex_mode == aiTextureMapMode_Wrap) { + //Default + } else if (tex_mode == aiTextureMapMode_Clamp) { + flags = flags & ~Texture::FLAG_REPEAT; + } else if (tex_mode == aiTextureMapMode_Mirror) { + flags = flags | Texture::FLAG_MIRRORED_REPEAT; + } + texture->set_flags(flags); +} + +void EditorSceneImporterAssimp::_find_texture_path(const String &r_p_path, String &r_path, bool &r_found) { + + _Directory dir; + + List<String> exts; + ImageLoader::get_recognized_extensions(&exts); + + Vector<String> split_path = r_path.get_basename().split("*"); + if (split_path.size() == 2) { + r_found = true; + return; + } + + if (dir.file_exists(r_p_path.get_base_dir() + r_path.get_file())) { + r_path = r_p_path.get_base_dir() + r_path.get_file(); + r_found = true; + return; + } + + for (int32_t i = 0; i < exts.size(); i++) { + if (r_found) { + return; + } + if (r_found == false) { + _find_texture_path(r_p_path, dir, r_path, r_found, "." + exts[i]); + } + } +} + +void EditorSceneImporterAssimp::_find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension) { + String name = path.get_basename() + extension; + if (dir.file_exists(name)) { + found = true; + path = name; + return; + } + String name_ignore_sub_directory = p_path.get_base_dir() + "/" + path.get_file().get_basename() + extension; + if (dir.file_exists(name_ignore_sub_directory)) { + found = true; + path = name_ignore_sub_directory; + return; + } + + String name_find_texture_sub_directory = p_path.get_base_dir() + "/textures/" + path.get_file().get_basename() + extension; + if (dir.file_exists(name_find_texture_sub_directory)) { + found = true; + path = name_find_texture_sub_directory; + return; + } + String name_find_texture_upper_sub_directory = p_path.get_base_dir() + "/Textures/" + path.get_file().get_basename() + extension; + if (dir.file_exists(name_find_texture_upper_sub_directory)) { + found = true; + path = name_find_texture_upper_sub_directory; + return; + } + String name_find_texture_outside_sub_directory = p_path.get_base_dir() + "/../textures/" + path.get_file().get_basename() + extension; + if (dir.file_exists(name_find_texture_outside_sub_directory)) { + found = true; + path = name_find_texture_outside_sub_directory; + return; + } + + String name_find_upper_texture_outside_sub_directory = p_path.get_base_dir() + "/../Textures/" + path.get_file().get_basename() + extension; + if (dir.file_exists(name_find_upper_texture_outside_sub_directory)) { + found = true; + path = name_find_upper_texture_outside_sub_directory; + return; + } +} + +String EditorSceneImporterAssimp::_assimp_get_string(const aiString p_string) const { + //convert an assimp String to a Godot String + String name; + name.parse_utf8(p_string.C_Str() /*,p_string.length*/); + if (name.find(":") != -1) { + String replaced_name = name.split(":")[1]; + print_verbose("Replacing " + name + " containing : with " + replaced_name); + name = replaced_name; + } + + name = name.replace(".", ""); //can break things, specially bone names + + return name; +} + +String EditorSceneImporterAssimp::_assimp_anim_string_to_string(const aiString p_string) const { + + String name; + name.parse_utf8(p_string.C_Str() /*,p_string.length*/); + if (name.find(":") != -1) { + String replaced_name = name.split(":")[1]; + print_verbose("Replacing " + name + " containing : with " + replaced_name); + name = replaced_name; + } + return name; +} + +String EditorSceneImporterAssimp::_assimp_raw_string_to_string(const aiString p_string) const { + String name; + name.parse_utf8(p_string.C_Str() /*,p_string.length*/); + return name; +} + +Ref<Animation> EditorSceneImporterAssimp::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { + return Ref<Animation>(); +} + +const Transform EditorSceneImporterAssimp::_assimp_matrix_transform(const aiMatrix4x4 p_matrix) { + aiMatrix4x4 matrix = p_matrix; + Transform xform; + //xform.set(matrix.a1, matrix.b1, matrix.c1, matrix.a2, matrix.b2, matrix.c2, matrix.a3, matrix.b3, matrix.c3, matrix.a4, matrix.b4, matrix.c4); + xform.set(matrix.a1, matrix.a2, matrix.a3, matrix.b1, matrix.b2, matrix.b3, matrix.c1, matrix.c2, matrix.c3, matrix.a4, matrix.b4, matrix.c4); + return xform; +} diff --git a/modules/assimp/editor_scene_importer_assimp.h b/modules/assimp/editor_scene_importer_assimp.h new file mode 100644 index 0000000000..598845236e --- /dev/null +++ b/modules/assimp/editor_scene_importer_assimp.h @@ -0,0 +1,234 @@ +/*************************************************************************/ +/* editor_scene_importer_assimp.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 EDITOR_SCENE_IMPORTER_ASSIMP_H +#define EDITOR_SCENE_IMPORTER_ASSIMP_H + +#ifdef TOOLS_ENABLED +#include "core/bind/core_bind.h" +#include "core/io/resource_importer.h" +#include "core/vector.h" +#include "editor/import/resource_importer_scene.h" +#include "editor/project_settings_editor.h" +#include "scene/3d/mesh_instance.h" +#include "scene/3d/skeleton.h" +#include "scene/3d/spatial.h" +#include "scene/animation/animation_player.h" +#include "scene/resources/animation.h" +#include "scene/resources/surface_tool.h" + +#include "assimp/DefaultLogger.hpp" +#include "assimp/LogStream.hpp" +#include "assimp/Logger.hpp" +#include "assimp/matrix4x4.h" +#include "assimp/scene.h" +#include "assimp/types.h" + +class AssimpStream : public Assimp::LogStream { +public: + // Constructor + AssimpStream(); + + // Destructor + ~AssimpStream(); + // Write something using your own functionality + void write(const char *message); +}; + +#define AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR "$raw.Maya|baseColor", 0, 0 +#define AI_MATKEY_FBX_MAYA_METALNESS_FACTOR "$raw.Maya|metalness", 0, 0 +#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR "$raw.Maya|diffuseRoughness", 0, 0 + +#define AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE "$raw.Maya|metalness|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_METALNESS_UV_XFORM "$raw.Maya|metalness|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE "$raw.Maya|diffuseRoughness|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_UV_XFORM "$raw.Maya|diffuseRoughness|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE "$raw.Maya|baseColor|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM "$raw.Maya|baseColor|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE "$raw.Maya|normalCamera|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo", aiTextureType_UNKNOWN, 0 + +#define AI_MATKEY_FBX_NORMAL_TEXTURE "$raw.Maya|normalCamera|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo", aiTextureType_UNKNOWN, 0 + +#define AI_MATKEY_FBX_MAYA_STINGRAY_DISPLACEMENT_SCALING_FACTOR "$raw.Maya|displacementscaling", 0, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR "$raw.Maya|base_color", 0, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR "$raw.Maya|emissive", 0, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR "$raw.Maya|metallic", 0, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR "$raw.Maya|roughness", 0, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR "$raw.Maya|emissive_intensity", 0, 0 + +#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE "$raw.Maya|TEX_normal_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_UV_XFORM "$raw.Maya|TEX_normal_map|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE "$raw.Maya|TEX_color_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM "$raw.Maya|TEX_color_map|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE "$raw.Maya|TEX_metallic_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_UV_XFORM "$raw.Maya|TEX_metallic_map|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE "$raw.Maya|TEX_roughness_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_UV_XFORM "$raw.Maya|TEX_roughness_map|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE "$raw.Maya|TEX_emissive_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_UV_XFORM "$raw.Maya|TEX_emissive_map|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_TEXTURE "$raw.Maya|TEX_ao_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_UV_XFORM "$raw.Maya|TEX_ao_map|uvtrafo", aiTextureType_UNKNOWN, 0 + +class EditorSceneImporterAssimp : public EditorSceneImporter { +private: + GDCLASS(EditorSceneImporterAssimp, EditorSceneImporter); + const String ASSIMP_FBX_KEY = "_$AssimpFbx$"; + + struct AssetImportAnimation { + enum Interpolation { + INTERP_LINEAR, + INTERP_STEP, + INTERP_CATMULLROMSPLINE, + INTERP_CUBIC_SPLINE + }; + }; + + struct AssetImportFbx { + enum ETimeMode { + TIME_MODE_DEFAULT = 0, + TIME_MODE_120 = 1, + TIME_MODE_100 = 2, + TIME_MODE_60 = 3, + TIME_MODE_50 = 4, + TIME_MODE_48 = 5, + TIME_MODE_30 = 6, + TIME_MODE_30_DROP = 7, + TIME_MODE_NTSC_DROP_FRAME = 8, + TIME_MODE_NTSC_FULL_FRAME = 9, + TIME_MODE_PAL = 10, + TIME_MODE_CINEMA = 11, + TIME_MODE_1000 = 12, + TIME_MODE_CINEMA_ND = 13, + TIME_MODE_CUSTOM = 14, + TIME_MODE_TIME_MODE_COUNT = 15 + }; + enum UpAxis { + UP_VECTOR_AXIS_X = 1, + UP_VECTOR_AXIS_Y = 2, + UP_VECTOR_AXIS_Z = 3 + }; + enum FrontAxis { + FRONT_PARITY_EVEN = 1, + FRONT_PARITY_ODD = 2, + }; + + enum CoordAxis { + COORD_RIGHT = 0, + COORD_LEFT = 1 + }; + }; + + struct ImportState { + + String path; + const aiScene *assimp_scene; + uint32_t max_bone_weights; + Spatial *root; + Map<String, Ref<Mesh> > mesh_cache; + Map<int, Ref<Material> > material_cache; + Map<String, int> light_cache; + Map<String, int> camera_cache; + Vector<Skeleton *> skeletons; + Map<String, int> bone_owners; //maps bones to skeleton index owned by + Map<String, Node *> node_map; + Map<MeshInstance *, Skeleton *> mesh_skeletons; + bool fbx; //for some reason assimp does some things different for FBX + AnimationPlayer *animation_player; + }; + + struct BoneInfo { + uint32_t bone; + float weight; + }; + + struct SkeletonHole { //nodes may be part of the skeleton by used by vertex + String name; + String parent; + Transform pose; + const aiNode *node; + }; + + const Transform _assimp_matrix_transform(const aiMatrix4x4 p_matrix); + String _assimp_get_string(const aiString p_string) const; + Transform _get_global_assimp_node_transform(const aiNode *p_current_node); + + void _calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w); + void _set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<Texture> texture); + void _find_texture_path(const String &p_path, String &path, bool &r_found); + void _find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension); + + Ref<Texture> _load_texture(ImportState &state, String p_path); + Ref<Material> _generate_material_from_index(ImportState &state, int p_index, bool p_double_sided); + Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, Skeleton *p_skeleton = NULL, bool p_double_sided_material = false); + void _generate_node(ImportState &state, const aiNode *p_assimp_node, Node *p_parent); + void _generate_bone_groups(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<String, Transform> &bind_xforms); + void _fill_node_relationships(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, int p_skeleton_id, Skeleton *p_skeleton, const String &p_parent_name, int &holecount, const Vector<SkeletonHole> &p_holes, const Map<String, Transform> &bind_xforms); + void _generate_skeletons(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, const Map<String, Transform> &bind_xforms); + + void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int p_track, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, Skeleton *p_skeleton, const NodePath &p_path, const String &p_name); + + void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps); + + Spatial *_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights); + + String _assimp_anim_string_to_string(const aiString p_string) const; + String _assimp_raw_string_to_string(const aiString p_string) const; + float _get_fbx_fps(int32_t time_mode, const aiScene *p_scene); + template <class T> + T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp); + void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const; + + struct ImportFormat { + Vector<String> extensions; + bool is_default; + }; + +protected: + static void _bind_methods(); + +public: + EditorSceneImporterAssimp() { + Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE); + unsigned int severity = Assimp::Logger::Info | Assimp::Logger::Err | Assimp::Logger::Warn; + Assimp::DefaultLogger::get()->attachStream(new AssimpStream(), severity); + } + ~EditorSceneImporterAssimp() { + Assimp::DefaultLogger::kill(); + } + + virtual void get_extensions(List<String> *r_extensions) const; + virtual uint32_t get_import_flags() const; + virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL); + virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps); +}; +#endif +#endif diff --git a/modules/assimp/godot_update_assimp.sh b/modules/assimp/godot_update_assimp.sh new file mode 100644 index 0000000000..dcf1e6d4a2 --- /dev/null +++ b/modules/assimp/godot_update_assimp.sh @@ -0,0 +1,261 @@ +rm -rf ../../thirdparty/assimp +cd ../../thirdparty/ +git clone https://github.com/assimp/assimp.git +cd assimp +rm -rf code/3DSExporter.h +rm -rf code/3DSLoader.h +rm -rf code/3MFXmlTags.h +rm -rf code/ABCImporter.h +rm -rf code/ACLoader.h +rm -rf code/AMFImporter_Macro.hpp +rm -rf code/ASELoader.h +rm -rf code/assbin_chunks.h +rm -rf code/AssbinExporter.h +rm -rf code/AssbinLoader.h +rm -rf code/AssimpCExport.cpp +rm -rf code/AssxmlExporter.h +rm -rf code/B3DImporter.h +# rm -rf code/BaseProcess.cpp +# rm -rf code/BaseProcess.h +# rm -rf code/Bitmap.cpp +rm -rf code/BlenderBMesh.cpp +rm -rf code/BlenderBMesh.h +rm -rf code/BlenderCustomData.cpp +rm -rf code/BlenderCustomData.h +rm -rf code/BlenderIntermediate.h +rm -rf code/BlenderLoader.h +rm -rf code/BlenderModifier.h +rm -rf code/BlenderSceneGen.h +rm -rf code/BlenderTessellator.h +rm -rf code/BVHLoader.h +rm -rf code/C4DImporter.h +# rm -rf code/CalcTangentsProcess.h +# rm -rf code/CInterfaceIOWrapper.cpp +# rm -rf code/CInterfaceIOWrapper.h +rm -rf code/COBLoader.h +rm -rf code/COBScene.h +rm -rf code/ColladaExporter.h +rm -rf code/ColladaLoader.h +# rm -rf code/ComputeUVMappingProcess.h +# rm -rf code/ConvertToLHProcess.h +# rm -rf code/CreateAnimMesh.cpp +rm -rf code/CSMLoader.h +rm -rf code/D3MFExporter.h +rm -rf code/D3MFImporter.h +rm -rf code/D3MFOpcPackage.h +# rm -rf code/DeboneProcess.h +# rm -rf code/DefaultIOStream.cpp +# rm -rf code/DefaultIOSystem.cpp +# rm -rf code/DefaultProgressHandler.h +# rm -rf code/DropFaceNormalsProcess.cpp +# rm -rf code/DropFaceNormalsProcess.h +rm -rf code/DXFHelper.h +rm -rf code/DXFLoader.h +# rm -rf code/EmbedTexturesProcess.cpp +# rm -rf code/EmbedTexturesProcess.h +# rm -rf code/FBXCommon.h +# rm -rf code/FBXCompileConfig.h +# rm -rf code/FBXDeformer.cpp +# rm -rf code/FBXDocumentUtil.cpp +# rm -rf code/FBXDocumentUtil.h +# rm -rf code/FBXExporter.h +# rm -rf code/FBXExportNode.h +# rm -rf code/FBXExportProperty.h +# rm -rf code/FBXImporter.cpp +# rm -rf code/FBXImporter.h +# rm -rf code/FBXImportSettings.h +# rm -rf code/FBXMeshGeometry.h +# rm -rf code/FBXModel.cpp +# rm -rf code/FBXNodeAttribute.cpp +# rm -rf code/FBXParser.h +# rm -rf code/FBXProperties.cpp +# rm -rf code/FBXProperties.h +# rm -rf code/FBXTokenizer.cpp +# rm -rf code/FBXTokenizer.h +# rm -rf code/FBXUtil.cpp +# rm -rf code/FBXUtil.h +# rm -rf code/FileLogStream.h +# rm -rf code/FindDegenerates.h +# rm -rf code/FindInstancesProcess.h +# rm -rf code/FindInvalidDataProcess.h +rm -rf code/FIReader.hpp +# rm -rf code/FixNormalsStep.cpp +# rm -rf code/FixNormalsStep.h +# rm -rf code/GenFaceNormalsProcess.cpp +# rm -rf code/GenFaceNormalsProcess.h +# rm -rf code/GenVertexNormalsProcess.cpp +# rm -rf code/GenVertexNormalsProcess.h +rm -rf code/glTF2Asset.h +rm -rf code/glTF2Asset.inl +rm -rf code/glTF2AssetWriter.inl +rm -rf code/glTF2Exporter.cpp +rm -rf code/glTF2Importer.cpp +rm -rf code/glTF2AssetWriter.h +rm -rf code/glTFAsset.h +rm -rf code/glTFAsset.inl +rm -rf code/glTFAssetWriter.inl +rm -rf code/glTFExporter.cpp +rm -rf code/glTFImporter.cpp +rm -rf code/glTF2Exporter.h +rm -rf code/glTF2Importer.h +rm -rf code/glTFAssetWriter.h +rm -rf code/glTFExporter.h +rm -rf code/glTFImporter.h +rm -rf code/HalfLifeFileData.h +rm -rf code/HMPFileData.h +rm -rf code/HMPLoader.h +rm -rf code/HMPLoader.cpp +rm -rf code/IFF.h +# rm -rf code/Importer.h +# rm -rf code/ImproveCacheLocality.h +rm -rf code/IRRLoader.h +rm -rf code/IRRMeshLoader.h +rm -rf code/IRRShared.h +# rm -rf code/JoinVerticesProcess.h +# rm -rf code/LimitBoneWeightsProcess.cpp +# rm -rf code/LimitBoneWeightsProcess.h +rm -rf code/LWSLoader.h +rm -rf code/makefile.mingw +# rm -rf code/MakeVerboseFormat.cpp +# rm -rf code/MakeVerboseFormat.h +# rm -rf code/MaterialSystem.h +rm -rf code/MD2FileData.h +rm -rf code/MD2Loader.h +rm -rf code/MD2NormalTable.h +rm -rf code/MD3FileData.h +rm -rf code/MD3Loader.h +rm -rf code/MD4FileData.h +rm -rf code/MD5Loader.h +rm -rf code/MD5Parser.cpp +rm -rf code/MDCFileData.h +rm -rf code/MDCLoader.h +rm -rf code/MDLDefaultColorMap.h +# rm -rf code/MMDCpp14.h +# rm -rf code/MMDImporter.h +rm -rf code/MS3DLoader.h +rm -rf code/NDOLoader.h +rm -rf code/NFFLoader.h +rm -rf code/ObjExporter.h +rm -rf code/ObjFileImporter.h +rm -rf code/ObjFileMtlImporter.h +rm -rf code/ObjFileParser.h +rm -rf code/ObjTools.h +rm -rf code/ObjExporter.cpp +rm -rf code/ObjFileImporter.cpp +rm -rf code/ObjFileMtlImporter.cpp +rm -rf code/ObjFileParser.cpp +rm -rf code/OFFLoader.h +rm -rf code/OFFLoader.cpp +rm -rf code/OgreImporter.cpp +rm -rf code/OgreImporter.h +rm -rf code/OgreParsingUtils.h +rm -rf code/OgreXmlSerializer.h +rm -rf code/OgreXmlSerializer.cpp +rm -rf code/OgreBinarySerializer.cpp +rm -rf code/OpenGEXExporter.cpp +rm -rf code/OpenGEXExporter.h +rm -rf code/OpenGEXImporter.h +rm -rf code/OpenGEXStructs.h +rm -rf code/OpenGEXImporter.cpp +# rm -rf code/OptimizeGraph.h +# rm -rf code/OptimizeMeshes.cpp +# rm -rf code/OptimizeMeshes.h +rm -rf code/PlyExporter.h +rm -rf code/PlyLoader.h +# rm -rf code/PolyTools.h +# rm -rf code/PostStepRegistry.cpp +# rm -rf code/PretransformVertices.h +rm -rf code/Q3BSPFileData.h +rm -rf code/Q3BSPFileImporter.h +rm -rf code/Q3BSPFileParser.cpp +rm -rf code/Q3BSPFileParser.h +rm -rf code/Q3BSPZipArchive.cpp +rm -rf code/Q3BSPZipArchive.h +rm -rf code/Q3DLoader.h +rm -rf code/Q3DLoader.cpp +rm -rf code/Q3BSPFileImporter.cpp +rm -rf code/RawLoader.h +# rm -rf code/RemoveComments.cpp +# rm -rf code/RemoveRedundantMaterials.cpp +# rm -rf code/RemoveRedundantMaterials.h +# rm -rf code/RemoveVCProcess.h +# rm -rf code/ScaleProcess.cpp +# rm -rf code/ScaleProcess.h +# rm -rf code/scene.cpp +# rm -rf code/ScenePreprocessor.cpp +# rm -rf code/ScenePreprocessor.h +# rm -rf code/ScenePrivate.h +# rm -rf code/SGSpatialSort.cpp +rm -rf code/SIBImporter.h +rm -rf code/SMDLoader.cpp +# rm -rf code/simd.cpp +# rm -rf code/simd.h +# rm -rf code/SortByPTypeProcess.h +# rm -rf code/SplitByBoneCountProcess.h +# rm -rf code/SplitLargeMeshes.h +# rm -rf code/StdOStreamLogStream.h +rm -rf code/StepExporter.h +rm -rf code/StepExporter.cpp +rm -rf code/STLExporter.cpp +rm -rf code/STLExporter.h +rm -rf code/STLLoader.h +rm -rf code/STLLoader.cpp +# rm -rf code/TargetAnimation.cpp +# rm -rf code/TargetAnimation.h +rm -rf code/TerragenLoader.h +rm -rf code/TerragenLoader.cpp +# rm -rf code/TextureTransform.h +# rm -rf code/TriangulateProcess.h +rm -rf code/UnrealLoader.h +# rm -rf code/ValidateDataStructure.h +# rm -rf code/Version.cpp +# rm -rf code/VertexTriangleAdjacency.cpp +# rm -rf code/VertexTriangleAdjacency.h +# rm -rf code/Win32DebugLogStream.h +rm -rf code/X3DImporter_Macro.hpp +rm -rf code/X3DImporter_Metadata.cpp +rm -rf code/X3DImporter_Networking.cpp +rm -rf code/X3DImporter_Texturing.cpp +rm -rf code/X3DImporter_Shape.cpp +rm -rf code/X3DImporter_Rendering.cpp +rm -rf code/X3DImporter_Postprocess.cpp +rm -rf code/X3DImporter_Light.cpp +rm -rf code/X3DImporter_Group.cpp +rm -rf code/X3DImporter_Geometry3D.cpp +rm -rf code/X3DImporter_Geometry2D.cpp +rm -rf code/X3DImporter.cpp +rm -rf code/X3DExporter.cpp +rm -rf code/X3DVocabulary.cpp +rm -rf code/XFileExporter.h +rm -rf code/XFileExporter.cpp +rm -rf code/XFileHelper.h +rm -rf code/XFileHelper.cpp +rm -rf code/XFileImporter.h +rm -rf code/XFileImporter.cpp +rm -rf code/XFileParser.h +rm -rf code/XFileParser.cpp +rm -rf code/XGLLoader.h +rm -rf code/XGLLoader.cpp +rm -rf code/Importer +rm -rf .git +rm -rf cmake-modules +rm -rf doc +rm -rf packaging +rm -rf port +rm -rf samples +rm -rf scripts +rm -rf test +rm -rf tools +rm -rf contrib/zlib +rm -rf contrib/android-cmake +rm -rf contrib/gtest +rm -rf contrib/clipper +rm -rf contrib/irrXML +rm -rf contrib/Open3DGC +rm -rf contrib/openddlparser +rm -rf contrib/poly2tri +rm -rf contrib/rapidjson +rm -rf contrib/unzip +rm -rf contrib/zip +rm -rf contrib/stb_image +rm .travis* diff --git a/modules/assimp/register_types.cpp b/modules/assimp/register_types.cpp new file mode 100644 index 0000000000..2e8181372e --- /dev/null +++ b/modules/assimp/register_types.cpp @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "editor/editor_node.h" +#include "editor_scene_importer_assimp.h" + +#ifdef TOOLS_ENABLED +static void _editor_init() { + Ref<EditorSceneImporterAssimp> import_assimp; + import_assimp.instance(); + ResourceImporterScene::get_singleton()->add_importer(import_assimp); +} +#endif + +void register_assimp_types() { + +#ifdef TOOLS_ENABLED + ClassDB::APIType prev_api = ClassDB::get_current_api(); + ClassDB::set_current_api(ClassDB::API_EDITOR); + + ClassDB::register_class<EditorSceneImporterAssimp>(); + + ClassDB::set_current_api(prev_api); + + EditorNode::add_init_callback(_editor_init); +#endif +} + +void unregister_assimp_types() { +} diff --git a/modules/thekla_unwrap/register_types.h b/modules/assimp/register_types.h index b911eed622..f841cd26b2 100644 --- a/modules/thekla_unwrap/register_types.h +++ b/modules/assimp/register_types.h @@ -28,5 +28,5 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -void register_thekla_unwrap_types(); -void unregister_thekla_unwrap_types(); +void register_assimp_types(); +void unregister_assimp_types(); diff --git a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml index 1f91349f32..078bcc45a8 100644 --- a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml +++ b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/bullet/doc_classes/BulletPhysicsServer.xml b/modules/bullet/doc_classes/BulletPhysicsServer.xml index 8adc659b2c..2a37f6af5e 100644 --- a/modules/bullet/doc_classes/BulletPhysicsServer.xml +++ b/modules/bullet/doc_classes/BulletPhysicsServer.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index 1aba31f03d..b590d63167 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -510,16 +510,17 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) { // Compute min and max heights if not specified. if (!d.has("min_height") && !d.has("max_height")) { - PoolVector<real_t>::Read r = heights.read(); - int heights_size = heights.size(); + PoolVector<real_t>::Read r = l_heights.read(); + int heights_size = l_heights.size(); for (int i = 0; i < heights_size; ++i) { real_t h = r[i]; - if (h < l_min_height) + if (h < l_min_height) { l_min_height = h; - else if (h > l_max_height) + } else if (h > l_max_height) { l_max_height = h; + } } } diff --git a/modules/csg/doc_classes/CSGBox.xml b/modules/csg/doc_classes/CSGBox.xml index 1684850f0a..e508468415 100644 --- a/modules/csg/doc_classes/CSGBox.xml +++ b/modules/csg/doc_classes/CSGBox.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/csg/doc_classes/CSGCombiner.xml b/modules/csg/doc_classes/CSGCombiner.xml index 819a4a3a22..51428b25f8 100644 --- a/modules/csg/doc_classes/CSGCombiner.xml +++ b/modules/csg/doc_classes/CSGCombiner.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/csg/doc_classes/CSGCylinder.xml b/modules/csg/doc_classes/CSGCylinder.xml index 50a88d6773..9adcbc4502 100644 --- a/modules/csg/doc_classes/CSGCylinder.xml +++ b/modules/csg/doc_classes/CSGCylinder.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/csg/doc_classes/CSGMesh.xml b/modules/csg/doc_classes/CSGMesh.xml index fc9815d7c0..c1f04d724a 100644 --- a/modules/csg/doc_classes/CSGMesh.xml +++ b/modules/csg/doc_classes/CSGMesh.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/csg/doc_classes/CSGPolygon.xml b/modules/csg/doc_classes/CSGPolygon.xml index ae75f7e01b..931c7cba51 100644 --- a/modules/csg/doc_classes/CSGPolygon.xml +++ b/modules/csg/doc_classes/CSGPolygon.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/csg/doc_classes/CSGPrimitive.xml b/modules/csg/doc_classes/CSGPrimitive.xml index 502a8230e4..869e4006fe 100644 --- a/modules/csg/doc_classes/CSGPrimitive.xml +++ b/modules/csg/doc_classes/CSGPrimitive.xml @@ -7,8 +7,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/csg/doc_classes/CSGShape.xml b/modules/csg/doc_classes/CSGShape.xml index ccfc5a04c0..152a8cd3bc 100644 --- a/modules/csg/doc_classes/CSGShape.xml +++ b/modules/csg/doc_classes/CSGShape.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_collision_layer_bit" qualifiers="const"> <return type="bool"> diff --git a/modules/csg/doc_classes/CSGSphere.xml b/modules/csg/doc_classes/CSGSphere.xml index 088c9f14eb..5fe55996e5 100644 --- a/modules/csg/doc_classes/CSGSphere.xml +++ b/modules/csg/doc_classes/CSGSphere.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/csg/doc_classes/CSGTorus.xml b/modules/csg/doc_classes/CSGTorus.xml index 946637bd2c..e438b9e405 100644 --- a/modules/csg/doc_classes/CSGTorus.xml +++ b/modules/csg/doc_classes/CSGTorus.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index 0a94690989..059c06c37c 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -432,7 +432,8 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, } break; - default: {} + default: { + } } wb = PoolVector<uint8_t>::Write(); diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index c1bec533dd..cecf08e9a5 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -10,8 +10,6 @@ <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link> <link>http://enet.bespin.org/usergroup0.html</link> </tutorials> - <demos> - </demos> <methods> <method name="close_connection"> <return type="void"> diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 000917507a..193f1bb48d 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -690,7 +690,9 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer * case COMPRESS_ZSTD: { mode = Compression::MODE_ZSTD; } break; - default: { ERR_FAIL_V(0); } + default: { + ERR_FAIL_V(0); + } } int req_size = Compression::get_max_compressed_buffer_size(ofs, mode); @@ -727,7 +729,8 @@ size_t NetworkedMultiplayerENet::enet_decompress(void *context, const enet_uint8 ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_ZSTD); } break; - default: {} + default: { + } } if (ret < 0) { return 0; diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp index 5410d367db..6f54436bf9 100644 --- a/modules/etc/image_etc.cpp +++ b/modules/etc/image_etc.cpp @@ -115,7 +115,8 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f case Image::FORMAT_RGBA5551: { detected_channels = Image::DETECTED_RGBA; } break; - default: {} + default: { + } } } diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.cpp b/modules/gdnative/arvr/arvr_interface_gdnative.cpp index 9cd37ac950..8c602e0cba 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.cpp +++ b/modules/gdnative/arvr/arvr_interface_gdnative.cpp @@ -198,6 +198,17 @@ CameraMatrix ARVRInterfaceGDNative::get_projection_for_eye(ARVRInterface::Eyes p return cm; } +unsigned int ARVRInterfaceGDNative::get_external_texture_for_eye(ARVRInterface::Eyes p_eye) { + + ERR_FAIL_COND_V(interface == NULL, 0); + + if ((interface->version.major > 1) || ((interface->version.major) == 1 && (interface->version.minor >= 1))) { + return (unsigned int)interface->get_external_texture_for_eye(data, (godot_int)p_eye); + } else { + return 0; + } +} + void ARVRInterfaceGDNative::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { ERR_FAIL_COND(interface == NULL); diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.h b/modules/gdnative/arvr/arvr_interface_gdnative.h index 015d0c8a2a..3f966ece51 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.h +++ b/modules/gdnative/arvr/arvr_interface_gdnative.h @@ -78,6 +78,7 @@ public: // and a CameraMatrix version to ARVRServer virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + virtual unsigned int get_external_texture_for_eye(ARVRInterface::Eyes p_eye); virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); virtual void process(); diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index a36e76287a..fde7f1a6e0 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -16,6 +16,7 @@ def get_doc_classes(): "ResourceFormatLoaderVideoStreamGDNative", "StreamPeerGDNative", "VideoStreamGDNative", + "WebRTCPeerGDNative", ] def get_doc_path(): diff --git a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml index afb014d608..efdb948660 100644 --- a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml +++ b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/GDNative.xml b/modules/gdnative/doc_classes/GDNative.xml index e5a59aad07..8750ddc56d 100644 --- a/modules/gdnative/doc_classes/GDNative.xml +++ b/modules/gdnative/doc_classes/GDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="call_native"> <return type="Variant"> diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index ba5278d440..8bfd386b8d 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_current_dependencies" qualifiers="const"> <return type="PoolStringArray"> diff --git a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml index ba481a6d6e..b9a01672a6 100644 --- a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml +++ b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml index c50f9eee22..ac8b793b22 100644 --- a/modules/gdnative/doc_classes/NativeScript.xml +++ b/modules/gdnative/doc_classes/NativeScript.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_class_documentation" qualifiers="const"> <return type="String"> diff --git a/modules/gdnative/doc_classes/PacketPeerGDNative.xml b/modules/gdnative/doc_classes/PacketPeerGDNative.xml index f4d7d22f5b..acfb597cff 100644 --- a/modules/gdnative/doc_classes/PacketPeerGDNative.xml +++ b/modules/gdnative/doc_classes/PacketPeerGDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/PluginScript.xml b/modules/gdnative/doc_classes/PluginScript.xml index 8510708124..b07122bbdf 100644 --- a/modules/gdnative/doc_classes/PluginScript.xml +++ b/modules/gdnative/doc_classes/PluginScript.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="new" qualifiers="vararg"> <return type="Object"> diff --git a/modules/gdnative/doc_classes/ResourceFormatLoaderVideoStreamGDNative.xml b/modules/gdnative/doc_classes/ResourceFormatLoaderVideoStreamGDNative.xml index 8e7f4698ff..cd8b336778 100644 --- a/modules/gdnative/doc_classes/ResourceFormatLoaderVideoStreamGDNative.xml +++ b/modules/gdnative/doc_classes/ResourceFormatLoaderVideoStreamGDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/StreamPeerGDNative.xml b/modules/gdnative/doc_classes/StreamPeerGDNative.xml index eddebf4889..f7e0d76fdb 100644 --- a/modules/gdnative/doc_classes/StreamPeerGDNative.xml +++ b/modules/gdnative/doc_classes/StreamPeerGDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/VideoStreamGDNative.xml b/modules/gdnative/doc_classes/VideoStreamGDNative.xml index d5c5ed7ccf..ed7678b7be 100644 --- a/modules/gdnative/doc_classes/VideoStreamGDNative.xml +++ b/modules/gdnative/doc_classes/VideoStreamGDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_file"> <return type="String"> diff --git a/modules/gdnative/doc_classes/WebRTCPeerGDNative.xml b/modules/gdnative/doc_classes/WebRTCPeerGDNative.xml new file mode 100644 index 0000000000..478889e031 --- /dev/null +++ b/modules/gdnative/doc_classes/WebRTCPeerGDNative.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCPeerGDNative" inherits="WebRTCPeer" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 71e7eecd1d..9882a89794 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -6427,7 +6427,24 @@ "major": 3, "minor": 1 }, - "next": null, + "next": { + "type": "NET", + "version": { + "major": 3, + "minor": 2 + }, + "next": null, + "api": [ + { + "name": "godot_net_bind_webrtc_peer", + "return_type": "void", + "arguments": [ + ["godot_object *", "p_obj"], + ["const godot_net_webrtc_peer *", "p_interface"] + ] + } + ] + }, "api": [ { "name": "godot_net_bind_stream_peer", diff --git a/modules/gdnative/include/arvr/godot_arvr.h b/modules/gdnative/include/arvr/godot_arvr.h index 0d14f3743f..321b471d0e 100644 --- a/modules/gdnative/include/arvr/godot_arvr.h +++ b/modules/gdnative/include/arvr/godot_arvr.h @@ -42,7 +42,7 @@ extern "C" { // Use these to populate version in your plugin #define GODOTVR_API_MAJOR 1 -#define GODOTVR_API_MINOR 0 +#define GODOTVR_API_MINOR 1 typedef struct { godot_gdnative_api_version version; /* version of our API */ @@ -61,6 +61,8 @@ typedef struct { void (*fill_projection_for_eye)(void *, godot_real *, godot_int, godot_real, godot_real, godot_real); void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *); void (*process)(void *); + // only in 1.1 onwards + godot_int (*get_external_texture_for_eye)(void *, godot_int); } godot_arvr_interface_gdnative; void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_interface); diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h index d7de04e725..c1bc9daab5 100644 --- a/modules/gdnative/include/net/godot_net.h +++ b/modules/gdnative/include/net/godot_net.h @@ -111,6 +111,35 @@ typedef struct { /* Binds a MultiplayerPeerGDNative to the provided interface */ void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *); +typedef struct { + godot_gdnative_api_version version; /* version of our API */ + + godot_object *data; /* User reference */ + + /* This is PacketPeer */ + godot_error (*get_packet)(void *, const uint8_t **, int *); + godot_error (*put_packet)(void *, const uint8_t *, int); + godot_int (*get_available_packet_count)(const void *); + godot_int (*get_max_packet_size)(const void *); + + /* This is WebRTCPeer */ + void (*set_write_mode)(void *, godot_int); + godot_int (*get_write_mode)(const void *); + bool (*was_string_packet)(const void *); + godot_int (*get_connection_state)(const void *); + + godot_error (*create_offer)(void *); + godot_error (*set_remote_description)(void *, const char *, const char *); + godot_error (*set_local_description)(void *, const char *, const char *); + godot_error (*add_ice_candidate)(void *, const char *, int, const char *); + godot_error (*poll)(void *); + + void *next; /* For extension? */ +} godot_net_webrtc_peer; + +/* Binds a PacketPeerGDNative to the provided interface */ +void GDAPI godot_net_bind_webrtc_peer(godot_object *p_obj, const godot_net_webrtc_peer *); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 7eb4294732..4577c7327a 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -323,6 +323,9 @@ List<ClassAPI> generate_c_api_classes() { arg_type = "Variant"; } else if (arg_info.type == Variant::OBJECT) { arg_type = arg_info.class_name; + if (arg_type == "") { + arg_type = Variant::get_type_name(arg_info.type); + } } else { arg_type = Variant::get_type_name(arg_info.type); } diff --git a/modules/gdnative/net/SCsub b/modules/gdnative/net/SCsub index e915703935..18ab9986b0 100644 --- a/modules/gdnative/net/SCsub +++ b/modules/gdnative/net/SCsub @@ -3,5 +3,11 @@ Import('env') Import('env_gdnative') -env_gdnative.add_source_files(env.modules_sources, '*.cpp') +env_net = env_gdnative.Clone() + +has_webrtc = env_net["module_webrtc_enabled"] +if has_webrtc: + env_net.Append(CPPDEFINES=['WEBRTC_GDNATIVE_ENABLED']) + +env_net.add_source_files(env.modules_sources, '*.cpp') diff --git a/drivers/gl_context/context_gl.h b/modules/gdnative/net/webrtc_peer_gdnative.cpp index 890683b7c5..60b1ed4fe4 100644 --- a/drivers/gl_context/context_gl.h +++ b/modules/gdnative/net/webrtc_peer_gdnative.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* context_gl.h */ +/* packet_peer_gdnative.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,39 +28,18 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CONTEXT_GL_H -#define CONTEXT_GL_H - -#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) - -#include "core/typedefs.h" - -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - -class ContextGL { - - static ContextGL *singleton; - -public: - static ContextGL *get_singleton(); - - virtual void release_current() = 0; - - virtual void make_current() = 0; - - virtual void swap_buffers() = 0; - - virtual Error initialize() = 0; - - virtual void set_use_vsync(bool p_use) = 0; - virtual bool is_using_vsync() const = 0; - - ContextGL(); - virtual ~ContextGL(); -}; +#include "modules/gdnative/gdnative.h" +#include "modules/gdnative/include/net/godot_net.h" +#ifdef WEBRTC_GDNATIVE_ENABLED +#include "modules/webrtc/webrtc_peer_gdnative.h" #endif +extern "C" { + +void GDAPI godot_net_bind_webrtc_peer(godot_object *p_obj, const godot_net_webrtc_peer *p_impl) { +#ifdef WEBRTC_GDNATIVE_ENABLED + ((WebRTCPeerGDNative *)p_obj)->set_native_webrtc_peer(p_impl); #endif +} +} diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp index 3450a032c5..8dbbd2e4eb 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.cpp +++ b/modules/gdnative/pluginscript/pluginscript_script.cpp @@ -34,7 +34,7 @@ #include "pluginscript_instance.h" #include "pluginscript_script.h" -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED #define __ASSERT_SCRIPT_REASON "Cannot retrieve pluginscript class for this script, is you code correct ?" #define ASSERT_SCRIPT_VALID() \ { \ diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 46796c68eb..d606a41fab 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -10,8 +10,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_as_byte_code" qualifiers="const"> <return type="PoolByteArray"> diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml index f38f39b612..690953108f 100644 --- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml +++ b/modules/gdscript/doc_classes/GDScriptFunctionState.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="is_valid" qualifiers="const"> <return type="bool"> diff --git a/modules/gdscript/doc_classes/GDScriptNativeClass.xml b/modules/gdscript/doc_classes/GDScriptNativeClass.xml index e86b69c31c..70583d47a7 100644 --- a/modules/gdscript/doc_classes/GDScriptNativeClass.xml +++ b/modules/gdscript/doc_classes/GDScriptNativeClass.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="new"> <return type="Variant"> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 4385cf12ad..eada389c51 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -597,7 +597,7 @@ Error GDScript::reload(bool p_keep_state) { return err; } } -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) { const GDScriptWarning &warning = E->get(); if (ScriptDebugger::get_singleton()) { @@ -1945,6 +1945,10 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(1); return "The local variable '" + symbols[0] + "' is declared but never used in the block."; } break; + case SHADOWED_VARIABLE: { + CHECK_SYMBOLS(2); + return "The local variable '" + symbols[0] + "' is shadowing an already defined variable at line " + symbols[1] + "."; + } break; case UNUSED_CLASS_VARIABLE: { CHECK_SYMBOLS(1); return "The class variable '" + symbols[0] + "' is declared but never used in the script."; @@ -2048,6 +2052,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "UNASSIGNED_VARIABLE", "UNASSIGNED_VARIABLE_OP_ASSIGN", "UNUSED_VARIABLE", + "SHADOWED_VARIABLE", "UNUSED_CLASS_VARIABLE", "UNUSED_ARGUMENT", "UNREACHABLE_CODE", diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index ded873c7d3..1d75d9e2fe 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -273,6 +273,7 @@ struct GDScriptWarning { UNASSIGNED_VARIABLE, // Variable used but never assigned UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc) UNUSED_VARIABLE, // Local variable is declared but never used + SHADOWED_VARIABLE, // Variable name shadowed by other variable UNUSED_CLASS_VARIABLE, // Class variable is declared but never used in the file UNUSED_ARGUMENT, // Function argument is never used UNREACHABLE_CODE, // Code after a return statement @@ -444,6 +445,7 @@ public: virtual void get_reserved_words(List<String> *p_words) const; virtual void get_comment_delimiters(List<String> *p_delimiters) const; virtual void get_string_delimiters(List<String> *p_delimiters) const; + virtual String _get_processed_template(const String &p_template, const String &p_base_class_name) const; virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index ae67521749..f7be0ce37c 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -480,16 +480,16 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: switch (cast_type.kind) { case GDScriptDataType::BUILTIN: { codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN); - codegen.opcodes.push_back(cn->cast_type.builtin_type); + codegen.opcodes.push_back(cast_type.builtin_type); } break; case GDScriptDataType::NATIVE: { int class_idx; - if (GDScriptLanguage::get_singleton()->get_global_map().has(cn->cast_type.native_type)) { + if (GDScriptLanguage::get_singleton()->get_global_map().has(cast_type.native_type)) { - class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cn->cast_type.native_type]; + class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cast_type.native_type]; class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root) } else { - _set_error("Invalid native class type '" + String(cn->cast_type.native_type) + "'.", cn); + _set_error("Invalid native class type '" + String(cast_type.native_type) + "'.", cn); return -1; } codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator @@ -498,7 +498,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: case GDScriptDataType::SCRIPT: case GDScriptDataType::GDSCRIPT: { - Variant script = cn->cast_type.script_type; + Variant script = cast_type.script_type; int idx = codegen.get_constant_pos(script); idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access) @@ -1863,6 +1863,19 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->base = base; p_script->_base = base.ptr(); p_script->member_indices = base->member_indices; + + if (p_class->base_type.kind == GDScriptParser::DataType::CLASS) { + if (!parsed_classes.has(p_script->_base)) { + if (parsing_classes.has(p_script->_base)) { + _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class); + return ERR_PARSE_ERROR; + } + Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state); + if (err) { + return err; + } + } + } } break; default: { _set_error("Parser bug: invalid inheritance.", p_class); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index fafc73b7e6..df8fc2267d 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -44,12 +44,43 @@ void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const p_delimiters->push_back("#"); } + void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("\" \""); p_delimiters->push_back("' '"); p_delimiters->push_back("\"\"\" \"\"\""); } + +String GDScriptLanguage::_get_processed_template(const String &p_template, const String &p_base_class_name) const { + + String processed_template = p_template; + +#ifdef TOOLS_ENABLED + if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { + processed_template = processed_template.replace("%INT_TYPE%", ": int"); + processed_template = processed_template.replace("%STRING_TYPE%", ": String"); + processed_template = processed_template.replace("%FLOAT_TYPE%", ": float"); + processed_template = processed_template.replace("%VOID_RETURN%", " -> void"); + } else { + processed_template = processed_template.replace("%INT_TYPE%", ""); + processed_template = processed_template.replace("%STRING_TYPE%", ""); + processed_template = processed_template.replace("%FLOAT_TYPE%", ""); + processed_template = processed_template.replace("%VOID_RETURN%", ""); + } +#else + processed_template = processed_template.replace("%INT_TYPE%", ""); + processed_template = processed_template.replace("%STRING_TYPE%", ""); + processed_template = processed_template.replace("%FLOAT_TYPE%", ""); + processed_template = processed_template.replace("%VOID_RETURN%", ""); +#endif + + processed_template = processed_template.replace("%BASE%", p_base_class_name); + processed_template = processed_template.replace("%TS%", _get_indentation()); + + return processed_template; +} + Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { String _template = "extends %BASE%\n" "\n" @@ -65,27 +96,7 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str "#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:\n" "#%TS%pass\n"; -#ifdef TOOLS_ENABLED - if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { - _template = _template.replace("%INT_TYPE%", ": int"); - _template = _template.replace("%STRING_TYPE%", ": String"); - _template = _template.replace("%FLOAT_TYPE%", ": float"); - _template = _template.replace("%VOID_RETURN%", " -> void"); - } else { - _template = _template.replace("%INT_TYPE%", ""); - _template = _template.replace("%STRING_TYPE%", ""); - _template = _template.replace("%FLOAT_TYPE%", ""); - _template = _template.replace("%VOID_RETURN%", ""); - } -#else - _template = _template.replace("%INT_TYPE%", ""); - _template = _template.replace("%STRING_TYPE%", ""); - _template = _template.replace("%FLOAT_TYPE%", ""); - _template = _template.replace("%VOID_RETURN%", ""); -#endif - - _template = _template.replace("%BASE%", p_base_class_name); - _template = _template.replace("%TS%", _get_indentation()); + _template = _get_processed_template(_template, p_base_class_name); Ref<GDScript> script; script.instance(); @@ -101,10 +112,8 @@ bool GDScriptLanguage::is_using_templates() { void GDScriptLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { - String src = p_script->get_source_code(); - src = src.replace("%BASE%", p_base_class_name); - src = src.replace("%TS%", _get_indentation()); - p_script->set_source_code(src); + String _template = _get_processed_template(p_script->get_source_code(), p_base_class_name); + p_script->set_source_code(_template); } bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const { @@ -1061,7 +1070,8 @@ static bool _guess_expression_type(GDScriptCompletionContext &p_context, const G case GDScriptParser::OperatorNode::OP_BIT_AND: vop = Variant::OP_BIT_AND; break; case GDScriptParser::OperatorNode::OP_BIT_OR: vop = Variant::OP_BIT_OR; break; case GDScriptParser::OperatorNode::OP_BIT_XOR: vop = Variant::OP_BIT_XOR; break; - default: {} + default: { + } } if (vop == Variant::OP_MAX) { @@ -1116,7 +1126,8 @@ static bool _guess_expression_type(GDScriptCompletionContext &p_context, const G } break; } } break; - default: {} + default: { + } } // It may have found a null, but that's never useful @@ -3371,7 +3382,8 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol return OK; } } break; - default: {} + default: { + } } return ERR_CANT_RESOLVE; diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 46c9efd54f..4fd136d5cc 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -74,6 +74,7 @@ const char *GDScriptFunctions::get_func_name(Function p_func) { "lerp", "inverse_lerp", "range_lerp", + "smoothstep", "dectime", "randomize", "randi", @@ -369,6 +370,13 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ VALIDATE_ARG_NUM(4); r_ret = Math::range_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2], (double)*p_args[3], (double)*p_args[4]); } break; + case MATH_SMOOTHSTEP: { + VALIDATE_ARG_COUNT(3); + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + r_ret = Math::smoothstep((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); + } break; case MATH_DECTIME: { VALIDATE_ARG_COUNT(3); VALIDATE_ARG_NUM(0); @@ -1435,6 +1443,7 @@ bool GDScriptFunctions::is_deterministic(Function p_func) { case MATH_LERP: case MATH_INVERSE_LERP: case MATH_RANGE_LERP: + case MATH_SMOOTHSTEP: case MATH_DECTIME: case MATH_DEG2RAD: case MATH_RAD2DEG: @@ -1618,6 +1627,11 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { mi.return_val.type = Variant::REAL; return mi; } break; + case MATH_SMOOTHSTEP: { + MethodInfo mi("smoothstep", PropertyInfo(Variant::REAL, "from"), PropertyInfo(Variant::REAL, "to"), PropertyInfo(Variant::REAL, "weight")); + mi.return_val.type = Variant::REAL; + return mi; + } break; case MATH_DECTIME: { MethodInfo mi("dectime", PropertyInfo(Variant::REAL, "value"), PropertyInfo(Variant::REAL, "amount"), PropertyInfo(Variant::REAL, "step")); mi.return_val.type = Variant::REAL; diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h index fcb8f32e54..14bf3d7560 100644 --- a/modules/gdscript/gdscript_functions.h +++ b/modules/gdscript/gdscript_functions.h @@ -65,6 +65,7 @@ public: MATH_LERP, MATH_INVERSE_LERP, MATH_RANGE_LERP, + MATH_SMOOTHSTEP, MATH_DECTIME, MATH_RANDOMIZE, MATH_RAND, diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 6b7379d350..80af094c2c 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -777,7 +777,8 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } _add_warning(GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, -1, identifier.operator String()); } - } // fallthrough + FALLTHROUGH; + } case GDScriptTokenizer::TK_OP_ASSIGN: { lv->assignments += 1; lv->usages--; // Assignment is not really usage @@ -887,7 +888,8 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s case GDScriptTokenizer::TK_OP_SUB: e.op = OperatorNode::OP_NEG; break; case GDScriptTokenizer::TK_OP_NOT: e.op = OperatorNode::OP_NOT; break; case GDScriptTokenizer::TK_OP_BIT_INVERT: e.op = OperatorNode::OP_BIT_INVERT; break; - default: {} + default: { + } } tokenizer->advance(); @@ -1874,7 +1876,9 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to } } break; - default: { break; } + default: { + break; + } } //now se if all are constants if (!all_constants) @@ -1987,7 +1991,9 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to return op->arguments[2]; } } break; - default: { ERR_FAIL_V(op); } + default: { + ERR_FAIL_V(op); + } } ERR_FAIL_V(op); @@ -3494,6 +3500,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("'class_name' is only valid for the main class namespace."); return; } + if (self_path.empty()) { + _set_error("'class_name' not allowed in built-in scripts."); + return; + } if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) { _set_error("'class_name' syntax: 'class_name <UniqueName>'"); @@ -3636,7 +3646,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - }; //fallthrough to function + FALLTHROUGH; + } case GDScriptTokenizer::TK_PR_FUNCTION: { bool _static = false; @@ -3679,6 +3690,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_VARIABLE, -1, name); } } + for (int i = 0; i < p_class->subclasses.size(); i++) { + if (p_class->subclasses[i]->name == name) { + _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_CONSTANT, -1, name); + } + } #endif // DEBUG_ENABLED if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) { @@ -4086,7 +4102,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { break; } - }; //fallthrough to use the same + FALLTHROUGH; + } case Variant::REAL: { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "EASE") { @@ -4511,6 +4528,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { #ifdef DEBUG_ENABLED _add_warning(GDScriptWarning::DEPRECATED_KEYWORD, tokenizer->get_token_line(), "slave", "puppet"); #endif + FALLTHROUGH; case GDScriptTokenizer::TK_PR_PUPPET: { //may be fallthrough from export, ignore if so @@ -4578,7 +4596,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { continue; } break; case GDScriptTokenizer::TK_PR_VAR: { - //variale declaration and (eventual) initialization + // variable declaration and (eventual) initialization ClassNode::Member member; @@ -4621,6 +4639,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } } + + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == member.identifier) { + _set_error("A class named '" + String(member.identifier) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } #ifdef DEBUG_ENABLED for (int i = 0; i < current_class->functions.size(); i++) { if (current_class->functions[i]->name == member.identifier) { @@ -4865,6 +4890,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == const_id) { + _set_error("A class named '" + String(const_id) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + tokenizer->advance(); if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) { @@ -4935,6 +4967,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == enum_name) { + _set_error("A class named '" + String(enum_name) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + tokenizer->advance(); } if (tokenizer->get_token() != GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) { @@ -5020,6 +5059,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == const_id) { + _set_error("A class named '" + String(const_id) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + ClassNode::Constant constant; constant.type.has_type = true; constant.type.kind = DataType::BUILTIN; @@ -5290,7 +5336,8 @@ String GDScriptParser::DataType::to_string() const { if (!gds_class.empty()) { return gds_class; } - } // fallthrough + FALLTHROUGH; + } case SCRIPT: { if (is_meta_type) { return script_type->get_class_name().operator String(); @@ -6346,7 +6393,8 @@ 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: {} + default: { + } } } if (error) { @@ -6464,7 +6512,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } } } break; - default: {} + default: { + } } p_node->set_datatype(_resolve_type(node_type, p_node->line)); @@ -7609,6 +7658,11 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { if (p_function->arguments_usage[i] == 0 && !p_function->arguments[i].operator String().begins_with("_")) { _add_warning(GDScriptWarning::UNUSED_ARGUMENT, p_function->line, p_function->name, p_function->arguments[i].operator String()); } + for (int j = 0; j < current_class->variables.size(); j++) { + if (current_class->variables[j].identifier == p_function->arguments[i]) { + _add_warning(GDScriptWarning::SHADOWED_VARIABLE, p_function->line, p_function->arguments[i], itos(current_class->variables[j].line)); + } + } #endif // DEBUG_ENABLED } @@ -7682,6 +7736,17 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { p_function->return_type.has_type = false; p_function->return_type.may_yield = true; } + +#ifdef DEBUG_ENABLED + for (Map<StringName, LocalVarNode *>::Element *E = p_function->body->variables.front(); E; E = E->next()) { + LocalVarNode *lv = E->get(); + for (int i = 0; i < current_class->variables.size(); i++) { + if (current_class->variables[i].identifier == lv->name) { + _add_warning(GDScriptWarning::SHADOWED_VARIABLE, lv->line, lv->name, itos(current_class->variables[i].line)); + } + } + } +#endif // DEBUG_ENABLED } void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) { @@ -8046,7 +8111,8 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { if (cn->value.get_type() == Variant::STRING) { break; } - } // falthrough + FALLTHROUGH; + } default: { _mark_line_as_safe(statement->line); _reduce_node_type(statement); // Test for safety anyway diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 8962e3bb34..36503af4d7 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -339,7 +339,8 @@ StringName GDScriptTokenizer::get_token_literal(int p_offset) const { return "null"; case Variant::BOOL: return value ? "true" : "false"; - default: {} + default: { + } } } case TK_OP_AND: @@ -534,13 +535,14 @@ void GDScriptTokenizerText::_advance() { } } #ifdef DEBUG_ENABLED - if (comment.begins_with("#warning-ignore:")) { - String code = comment.get_slice(":", 1); + String comment_content = comment.trim_prefix("#").trim_prefix(" "); + if (comment_content.begins_with("warning-ignore:")) { + String code = comment_content.get_slice(":", 1); warning_skips.push_back(Pair<int, String>(line, code.strip_edges().to_lower())); - } else if (comment.begins_with("#warning-ignore-all:")) { - String code = comment.get_slice(":", 1); + } else if (comment_content.begins_with("warning-ignore-all:")) { + String code = comment_content.get_slice(":", 1); warning_global_skips.insert(code.strip_edges().to_lower()); - } else if (comment.strip_edges() == "#warnings-disable") { + } else if (comment_content.strip_edges() == "warnings-disable") { ignore_warnings = true; } #endif // DEBUG_ENABLED @@ -744,7 +746,7 @@ void GDScriptTokenizerText::_advance() { } INCPOS(1); is_node_path = true; - + FALLTHROUGH; case '\'': case '"': { @@ -1302,7 +1304,8 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) ERR_FAIL_V(Vector<uint8_t>()); } break; - default: {} + default: { + } }; token_array.push_back(token); diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 655be4eb20..f8f9fc1af9 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -12,8 +12,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index 0a75ad4784..8876bcbe9d 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -5,12 +5,16 @@ </brief_description> <description> This is a generic mobile VR implementation where you need to provide details about the phone and HMD used. It does not rely on any existing framework. This is the most basic interface we have. For the best effect you do need a mobile phone with a gyroscope and accelerometer. - Note that even though there is no positional tracking the camera will assume the headset is at a height of 1.85 meters. + Note that even though there is no positional tracking the camera will assume the headset is at a height of 1.85 meters, you can change this by setting [member eye_height]. + You can initialise this interface as follows: + [codeblock] + var interface = ARVRServer.find_interface("Native mobile") + if interface and interface.initialize(): + get_viewport().arvr = true + [/codeblock] </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -20,6 +24,9 @@ <member name="display_width" type="float" setter="set_display_width" getter="get_display_width"> The width of the display in centimeters. </member> + <member name="eye_height" type="float" setter="set_eye_height" getter="get_eye_height"> + The height at which the camera is placed in relation to the ground (i.e. [ARVROrigin] node). + </member> <member name="iod" type="float" setter="set_iod" getter="get_iod"> The interocular distance, also known as the interpupillary distance. The distance between the pupils of the left and right eye. </member> diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index b4fbd417d7..dc7ed03548 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -200,6 +200,9 @@ void MobileVRInterface::set_position_from_sensors() { }; void MobileVRInterface::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_eye_height", "eye_height"), &MobileVRInterface::set_eye_height); + ClassDB::bind_method(D_METHOD("get_eye_height"), &MobileVRInterface::get_eye_height); + ClassDB::bind_method(D_METHOD("set_iod", "iod"), &MobileVRInterface::set_iod); ClassDB::bind_method(D_METHOD("get_iod"), &MobileVRInterface::get_iod); @@ -218,6 +221,7 @@ void MobileVRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("set_k2", "k"), &MobileVRInterface::set_k2); ClassDB::bind_method(D_METHOD("get_k2"), &MobileVRInterface::get_k2); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "eye_height", PROPERTY_HINT_RANGE, "0.0,3.0,0.1"), "set_eye_height", "get_eye_height"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "iod", PROPERTY_HINT_RANGE, "4.0,10.0,0.1"), "set_iod", "get_iod"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "display_width", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_width", "get_display_width"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "display_to_lens", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_to_lens", "get_display_to_lens"); @@ -226,6 +230,14 @@ void MobileVRInterface::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "k2", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k2", "get_k2"); } +void MobileVRInterface::set_eye_height(const real_t p_eye_height) { + eye_height = p_eye_height; +} + +real_t MobileVRInterface::get_eye_height() const { + return eye_height; +} + void MobileVRInterface::set_iod(const real_t p_iod) { intraocular_dist = p_iod; }; @@ -328,6 +340,7 @@ Size2 MobileVRInterface::get_render_targetsize() { // we use half our window size Size2 target_size = OS::get_singleton()->get_window_size(); + target_size.x *= 0.5 * oversample; target_size.y *= oversample; diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index adc420ea5f..e595daf16e 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -107,6 +107,9 @@ protected: static void _bind_methods(); public: + void set_eye_height(const real_t p_eye_height); + real_t get_eye_height() const; + void set_iod(const real_t p_iod); real_t get_iod() const; diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 706949154e..341d57f3e4 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -5,70 +5,6 @@ Import('env_modules') env_mono = env_modules.Clone() -# TODO move functions to their own modules - -def make_cs_files_header(src, dst, version_dst): - from compat import byte_to_str - - with open(dst, 'w') as header: - header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') - header.write('#ifndef CS_COMPRESSED_H\n') - header.write('#define CS_COMPRESSED_H\n\n') - header.write('#ifdef TOOLS_ENABLED\n\n') - header.write('#include "core/map.h"\n') - header.write('#include "core/ustring.h"\n') - inserted_files = '' - import os - latest_mtime = 0 - cs_file_count = 0 - for root, _, files in os.walk(src): - files = [f for f in files if f.endswith('.cs')] - for file in files: - cs_file_count += 1 - filepath = os.path.join(root, file) - filepath_src_rel = os.path.relpath(filepath, src) - mtime = os.path.getmtime(filepath) - latest_mtime = mtime if mtime > latest_mtime else latest_mtime - with open(filepath, 'rb') as f: - buf = f.read() - decomp_size = len(buf) - import zlib - buf = zlib.compress(buf) - name = str(cs_file_count) - header.write('\n') - header.write('// ' + filepath_src_rel + '\n') - header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n') - header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n') - header.write('static const unsigned char _cs_' + name + '_compressed[] = { ') - for i, buf_idx in enumerate(range(len(buf))): - if i > 0: - header.write(', ') - header.write(byte_to_str(buf[buf_idx])) - inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \ - 'CompressedFile(_cs_' + name + '_compressed_size, ' \ - '_cs_' + name + '_uncompressed_size, ' \ - '_cs_' + name + '_compressed));\n' - header.write(' };\n') - header.write('\nstruct CompressedFile\n' '{\n' - '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' - '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' - '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n' - '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n' - '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n' - ) - header.write('\n#endif // TOOLS_ENABLED\n') - header.write('\n#endif // CS_COMPRESSED_H\n') - - glue_version = int(latest_mtime) # The latest modified time will do for now - - with open(version_dst, 'w') as version_header: - version_header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') - version_header.write('#ifndef CS_GLUE_VERSION_H\n') - version_header.write('#define CS_GLUE_VERSION_H\n\n') - version_header.write('#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n') - version_header.write('\n#endif // CS_GLUE_VERSION_H\n') - - env_mono.add_source_files(env.modules_sources, '*.cpp') env_mono.add_source_files(env.modules_sources, 'glue/*.cpp') env_mono.add_source_files(env.modules_sources, 'mono_gd/*.cpp') @@ -77,7 +13,12 @@ env_mono.add_source_files(env.modules_sources, 'utils/*.cpp') if env['tools']: env_mono.add_source_files(env.modules_sources, 'editor/*.cpp') # NOTE: It is safe to generate this file here, since this is still executed serially - make_cs_files_header('glue/Managed/Files', 'glue/cs_compressed.gen.h', 'glue/cs_glue_version.gen.h') + import build_scripts.make_cs_compressed_header as make_cs_compressed_header + make_cs_compressed_header.generate_header( + 'glue/Managed/Files', + 'glue/cs_compressed.gen.h', + 'glue/cs_glue_version.gen.h' + ) vars = Variables() vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True)) @@ -88,273 +29,29 @@ vars.Update(env_mono) if env_mono['mono_glue']: env_mono.Append(CPPDEFINES=['MONO_GLUE_ENABLED']) + import os.path + if not os.path.isfile('glue/mono_glue.gen.cpp'): + raise RuntimeError('Missing mono glue sources. Did you forget to generate them?') + if env_mono['tools'] or env_mono['target'] != 'release': env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD']) -# Configure TLS checks +# Configure Thread Local Storage + +import build_scripts.tls_configure as tls_configure -import tls_configure conf = Configure(env_mono) tls_configure.configure(conf) env_mono = conf.Finish() +# Configure Mono -# Build GodotSharpTools solution - - -import os - - -def find_nuget_unix(): - import os - - if 'NUGET_PATH' in os.environ: - hint_path = os.environ['NUGET_PATH'] - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - hint_path = os.path.join(hint_path, 'nuget') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - - import os.path - import sys - - hint_dirs = ['/opt/novell/mono/bin'] - if sys.platform == 'darwin': - hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs - - for hint_dir in hint_dirs: - hint_path = os.path.join(hint_dir, 'nuget') - if os.path.isfile(hint_path): - return hint_path - elif os.path.isfile(hint_path + '.exe'): - return hint_path + '.exe' - - for hint_dir in os.environ['PATH'].split(os.pathsep): - hint_dir = hint_dir.strip('"') - hint_path = os.path.join(hint_dir, 'nuget') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): - return hint_path + '.exe' - - return None - - -def find_nuget_windows(): - import os - - if 'NUGET_PATH' in os.environ: - hint_path = os.environ['NUGET_PATH'] - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - hint_path = os.path.join(hint_path, 'nuget.exe') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - - import mono_reg_utils as monoreg - - mono_root = '' - bits = env['bits'] - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - - if mono_root: - mono_bin_dir = os.path.join(mono_root, 'bin') - nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat') - - if os.path.isfile(nuget_mono): - return nuget_mono - - # Standalone NuGet - - for hint_dir in os.environ['PATH'].split(os.pathsep): - hint_dir = hint_dir.strip('"') - hint_path = os.path.join(hint_dir, 'nuget.exe') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - - return None - - -def find_msbuild_unix(filename): - import os.path - import sys - - hint_dirs = ['/opt/novell/mono/bin'] - if sys.platform == 'darwin': - hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs - - for hint_dir in hint_dirs: - hint_path = os.path.join(hint_dir, filename) - if os.path.isfile(hint_path): - return hint_path - elif os.path.isfile(hint_path + '.exe'): - return hint_path + '.exe' +import build_scripts.mono_configure as mono_configure - for hint_dir in os.environ['PATH'].split(os.pathsep): - hint_dir = hint_dir.strip('"') - hint_path = os.path.join(hint_dir, filename) - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): - return hint_path + '.exe' +mono_configure.configure(env, env_mono) - return None +# Build GodotSharpTools +import build_scripts.godotsharptools_build as godotsharptools_build -def find_msbuild_windows(): - import mono_reg_utils as monoreg - - mono_root = '' - bits = env['bits'] - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - - if not mono_root: - raise RuntimeError('Cannot find mono root directory') - - framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5') - mono_bin_dir = os.path.join(mono_root, 'bin') - msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat') - - if os.path.isfile(msbuild_mono): - # The (Csc/Vbc/Fsc)ToolExe environment variables are required when - # building with Mono's MSBuild. They must point to the batch files - # in Mono's bin directory to make sure they are executed with Mono. - mono_msbuild_env = { - 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'), - 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'), - 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat') - } - return (msbuild_mono, framework_path, mono_msbuild_env) - - msbuild_tools_path = monoreg.find_msbuild_tools_path_reg() - - if msbuild_tools_path: - return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {}) - - return None - - -def mono_build_solution(source, target, env): - import subprocess - import mono_reg_utils as monoreg - from shutil import copyfile - - sln_path = os.path.abspath(str(source[0])) - target_path = os.path.abspath(str(target[0])) - - framework_path = '' - msbuild_env = os.environ.copy() - - # Needed when running from Developer Command Prompt for VS - if 'PLATFORM' in msbuild_env: - del msbuild_env['PLATFORM'] - - # Find MSBuild - if os.name == 'nt': - msbuild_info = find_msbuild_windows() - if msbuild_info is None: - raise RuntimeError('Cannot find MSBuild executable') - msbuild_path = msbuild_info[0] - framework_path = msbuild_info[1] - msbuild_env.update(msbuild_info[2]) - else: - msbuild_path = find_msbuild_unix('msbuild') - if msbuild_path is None: - xbuild_fallback = env['xbuild_fallback'] - - if xbuild_fallback and os.name == 'nt': - print('Option \'xbuild_fallback\' not supported on Windows') - xbuild_fallback = False - - if xbuild_fallback: - print('Cannot find MSBuild executable, trying with xbuild') - print('Warning: xbuild is deprecated') - - msbuild_path = find_msbuild_unix('xbuild') - - if msbuild_path is None: - raise RuntimeError('Cannot find xbuild executable') - else: - raise RuntimeError('Cannot find MSBuild executable') - - print('MSBuild path: ' + msbuild_path) - - # Find NuGet - nuget_path = find_nuget_windows() if os.name == 'nt' else find_nuget_unix() - if nuget_path is None: - raise RuntimeError('Cannot find NuGet executable') - - print('NuGet path: ' + nuget_path) - - # Do NuGet restore - - try: - subprocess.check_call([nuget_path, 'restore', sln_path]) - except subprocess.CalledProcessError: - raise RuntimeError('GodotSharpTools: NuGet restore failed') - - # Build solution - - build_config = 'Release' - - msbuild_args = [ - msbuild_path, - sln_path, - '/p:Configuration=' + build_config, - ] - - if framework_path: - msbuild_args += ['/p:FrameworkPathOverride=' + framework_path] - - try: - subprocess.check_call(msbuild_args, env=msbuild_env) - except subprocess.CalledProcessError: - raise RuntimeError('GodotSharpTools: Build failed') - - # Copy files - - src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config)) - dst_dir = os.path.abspath(os.path.join(target_path, os.pardir)) - asm_file = 'GodotSharpTools.dll' - - if not os.path.isdir(dst_dir): - if os.path.exists(dst_dir): - raise RuntimeError('Target directory is a file') - os.makedirs(dst_dir) - - copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) - - # Dependencies - copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll")) - -if env['tools']: - output_dir = Dir('#bin').abspath - editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') - - mono_sln_builder = Builder(action=mono_build_solution) - env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder}) - env_mono.MonoBuildSolution( - os.path.join(editor_tools_dir, 'GodotSharpTools.dll'), - 'editor/GodotSharpTools/GodotSharpTools.sln' - ) +godotsharptools_build.build(env_mono) diff --git a/modules/mono/build_scripts/__init__.py b/modules/mono/build_scripts/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/modules/mono/build_scripts/__init__.py diff --git a/modules/mono/build_scripts/godotsharptools_build.py b/modules/mono/build_scripts/godotsharptools_build.py new file mode 100644 index 0000000000..af3a5cb5c6 --- /dev/null +++ b/modules/mono/build_scripts/godotsharptools_build.py @@ -0,0 +1,263 @@ +# Build GodotSharpTools solution + + +import os + +from SCons.Script import Builder, Dir + + +def find_nuget_unix(): + import os + + if 'NUGET_PATH' in os.environ: + hint_path = os.environ['NUGET_PATH'] + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + hint_path = os.path.join(hint_path, 'nuget') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + import os.path + import sys + + hint_dirs = ['/opt/novell/mono/bin'] + if sys.platform == 'darwin': + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path): + return hint_path + elif os.path.isfile(hint_path + '.exe'): + return hint_path + '.exe' + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): + return hint_path + '.exe' + + return None + + +def find_nuget_windows(env): + import os + + if 'NUGET_PATH' in os.environ: + hint_path = os.environ['NUGET_PATH'] + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + hint_path = os.path.join(hint_path, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + from . import mono_reg_utils as monoreg + + mono_root = '' + bits = env['bits'] + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + + if mono_root: + mono_bin_dir = os.path.join(mono_root, 'bin') + nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat') + + if os.path.isfile(nuget_mono): + return nuget_mono + + # Standalone NuGet + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + return None + + +def find_msbuild_unix(filename): + import os.path + import sys + + hint_dirs = ['/opt/novell/mono/bin'] + if sys.platform == 'darwin': + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, filename) + if os.path.isfile(hint_path): + return hint_path + elif os.path.isfile(hint_path + '.exe'): + return hint_path + '.exe' + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, filename) + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): + return hint_path + '.exe' + + return None + + +def find_msbuild_windows(env): + from . import mono_reg_utils as monoreg + + mono_root = '' + bits = env['bits'] + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + + if not mono_root: + raise RuntimeError('Cannot find mono root directory') + + framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5') + mono_bin_dir = os.path.join(mono_root, 'bin') + msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat') + + if os.path.isfile(msbuild_mono): + # The (Csc/Vbc/Fsc)ToolExe environment variables are required when + # building with Mono's MSBuild. They must point to the batch files + # in Mono's bin directory to make sure they are executed with Mono. + mono_msbuild_env = { + 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'), + 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'), + 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat') + } + return (msbuild_mono, framework_path, mono_msbuild_env) + + msbuild_tools_path = monoreg.find_msbuild_tools_path_reg() + + if msbuild_tools_path: + return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {}) + + return None + + +def mono_build_solution(source, target, env): + import subprocess + from shutil import copyfile + + sln_path = os.path.abspath(str(source[0])) + target_path = os.path.abspath(str(target[0])) + + framework_path = '' + msbuild_env = os.environ.copy() + + # Needed when running from Developer Command Prompt for VS + if 'PLATFORM' in msbuild_env: + del msbuild_env['PLATFORM'] + + # Find MSBuild + if os.name == 'nt': + msbuild_info = find_msbuild_windows(env) + if msbuild_info is None: + raise RuntimeError('Cannot find MSBuild executable') + msbuild_path = msbuild_info[0] + framework_path = msbuild_info[1] + msbuild_env.update(msbuild_info[2]) + else: + msbuild_path = find_msbuild_unix('msbuild') + if msbuild_path is None: + xbuild_fallback = env['xbuild_fallback'] + + if xbuild_fallback and os.name == 'nt': + print('Option \'xbuild_fallback\' not supported on Windows') + xbuild_fallback = False + + if xbuild_fallback: + print('Cannot find MSBuild executable, trying with xbuild') + print('Warning: xbuild is deprecated') + + msbuild_path = find_msbuild_unix('xbuild') + + if msbuild_path is None: + raise RuntimeError('Cannot find xbuild executable') + else: + raise RuntimeError('Cannot find MSBuild executable') + + print('MSBuild path: ' + msbuild_path) + + # Find NuGet + nuget_path = find_nuget_windows(env) if os.name == 'nt' else find_nuget_unix() + if nuget_path is None: + raise RuntimeError('Cannot find NuGet executable') + + print('NuGet path: ' + nuget_path) + + # Do NuGet restore + + try: + subprocess.check_call([nuget_path, 'restore', sln_path]) + except subprocess.CalledProcessError: + raise RuntimeError('GodotSharpTools: NuGet restore failed') + + # Build solution + + build_config = 'Release' + + msbuild_args = [ + msbuild_path, + sln_path, + '/p:Configuration=' + build_config, + ] + + if framework_path: + msbuild_args += ['/p:FrameworkPathOverride=' + framework_path] + + try: + subprocess.check_call(msbuild_args, env=msbuild_env) + except subprocess.CalledProcessError: + raise RuntimeError('GodotSharpTools: Build failed') + + # Copy files + + src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config)) + dst_dir = os.path.abspath(os.path.join(target_path, os.pardir)) + asm_file = 'GodotSharpTools.dll' + + if not os.path.isdir(dst_dir): + if os.path.exists(dst_dir): + raise RuntimeError('Target directory is a file') + os.makedirs(dst_dir) + + copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) + + # Dependencies + copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll")) + +def build(env_mono): + if not env_mono['tools']: + return + + output_dir = Dir('#bin').abspath + editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') + + mono_sln_builder = Builder(action=mono_build_solution) + env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder}) + env_mono.MonoBuildSolution( + os.path.join(editor_tools_dir, 'GodotSharpTools.dll'), + 'editor/GodotSharpTools/GodotSharpTools.sln' + ) diff --git a/modules/mono/build_scripts/make_cs_compressed_header.py b/modules/mono/build_scripts/make_cs_compressed_header.py new file mode 100644 index 0000000000..1f9177cef8 --- /dev/null +++ b/modules/mono/build_scripts/make_cs_compressed_header.py @@ -0,0 +1,61 @@ + +def generate_header(src, dst, version_dst): + from compat import byte_to_str + + with open(dst, 'w') as header: + header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') + header.write('#ifndef CS_COMPRESSED_H\n') + header.write('#define CS_COMPRESSED_H\n\n') + header.write('#ifdef TOOLS_ENABLED\n\n') + header.write('#include "core/map.h"\n') + header.write('#include "core/ustring.h"\n') + inserted_files = '' + import os + latest_mtime = 0 + cs_file_count = 0 + for root, _, files in os.walk(src): + files = [f for f in files if f.endswith('.cs')] + for file in files: + cs_file_count += 1 + filepath = os.path.join(root, file) + filepath_src_rel = os.path.relpath(filepath, src) + mtime = os.path.getmtime(filepath) + latest_mtime = mtime if mtime > latest_mtime else latest_mtime + with open(filepath, 'rb') as f: + buf = f.read() + decomp_size = len(buf) + import zlib + buf = zlib.compress(buf) + name = str(cs_file_count) + header.write('\n') + header.write('// ' + filepath_src_rel + '\n') + header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n') + header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n') + header.write('static const unsigned char _cs_' + name + '_compressed[] = { ') + for i, buf_idx in enumerate(range(len(buf))): + if i > 0: + header.write(', ') + header.write(byte_to_str(buf[buf_idx])) + inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \ + 'CompressedFile(_cs_' + name + '_compressed_size, ' \ + '_cs_' + name + '_uncompressed_size, ' \ + '_cs_' + name + '_compressed));\n' + header.write(' };\n') + header.write('\nstruct CompressedFile\n' '{\n' + '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' + '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' + '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n' + '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n' + '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n' + ) + header.write('\n#endif // TOOLS_ENABLED\n') + header.write('\n#endif // CS_COMPRESSED_H\n') + + glue_version = int(latest_mtime) # The latest modified time will do for now + + with open(version_dst, 'w') as version_header: + version_header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') + version_header.write('#ifndef CS_GLUE_VERSION_H\n') + version_header.write('#define CS_GLUE_VERSION_H\n\n') + version_header.write('#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n') + version_header.write('\n#endif // CS_GLUE_VERSION_H\n') diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py new file mode 100644 index 0000000000..160580e116 --- /dev/null +++ b/modules/mono/build_scripts/mono_configure.py @@ -0,0 +1,414 @@ +import imp +import os +import sys +import subprocess + +from distutils.version import LooseVersion +from SCons.Script import BoolVariable, Dir, Environment, Variables + +if os.name == 'nt': + from . import mono_reg_utils as monoreg + + +def find_file_in_dir(directory, files, prefix='', extension=''): + if not extension.startswith('.'): + extension = '.' + extension + for curfile in files: + if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): + return curfile + return '' + + +def copy_file(src_dir, dst_dir, name): + from shutil import copyfile + + src_path = os.path.join(src_dir, name) + dst_path = os.path.join(dst_dir, name) + + if not os.path.isdir(dst_dir): + os.mkdir(dst_dir) + + copyfile(src_path, dst_path) + + +def configure(env, env_mono): + envvars = Variables() + envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) + envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False)) + envvars.Update(env) + + bits = env['bits'] + + tools_enabled = env['tools'] + mono_static = env['mono_static'] + copy_mono_root = env['copy_mono_root'] + + mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] + + if env['platform'] == 'windows': + mono_root = '' + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + elif os.name == 'nt': + mono_root = monoreg.find_mono_root_dir(bits) + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + elif os.name == 'nt': + mono_root = monoreg.find_mono_root_dir(bits) + + if not mono_root: + raise RuntimeError('Mono installation directory not found') + + print('Found Mono root directory: ' + mono_root) + + mono_version = mono_root_try_find_mono_version(mono_root) + configure_for_mono_version(env_mono, mono_version) + + mono_lib_path = os.path.join(mono_root, 'lib') + + env.Append(LIBPATH=mono_lib_path) + env_mono.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + + if mono_static: + lib_suffix = Environment()['LIBSUFFIX'] + + if env.msvc: + mono_static_lib_name = 'libmono-static-sgen' + else: + mono_static_lib_name = 'libmonosgen-2.0' + + if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)): + raise RuntimeError('Could not find static mono library in: ' + mono_lib_path) + + if env.msvc: + env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix) + + env.Append(LINKFLAGS='Mincore' + lib_suffix) + env.Append(LINKFLAGS='msvcrt' + lib_suffix) + env.Append(LINKFLAGS='LIBCMT' + lib_suffix) + env.Append(LINKFLAGS='Psapi' + lib_suffix) + else: + env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)) + + env.Append(LIBS='psapi') + env.Append(LIBS='version') + else: + mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') + + if not mono_lib_name: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + if env.msvc: + env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + else: + env.Append(LIBS=mono_lib_name) + + mono_bin_path = os.path.join(mono_root, 'bin') + + mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') + + if not mono_dll_name: + raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) + + copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll') + else: + is_apple = (sys.platform == 'darwin' or "osxcross" in env) + + sharedlib_ext = '.dylib' if is_apple else '.so' + + mono_root = '' + mono_lib_path = '' + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + + if not mono_root and is_apple: + # Try with some known directories under OSX + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono'] + for hint_dir in hint_dirs: + if os.path.isdir(hint_dir): + mono_root = hint_dir + break + + # We can't use pkg-config to link mono statically, + # but we can still use it to find the mono root directory + if not mono_root and mono_static: + mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext) + if not mono_root: + raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') + + if mono_root: + print('Found Mono root directory: ' + mono_root) + + mono_version = mono_root_try_find_mono_version(mono_root) + configure_for_mono_version(env_mono, mono_version) + + mono_lib_path = os.path.join(mono_root, 'lib') + + env.Append(LIBPATH=mono_lib_path) + env_mono.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + + mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a') + + if not mono_lib: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + env_mono.Append(CPPFLAGS=['-D_REENTRANT']) + + if mono_static: + mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a') + + if is_apple: + env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) + else: + env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) + else: + env.Append(LIBS=[mono_lib]) + + if is_apple: + env.Append(LIBS=['iconv', 'pthread']) + else: + env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) + + if not mono_static: + mono_so_name = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension=sharedlib_ext) + + if not mono_so_name: + raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path) + + copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + else: + assert not mono_static + + # TODO: Add option to force using pkg-config + print('Mono root directory not found. Using pkg-config instead') + + mono_version = pkgconfig_try_find_mono_version() + configure_for_mono_version(env_mono, mono_version) + + env.ParseConfig('pkg-config monosgen-2 --libs') + env_mono.ParseConfig('pkg-config monosgen-2 --cflags') + + mono_lib_path = '' + mono_so_name = '' + + tmpenv = Environment() + tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) + tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') + + for hint_dir in tmpenv['LIBPATH']: + name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) + if name_found: + mono_lib_path = hint_dir + mono_so_name = name_found + break + + if not mono_so_name: + raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH'])) + + copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + + env.Append(LINKFLAGS='-rdynamic') + + if not tools_enabled: + if not mono_root: + mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() + + make_template_dir(env, mono_root) + + if copy_mono_root: + if not mono_root: + mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() + + if tools_enabled: + copy_mono_root_files(env, mono_root) + else: + print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.") + + +def make_template_dir(env, mono_root): + from shutil import rmtree + + platform = env['platform'] + target = env['target'] + + template_dir_name = '' + + if platform in ['windows', 'osx', 'x11']: + template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) + else: + assert False + + output_dir = Dir('#bin').abspath + template_dir = os.path.join(output_dir, template_dir_name) + + template_mono_root_dir = os.path.join(template_dir, 'Mono') + + if os.path.isdir(template_mono_root_dir): + rmtree(template_mono_root_dir) # Clean first + + # Copy etc/mono/ + + template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') + copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) + + # Copy the required shared libraries + + copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform']) + + +def copy_mono_root_files(env, mono_root): + from glob import glob + from shutil import copy + from shutil import rmtree + + if not mono_root: + raise RuntimeError('Mono installation directory not found') + + output_dir = Dir('#bin').abspath + editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono') + + if os.path.isdir(editor_mono_root_dir): + rmtree(editor_mono_root_dir) # Clean first + + # Copy etc/mono/ + + editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono') + copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform']) + + # Copy the required shared libraries + + copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform']) + + # Copy framework assemblies + + mono_framework_dir = os.path.join(mono_root, 'lib', 'mono', '4.5') + mono_framework_facades_dir = os.path.join(mono_framework_dir, 'Facades') + + editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5') + editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades') + + if not os.path.isdir(editor_mono_framework_dir): + os.makedirs(editor_mono_framework_dir) + if not os.path.isdir(editor_mono_framework_facades_dir): + os.makedirs(editor_mono_framework_facades_dir) + + for assembly in glob(os.path.join(mono_framework_dir, '*.dll')): + copy(assembly, editor_mono_framework_dir) + for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')): + copy(assembly, editor_mono_framework_facades_dir) + + +def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform): + from distutils.dir_util import copy_tree + from glob import glob + from shutil import copy + + if not os.path.isdir(target_mono_config_dir): + os.makedirs(target_mono_config_dir) + + mono_etc_dir = os.path.join(mono_root, 'etc', 'mono') + if not os.path.isdir(mono_etc_dir): + mono_etc_dir = '' + etc_hint_dirs = [] + if platform != 'windows': + etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono'] + if 'MONO_CFG_DIR' in os.environ: + etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')] + for etc_hint_dir in etc_hint_dirs: + if os.path.isdir(etc_hint_dir): + mono_etc_dir = etc_hint_dir + break + if not mono_etc_dir: + raise RuntimeError('Mono installation etc directory not found') + + copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0')) + copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0')) + copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5')) + copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig')) + + for file in glob(os.path.join(mono_etc_dir, '*')): + if os.path.isfile(file): + copy(file, target_mono_config_dir) + + +def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform): + from shutil import copy + + if platform == 'windows': + target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin') + + if not os.path.isdir(target_mono_bin_dir): + os.makedirs(target_mono_bin_dir) + + copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll')) + else: + target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib') + + if not os.path.isdir(target_mono_lib_dir): + os.makedirs(target_mono_lib_dir) + + if platform == 'osx': + copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib')) + elif platform == 'x11': + copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so')) + copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so')) + + +def configure_for_mono_version(env, mono_version): + if mono_version is None: + raise RuntimeError('Mono JIT compiler version not found') + print('Found Mono JIT compiler version: ' + str(mono_version)) + if mono_version >= LooseVersion('5.12.0'): + env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS']) + + +def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext): + tmpenv = Environment() + tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) + tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') + for hint_dir in tmpenv['LIBPATH']: + name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) + if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')): + return os.path.join(hint_dir, '..') + return '' + + +def pkgconfig_try_find_mono_version(): + from compat import decode_utf8 + + lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines() + greater_version = None + for line in lines: + try: + version = LooseVersion(decode_utf8(line)) + if greater_version is None or version > greater_version: + greater_version = version + except ValueError: + pass + return greater_version + + +def mono_root_try_find_mono_version(mono_root): + from compat import decode_utf8 + + mono_bin = os.path.join(mono_root, 'bin') + if os.path.isfile(os.path.join(mono_bin, 'mono')): + mono_binary = os.path.join(mono_bin, 'mono') + elif os.path.isfile(os.path.join(mono_bin, 'mono.exe')): + mono_binary = os.path.join(mono_bin, 'mono.exe') + else: + return None + output = subprocess.check_output([mono_binary, '--version']) + first_line = decode_utf8(output.splitlines()[0]) + try: + return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())]) + except (ValueError, IndexError): + return None diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py index c8ebb54ded..583708bf07 100644 --- a/modules/mono/mono_reg_utils.py +++ b/modules/mono/build_scripts/mono_reg_utils.py @@ -40,7 +40,7 @@ def _reg_open_key_bits(key, subkey, bits): def _find_mono_in_reg(subkey, bits): try: with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: - value, regtype = winreg.QueryValueEx(hKey, 'SdkInstallRoot') + value = winreg.QueryValueEx(hKey, 'SdkInstallRoot')[0] return value except (WindowsError, OSError): return None @@ -49,7 +49,7 @@ def _find_mono_in_reg(subkey, bits): def _find_mono_in_reg_old(subkey, bits): try: with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: - default_clr, regtype = winreg.QueryValueEx(hKey, 'DefaultCLR') + default_clr = winreg.QueryValueEx(hKey, 'DefaultCLR')[0] if default_clr: return _find_mono_in_reg(subkey + '\\' + default_clr, bits) return None @@ -91,7 +91,13 @@ def find_msbuild_tools_path_reg(): if not val: raise ValueError('Value of `installationPath` entry is empty') - return os.path.join(val, "MSBuild\\15.0\\Bin") + # Since VS2019, the directory is simply named "Current" + msbuild_dir = os.path.join(val, 'MSBuild\\Current\\Bin') + if os.path.isdir(msbuild_dir): + return msbuild_dir + + # Directory name "15.0" is used in VS 2017 + return os.path.join(val, 'MSBuild\\15.0\\Bin') raise ValueError('Cannot find `installationPath` entry') except ValueError as e: @@ -106,7 +112,7 @@ def find_msbuild_tools_path_reg(): try: subkey = r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0' with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey: - value, regtype = winreg.QueryValueEx(hKey, 'MSBuildToolsPath') + value = winreg.QueryValueEx(hKey, 'MSBuildToolsPath')[0] return value except (WindowsError, OSError): return '' diff --git a/modules/mono/tls_configure.py b/modules/mono/build_scripts/tls_configure.py index 622280b00b..622280b00b 100644 --- a/modules/mono/tls_configure.py +++ b/modules/mono/build_scripts/tls_configure.py diff --git a/modules/mono/config.py b/modules/mono/config.py index a81ecfce70..3b2e96765e 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -1,25 +1,12 @@ - -import imp -import os -import sys -import subprocess - -from distutils.version import LooseVersion -from SCons.Script import BoolVariable, Dir, Environment, File, SCons, Variables - - -monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') - - def can_build(env, platform): if platform in ['javascript']: return False # Not yet supported return True -def is_enabled(): - # The module is disabled by default. Use module_mono_enabled=yes to enable it. - return False +def configure(env): + env.use_ptrcall = True + env.add_module_version_string('mono') def get_doc_classes(): @@ -34,407 +21,6 @@ def get_doc_path(): return 'doc_classes' -def find_file_in_dir(directory, files, prefix='', extension=''): - if not extension.startswith('.'): - extension = '.' + extension - for curfile in files: - if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): - return curfile - return '' - - -def copy_file(src_dir, dst_dir, name): - from shutil import copyfile - - src_path = os.path.join(src_dir, name) - dst_path = os.path.join(dst_dir, name) - - if not os.path.isdir(dst_dir): - os.mkdir(dst_dir) - - copyfile(src_path, dst_path) - - -def configure(env): - env.use_ptrcall = True - env.add_module_version_string('mono') - - envvars = Variables() - envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) - envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False)) - envvars.Update(env) - - bits = env['bits'] - - tools_enabled = env['tools'] - mono_static = env['mono_static'] - copy_mono_root = env['copy_mono_root'] - - mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] - - if env['platform'] == 'windows': - mono_root = '' - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - elif os.name == 'nt': - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - elif os.name == 'nt': - mono_root = monoreg.find_mono_root_dir(bits) - - if not mono_root: - raise RuntimeError('Mono installation directory not found') - - print('Found Mono root directory: ' + mono_root) - - mono_version = mono_root_try_find_mono_version(mono_root) - configure_for_mono_version(env, mono_version) - - mono_lib_path = os.path.join(mono_root, 'lib') - - env.Append(LIBPATH=mono_lib_path) - env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) - - if mono_static: - lib_suffix = Environment()['LIBSUFFIX'] - - if env.msvc: - mono_static_lib_name = 'libmono-static-sgen' - else: - mono_static_lib_name = 'libmonosgen-2.0' - - if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)): - raise RuntimeError('Could not find static mono library in: ' + mono_lib_path) - - if env.msvc: - env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix) - - env.Append(LINKFLAGS='Mincore' + lib_suffix) - env.Append(LINKFLAGS='msvcrt' + lib_suffix) - env.Append(LINKFLAGS='LIBCMT' + lib_suffix) - env.Append(LINKFLAGS='Psapi' + lib_suffix) - else: - env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)) - - env.Append(LIBS='psapi') - env.Append(LIBS='version') - else: - mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') - - if not mono_lib_name: - raise RuntimeError('Could not find mono library in: ' + mono_lib_path) - - if env.msvc: - env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) - else: - env.Append(LIBS=mono_lib_name) - - mono_bin_path = os.path.join(mono_root, 'bin') - - mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') - - if not mono_dll_name: - raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) - - copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll') - else: - is_apple = (sys.platform == 'darwin' or "osxcross" in env) - - sharedlib_ext = '.dylib' if is_apple else '.so' - - mono_root = '' - mono_lib_path = '' - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - - if not mono_root and is_apple: - # Try with some known directories under OSX - hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono'] - for hint_dir in hint_dirs: - if os.path.isdir(hint_dir): - mono_root = hint_dir - break - - # We can't use pkg-config to link mono statically, - # but we can still use it to find the mono root directory - if not mono_root and mono_static: - mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext) - if not mono_root: - raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') - - if mono_root: - print('Found Mono root directory: ' + mono_root) - - mono_version = mono_root_try_find_mono_version(mono_root) - configure_for_mono_version(env, mono_version) - - mono_lib_path = os.path.join(mono_root, 'lib') - - env.Append(LIBPATH=mono_lib_path) - env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) - - mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a') - - if not mono_lib: - raise RuntimeError('Could not find mono library in: ' + mono_lib_path) - - env.Append(CPPFLAGS=['-D_REENTRANT']) - - if mono_static: - mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a') - - if is_apple: - env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) - else: - env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) - else: - env.Append(LIBS=[mono_lib]) - - if is_apple: - env.Append(LIBS=['iconv', 'pthread']) - else: - env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) - - if not mono_static: - mono_so_name = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension=sharedlib_ext) - - if not mono_so_name: - raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path) - - copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) - else: - assert not mono_static - - # TODO: Add option to force using pkg-config - print('Mono root directory not found. Using pkg-config instead') - - mono_version = pkgconfig_try_find_mono_version() - configure_for_mono_version(env, mono_version) - - env.ParseConfig('pkg-config monosgen-2 --cflags --libs') - - mono_lib_path = '' - mono_so_name = '' - - tmpenv = Environment() - tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) - tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') - - for hint_dir in tmpenv['LIBPATH']: - name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) - if name_found: - mono_lib_path = hint_dir - mono_so_name = name_found - break - - if not mono_so_name: - raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH'])) - - copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) - - env.Append(LINKFLAGS='-rdynamic') - - if not tools_enabled: - if not mono_root: - mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() - - make_template_dir(env, mono_root) - - if copy_mono_root: - if not mono_root: - mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() - - if tools_enabled: - copy_mono_root_files(env, mono_root) - else: - print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.") - - -def make_template_dir(env, mono_root): - from shutil import rmtree - - platform = env['platform'] - target = env['target'] - - template_dir_name = '' - - if platform in ['windows', 'osx', 'x11']: - template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) - else: - assert False - - output_dir = Dir('#bin').abspath - template_dir = os.path.join(output_dir, template_dir_name) - - template_mono_root_dir = os.path.join(template_dir, 'Mono') - - if os.path.isdir(template_mono_root_dir): - rmtree(template_mono_root_dir) # Clean first - - # Copy etc/mono/ - - template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') - copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) - - # Copy the required shared libraries - - copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform']) - - -def copy_mono_root_files(env, mono_root): - from glob import glob - from shutil import copy - from shutil import rmtree - - if not mono_root: - raise RuntimeError('Mono installation directory not found') - - output_dir = Dir('#bin').abspath - editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono') - - if os.path.isdir(editor_mono_root_dir): - rmtree(editor_mono_root_dir) # Clean first - - # Copy etc/mono/ - - editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono') - copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform']) - - # Copy the required shared libraries - - copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform']) - - # Copy framework assemblies - - mono_framework_dir = os.path.join(mono_root, 'lib', 'mono', '4.5') - mono_framework_facades_dir = os.path.join(mono_framework_dir, 'Facades') - - editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5') - editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades') - - if not os.path.isdir(editor_mono_framework_dir): - os.makedirs(editor_mono_framework_dir) - if not os.path.isdir(editor_mono_framework_facades_dir): - os.makedirs(editor_mono_framework_facades_dir) - - for assembly in glob(os.path.join(mono_framework_dir, '*.dll')): - copy(assembly, editor_mono_framework_dir) - for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')): - copy(assembly, editor_mono_framework_facades_dir) - - -def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform): - from distutils.dir_util import copy_tree - from glob import glob - from shutil import copy - - if not os.path.isdir(target_mono_config_dir): - os.makedirs(target_mono_config_dir) - - mono_etc_dir = os.path.join(mono_root, 'etc', 'mono') - if not os.path.isdir(mono_etc_dir): - mono_etc_dir = '' - etc_hint_dirs = [] - if platform != 'windows': - etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono'] - if 'MONO_CFG_DIR' in os.environ: - etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')] - for etc_hint_dir in etc_hint_dirs: - if os.path.isdir(etc_hint_dir): - mono_etc_dir = etc_hint_dir - break - if not mono_etc_dir: - raise RuntimeError('Mono installation etc directory not found') - - copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0')) - copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0')) - copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5')) - copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig')) - - for file in glob(os.path.join(mono_etc_dir, '*')): - if os.path.isfile(file): - copy(file, target_mono_config_dir) - - -def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform): - from shutil import copy - - if platform == 'windows': - target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin') - - if not os.path.isdir(target_mono_bin_dir): - os.makedirs(target_mono_bin_dir) - - copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll')) - else: - target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib') - - if not os.path.isdir(target_mono_lib_dir): - os.makedirs(target_mono_lib_dir) - - if platform == 'osx': - copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib')) - elif platform == 'x11': - copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so')) - copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so')) - - -def configure_for_mono_version(env, mono_version): - if mono_version is None: - raise RuntimeError('Mono JIT compiler version not found') - print('Found Mono JIT compiler version: ' + str(mono_version)) - if mono_version >= LooseVersion('5.12.0'): - env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS']) - - -def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext): - tmpenv = Environment() - tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) - tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') - for hint_dir in tmpenv['LIBPATH']: - name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) - if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')): - return os.path.join(hint_dir, '..') - return '' - - -def pkgconfig_try_find_mono_version(): - from compat import decode_utf8 - - lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines() - greater_version = None - for line in lines: - try: - version = LooseVersion(decode_utf8(line)) - if greater_version is None or version > greater_version: - greater_version = version - except ValueError: - pass - return greater_version - - -def mono_root_try_find_mono_version(mono_root): - from compat import decode_utf8 - - mono_bin = os.path.join(mono_root, 'bin') - if os.path.isfile(os.path.join(mono_bin, 'mono')): - mono_binary = os.path.join(mono_bin, 'mono') - elif os.path.isfile(os.path.join(mono_bin, 'mono.exe')): - mono_binary = os.path.join(mono_bin, 'mono.exe') - else: - return None - output = subprocess.check_output([mono_binary, '--version']) - first_line = decode_utf8(output.splitlines()[0]) - try: - return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())]) - except (ValueError, IndexError): - return None +def is_enabled(): + # The module is disabled by default. Use module_mono_enabled=yes to enable it. + return False diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index dfdb4a0b3a..3c9644127c 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -571,7 +571,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec MonoException *exc = NULL; - MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, (MonoObject **)&exc); + MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -595,7 +595,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec MonoString *file_name; int file_line_num; MonoString *method_decl; - invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc); + invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -625,7 +625,7 @@ void CSharpLanguage::frame() { if (task_scheduler) { MonoException *exc = NULL; - invoke_method_thunk(CACHED_METHOD_THUNK(GodotTaskScheduler, Activate), task_scheduler, (MonoObject **)&exc); + invoke_method_thunk(CACHED_METHOD_THUNK(GodotTaskScheduler, Activate), task_scheduler, &exc); if (exc) { GDMonoUtils::debug_unhandled_exception(exc); @@ -1424,6 +1424,34 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const { for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) { p_properties->push_back(E->value()); } + + // Call _get_property_list + + ERR_FAIL_COND(!script.is_valid()); + + MonoObject *mono_object = get_mono_object(); + ERR_FAIL_NULL(mono_object); + + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get_property_list), 0); + + if (method) { + MonoObject *ret = method->invoke(mono_object); + + if (ret) { + Array array = Array(GDMonoMarshal::mono_object_to_variant(ret)); + for (int i = 0, size = array.size(); i < size; i++) + p_properties->push_back(PropertyInfo::from_dict(array.get(i))); + return; + } + + break; + } + + top = top->get_parent_class(); + } } Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const { @@ -2166,7 +2194,8 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_ CRASH_NOW(); } - Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type); + GDMonoMarshal::ExportInfo export_info; + Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &export_info); if (!p_member->has_attribute(CACHED_CLASS(ExportAttribute))) { r_prop_info = PropertyInfo(variant_type, name.operator String(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); @@ -2191,6 +2220,7 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_ ERR_PRINTS("Unknown type of exported member: " + p_class->get_full_name() + "." + name.operator String()); return false; } else if (variant_type == Variant::INT && type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(type.type_class->get_mono_ptr())) { + // TODO: Move to ExportInfo? variant_type = Variant::INT; hint = PROPERTY_HINT_ENUM; @@ -2257,6 +2287,11 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_ hint = PROPERTY_HINT_RESOURCE_TYPE; hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class); + } else if (variant_type == Variant::ARRAY && export_info.array.element_type != Variant::NIL) { + hint = PROPERTY_HINT_TYPE_STRING; + hint_string = itos(export_info.array.element_type) + ":"; + } else if (variant_type == Variant::DICTIONARY && export_info.dictionary.key_type != Variant::NIL && export_info.dictionary.value_type != Variant::NIL) { + // TODO: There is no hint for this yet } else { hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); @@ -3020,6 +3055,7 @@ CSharpLanguage::StringNameCache::StringNameCache() { _signal_callback = StaticCString::create("_signal_callback"); _set = StaticCString::create("_set"); _get = StaticCString::create("_get"); + _get_property_list = StaticCString::create("_get_property_list"); _notification = StaticCString::create("_notification"); _script_source = StaticCString::create("script/source"); dotctor = StaticCString::create(".ctor"); diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 99877a4379..050527d52b 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -298,6 +298,7 @@ class CSharpLanguage : public ScriptLanguage { StringName _signal_callback; StringName _set; StringName _get; + StringName _get_property_list; StringName _notification; StringName _script_source; StringName dotctor; // .ctor diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml index a821713d0d..826c106d7e 100644 --- a/modules/mono/doc_classes/@C#.xml +++ b/modules/mono/doc_classes/@C#.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index 7f22388132..de2e246ea9 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="new" qualifiers="vararg"> <return type="Object"> diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml index 21835e639c..18556a84ba 100644 --- a/modules/mono/doc_classes/GodotSharp.xml +++ b/modules/mono/doc_classes/GodotSharp.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="attach_thread"> <return type="void"> diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 35a89956a8..fe7ced060d 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -100,7 +100,7 @@ #define BINDINGS_GENERATOR_VERSION UINT32_C(8) -const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n"; +const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n"); bool BindingsGenerator::verbose_output = false; @@ -277,7 +277,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf } else if (code_tag) { xml_output.append("["); pos = brk_pos + 1; - } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ")) { + } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ")) { String link_target = tag.substr(tag.find(" ") + 1, tag.length()); String link_tag = tag.substr(0, tag.find(" ")); @@ -386,6 +386,97 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf xml_output.append(link_target); xml_output.append("</c>"); } + } else if (link_tag == "const") { + if (!target_itype || !target_itype->is_object_type) { + if (OS::get_singleton()->is_stdout_verbose()) { + if (target_itype) { + OS::get_singleton()->print("Cannot resolve constant reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data()); + } else { + OS::get_singleton()->print("Cannot resolve type from constant reference in documentation: %s\n", link_target.utf8().get_data()); + } + } + + // TODO Map what we can + xml_output.append("<c>"); + xml_output.append(link_target); + xml_output.append("</c>"); + } else if (!target_itype && target_cname == name_cache.type_at_GlobalScope) { + String target_name = (String)target_cname; + + // Try to find as a global constant + const ConstantInterface *target_iconst = find_constant_by_name(target_name, global_constants); + + if (target_iconst) { + // Found global constant + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "." BINDINGS_GLOBAL_SCOPE_CLASS "."); + xml_output.append(target_iconst->proxy_name); + xml_output.append("\"/>"); + } else { + // Try to find as global enum constant + const EnumInterface *target_ienum = NULL; + + for (const List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) { + target_ienum = &E->get(); + target_iconst = find_constant_by_name(target_name, target_ienum->constants); + if (target_iconst) + break; + } + + if (target_iconst) { + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); + xml_output.append(target_ienum->cname); + xml_output.append("."); + xml_output.append(target_iconst->proxy_name); + xml_output.append("\"/>"); + } else { + ERR_PRINTS("Cannot resolve global constant reference in documentation: " + link_target); + + xml_output.append("<c>"); + xml_output.append(link_target); + xml_output.append("</c>"); + } + } + } else { + String target_name = (String)target_cname; + + // Try to find the constant in the current class + const ConstantInterface *target_iconst = find_constant_by_name(target_name, target_itype->constants); + + if (target_iconst) { + // Found constant in current class + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); + xml_output.append(target_itype->proxy_name); + xml_output.append("."); + xml_output.append(target_iconst->proxy_name); + xml_output.append("\"/>"); + } else { + // Try to find as enum constant in the current class + const EnumInterface *target_ienum = NULL; + + for (const List<EnumInterface>::Element *E = target_itype->enums.front(); E; E = E->next()) { + target_ienum = &E->get(); + target_iconst = find_constant_by_name(target_name, target_ienum->constants); + if (target_iconst) + break; + } + + if (target_iconst) { + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); + xml_output.append(target_itype->proxy_name); + xml_output.append("."); + xml_output.append(target_ienum->cname); + xml_output.append("."); + xml_output.append(target_iconst->proxy_name); + xml_output.append("\"/>"); + } else { + ERR_PRINTS("Cannot resolve constant reference in documentation: " + link_target); + + xml_output.append("<c>"); + xml_output.append(link_target); + xml_output.append("</c>"); + } + } + } } pos = brk_end + 1; @@ -414,7 +505,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf } else if (tag == "Nil") { xml_output.append("<see langword=\"null\"/>"); } else if (tag.begins_with("@")) { - // @Global Scope, @GDScript, etc + // @GlobalScope, @GDScript, etc xml_output.append("<c>"); xml_output.append(tag); xml_output.append("</c>"); @@ -2406,6 +2497,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg r_iarg.default_argument = "null"; break; } + FALLTHROUGH; case Variant::DICTIONARY: case Variant::_RID: r_iarg.default_argument = "new %s()"; @@ -2428,7 +2520,8 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity"; r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; break; - default: {} + default: { + } } if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null") diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 7eaebeabbd..42071f9c0d 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -497,6 +497,7 @@ class BindingsGenerator { StringName type_Object; StringName type_Reference; StringName type_String; + StringName type_at_GlobalScope; StringName enum_Error; NameCache() { @@ -509,6 +510,7 @@ class BindingsGenerator { type_Object = StaticCString::create("Object"); type_Reference = StaticCString::create("Reference"); type_String = StaticCString::create("String"); + type_at_GlobalScope = StaticCString::create("@GlobalScope"); enum_Error = StaticCString::create("Error"); } @@ -527,6 +529,15 @@ class BindingsGenerator { return NULL; } + const ConstantInterface *find_constant_by_name(const String &p_name, const List<ConstantInterface> &p_constants) const { + for (const List<ConstantInterface>::Element *E = p_constants.front(); E; E = E->next()) { + if (E->get().name == p_name) + return &E->get(); + } + + return NULL; + } + inline String get_unique_sig(const TypeInterface &p_type) { if (p_type.is_reference) return "Ref"; diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index 921b9f987b..9d42528927 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -502,11 +502,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { String settings_hint_str = "Disabled"; -#ifdef WINDOWS_ENABLED +#if defined(WINDOWS_ENABLED) settings_hint_str += ",MonoDevelop,Visual Studio Code"; -#elif OSX_ENABLED +#elif defined(OSX_ENABLED) settings_hint_str += ",Visual Studio,MonoDevelop,Visual Studio Code"; -#elif UNIX_ENABLED +#elif defined(UNIX_ENABLED) settings_hint_str += ",MonoDevelop,Visual Studio Code"; #endif diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h index cf0d2aec84..d9523c384c 100644 --- a/modules/mono/editor/godotsharp_editor.h +++ b/modules/mono/editor/godotsharp_editor.h @@ -81,15 +81,15 @@ public: enum ExternalEditor { EDITOR_NONE, -#ifdef WINDOWS_ENABLED +#if defined(WINDOWS_ENABLED) //EDITOR_VISUALSTUDIO, // TODO EDITOR_MONODEVELOP, EDITOR_VSCODE -#elif OSX_ENABLED +#elif defined(OSX_ENABLED) EDITOR_VISUALSTUDIO_MAC, EDITOR_MONODEVELOP, EDITOR_VSCODE -#elif UNIX_ENABLED +#elif defined(UNIX_ENABLED) EDITOR_MONODEVELOP, EDITOR_VSCODE #endif diff --git a/modules/mono/glue/Managed/Files/Array.cs b/modules/mono/glue/Managed/Files/Array.cs index 1ee64f3b71..0e7b0362e0 100644 --- a/modules/mono/glue/Managed/Files/Array.cs +++ b/modules/mono/glue/Managed/Files/Array.cs @@ -28,7 +28,7 @@ namespace Godot.Collections } } - public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable + public class Array : IList, IDisposable { ArraySafeHandle safeHandle; bool disposed = false; @@ -38,6 +38,14 @@ namespace Godot.Collections safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); } + public Array(IEnumerable collection) : this() + { + if (collection == null) + throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); + + MarshalUtils.EnumerableToArray(collection, GetPtr()); + } + internal Array(ArraySafeHandle handle) { safeHandle = handle; @@ -56,6 +64,13 @@ namespace Godot.Collections return safeHandle.DangerousGetHandle(); } + public Error Resize(int newSize) + { + return godot_icall_Array_Resize(GetPtr(), newSize); + } + + // IDisposable + public void Dispose() { if (disposed) @@ -70,62 +85,55 @@ namespace Godot.Collections disposed = true; } + // IList + + public bool IsReadOnly => false; + + public bool IsFixedSize => false; + public object this[int index] { - get - { - return godot_icall_Array_At(GetPtr(), index); - } - set - { - godot_icall_Array_SetAt(GetPtr(), index, value); - } + get => godot_icall_Array_At(GetPtr(), index); + set => godot_icall_Array_SetAt(GetPtr(), index, value); } - public int Count - { - get - { - return godot_icall_Array_Count(GetPtr()); - } - } + public int Add(object value) => godot_icall_Array_Add(GetPtr(), value); - public bool IsReadOnly - { - get - { - return false; - } - } + public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value); - public void Add(object item) - { - godot_icall_Array_Add(GetPtr(), item); - } + public void Clear() => godot_icall_Array_Clear(GetPtr()); - public void Clear() - { - godot_icall_Array_Clear(GetPtr()); - } + public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value); - public bool Contains(object item) - { - return godot_icall_Array_Contains(GetPtr(), item); - } + public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value); + + public void Remove(object value) => godot_icall_Array_Remove(GetPtr(), value); + + public void RemoveAt(int index) => godot_icall_Array_RemoveAt(GetPtr(), index); + + // ICollection + + public int Count => godot_icall_Array_Count(GetPtr()); + + public object SyncRoot => this; - public void CopyTo(object[] array, int arrayIndex) + public bool IsSynchronized => false; + + public void CopyTo(System.Array array, int index) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); // Internal call may throw ArgumentException - godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex); + godot_icall_Array_CopyTo(GetPtr(), array, index); } - public IEnumerator<object> GetEnumerator() + // IEnumerable + + public IEnumerator GetEnumerator() { int count = Count; @@ -135,34 +143,9 @@ namespace Godot.Collections } } - public int IndexOf(object item) + public override string ToString() { - return godot_icall_Array_IndexOf(GetPtr(), item); - } - - public void Insert(int index, object item) - { - godot_icall_Array_Insert(GetPtr(), index, item); - } - - public bool Remove(object item) - { - return godot_icall_Array_Remove(GetPtr(), item); - } - - public void RemoveAt(int index) - { - godot_icall_Array_RemoveAt(GetPtr(), index); - } - - public Error Resize(int newSize) - { - return godot_icall_Array_Resize(GetPtr(), newSize); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); + return godot_icall_Array_ToString(GetPtr()); } [MethodImpl(MethodImplOptions.InternalCall)] @@ -184,7 +167,7 @@ namespace Godot.Collections internal extern static int godot_icall_Array_Count(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_Add(IntPtr ptr, object item); + internal extern static int godot_icall_Array_Add(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_Clear(IntPtr ptr); @@ -193,7 +176,7 @@ namespace Godot.Collections internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex); + internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item); @@ -212,6 +195,9 @@ namespace Godot.Collections [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Array_ToString(IntPtr ptr); } public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> @@ -231,6 +217,14 @@ namespace Godot.Collections objectArray = new Array(); } + public Array(IEnumerable<T> collection) + { + if (collection == null) + throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); + + objectArray = new Array(collection); + } + public Array(Array array) { objectArray = array; @@ -246,11 +240,23 @@ namespace Godot.Collections objectArray = new Array(handle); } + internal IntPtr GetPtr() + { + return objectArray.GetPtr(); + } + public static explicit operator Array(Array<T> from) { return from.objectArray; } + public Error Resize(int newSize) + { + return objectArray.Resize(newSize); + } + + // IList<T> + public T this[int index] { get @@ -263,6 +269,23 @@ namespace Godot.Collections } } + public int IndexOf(T item) + { + return objectArray.IndexOf(item); + } + + public void Insert(int index, T item) + { + objectArray.Insert(index, item); + } + + public void RemoveAt(int index) + { + objectArray.RemoveAt(index); + } + + // ICollection<T> + public int Count { get @@ -317,6 +340,13 @@ namespace Godot.Collections } } + public bool Remove(T item) + { + return Array.godot_icall_Array_Remove(GetPtr(), item); + } + + // IEnumerable<T> + public IEnumerator<T> GetEnumerator() { int count = objectArray.Count; @@ -327,39 +357,11 @@ namespace Godot.Collections } } - public int IndexOf(T item) - { - return objectArray.IndexOf(item); - } - - public void Insert(int index, T item) - { - objectArray.Insert(index, item); - } - - public bool Remove(T item) - { - return objectArray.Remove(item); - } - - public void RemoveAt(int index) - { - objectArray.RemoveAt(index); - } - - public Error Resize(int newSize) - { - return objectArray.Resize(newSize); - } - IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - internal IntPtr GetPtr() - { - return objectArray.GetPtr(); - } + public override string ToString() => objectArray.ToString(); } } diff --git a/modules/mono/glue/Managed/Files/Dictionary.cs b/modules/mono/glue/Managed/Files/Dictionary.cs index fb4521065f..6ab8549a01 100644 --- a/modules/mono/glue/Managed/Files/Dictionary.cs +++ b/modules/mono/glue/Managed/Files/Dictionary.cs @@ -29,9 +29,7 @@ namespace Godot.Collections } public class Dictionary : - IDictionary<object, object>, - ICollection<KeyValuePair<object, object>>, - IEnumerable<KeyValuePair<object, object>>, + IDictionary, IDisposable { DictionarySafeHandle safeHandle; @@ -42,6 +40,14 @@ namespace Godot.Collections safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); } + public Dictionary(IDictionary dictionary) : this() + { + if (dictionary == null) + throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); + + MarshalUtils.IDictionaryToDictionary(dictionary, GetPtr()); + } + internal Dictionary(DictionarySafeHandle handle) { safeHandle = handle; @@ -74,19 +80,9 @@ namespace Godot.Collections disposed = true; } - public object this[object key] - { - get - { - return godot_icall_Dictionary_GetValue(GetPtr(), key); - } - set - { - godot_icall_Dictionary_SetValue(GetPtr(), key, value); - } - } + // IDictionary - public ICollection<object> Keys + public ICollection Keys { get { @@ -95,7 +91,7 @@ namespace Godot.Collections } } - public ICollection<object> Values + public ICollection Values { get { @@ -104,97 +100,102 @@ namespace Godot.Collections } } - public int Count - { - get - { - return godot_icall_Dictionary_Count(GetPtr()); - } - } + public bool IsFixedSize => false; - public bool IsReadOnly - { - get - { - return false; - } - } + public bool IsReadOnly => false; - public void Add(object key, object value) + public object this[object key] { - godot_icall_Dictionary_Add(GetPtr(), key, value); + get => godot_icall_Dictionary_GetValue(GetPtr(), key); + set => godot_icall_Dictionary_SetValue(GetPtr(), key, value); } - public void Add(KeyValuePair<object, object> item) - { - Add(item.Key, item.Value); - } + public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value); - public void Clear() - { - godot_icall_Dictionary_Clear(GetPtr()); - } + public void Clear() => godot_icall_Dictionary_Clear(GetPtr()); - public bool Contains(KeyValuePair<object, object> item) - { - return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value); - } + public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key); - public bool ContainsKey(object key) - { - return godot_icall_Dictionary_ContainsKey(GetPtr(), key); - } + public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this); + + public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key); + + // ICollection + + public object SyncRoot => this; - public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex) + public bool IsSynchronized => false; + + public int Count => godot_icall_Dictionary_Count(GetPtr()); + + public void CopyTo(System.Array array, int index) { - // TODO 3 internal calls, can reduce to 1 + // TODO Can be done with single internal call + + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); + Array keys = (Array)Keys; Array values = (Array)Values; int count = Count; + if (array.Length < (index + count)) + throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + for (int i = 0; i < count; i++) { - // TODO 2 internal calls, can reduce to 1 - array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]); - arrayIndex++; + array.SetValue(new DictionaryEntry(keys[i], values[i]), index); + index++; } } - public IEnumerator<KeyValuePair<object, object>> GetEnumerator() + // IEnumerable + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private class DictionaryEnumerator : IDictionaryEnumerator { - // TODO 3 internal calls, can reduce to 1 - Array keys = (Array)Keys; - Array values = (Array)Values; - int count = Count; + Array keys; + Array values; + int count; + int index = -1; - for (int i = 0; i < count; i++) + public DictionaryEnumerator(Dictionary dictionary) { - // TODO 2 internal calls, can reduce to 1 - yield return new KeyValuePair<object, object>(keys[i], values[i]); + // TODO 3 internal calls, can reduce to 1 + keys = (Array)dictionary.Keys; + values = (Array)dictionary.Values; + count = dictionary.Count; } - } - public bool Remove(object key) - { - return godot_icall_Dictionary_RemoveKey(GetPtr(), key); - } + public object Current => Entry; - public bool Remove(KeyValuePair<object, object> item) - { - return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); - } + public DictionaryEntry Entry => + // TODO 2 internal calls, can reduce to 1 + new DictionaryEntry(keys[index], values[index]); - public bool TryGetValue(object key, out object value) - { - object retValue; - bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue); - value = found ? retValue : default(object); - return found; + public object Key => Entry.Key; + + public object Value => Entry.Value; + + public bool MoveNext() + { + index++; + return index < count; + } + + public void Reset() + { + index = -1; + } } - IEnumerator IEnumerable.GetEnumerator() + public override string ToString() { - return GetEnumerator(); + return godot_icall_Dictionary_ToString(GetPtr()); } [MethodImpl(MethodImplOptions.InternalCall)] @@ -247,12 +248,13 @@ namespace Godot.Collections [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr); } public class Dictionary<TKey, TValue> : - IDictionary<TKey, TValue>, - ICollection<KeyValuePair<TKey, TValue>>, - IEnumerable<KeyValuePair<TKey, TValue>> + IDictionary<TKey, TValue> { Dictionary objectDict; @@ -269,6 +271,23 @@ namespace Godot.Collections objectDict = new Dictionary(); } + public Dictionary(IDictionary<TKey, TValue> dictionary) + { + objectDict = new Dictionary(); + + if (dictionary == null) + throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); + + // TODO: Can be optimized + + IntPtr godotDictionaryPtr = GetPtr(); + + foreach (KeyValuePair<TKey, TValue> entry in dictionary) + { + Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); + } + } + public Dictionary(Dictionary dictionary) { objectDict = dictionary; @@ -289,6 +308,13 @@ namespace Godot.Collections return from.objectDict; } + internal IntPtr GetPtr() + { + return objectDict.GetPtr(); + } + + // IDictionary<TKey, TValue> + public TValue this[TKey key] { get @@ -319,6 +345,31 @@ namespace Godot.Collections } } + public void Add(TKey key, TValue value) + { + objectDict.Add(key, value); + } + + public bool ContainsKey(TKey key) + { + return objectDict.Contains(key); + } + + public bool Remove(TKey key) + { + return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key); + } + + public bool TryGetValue(TKey key, out TValue value) + { + object retValue; + bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass); + value = found ? (TValue)retValue : default(TValue); + return found; + } + + // ICollection<KeyValuePair<TKey, TValue>> + public int Count { get @@ -335,11 +386,6 @@ namespace Godot.Collections } } - public void Add(TKey key, TValue value) - { - objectDict.Add(key, value); - } - public void Add(KeyValuePair<TKey, TValue> item) { objectDict.Add(item.Key, item.Value); @@ -355,18 +401,22 @@ namespace Godot.Collections return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); } - public bool ContainsKey(TKey key) - { - return objectDict.ContainsKey(key); - } - public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + // TODO 3 internal calls, can reduce to 1 Array<TKey> keys = (Array<TKey>)Keys; Array<TValue> values = (Array<TValue>)Values; int count = Count; + if (array.Length < (arrayIndex + count)) + throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + for (int i = 0; i < count; i++) { // TODO 2 internal calls, can reduce to 1 @@ -375,6 +425,13 @@ namespace Godot.Collections } } + public bool Remove(KeyValuePair<TKey, TValue> item) + { + return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); ; + } + + // IEnumerable<KeyValuePair<TKey, TValue>> + public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { // TODO 3 internal calls, can reduce to 1 @@ -389,32 +446,11 @@ namespace Godot.Collections } } - public bool Remove(TKey key) - { - return objectDict.Remove(key); - } - - public bool Remove(KeyValuePair<TKey, TValue> item) - { - return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value)); - } - - public bool TryGetValue(TKey key, out TValue value) - { - object retValue; - bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass); - value = found ? (TValue)retValue : default(TValue); - return found; - } - IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - internal IntPtr GetPtr() - { - return objectDict.GetPtr(); - } + public override string ToString() => objectDict.ToString(); } } diff --git a/modules/mono/glue/Managed/Files/DynamicObject.cs b/modules/mono/glue/Managed/Files/DynamicObject.cs index 9504415664..9860feafdd 100644 --- a/modules/mono/glue/Managed/Files/DynamicObject.cs +++ b/modules/mono/glue/Managed/Files/DynamicObject.cs @@ -26,7 +26,7 @@ namespace Godot /// dynamic sprite = GetNode("Sprite").DynamicGodotObject; /// sprite.add_child(this); /// - /// if ((sprite.hframes * sprite.vframes) > 0) + /// if ((sprite.hframes * sprite.vframes) > 0) /// sprite.frame = 0; /// </code> /// </example> diff --git a/modules/mono/glue/Managed/Files/MarshalUtils.cs b/modules/mono/glue/Managed/Files/MarshalUtils.cs index f7699a15bf..7e72b0edb5 100644 --- a/modules/mono/glue/Managed/Files/MarshalUtils.cs +++ b/modules/mono/glue/Managed/Files/MarshalUtils.cs @@ -1,18 +1,68 @@ using System; -using Godot.Collections; +using System.Collections; namespace Godot { + using Array = Godot.Collections.Array; + using Dictionary = Godot.Collections.Dictionary; + static class MarshalUtils { - static bool IsArrayGenericType(Type type) + static bool TypeIsGenericArray(Type type) { - return type.GetGenericTypeDefinition() == typeof(Array<>); + return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); + } + + static bool TypeIsGenericDictionary(Type type) + { + return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); + } + + static void ArrayGetElementType(Type type, out Type elementType) + { + elementType = type.GetGenericArguments()[0]; + } + + static void DictionaryGetKeyValueTypes(Type type, out Type keyType, out Type valueType) + { + var genericArgs = type.GetGenericArguments(); + + keyType = genericArgs[0]; + valueType = genericArgs[1]; + } + + // TODO Add support for IEnumerable<T> and IDictionary<TKey, TValue> + // TODO: EnumerableToArray and IDictionaryToDictionary can be optimized + + internal static void EnumerableToArray(IEnumerable enumerable, IntPtr godotArrayPtr) + { + if (enumerable is ICollection collection) + { + int count = collection.Count; + + object[] tempArray = new object[count]; + collection.CopyTo(tempArray, 0); + + for (int i = 0; i < count; i++) + { + Array.godot_icall_Array_Add(godotArrayPtr, tempArray[i]); + } + } + else + { + foreach (object element in enumerable) + { + Array.godot_icall_Array_Add(godotArrayPtr, element); + } + } } - static bool IsDictionaryGenericType(Type type) + internal static void IDictionaryToDictionary(IDictionary dictionary, IntPtr godotDictionaryPtr) { - return type.GetGenericTypeDefinition() == typeof(Dictionary<, >); + foreach (DictionaryEntry entry in dictionary) + { + Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); + } } } } diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs index 5f5de12959..a064278237 100644 --- a/modules/mono/glue/Managed/Files/Mathf.cs +++ b/modules/mono/glue/Managed/Files/Mathf.cs @@ -261,6 +261,16 @@ namespace Godot return (real_t)Math.Sinh(s); } + public static real_t SmoothStep(real_t from, real_t to, real_t weight) + { + if (IsEqualApprox(from, to)) + { + return from; + } + real_t x = Clamp((weight - from) / (to - from), (real_t)0.0, (real_t)1.0); + return x * x * (3 - 2 * x); + } + public static real_t Sqrt(real_t s) { return (real_t)Math.Sqrt(s); diff --git a/modules/mono/glue/Managed/Files/Object.base.cs b/modules/mono/glue/Managed/Files/Object.base.cs index e152d56871..de80f7fddc 100644 --- a/modules/mono/glue/Managed/Files/Object.base.cs +++ b/modules/mono/glue/Managed/Files/Object.base.cs @@ -73,6 +73,11 @@ namespace Godot disposed = true; } + public override string ToString() + { + return godot_icall_Object_ToString(GetPtr(this)); + } + /// <summary> /// Returns a new <see cref="Godot.SignalAwaiter"/> awaiter configured to complete when the instance /// <paramref name="source"/> emits the signal specified by the <paramref name="signal"/> parameter. @@ -88,7 +93,7 @@ namespace Godot /// <code> /// public override void _Ready() /// { - /// for (int i = 0; i < 100; i++) + /// for (int i = 0; i < 100; i++) /// { /// await ToSignal(GetTree(), "idle_frame"); /// GD.Print($"Frame {i}"); @@ -115,6 +120,9 @@ namespace Godot [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer); + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Object_ToString(IntPtr ptr); + // Used by the generated API [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_Object_ClassDB_get_method(string type, string method); diff --git a/modules/mono/glue/Managed/Files/RID.cs b/modules/mono/glue/Managed/Files/RID.cs index f1268c8518..12064beca2 100644 --- a/modules/mono/glue/Managed/Files/RID.cs +++ b/modules/mono/glue/Managed/Files/RID.cs @@ -70,6 +70,8 @@ namespace Godot return godot_icall_RID_get_id(RID.GetPtr(this)); } + public override string ToString() => "[RID]"; + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_RID_Ctor(IntPtr from); diff --git a/modules/mono/glue/Managed/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs index 73a3252fdb..908162ec45 100644 --- a/modules/mono/glue/Managed/Files/Vector2.cs +++ b/modules/mono/glue/Managed/Files/Vector2.cs @@ -132,6 +132,11 @@ namespace Godot (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); } + public Vector2 DirectionTo(Vector2 b) + { + return new Vector2(b.x - x, b.y - y).Normalized(); + } + public real_t DistanceSquaredTo(Vector2 to) { return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y); diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs index f6ff27989d..0c96d346a9 100644 --- a/modules/mono/glue/Managed/Files/Vector3.cs +++ b/modules/mono/glue/Managed/Files/Vector3.cs @@ -126,6 +126,11 @@ namespace Godot ); } + public Vector3 DirectionTo(Vector3 b) + { + return new Vector3(b.x - x, b.y - y, b.z - z).Normalized(); + } + public real_t DistanceSquaredTo(Vector3 b) { return (b - this).LengthSquared(); diff --git a/modules/mono/glue/Managed/IgnoredFiles/Variant.cs b/modules/mono/glue/Managed/IgnoredFiles/Variant.cs new file mode 100644 index 0000000000..802140b062 --- /dev/null +++ b/modules/mono/glue/Managed/IgnoredFiles/Variant.cs @@ -0,0 +1,11 @@ + +namespace Godot +{ + public static class Variant + { + public enum Type + { + + } + } +} diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index b690de0d20..7385014a53 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -192,7 +192,7 @@ MonoBoolean godot_icall_DynamicGodotObject_InvokeMember(Object *p_ptr, MonoStrin *r_result = GDMonoMarshal::variant_to_mono_object(result); - return error.error == OK; + return error.error == Variant::CallError::CALL_OK; } MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString *p_name, MonoObject **r_result) { @@ -218,11 +218,16 @@ MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString * return valid; } +MonoString *godot_icall_Object_ToString(Object *p_ptr) { + return GDMonoMarshal::mono_string_from_godot(Variant(p_ptr).operator String()); +} + void godot_register_object_icalls() { mono_add_internal_call("Godot.Object::godot_icall_Object_Ctor", (void *)godot_icall_Object_Ctor); mono_add_internal_call("Godot.Object::godot_icall_Object_Disposed", (void *)godot_icall_Object_Disposed); mono_add_internal_call("Godot.Object::godot_icall_Reference_Disposed", (void *)godot_icall_Reference_Disposed); mono_add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", (void *)godot_icall_Object_ClassDB_get_method); + mono_add_internal_call("Godot.Object::godot_icall_Object_ToString", (void *)godot_icall_Object_ToString); mono_add_internal_call("Godot.Object::godot_icall_Object_weakref", (void *)godot_icall_Object_weakref); mono_add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", (void *)godot_icall_SignalAwaiter_connect); mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", (void *)godot_icall_DynamicGodotObject_SetMemberList); diff --git a/modules/mono/glue/base_object_glue.h b/modules/mono/glue/base_object_glue.h index 9b5224a347..e4ac9fb99d 100644 --- a/modules/mono/glue/base_object_glue.h +++ b/modules/mono/glue/base_object_glue.h @@ -60,6 +60,8 @@ MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString * MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *p_name, MonoObject *p_value); +MonoString *godot_icall_Object_ToString(Object *p_ptr); + // Register internal calls void godot_register_object_icalls(); diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index 1aad1c53bc..4aef5684fd 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -73,8 +73,9 @@ int godot_icall_Array_Count(Array *ptr) { return ptr->size(); } -void godot_icall_Array_Add(Array *ptr, MonoObject *item) { +int godot_icall_Array_Add(Array *ptr, MonoObject *item) { ptr->append(GDMonoMarshal::mono_object_to_variant(item)); + return ptr->size(); } void godot_icall_Array_Clear(Array *ptr) { @@ -142,6 +143,10 @@ void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, *type_class = GDMono::get_singleton()->get_class(type_class_raw); } +MonoString *godot_icall_Array_ToString(Array *ptr) { + return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String()); +} + Dictionary *godot_icall_Dictionary_Ctor() { return memnew(Dictionary); } @@ -263,6 +268,10 @@ void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltyp *type_class = GDMono::get_singleton()->get_class(type_class_raw); } +MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) { + return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String()); +} + void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor); @@ -280,6 +289,7 @@ void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", (void *)godot_icall_Array_Resize); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", (void *)godot_icall_Array_Generic_GetElementTypeInfo); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", (void *)godot_icall_Array_ToString); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor); @@ -298,6 +308,7 @@ void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", (void *)godot_icall_Dictionary_TryGetValue_Generic); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", (void *)godot_icall_Dictionary_Generic_GetValueTypeInfo); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", (void *)godot_icall_Dictionary_ToString); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h index 85a2e243a2..54ed42c3b6 100644 --- a/modules/mono/glue/collections_glue.h +++ b/modules/mono/glue/collections_glue.h @@ -51,7 +51,7 @@ void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value); int godot_icall_Array_Count(Array *ptr); -void godot_icall_Array_Add(Array *ptr, MonoObject *item); +int godot_icall_Array_Add(Array *ptr, MonoObject *item); void godot_icall_Array_Clear(Array *ptr); @@ -71,6 +71,8 @@ Error godot_icall_Array_Resize(Array *ptr, int new_size); void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class); +MonoString *godot_icall_Array_ToString(Array *ptr); + // Dictionary Dictionary *godot_icall_Dictionary_Ctor(); @@ -107,6 +109,8 @@ MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObje void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class); +MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr); + // Register internal calls void godot_register_collections_icalls(); diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index bba7df2c6a..bfb6c13224 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -30,6 +30,7 @@ #include "gd_mono.h" +#include <mono/metadata/environment.h> #include <mono/metadata/exception.h> #include <mono/metadata/mono-config.h> #include <mono/metadata/mono-debug.h> @@ -212,7 +213,7 @@ void GDMono::initialize() { String config_dir; #ifdef TOOLS_ENABLED -#ifdef WINDOWS_ENABLED +#if defined(WINDOWS_ENABLED) mono_reg_info = MonoRegUtils::find_mono(); if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) { @@ -222,7 +223,7 @@ void GDMono::initialize() { if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) { config_dir = mono_reg_info.config_dir; } -#elif OSX_ENABLED +#elif defined(OSX_ENABLED) const char *c_assembly_rootdir = mono_assembly_getrootdir(); const char *c_config_dir = mono_get_config_dir(); @@ -1008,7 +1009,9 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) { if (ScriptDebugger::get_singleton()) ScriptDebugger::get_singleton()->idle_poll(); #endif - abort(); + + exit(mono_environment_exitcode_get()); + GD_UNREACHABLE(); } diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index 92324d73f9..4342f46109 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -55,7 +55,8 @@ String GDMonoClass::get_full_name() const { } MonoType *GDMonoClass::get_mono_type() { - // Care, you cannot compare MonoType pointers + // Careful, you cannot compare two MonoType*. + // There is mono_metadata_type_equal, how is this different from comparing two MonoClass*? return get_mono_type(mono_class); } @@ -260,6 +261,11 @@ bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) { return get_fetched_method_unknown_params(p_name) != NULL; } +bool GDMonoClass::implements_interface(GDMonoClass *p_interface) { + + return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr()); +} + GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) { MethodKey key = MethodKey(p_name, p_params_count); diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index 4af909450e..249422b844 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -135,6 +135,8 @@ public: void fetch_attributes(); void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base); + bool implements_interface(GDMonoClass *p_interface); + GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0); GDMonoMethod *get_method(MonoMethod *p_raw_method); GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name); diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 5e9d4db122..9779797d1a 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -313,6 +313,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name()); ERR_FAIL(); } break; @@ -422,8 +434,8 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ MonoException *exc = NULL; - GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); + GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); + MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_dict) { @@ -434,8 +446,8 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ exc = NULL; - GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); + GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); + MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_array) { @@ -443,6 +455,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ mono_field_set_value(p_object, mono_field, managed); break; } + + if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); + mono_field_set_value(p_object, mono_field, managed); + break; + } } break; default: { diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index dd8c047386..d7962eac8b 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -35,24 +35,12 @@ class GDMonoAssembly; class GDMonoClass; -class IMonoClassMember; class GDMonoField; -class GDMonoProperty; class GDMonoMethod; +class GDMonoProperty; -struct ManagedType { - int type_encoding; - GDMonoClass *type_class; - - ManagedType() : - type_encoding(0), - type_class(NULL) { - } +class IMonoClassMember; - ManagedType(int p_type_encoding, GDMonoClass *p_type_class) : - type_encoding(p_type_encoding), - type_class(p_type_class) { - } -}; +#include "managed_type.h" #endif // GD_MONO_HEADER_H diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index c8cba5cf1b..63bcfe053c 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -111,7 +111,8 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { void unhandled_exception(MonoException *p_exc) { mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well - abort(); + // Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders + GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL); GD_UNREACHABLE(); } diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index ec89df1959..3191cdbd53 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -72,8 +72,11 @@ static void mono_log_callback(const char *log_domain, const char *log_level, con if (fatal) { ERR_PRINTS("Mono: FATAL ERROR, ABORTING! Logfile: " + GDMonoLog::get_singleton()->get_log_file_path() + "\n"); - // If we were to abort without flushing, the log wouldn't get written. + // Make sure to flush before aborting f->flush(); + f->close(); + memdelete(f); + abort(); } } @@ -93,17 +96,9 @@ bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) { return true; } -void GDMonoLog::_open_log_file(const String &p_file_path) { - - log_file = FileAccess::open(p_file_path, FileAccess::WRITE); - - ERR_EXPLAIN("Failed to create log file"); - ERR_FAIL_COND(!log_file); -} - void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { - static const uint64_t MAX_SECS = 5 * 86400; + static const uint64_t MAX_SECS = 5 * 86400; // 5 days DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND(!da); @@ -120,10 +115,9 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { if (!current.ends_with(".txt")) continue; - String name = current.get_basename(); - uint64_t unixtime = (uint64_t)name.to_int64(); + uint64_t modified_time = FileAccess::get_modified_time(da->get_current_dir().plus_file(current)); - if (OS::get_singleton()->get_unix_time() - unixtime > MAX_SECS) { + if (OS::get_singleton()->get_unix_time() - modified_time > MAX_SECS) { da->remove(current); } } @@ -131,28 +125,70 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { da->list_dir_end(); } +static String format(const char *p_fmt, ...) { + va_list args; + + va_start(args, p_fmt); + int len = vsnprintf(NULL, 0, p_fmt, args); + va_end(args); + + len += 1; // for the trailing '/0' + + char *buffer(memnew_arr(char, len)); + + va_start(args, p_fmt); + vsnprintf(buffer, len, p_fmt, args); + va_end(args); + + String res(buffer); + memdelete_arr(buffer); + + return res; +} + void GDMonoLog::initialize() { + CharString log_level = OS::get_singleton()->get_environment("GODOT_MONO_LOG_LEVEL").utf8(); + + if (log_level.length() != 0 && log_level_get_id(log_level.get_data()) == -1) { + ERR_PRINTS(String() + "Mono: Ignoring invalid log level (GODOT_MONO_LOG_LEVEL): " + log_level.get_data()); + log_level = CharString(); + } + + if (log_level.length() == 0) { #ifdef DEBUG_ENABLED - const char *log_level = "debug"; + log_level = String("info").utf8(); #else - const char *log_level = "warning"; + log_level = String("warning").utf8(); #endif + } String logs_dir = GodotSharpDirs::get_mono_logs_dir(); if (_try_create_logs_dir(logs_dir)) { _delete_old_log_files(logs_dir); - log_file_path = logs_dir.plus_file(String::num_int64(OS::get_singleton()->get_unix_time()) + ".txt"); - _open_log_file(log_file_path); + OS::Date date_now = OS::get_singleton()->get_date(); + OS::Time time_now = OS::get_singleton()->get_time(); + int pid = OS::get_singleton()->get_process_id(); + + String log_file_name = format("%d-%02d-%02d %02d:%02d:%02d (%d).txt", + date_now.year, date_now.month, date_now.day, + time_now.hour, time_now.min, time_now.sec, pid); + + log_file_path = logs_dir.plus_file(log_file_name); + + log_file = FileAccess::open(log_file_path, FileAccess::WRITE); + if (!log_file) { + ERR_PRINT("Mono: Cannot create log file"); + } } - mono_trace_set_level_string(log_level); - log_level_id = log_level_get_id(log_level); + mono_trace_set_level_string(log_level.get_data()); + log_level_id = log_level_get_id(log_level.get_data()); if (log_file) { - print_verbose("Mono: Logfile is " + log_file_path); + OS::get_singleton()->print("Mono: Logfile is: %s\n", log_file_path.utf8().get_data()); mono_trace_set_log_handler(mono_log_callback, this); } else { OS::get_singleton()->printerr("Mono: No log file, using default log handler\n"); diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h index a477e5edee..91d0557aa3 100644 --- a/modules/mono/mono_gd/gd_mono_log.h +++ b/modules/mono/mono_gd/gd_mono_log.h @@ -41,7 +41,6 @@ class GDMonoLog { String log_file_path; bool _try_create_logs_dir(const String &p_logs_dir); - void _open_log_file(const String &p_file_path); void _delete_old_log_files(const String &p_logs_dir); static GDMonoLog *singleton; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 7fe8ae608a..de4f3650bd 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -35,7 +35,7 @@ namespace GDMonoMarshal { -Variant::Type managed_to_variant_type(const ManagedType &p_type) { +Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info) { switch (p_type.type_encoding) { case MONO_TYPE_BOOLEAN: return Variant::BOOL; @@ -156,26 +156,66 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { if (CACHED_CLASS(Array) == type_class) { return Variant::ARRAY; } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + return Variant::DICTIONARY; + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + return Variant::ARRAY; + } } break; case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); MonoException *exc = NULL; - GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); + GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); + MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_dict) { + if (r_export_info) { + MonoReflectionType *key_reftype; + MonoReflectionType *value_reftype; + + exc = NULL; + invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes), + reftype, &key_reftype, &value_reftype, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + r_export_info->dictionary.key_type = managed_to_variant_type(ManagedType::from_reftype(key_reftype)); + r_export_info->dictionary.value_type = managed_to_variant_type(ManagedType::from_reftype(value_reftype)); + } + return Variant::DICTIONARY; } exc = NULL; - GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); + GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); + MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_array) { + if (r_export_info) { + MonoReflectionType *elem_reftype; + + exc = NULL; + invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType), + reftype, &elem_reftype, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + r_export_info->array.element_type = managed_to_variant_type(ManagedType::from_reftype(elem_reftype)); + } + + return Variant::ARRAY; + } + + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + return Variant::DICTIONARY; + } + + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { return Variant::ARRAY; } } break; @@ -453,6 +493,14 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty if (CACHED_CLASS(Array) == type_class) { return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); + } } break; case MONO_TYPE_OBJECT: { // Variant @@ -548,8 +596,8 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); MonoException *exc = NULL; - GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); + GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); + MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_dict) { @@ -557,13 +605,21 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } exc = NULL; - GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); + GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); + MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_array) { return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class); } + + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); + } + + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); + } } break; } break; } @@ -577,15 +633,9 @@ Variant mono_object_to_variant(MonoObject *p_obj) { if (!p_obj) return Variant(); - GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj)); - ERR_FAIL_COND_V(!tclass, Variant()); + ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj)); - MonoType *raw_type = tclass->get_mono_type(); - - ManagedType type; - - type.type_encoding = mono_type_get_type(raw_type); - type.type_class = tclass; + ERR_FAIL_COND_V(!type.type_class, Variant()); switch (type.type_encoding) { case MONO_TYPE_BOOLEAN: @@ -717,17 +767,33 @@ Variant mono_object_to_variant(MonoObject *p_obj) { if (CACHED_CLASS(Array) == type_class) { MonoException *exc = NULL; - Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, (MonoObject **)&exc); + Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } if (CACHED_CLASS(Dictionary) == type_class) { MonoException *exc = NULL; - Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, (MonoObject **)&exc); + Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + Dictionary dict; + MonoException *exc = NULL; + invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return dict; + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + Array array; + MonoException *exc = NULL; + invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return array; + } } break; case MONO_TYPE_GENERICINST: { @@ -735,8 +801,8 @@ Variant mono_object_to_variant(MonoObject *p_obj) { MonoException *exc = NULL; - GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); + GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); + MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_dict) { @@ -748,8 +814,8 @@ Variant mono_object_to_variant(MonoObject *p_obj) { exc = NULL; - GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); + GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); + MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_array) { @@ -758,6 +824,22 @@ Variant mono_object_to_variant(MonoObject *p_obj) { UNLIKELY_UNHANDLED_EXCEPTION(exc); return *unbox<Array *>(ret); } + + if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + Dictionary dict; + exc = NULL; + invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return dict; + } + + if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + Array array; + exc = NULL; + invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return array; + } } break; } diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 4f86e02f87..4a73f9e3e6 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -32,6 +32,7 @@ #define GDMONOMARSHAL_H #include "core/variant.h" + #include "gd_mono.h" #include "gd_mono_utils.h" @@ -56,7 +57,25 @@ T unbox(MonoObject *p_obj) { #define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x) #define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x) -Variant::Type managed_to_variant_type(const ManagedType &p_type); +// FIXME: Made this struct in a hurry. It could be done differently. +struct ExportInfo { + struct ArrayInfo { + Variant::Type element_type; + + ArrayInfo() : + element_type(Variant::NIL) {} + } array; + struct DictionaryInfo { + Variant::Type key_type; + Variant::Type value_type; + + DictionaryInfo() : + key_type(Variant::NIL), + value_type(Variant::NIL) {} + } dictionary; +}; + +Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info = NULL); // String diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 6cc1c8afc2..bcf5712d16 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -63,6 +63,7 @@ MonoCache mono_cache; #define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.field_##m_class##_##m_field, m_val) #define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.method_##m_class##_##m_method, m_val) #define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val) +#define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.property_##m_class##_##m_property, m_val) void MonoCache::clear_members() { @@ -81,6 +82,9 @@ void MonoCache::clear_members() { class_String = NULL; class_IntPtr = NULL; + class_System_Collections_IEnumerable = NULL; + class_System_Collections_IDictionary = NULL; + #ifdef DEBUG_ENABLED class_System_Diagnostics_StackTrace = NULL; methodthunk_System_Diagnostics_StackTrace_GetFrames = NULL; @@ -143,12 +147,17 @@ void MonoCache::clear_members() { methodthunk_GodotObject_Dispose = NULL; methodthunk_Array_GetPtr = NULL; methodthunk_Dictionary_GetPtr = NULL; - methodthunk_MarshalUtils_IsArrayGenericType = NULL; - methodthunk_MarshalUtils_IsDictionaryGenericType = NULL; methodthunk_SignalAwaiter_SignalCallback = NULL; methodthunk_SignalAwaiter_FailureCallback = NULL; methodthunk_GodotTaskScheduler_Activate = NULL; + methodthunk_MarshalUtils_TypeIsGenericArray = NULL; + methodthunk_MarshalUtils_TypeIsGenericDictionary = NULL; + methodthunk_MarshalUtils_ArrayGetElementType = NULL; + methodthunk_MarshalUtils_DictionaryGetKeyValueTypes = NULL; + methodthunk_MarshalUtils_EnumerableToArray = NULL; + methodthunk_MarshalUtils_IDictionaryToDictionary = NULL; + task_scheduler_handle = Ref<MonoGCHandle>(); } @@ -178,6 +187,9 @@ void update_corlib_cache() { CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class())); CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class())); + CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable")); + CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary")); + #ifdef DEBUG_ENABLED CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace")); CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_thunk("GetFrames")); @@ -242,12 +254,17 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, (GodotObject_Dispose)CACHED_CLASS(GodotObject)->get_method_thunk("Dispose", 0)); CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method_thunk("GetPtr", 0)); CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method_thunk("GetPtr", 0)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsArrayGenericType, (IsArrayGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsArrayGenericType", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsDictionaryGenericType, (IsDictionaryGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsDictionaryGenericType", 1)); CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("SignalCallback", 1)); CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0)); CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, (TypeIsGenericArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericArray", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, (TypeIsGenericDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericDictionary", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, (EnumerableToArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("EnumerableToArray", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, (IDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IDictionaryToDictionary", 2)); + #ifdef DEBUG_ENABLED CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4)); #endif @@ -615,11 +632,6 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) { } void debug_unhandled_exception(MonoException *p_exc) { -#ifdef DEBUG_ENABLED - GDMonoUtils::debug_send_unhandled_exception_error(p_exc); - if (ScriptDebugger::get_singleton()) - ScriptDebugger::get_singleton()->idle_poll(); -#endif GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well GD_UNREACHABLE(); } @@ -712,7 +724,7 @@ uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool & } void dispose(MonoObject *p_mono_object, MonoException **r_exc) { - invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, (MonoObject **)r_exc); + invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, r_exc); } } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index e88bf1ced9..87610e286c 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -49,18 +49,21 @@ namespace GDMonoUtils { -typedef void (*GodotObject_Dispose)(MonoObject *, MonoObject **); -typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **); -typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **); -typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **); -typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **); -typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **); -typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **); -typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); -typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **); -typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); -typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **); -typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **); +typedef void (*GodotObject_Dispose)(MonoObject *, MonoException **); +typedef Array *(*Array_GetPtr)(MonoObject *, MonoException **); +typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoException **); +typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoException **); +typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoException **); +typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoException **); +typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoException **); +typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoException **); + +typedef MonoBoolean (*TypeIsGenericArray)(MonoReflectionType *, MonoException **); +typedef MonoBoolean (*TypeIsGenericDictionary)(MonoReflectionType *, MonoException **); +typedef MonoBoolean (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **); +typedef MonoBoolean (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **); +typedef void (*EnumerableToArray)(MonoObject *, Array *, MonoException **); +typedef void (*IDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **); struct MonoCache { @@ -83,6 +86,9 @@ struct MonoCache { GDMonoClass *class_String; GDMonoClass *class_IntPtr; + GDMonoClass *class_System_Collections_IEnumerable; + GDMonoClass *class_System_Collections_IDictionary; + #ifdef DEBUG_ENABLED GDMonoClass *class_System_Diagnostics_StackTrace; StackTrace_GetFrames methodthunk_System_Diagnostics_StackTrace_GetFrames; @@ -146,12 +152,17 @@ struct MonoCache { GodotObject_Dispose methodthunk_GodotObject_Dispose; Array_GetPtr methodthunk_Array_GetPtr; Dictionary_GetPtr methodthunk_Dictionary_GetPtr; - IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType; - IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType; SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback; SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback; GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate; + TypeIsGenericArray methodthunk_MarshalUtils_TypeIsGenericArray; + TypeIsGenericDictionary methodthunk_MarshalUtils_TypeIsGenericDictionary; + ArrayGetElementType methodthunk_MarshalUtils_ArrayGetElementType; + DictionaryGetKeyValueTypes methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; + EnumerableToArray methodthunk_MarshalUtils_EnumerableToArray; + IDictionaryToDictionary methodthunk_MarshalUtils_IDictionaryToDictionary; + Ref<MonoGCHandle> task_scheduler_handle; bool corlib_cache_updated; @@ -255,6 +266,7 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc); #define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field) #define CACHED_METHOD(m_class, m_method) (GDMonoUtils::mono_cache.method_##m_class##_##m_method) #define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method) +#define CACHED_PROPERTY(m_class, m_property) (GDMonoUtils::mono_cache.property_##m_class##_##m_property) #ifdef REAL_T_IS_DOUBLE #define REAL_T_MONOCLASS CACHED_CLASS_RAW(double) diff --git a/modules/mono/mono_gd/managed_type.cpp b/modules/mono/mono_gd/managed_type.cpp new file mode 100644 index 0000000000..9f736b71cd --- /dev/null +++ b/modules/mono/mono_gd/managed_type.cpp @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* managed_type.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "managed_type.h" + +#include "gd_mono.h" +#include "gd_mono_class.h" + +ManagedType ManagedType::from_class(GDMonoClass *p_class) { + return ManagedType(mono_type_get_type(p_class->get_mono_type()), p_class); +} + +ManagedType ManagedType::from_class(MonoClass *p_mono_class) { + GDMonoClass *tclass = GDMono::get_singleton()->get_class(p_mono_class); + ERR_FAIL_COND_V(!tclass, ManagedType()); + + return ManagedType(mono_type_get_type(tclass->get_mono_type()), tclass); +} + +ManagedType ManagedType::from_type(MonoType *p_mono_type) { + MonoClass *mono_class = mono_class_from_mono_type(p_mono_type); + GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_class); + ERR_FAIL_COND_V(!tclass, ManagedType()); + + return ManagedType(mono_type_get_type(p_mono_type), tclass); +} + +ManagedType ManagedType::from_reftype(MonoReflectionType *p_mono_reftype) { + MonoType *mono_type = mono_reflection_type_get_type(p_mono_reftype); + return from_type(mono_type); +} diff --git a/modules/mono/mono_gd/managed_type.h b/modules/mono/mono_gd/managed_type.h new file mode 100644 index 0000000000..a537e56aea --- /dev/null +++ b/modules/mono/mono_gd/managed_type.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* managed_type.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 MANAGED_TYPE_H +#define MANAGED_TYPE_H + +#include <mono/metadata/object.h> + +#include "gd_mono_header.h" + +struct ManagedType { + int type_encoding; + GDMonoClass *type_class; + + static ManagedType from_class(GDMonoClass *p_class); + static ManagedType from_class(MonoClass *p_mono_class); + static ManagedType from_type(MonoType *p_mono_type); + static ManagedType from_reftype(MonoReflectionType *p_mono_reftype); + + ManagedType() : + type_encoding(0), + type_class(NULL) { + } + + ManagedType(int p_type_encoding, GDMonoClass *p_type_class) : + type_encoding(p_type_encoding), + type_class(p_type_class) { + } +}; + +#endif // MANAGED_TYPE_H diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index 5051d83694..5d37e8212f 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -100,7 +100,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc MonoException *exc = NULL; GD_MONO_BEGIN_RUNTIME_INVOKE; - invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback), get_target(), signal_args, (MonoObject **)&exc); + invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback), get_target(), signal_args, &exc); GD_MONO_END_RUNTIME_INVOKE; if (exc) { @@ -132,7 +132,7 @@ SignalAwaiterHandle::~SignalAwaiterHandle() { if (awaiter) { MonoException *exc = NULL; GD_MONO_BEGIN_RUNTIME_INVOKE; - invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback), awaiter, (MonoObject **)&exc); + invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback), awaiter, &exc); GD_MONO_END_RUNTIME_INVOKE; if (exc) { diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index d7f9b22c31..98aeadc8c8 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -202,9 +202,9 @@ String find_msbuild_tools_path() { } // Since VS2019, the directory is simply named "Current" - String msBuildDirectory = val + "MSBuild\\Current\\Bin"; - if (DirAccess::exists(msBuildDirectory)) { - return msBuildDirectory; + String msbuild_dir = val + "MSBuild\\Current\\Bin"; + if (DirAccess::exists(msbuild_dir)) { + return msbuild_dir; } // Directory name "15.0" is used in VS 2017 diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h index 276db8830b..488cc2619a 100644 --- a/modules/mono/utils/thread_local.h +++ b/modules/mono/utils/thread_local.h @@ -39,7 +39,7 @@ #error Platform or compiler not supported #endif -#ifdef __GNUC__ +#if defined(__GNUC__) #ifdef HAVE_GCC___THREAD #define _THREAD_LOCAL_(m_t) __thread m_t @@ -47,7 +47,7 @@ #define USE_CUSTOM_THREAD_LOCAL #endif -#elif _MSC_VER +#elif defined(_MSC_VER) #ifdef HAVE_DECLSPEC_THREAD #define _THREAD_LOCAL_(m_t) __declspec(thread) m_t diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml index 25f104b221..4caf6db1bb 100644 --- a/modules/opensimplex/doc_classes/NoiseTexture.xml +++ b/modules/opensimplex/doc_classes/NoiseTexture.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml index b5bc35df69..dc7fc7fe31 100644 --- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml +++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml @@ -23,8 +23,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_image"> <return type="Image"> @@ -37,6 +35,14 @@ Generate a noise image with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters. </description> </method> + <method name="get_noise_1d"> + <return type="float"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <description> + </description> + </method> <method name="get_noise_2d"> <return type="float"> </return> diff --git a/modules/opensimplex/open_simplex_noise.cpp b/modules/opensimplex/open_simplex_noise.cpp index b2d6b5e718..3a3a698e5c 100644 --- a/modules/opensimplex/open_simplex_noise.cpp +++ b/modules/opensimplex/open_simplex_noise.cpp @@ -173,6 +173,7 @@ void OpenSimplexNoise::_bind_methods() { 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_1d", "x"), &OpenSimplexNoise::get_noise_1d); 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); @@ -187,6 +188,11 @@ void OpenSimplexNoise::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "lacunarity", PROPERTY_HINT_RANGE, "0.1,4.0,0.01"), "set_lacunarity", "get_lacunarity"); } +float OpenSimplexNoise::get_noise_1d(float x) { + + return get_noise_2d(x, 1.0); +} + float OpenSimplexNoise::get_noise_2d(float x, float y) { x /= period; diff --git a/modules/opensimplex/open_simplex_noise.h b/modules/opensimplex/open_simplex_noise.h index ac4c6e361f..96885f5893 100644 --- a/modules/opensimplex/open_simplex_noise.h +++ b/modules/opensimplex/open_simplex_noise.h @@ -73,6 +73,7 @@ public: Ref<Image> get_image(int p_width, int p_height); Ref<Image> get_seamless_image(int p_size); + float get_noise_1d(float x); float get_noise_2d(float x, float y); float get_noise_3d(float x, float y, float z); float get_noise_4d(float x, float y, float z, float w); diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml index 20857572f3..0538753a47 100644 --- a/modules/regex/doc_classes/RegEx.xml +++ b/modules/regex/doc_classes/RegEx.xml @@ -38,8 +38,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml index 9efec91bdc..245201006d 100644 --- a/modules/regex/doc_classes/RegExMatch.xml +++ b/modules/regex/doc_classes/RegExMatch.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_end" qualifiers="const"> <return type="int"> diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp index d89839f06a..4f38357aa1 100644 --- a/modules/squish/image_compress_squish.cpp +++ b/modules/squish/image_compress_squish.cpp @@ -119,7 +119,8 @@ void image_compress_squish(Image *p_image, float p_lossy_quality, Image::Compres case Image::FORMAT_RGBA5551: { dc = Image::DETECTED_RGBA; } break; - default: {} + default: { + } } } diff --git a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml index 574ff1ff2a..388c5e81ed 100644 --- a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml +++ b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml index ade485e717..4dd77fed34 100644 --- a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml +++ b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/thekla_unwrap/SCsub b/modules/thekla_unwrap/SCsub deleted file mode 100644 index c47c760d5f..0000000000 --- a/modules/thekla_unwrap/SCsub +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python - -import platform - -Import('env') -Import('env_modules') - -env_thekla_unwrap = env_modules.Clone() - -# Thirdparty source files -if env['builtin_thekla_atlas']: - thirdparty_dir = "#thirdparty/thekla_atlas/" - thirdparty_sources = [ - "nvcore/Memory.cpp", - "nvcore/Debug.cpp", - "nvcore/StrLib.cpp", - "nvcore/FileSystem.cpp", - "nvcore/RadixSort.cpp", - "nvmath/Basis.cpp", - "nvmath/ConvexHull.cpp", - "nvmath/Fitting.cpp", - "nvmath/Plane.cpp", - "nvmath/ProximityGrid.cpp", - "nvmath/Random.cpp", - "nvmath/Solver.cpp", - "nvmath/Sparse.cpp", - "nvmath/TypeSerialization.cpp", - "poshlib/posh.c", - "nvimage/BitMap.cpp", - "nvimage/Image.cpp", - "nvmesh/BaseMesh.cpp", - "nvmesh/MeshBuilder.cpp", - "nvmesh/TriMesh.cpp", - "nvmesh/QuadTriMesh.cpp", - "nvmesh/MeshTopology.cpp", - "nvmesh/halfedge/Edge.cpp", - "nvmesh/halfedge/Mesh.cpp", - "nvmesh/halfedge/Face.cpp", - "nvmesh/halfedge/Vertex.cpp", - "nvmesh/geometry/Bounds.cpp", - "nvmesh/geometry/Measurements.cpp", - "nvmesh/raster/Raster.cpp", - "nvmesh/param/Atlas.cpp", - "nvmesh/param/AtlasBuilder.cpp", - "nvmesh/param/AtlasPacker.cpp", - "nvmesh/param/LeastSquaresConformalMap.cpp", - "nvmesh/param/OrthogonalProjectionMap.cpp", - "nvmesh/param/ParameterizationQuality.cpp", - "nvmesh/param/SingleFaceMap.cpp", - "nvmesh/param/Util.cpp", - "nvmesh/weld/VertexWeld.cpp", - "nvmesh/weld/Snap.cpp", - "thekla/thekla_atlas.cpp" - ] - thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - - env_thekla_unwrap.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "poshlib", thirdparty_dir + "nvcore", thirdparty_dir + "nvmesh"]) - - # upstream uses c++11 - if (not env.msvc): - env_thekla_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_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_FREEBSD", "-DPOSH_COMPILER_GCC"]) - elif platform.system() == "OpenBSD": - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_OPENBSD", "-DPOSH_COMPILER_GCC"]) - else: - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_LINUX", "-DPOSH_COMPILER_GCC"]) - elif env["platform"] == 'osx': - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_DARWIN", "-DPOSH_COMPILER_GCC"]) - elif env["platform"] == 'windows': - if env.msvc: - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_WIN32", "-DNV_CC_MSVC", "-DPOSH_COMPILER_MSVC" ]) - else: - 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 deleted file mode 100644 index fad6095064..0000000000 --- a/modules/thekla_unwrap/config.py +++ /dev/null @@ -1,6 +0,0 @@ -def can_build(env, platform): - #return (env['tools'] and platform not in ["android", "ios"]) - return False - -def configure(env): - pass diff --git a/modules/thekla_unwrap/register_types.cpp b/modules/thekla_unwrap/register_types.cpp deleted file mode 100644 index 2c35adfb83..0000000000 --- a/modules/thekla_unwrap/register_types.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/*************************************************************************/ -/* register_types.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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/thekla_atlas/thekla/thekla_atlas.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 thekla_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 - Thekla::Atlas_Input_Mesh input_mesh; - input_mesh.face_array = new Thekla::Atlas_Input_Face[p_index_count / 3]; - for (int i = 0; i < p_index_count / 3; i++) { - input_mesh.face_array[i].vertex_index[0] = p_indices[i * 3 + 0]; - input_mesh.face_array[i].vertex_index[1] = p_indices[i * 3 + 1]; - input_mesh.face_array[i].vertex_index[2] = p_indices[i * 3 + 2]; - //printf("face %i - %i, %i, %i - mat %i\n", i, input_mesh.face_array[i].vertex_index[0], input_mesh.face_array[i].vertex_index[1], input_mesh.face_array[i].vertex_index[2], p_face_materials[i]); - input_mesh.face_array[i].material_index = p_face_materials[i]; - } - input_mesh.vertex_array = new Thekla::Atlas_Input_Vertex[p_vertex_count]; - for (int i = 0; i < p_vertex_count; i++) { - input_mesh.vertex_array[i].first_colocal = i; //wtf - for (int j = 0; j < 3; j++) { - input_mesh.vertex_array[i].position[j] = p_vertices[i * 3 + j]; - input_mesh.vertex_array[i].normal[j] = p_normals[i * 3 + j]; - } - input_mesh.vertex_array[i].uv[0] = 0; - input_mesh.vertex_array[i].uv[1] = 0; - //printf("vertex %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].position[0], input_mesh.vertex_array[i].position[1], input_mesh.vertex_array[i].position[2]); - //printf("normal %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].normal[0], input_mesh.vertex_array[i].normal[1], input_mesh.vertex_array[i].normal[2]); - } - input_mesh.face_count = p_index_count / 3; - input_mesh.vertex_count = p_vertex_count; - - //set up options - Thekla::Atlas_Options options; - Thekla::atlas_set_default_options(&options); - options.packer_options.witness.packing_quality = 1; - options.packer_options.witness.texel_area = 1.0 / p_texel_size; - options.packer_options.witness.conservative = false; - - //generate - Thekla::Atlas_Error err; - Thekla::Atlas_Output_Mesh *output = atlas_generate(&input_mesh, &options, &err); - - delete[] input_mesh.face_array; - delete[] input_mesh.vertex_array; - - if (output == NULL) { - ERR_PRINT("could not generate atlas output mesh"); - return false; - } - - if (err != Thekla::Atlas_Error_Success) { - printf("error with atlas\n"); - } else { - *r_vertex = (int *)malloc(sizeof(int) * output->vertex_count); - *r_uv = (float *)malloc(sizeof(float) * output->vertex_count * 3); - *r_index = (int *)malloc(sizeof(int) * output->index_count); - - // printf("w: %i, h: %i\n", output->atlas_width, output->atlas_height); - for (int i = 0; i < output->vertex_count; i++) { - (*r_vertex)[i] = output->vertex_array[i].xref; - (*r_uv)[i * 2 + 0] = output->vertex_array[i].uv[0] / output->atlas_width; - (*r_uv)[i * 2 + 1] = output->vertex_array[i].uv[1] / output->atlas_height; - // printf("uv: %f,%f\n", (*r_uv)[i * 2 + 0], (*r_uv)[i * 2 + 1]); - } - *r_vertex_count = output->vertex_count; - - for (int i = 0; i < output->index_count; i++) { - (*r_index)[i] = output->index_array[i]; - } - - *r_index_count = output->index_count; - - *r_size_hint_x = output->atlas_height; - *r_size_hint_y = output->atlas_width; - } - - if (output) { - atlas_free(output); - } - - return err == Thekla::Atlas_Error_Success; -} - -void register_thekla_unwrap_types() { - - array_mesh_lightmap_unwrap_callback = thekla_mesh_lightmap_unwrap_callback; -} - -void unregister_thekla_unwrap_types() { -} diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index 2bd8ad862f..696101e252 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_file"> <return type="String"> diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index e69d9a0ae3..bd84a28c84 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -107,22 +107,31 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f } } - if (idxR == -1) { - ERR_PRINT("TinyEXR: R channel not found."); - // @todo { free exr_image } - return ERR_FILE_CORRUPT; - } + if (exr_header.num_channels == 1) { + // Grayscale channel only. + idxR = 0; + idxG = 0; + idxB = 0; + idxA = 0; + } else { + // Assume RGB(A) + if (idxR == -1) { + ERR_PRINT("TinyEXR: R channel not found."); + // @todo { free exr_image } + return ERR_FILE_CORRUPT; + } - if (idxG == -1) { - ERR_PRINT("TinyEXR: G channel not found.") - // @todo { free exr_image } - return ERR_FILE_CORRUPT; - } + if (idxG == -1) { + ERR_PRINT("TinyEXR: G channel not found.") + // @todo { free exr_image } + return ERR_FILE_CORRUPT; + } - if (idxB == -1) { - ERR_PRINT("TinyEXR: B channel not found.") - // @todo { free exr_image } - return ERR_FILE_CORRUPT; + if (idxB == -1) { + ERR_PRINT("TinyEXR: B channel not found.") + // @todo { free exr_image } + return ERR_FILE_CORRUPT; + } } // EXR image data loaded, now parse it into Godot-friendly image data diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml index 0f967c993b..0516ea9d54 100644 --- a/modules/upnp/doc_classes/UPNP.xml +++ b/modules/upnp/doc_classes/UPNP.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_device"> <return type="void"> diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml index c9b695a651..1d9a728365 100644 --- a/modules/upnp/doc_classes/UPNPDevice.xml +++ b/modules/upnp/doc_classes/UPNPDevice.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_port_mapping" qualifiers="const"> <return type="int"> diff --git a/modules/vhacd/SCsub b/modules/vhacd/SCsub new file mode 100644 index 0000000000..11cd5f4743 --- /dev/null +++ b/modules/vhacd/SCsub @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_vhacd = env_modules.Clone() + +# Thirdparty source files + +thirdparty_dir = "#thirdparty/vhacd/" + +thirdparty_sources = [ +"src/vhacdManifoldMesh.cpp", +"src/FloatMath.cpp", +"src/vhacdMesh.cpp", +"src/vhacdICHull.cpp", +"src/vhacdVolume.cpp", +"src/VHACD-ASYNC.cpp", +"src/btAlignedAllocator.cpp", +"src/vhacdRaycastMesh.cpp", +"src/VHACD.cpp", +"src/btConvexHullComputer.cpp" +] + +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +env_vhacd.Append(CPPPATH=[thirdparty_dir+"/inc"]) +env_vhacd.Append(CPPFLAGS=["-DGODOT_ENET"]) + +# upstream uses c++11 +if not env.msvc: + env_vhacd.Append(CCFLAGS="-std=c++11") + +env_thirdparty = env_vhacd.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + +env_vhacd.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/vhacd/config.py b/modules/vhacd/config.py new file mode 100644 index 0000000000..9ced70d2fb --- /dev/null +++ b/modules/vhacd/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + return True + +def configure(env): + pass + diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp new file mode 100644 index 0000000000..076a1738ab --- /dev/null +++ b/modules/vhacd/register_types.cpp @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "scene/resources/mesh.h" +#include "thirdparty/vhacd/public/VHACD.h" + +static Vector<Vector<Face3> > convex_decompose(const Vector<Face3> &p_faces) { + + Vector<float> vertices; + vertices.resize(p_faces.size() * 9); + Vector<uint32_t> indices; + indices.resize(p_faces.size() * 3); + + for (int i = 0; i < p_faces.size(); i++) { + for (int j = 0; j < 3; j++) { + vertices.write[i * 9 + j * 3 + 0] = p_faces[i].vertex[j].x; + vertices.write[i * 9 + j * 3 + 1] = p_faces[i].vertex[j].y; + vertices.write[i * 9 + j * 3 + 2] = p_faces[i].vertex[j].z; + indices.write[i * 3 + j] = i * 3 + j; + } + } + + VHACD::IVHACD *decomposer = VHACD::CreateVHACD(); + VHACD::IVHACD::Parameters params; + decomposer->Compute(vertices.ptr(), vertices.size() / 3, indices.ptr(), indices.size() / 3, params); + + int hull_count = decomposer->GetNConvexHulls(); + + Vector<Vector<Face3> > ret; + + for (int i = 0; i < hull_count; i++) { + Vector<Face3> triangles; + VHACD::IVHACD::ConvexHull hull; + decomposer->GetConvexHull(i, hull); + triangles.resize(hull.m_nTriangles); + for (uint32_t j = 0; j < hull.m_nTriangles; j++) { + Face3 f; + for (int k = 0; k < 3; k++) { + for (int l = 0; l < 3; l++) { + f.vertex[k][l] = hull.m_points[hull.m_triangles[j * 3 + k] * 3 + l]; + } + } + triangles.write[j] = f; + } + ret.push_back(triangles); + } + + decomposer->Clean(); + decomposer->Release(); + + return ret; +} + +void register_vhacd_types() { + Mesh::convex_composition_function = convex_decompose; +} + +void unregister_vhacd_types() { + Mesh::convex_composition_function = NULL; +} diff --git a/drivers/gl_context/context_gl.cpp b/modules/vhacd/register_types.h index 759ab6f3ec..cb948faf44 100644 --- a/drivers/gl_context/context_gl.cpp +++ b/modules/vhacd/register_types.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* context_gl.cpp */ +/* register_types.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,28 +28,5 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "context_gl.h" - -#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) - -ContextGL *ContextGL::singleton = NULL; - -ContextGL *ContextGL::get_singleton() { - - return singleton; -} - -ContextGL::ContextGL() { - - ERR_FAIL_COND(singleton); - - singleton = this; -} - -ContextGL::~ContextGL() { - - if (singleton == this) - singleton = NULL; -} - -#endif +void register_vhacd_types(); +void unregister_vhacd_types(); diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index f4a9bc68e6..8f0d881f30 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -11,8 +11,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_custom_signal"> <return type="void"> diff --git a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml index ce49cdf3a0..b591dc9a51 100644 --- a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index dd14d60327..74d5f29c02 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -204,7 +202,14 @@ <constant name="COLORN" value="62" enum="BuiltinFunc"> Return the [Color] with the given name and alpha ranging from 0 to 1. Note: names are defined in color_names.inc. </constant> - <constant name="FUNC_MAX" value="63" enum="BuiltinFunc"> + <constant name="MATH_SMOOTHSTEP" value="63" enum="BuiltinFunc"> + Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [code]MATH_LERP[/code], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula: + [codeblock] + var t = clamp((weight - from) / (to - from), 0.0, 1.0) + return t * t * (3.0 - 2.0 * t) + [/codeblock] + </constant> + <constant name="FUNC_MAX" value="64" enum="BuiltinFunc"> The maximum value the [member function] property can have. </constant> </constants> diff --git a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml index 32b5924cdc..6e48e7c416 100644 --- a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptComment.xml b/modules/visual_script/doc_classes/VisualScriptComment.xml index 990e0ecb85..9652654610 100644 --- a/modules/visual_script/doc_classes/VisualScriptComment.xml +++ b/modules/visual_script/doc_classes/VisualScriptComment.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptCondition.xml b/modules/visual_script/doc_classes/VisualScriptCondition.xml index 94c075205d..12d85429cf 100644 --- a/modules/visual_script/doc_classes/VisualScriptCondition.xml +++ b/modules/visual_script/doc_classes/VisualScriptCondition.xml @@ -15,8 +15,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptConstant.xml b/modules/visual_script/doc_classes/VisualScriptConstant.xml index 0fc4e87db4..b15af8e4ea 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstant.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptConstructor.xml b/modules/visual_script/doc_classes/VisualScriptConstructor.xml index 05fc3f318d..8da5055d83 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstructor.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstructor.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_constructor" qualifiers="const"> <return type="Dictionary"> diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml index 0ad4e7c1f5..2ad399c951 100644 --- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_get_caption" qualifiers="virtual"> <return type="String"> diff --git a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml index b933a25f1d..a0eed5b753 100644 --- a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml +++ b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptEditor.xml b/modules/visual_script/doc_classes/VisualScriptEditor.xml index be4606b57c..add2eb2275 100644 --- a/modules/visual_script/doc_classes/VisualScriptEditor.xml +++ b/modules/visual_script/doc_classes/VisualScriptEditor.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_custom_node"> <return type="void"> diff --git a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml index 3282269811..623aa1ba86 100644 --- a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml index 3e52fb818c..525389366a 100644 --- a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml +++ b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptExpression.xml b/modules/visual_script/doc_classes/VisualScriptExpression.xml index 4760685bfb..eb6cdbb302 100644 --- a/modules/visual_script/doc_classes/VisualScriptExpression.xml +++ b/modules/visual_script/doc_classes/VisualScriptExpression.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptFunction.xml b/modules/visual_script/doc_classes/VisualScriptFunction.xml index dc021196cd..152b48ca89 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunction.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunction.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml index e978437542..e4d0bb7672 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml index a5e15b8da2..a8e820e6ea 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="connect_to_signal"> <return type="void"> diff --git a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml index 2d609ed262..4099a351e6 100644 --- a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml index 16499e9ec9..ba0dc1a3d0 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml index 06844ac4ae..09a8f2c8ad 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptInputAction.xml b/modules/visual_script/doc_classes/VisualScriptInputAction.xml index 01887e0764..4bd1430cf6 100644 --- a/modules/visual_script/doc_classes/VisualScriptInputAction.xml +++ b/modules/visual_script/doc_classes/VisualScriptInputAction.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptIterator.xml b/modules/visual_script/doc_classes/VisualScriptIterator.xml index 496c24dee4..782bbfae20 100644 --- a/modules/visual_script/doc_classes/VisualScriptIterator.xml +++ b/modules/visual_script/doc_classes/VisualScriptIterator.xml @@ -15,8 +15,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml index cd7286b59b..5d061ea79f 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml index f8fe2f4b6b..049195d890 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml index 733b48203d..430f9ee7d4 100644 --- a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptNode.xml b/modules/visual_script/doc_classes/VisualScriptNode.xml index 86bd469d5c..89fb5c81f8 100644 --- a/modules/visual_script/doc_classes/VisualScriptNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptNode.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_default_input_value" qualifiers="const"> <return type="Variant"> diff --git a/modules/visual_script/doc_classes/VisualScriptOperator.xml b/modules/visual_script/doc_classes/VisualScriptOperator.xml index d722477653..a0827972e0 100644 --- a/modules/visual_script/doc_classes/VisualScriptOperator.xml +++ b/modules/visual_script/doc_classes/VisualScriptOperator.xml @@ -11,8 +11,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptPreload.xml b/modules/visual_script/doc_classes/VisualScriptPreload.xml index b811252c42..b3b39691c9 100644 --- a/modules/visual_script/doc_classes/VisualScriptPreload.xml +++ b/modules/visual_script/doc_classes/VisualScriptPreload.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml index 7f652b7012..697a31ca46 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml index ef9938c6a7..c41e3781d4 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml index 2a5c56cf69..40862b810b 100644 --- a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml +++ b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptReturn.xml b/modules/visual_script/doc_classes/VisualScriptReturn.xml index 7daddc7639..40c8963efe 100644 --- a/modules/visual_script/doc_classes/VisualScriptReturn.xml +++ b/modules/visual_script/doc_classes/VisualScriptReturn.xml @@ -13,8 +13,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml index 8604a0f5eb..81ef4ceedc 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml index 72a2faaa78..84718ba119 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptSelect.xml b/modules/visual_script/doc_classes/VisualScriptSelect.xml index c87f77ea65..3b1e7f7f02 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelect.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelect.xml @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptSelf.xml b/modules/visual_script/doc_classes/VisualScriptSelf.xml index 42c75e56a6..231e7d8f0d 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelf.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelf.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptSequence.xml b/modules/visual_script/doc_classes/VisualScriptSequence.xml index c26c72dd50..26af15b404 100644 --- a/modules/visual_script/doc_classes/VisualScriptSequence.xml +++ b/modules/visual_script/doc_classes/VisualScriptSequence.xml @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptSubCall.xml b/modules/visual_script/doc_classes/VisualScriptSubCall.xml index 712b4ed09b..51b0093209 100644 --- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptSubCall.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_subcall" qualifiers="virtual"> <return type="Variant"> diff --git a/modules/visual_script/doc_classes/VisualScriptSwitch.xml b/modules/visual_script/doc_classes/VisualScriptSwitch.xml index 0053733b34..35929b16ac 100644 --- a/modules/visual_script/doc_classes/VisualScriptSwitch.xml +++ b/modules/visual_script/doc_classes/VisualScriptSwitch.xml @@ -17,8 +17,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml index 85b3980e21..a9e920bca6 100644 --- a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml +++ b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml index 27bf223aac..e1c7c4e4d4 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml index c55c72d55e..7339482908 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml @@ -13,8 +13,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptWhile.xml b/modules/visual_script/doc_classes/VisualScriptWhile.xml index b7ed56e7d2..77211fa088 100644 --- a/modules/visual_script/doc_classes/VisualScriptWhile.xml +++ b/modules/visual_script/doc_classes/VisualScriptWhile.xml @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptYield.xml b/modules/visual_script/doc_classes/VisualScriptYield.xml index 5aa0f76a55..9f61a5f88c 100644 --- a/modules/visual_script/doc_classes/VisualScriptYield.xml +++ b/modules/visual_script/doc_classes/VisualScriptYield.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml index 8e3b2aec1d..6791375182 100644 --- a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index 0d379205e4..d207656705 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -102,6 +102,7 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "var2bytes", "bytes2var", "color_named", + "smoothstep", }; VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::find_function(const String &p_string) { @@ -204,6 +205,7 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { return 2; case MATH_LERP: case MATH_INVERSE_LERP: + case MATH_SMOOTHSTEP: case MATH_DECTIME: case MATH_WRAP: case MATH_WRAPF: @@ -337,6 +339,14 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const else return PropertyInfo(Variant::REAL, "ostop"); } break; + case MATH_SMOOTHSTEP: { + if (p_idx == 0) + return PropertyInfo(Variant::REAL, "from"); + else if (p_idx == 1) + return PropertyInfo(Variant::REAL, "to"); + else + return PropertyInfo(Variant::REAL, "weight"); + } break; case MATH_DECTIME: { if (p_idx == 0) return PropertyInfo(Variant::REAL, "value"); @@ -569,6 +579,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons case MATH_LERP: case MATH_INVERSE_LERP: case MATH_RANGE_LERP: + case MATH_SMOOTHSTEP: case MATH_DECTIME: { t = Variant::REAL; @@ -899,6 +910,12 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in VALIDATE_ARG_NUM(4); *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); } break; + case VisualScriptBuiltinFunc::MATH_SMOOTHSTEP: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::smoothstep((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; case VisualScriptBuiltinFunc::MATH_DECTIME: { VALIDATE_ARG_NUM(0); @@ -1270,7 +1287,8 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in *r_return = String(color); } break; - default: {} + default: { + } } } @@ -1379,6 +1397,7 @@ void VisualScriptBuiltinFunc::_bind_methods() { BIND_ENUM_CONSTANT(VAR_TO_BYTES); BIND_ENUM_CONSTANT(BYTES_TO_VAR); BIND_ENUM_CONSTANT(COLORN); + BIND_ENUM_CONSTANT(MATH_SMOOTHSTEP); BIND_ENUM_CONSTANT(FUNC_MAX); } @@ -1433,6 +1452,7 @@ void register_visual_script_builtin_func_node() { VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/inverse_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_INVERSE_LERP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/range_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANGE_LERP>); + VisualScriptLanguage::singleton->add_register_func("functions/built_in/smoothstep", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SMOOTHSTEP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>); diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index 2d8454ddd4..50854c16b1 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -101,6 +101,7 @@ public: VAR_TO_BYTES, BYTES_TO_VAR, COLORN, + MATH_SMOOTHSTEP, FUNC_MAX }; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 7e54891d97..6e7e566206 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -579,7 +579,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) { if (gnode->is_comment()) sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox("comment", "GraphNode"); - Color c = sbf->get_border_color(MARGIN_TOP); + Color c = sbf->get_border_color(); c.a = 1; if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); @@ -3054,10 +3054,10 @@ void VisualScriptEditor::_notification(int p_what) { Ref<StyleBoxFlat> sb = tm->get_stylebox("frame", "GraphNode"); if (!sb.is_null()) { Ref<StyleBoxFlat> frame_style = sb->duplicate(); - Color c = sb->get_border_color(MARGIN_TOP); + Color c = sb->get_border_color(); Color cn = E->get().second; cn.a = c.a; - frame_style->set_border_color_all(cn); + frame_style->set_border_color(cn); node_styles[E->get().first] = frame_style; } } diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index a76e4bc36f..772092fabe 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -1032,7 +1032,8 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; - default: {}; + default: { + }; } if (op == Variant::OP_MAX) { //stop appending stuff diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index bb0435a679..8fa7d2c0d4 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -1562,7 +1562,8 @@ public: case VisualScriptPropertySet::ASSIGN_OP_BIT_XOR: { value = Variant::evaluate(Variant::OP_BIT_XOR, value, p_argument); } break; - default: {} + default: { + } } if (index != StringName()) { diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml index 33dff0e93b..ff11bbb37d 100644 --- a/modules/webm/doc_classes/VideoStreamWebm.xml +++ b/modules/webm/doc_classes/VideoStreamWebm.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_file"> <return type="String"> diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub new file mode 100644 index 0000000000..446bd530c2 --- /dev/null +++ b/modules/webrtc/SCsub @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +# Thirdparty source files + +env_webrtc = env_modules.Clone() +use_gdnative = env_webrtc["module_gdnative_enabled"] + +if use_gdnative: # GDNative is retained in Javascript for export compatibility + env_webrtc.Append(CPPDEFINES=['WEBRTC_GDNATIVE_ENABLED']) + gdnative_includes = ["#modules/gdnative/include/"] + env_webrtc.Append(CPPPATH=gdnative_includes) + +env_webrtc.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/webrtc/config.py b/modules/webrtc/config.py new file mode 100644 index 0000000000..5ed245bad2 --- /dev/null +++ b/modules/webrtc/config.py @@ -0,0 +1,13 @@ +def can_build(env, platform): + return True + +def configure(env): + pass + +def get_doc_classes(): + return [ + "WebRTCPeer" + ] + +def get_doc_path(): + return "doc_classes" diff --git a/modules/webrtc/doc_classes/WebRTCPeer.xml b/modules/webrtc/doc_classes/WebRTCPeer.xml new file mode 100644 index 0000000000..18d1345623 --- /dev/null +++ b/modules/webrtc/doc_classes/WebRTCPeer.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCPeer" inherits="PacketPeer" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="add_ice_candidate"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="media" type="String"> + </argument> + <argument index="1" name="index" type="int"> + </argument> + <argument index="2" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="create_offer"> + <return type="int" enum="Error"> + </return> + <description> + </description> + </method> + <method name="get_connection_state" qualifiers="const"> + <return type="int" enum="WebRTCPeer.ConnectionState"> + </return> + <description> + </description> + </method> + <method name="poll"> + <return type="int" enum="Error"> + </return> + <description> + </description> + </method> + <method name="set_local_description"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="type" type="String"> + </argument> + <argument index="1" name="sdp" type="String"> + </argument> + <description> + </description> + </method> + <method name="set_remote_description"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="type" type="String"> + </argument> + <argument index="1" name="sdp" type="String"> + </argument> + <description> + </description> + </method> + <method name="was_string_packet" qualifiers="const"> + <return type="bool"> + </return> + <description> + </description> + </method> + </methods> + <members> + <member name="write_mode" type="int" setter="set_write_mode" getter="get_write_mode" enum="WebRTCPeer.WriteMode"> + </member> + </members> + <signals> + <signal name="new_ice_candidate"> + <argument index="0" name="media" type="String"> + </argument> + <argument index="1" name="index" type="int"> + </argument> + <argument index="2" name="name" type="String"> + </argument> + <description> + </description> + </signal> + <signal name="offer_created"> + <argument index="0" name="type" type="String"> + </argument> + <argument index="1" name="sdp" type="String"> + </argument> + <description> + </description> + </signal> + </signals> + <constants> + <constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode"> + </constant> + <constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode"> + </constant> + <constant name="STATE_NEW" value="0" enum="ConnectionState"> + </constant> + <constant name="STATE_CONNECTING" value="1" enum="ConnectionState"> + </constant> + <constant name="STATE_CONNECTED" value="2" enum="ConnectionState"> + </constant> + <constant name="STATE_DISCONNECTED" value="3" enum="ConnectionState"> + </constant> + <constant name="STATE_FAILED" value="4" enum="ConnectionState"> + </constant> + <constant name="STATE_CLOSED" value="5" enum="ConnectionState"> + </constant> + </constants> +</class> diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp new file mode 100644 index 0000000000..ee7a766bd9 --- /dev/null +++ b/modules/webrtc/register_types.cpp @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* 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 "webrtc_peer.h" + +#ifdef JAVASCRIPT_ENABLED +#include "emscripten.h" +#include "webrtc_peer_js.h" +#endif +#ifdef WEBRTC_GDNATIVE_ENABLED +#include "webrtc_peer_gdnative.h" +#endif + +void register_webrtc_types() { +#ifdef JAVASCRIPT_ENABLED + WebRTCPeerJS::make_default(); +#elif defined(WEBRTC_GDNATIVE_ENABLED) + WebRTCPeerGDNative::make_default(); +#endif + + ClassDB::register_custom_instance_class<WebRTCPeer>(); +#ifdef WEBRTC_GDNATIVE_ENABLED + ClassDB::register_class<WebRTCPeerGDNative>(); +#endif +} + +void unregister_webrtc_types() {} diff --git a/modules/webrtc/register_types.h b/modules/webrtc/register_types.h new file mode 100644 index 0000000000..18a5dcc5aa --- /dev/null +++ b/modules/webrtc/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_webrtc_types(); +void unregister_webrtc_types(); diff --git a/modules/webrtc/webrtc_peer.cpp b/modules/webrtc/webrtc_peer.cpp new file mode 100644 index 0000000000..30c4505df9 --- /dev/null +++ b/modules/webrtc/webrtc_peer.cpp @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* webrtc_peer.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 "webrtc_peer.h" + +WebRTCPeer *(*WebRTCPeer::_create)() = NULL; + +Ref<WebRTCPeer> WebRTCPeer::create_ref() { + + if (!_create) + return Ref<WebRTCPeer>(); + return Ref<WebRTCPeer>(_create()); +} + +WebRTCPeer *WebRTCPeer::create() { + + if (!_create) + return NULL; + return _create(); +} + +void WebRTCPeer::_bind_methods() { + ClassDB::bind_method(D_METHOD("create_offer"), &WebRTCPeer::create_offer); + ClassDB::bind_method(D_METHOD("set_local_description", "type", "sdp"), &WebRTCPeer::set_local_description); + ClassDB::bind_method(D_METHOD("set_remote_description", "type", "sdp"), &WebRTCPeer::set_remote_description); + ClassDB::bind_method(D_METHOD("poll"), &WebRTCPeer::poll); + ClassDB::bind_method(D_METHOD("add_ice_candidate", "media", "index", "name"), &WebRTCPeer::add_ice_candidate); + + ClassDB::bind_method(D_METHOD("was_string_packet"), &WebRTCPeer::was_string_packet); + ClassDB::bind_method(D_METHOD("set_write_mode", "write_mode"), &WebRTCPeer::set_write_mode); + ClassDB::bind_method(D_METHOD("get_write_mode"), &WebRTCPeer::get_write_mode); + ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeer::get_connection_state); + + ADD_SIGNAL(MethodInfo("offer_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp"))); + ADD_SIGNAL(MethodInfo("new_ice_candidate", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name"))); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "write_mode", PROPERTY_HINT_ENUM), "set_write_mode", "get_write_mode"); + + BIND_ENUM_CONSTANT(WRITE_MODE_TEXT); + BIND_ENUM_CONSTANT(WRITE_MODE_BINARY); + + BIND_ENUM_CONSTANT(STATE_NEW); + BIND_ENUM_CONSTANT(STATE_CONNECTING); + BIND_ENUM_CONSTANT(STATE_CONNECTED); + BIND_ENUM_CONSTANT(STATE_DISCONNECTED); + BIND_ENUM_CONSTANT(STATE_FAILED); + BIND_ENUM_CONSTANT(STATE_CLOSED); +} + +WebRTCPeer::WebRTCPeer() { +} + +WebRTCPeer::~WebRTCPeer() { +} diff --git a/modules/webrtc/webrtc_peer.h b/modules/webrtc/webrtc_peer.h new file mode 100644 index 0000000000..e141c14655 --- /dev/null +++ b/modules/webrtc/webrtc_peer.h @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* webrtc_peer.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 WEBRTC_PEER_H +#define WEBRTC_PEER_H + +#include "core/io/packet_peer.h" + +class WebRTCPeer : public PacketPeer { + GDCLASS(WebRTCPeer, PacketPeer); + +public: + enum WriteMode { + WRITE_MODE_TEXT, + WRITE_MODE_BINARY, + }; + + enum ConnectionState { + STATE_NEW, + STATE_CONNECTING, + STATE_CONNECTED, + STATE_DISCONNECTED, + STATE_FAILED, + STATE_CLOSED + }; + +protected: + static void _bind_methods(); + static WebRTCPeer *(*_create)(); + +public: + virtual void set_write_mode(WriteMode mode) = 0; + virtual WriteMode get_write_mode() const = 0; + virtual bool was_string_packet() const = 0; + virtual ConnectionState get_connection_state() const = 0; + + virtual Error create_offer() = 0; + virtual Error set_remote_description(String type, String sdp) = 0; + virtual Error set_local_description(String type, String sdp) = 0; + virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) = 0; + virtual Error poll() = 0; + + /** Inherited from PacketPeer: **/ + virtual int get_available_packet_count() const = 0; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) = 0; ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) = 0; + + virtual int get_max_packet_size() const = 0; + + static Ref<WebRTCPeer> create_ref(); + static WebRTCPeer *create(); + + WebRTCPeer(); + ~WebRTCPeer(); +}; + +VARIANT_ENUM_CAST(WebRTCPeer::WriteMode); +VARIANT_ENUM_CAST(WebRTCPeer::ConnectionState); +#endif // WEBRTC_PEER_H diff --git a/modules/webrtc/webrtc_peer_gdnative.cpp b/modules/webrtc/webrtc_peer_gdnative.cpp new file mode 100644 index 0000000000..f782944980 --- /dev/null +++ b/modules/webrtc/webrtc_peer_gdnative.cpp @@ -0,0 +1,114 @@ +/*************************************************************************/ +/* webrtc_peer_gdnative.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. */ +/*************************************************************************/ + +#ifdef WEBRTC_GDNATIVE_ENABLED + +#include "webrtc_peer_gdnative.h" + +void WebRTCPeerGDNative::_bind_methods() { +} + +WebRTCPeerGDNative::WebRTCPeerGDNative() { + interface = NULL; +} + +WebRTCPeerGDNative::~WebRTCPeerGDNative() { +} + +Error WebRTCPeerGDNative::create_offer() { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->create_offer(interface->data); +} + +Error WebRTCPeerGDNative::set_local_description(String p_type, String p_sdp) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->set_local_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data()); +} + +Error WebRTCPeerGDNative::set_remote_description(String p_type, String p_sdp) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->set_remote_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data()); +} + +Error WebRTCPeerGDNative::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->add_ice_candidate(interface->data, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data()); +} + +Error WebRTCPeerGDNative::poll() { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->poll(interface->data); +} + +void WebRTCPeerGDNative::set_write_mode(WriteMode p_mode) { + ERR_FAIL_COND(interface == NULL); + interface->set_write_mode(interface->data, p_mode); +} + +WebRTCPeer::WriteMode WebRTCPeerGDNative::get_write_mode() const { + ERR_FAIL_COND_V(interface == NULL, WRITE_MODE_BINARY); + return (WriteMode)interface->get_write_mode(interface->data); +} + +bool WebRTCPeerGDNative::was_string_packet() const { + ERR_FAIL_COND_V(interface == NULL, false); + return interface->was_string_packet(interface->data); +} + +WebRTCPeer::ConnectionState WebRTCPeerGDNative::get_connection_state() const { + ERR_FAIL_COND_V(interface == NULL, STATE_DISCONNECTED); + return STATE_DISCONNECTED; +} + +Error WebRTCPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size); +} + +Error WebRTCPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size); +} + +int WebRTCPeerGDNative::get_max_packet_size() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_max_packet_size(interface->data); +} + +int WebRTCPeerGDNative::get_available_packet_count() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_available_packet_count(interface->data); +} + +void WebRTCPeerGDNative::set_native_webrtc_peer(const godot_net_webrtc_peer *p_impl) { + interface = p_impl; +} + +#endif // WEBRTC_GDNATIVE_ENABLED diff --git a/modules/webrtc/webrtc_peer_gdnative.h b/modules/webrtc/webrtc_peer_gdnative.h new file mode 100644 index 0000000000..6786cec8ea --- /dev/null +++ b/modules/webrtc/webrtc_peer_gdnative.h @@ -0,0 +1,78 @@ +/*************************************************************************/ +/* webrtc_peer_gdnative.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. */ +/*************************************************************************/ + +#ifdef WEBRTC_GDNATIVE_ENABLED + +#ifndef WEBRTC_PEER_GDNATIVE_H +#define WEBRTC_PEER_GDNATIVE_H + +#include "modules/gdnative/include/net/godot_net.h" +#include "webrtc_peer.h" + +class WebRTCPeerGDNative : public WebRTCPeer { + GDCLASS(WebRTCPeerGDNative, WebRTCPeer); + +protected: + static void _bind_methods(); + +private: + const godot_net_webrtc_peer *interface; + +public: + static WebRTCPeer *_create() { return memnew(WebRTCPeerGDNative); } + static void make_default() { WebRTCPeer::_create = WebRTCPeerGDNative::_create; } + + void set_native_webrtc_peer(const godot_net_webrtc_peer *p_impl); + + virtual void set_write_mode(WriteMode mode); + virtual WriteMode get_write_mode() const; + virtual bool was_string_packet() const; + virtual ConnectionState get_connection_state() const; + + virtual Error create_offer(); + virtual Error set_remote_description(String type, String sdp); + virtual Error set_local_description(String type, String sdp); + virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName); + virtual Error poll(); + + /** Inherited from PacketPeer: **/ + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + + virtual int get_max_packet_size() const; + + WebRTCPeerGDNative(); + ~WebRTCPeerGDNative(); +}; + +#endif // WEBRTC_PEER_GDNATIVE_H + +#endif // WEBRTC_GDNATIVE_ENABLED diff --git a/modules/webrtc/webrtc_peer_js.cpp b/modules/webrtc/webrtc_peer_js.cpp new file mode 100644 index 0000000000..1282e075ab --- /dev/null +++ b/modules/webrtc/webrtc_peer_js.cpp @@ -0,0 +1,455 @@ +/*************************************************************************/ +/* webrtc_peer_js.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. */ +/*************************************************************************/ + +#ifdef JAVASCRIPT_ENABLED + +#include "webrtc_peer_js.h" +#include "emscripten.h" + +extern "C" { +EMSCRIPTEN_KEEPALIVE void _emrtc_on_ice_candidate(void *obj, char *p_MidName, int p_MlineIndexName, char *p_sdpName) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->emit_signal("new_ice_candidate", String(p_MidName), p_MlineIndexName, String(p_sdpName)); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_offer_created(void *obj, char *p_type, char *p_offer) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->emit_signal("offer_created", String(p_type), String(p_offer)); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_error(void *obj) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->_on_error(); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_open(void *obj) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->_on_open(); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_close(void *obj) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->_on_close(); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_message(void *obj, uint8_t *p_data, uint32_t p_size, bool p_is_string) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->_on_message(p_data, p_size, p_is_string); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_bind_channel(int p_id) { + /* clang-format off */ + EM_ASM({ + if (!Module.IDHandler.has($0)) { + return; // Godot Object is gone! + } + var dict = Module.IDHandler.get($0); + var channel = dict["channel"]; + var c_ptr = dict["ptr"]; + + channel.onopen = function (evt) { + ccall("_emrtc_on_open", + "void", + ["number"], + [c_ptr] + ); + }; + channel.onclose = function (evt) { + ccall("_emrtc_on_close", + "void", + ["number"], + [c_ptr] + ); + }; + channel.onerror = function (evt) { + ccall("_emrtc_on_error", + "void", + ["number"], + [c_ptr] + ); + }; + + channel.binaryType = "arraybuffer"; + channel.onmessage = function(event) { + var buffer; + var is_string = 0; + if (event.data instanceof ArrayBuffer) { + + buffer = new Uint8Array(event.data); + + } else if (event.data instanceof Blob) { + + alert("Blob type not supported"); + return; + + } else if (typeof event.data === "string") { + + is_string = 1; + var enc = new TextEncoder("utf-8"); + buffer = new Uint8Array(enc.encode(event.data)); + + } else { + + alert("Unknown message type"); + return; + + } + var len = buffer.length*buffer.BYTES_PER_ELEMENT; + var out = Module._malloc(len); + Module.HEAPU8.set(buffer, out); + ccall("_emrtc_on_message", + "void", + ["number", "number", "number", "number"], + [c_ptr, out, len, is_string] + ); + Module._free(out); + } + }, p_id); + /* clang-format on */ +} +} + +void _emrtc_create_pc(int p_id) { + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var c_ptr = dict["ptr"]; + // Setup local connaction + var conn = new RTCPeerConnection(); + conn.onicecandidate = function(event) { + if (!Module.IDHandler.get($0)) return; + if (!event.candidate) return; + + var c = event.candidate; + // should emit on ice candidate + ccall("_emrtc_on_ice_candidate", + "void", + ["number", "string", "number", "string"], + [c_ptr, c.sdpMid, c.sdpMLineIndex, c.candidate] + ); + }; + conn.ondatachannel = function (evt) { + var dict = Module.IDHandler.get($0); + if (!dict || dict["channel"]) { + return; + } + var channel = evt.channel; + dict["channel"] = channel; + ccall("_emrtc_bind_channel", + "void", + ["number"], + [$0] + ); + }; + dict["conn"] = conn; + }, p_id); + /* clang-format on */ +} + +void WebRTCPeerJS::_on_open() { + in_buffer.resize(16); + _conn_state = STATE_CONNECTED; +} + +void WebRTCPeerJS::_on_close() { + close(); +} + +void WebRTCPeerJS::_on_error() { + close(); + _conn_state = STATE_FAILED; +} + +void WebRTCPeerJS::_on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string) { + if (in_buffer.space_left() < p_size + 5) { + ERR_EXPLAIN("Buffer full! Dropping data"); + ERR_FAIL(); + } + + uint8_t is_string = p_is_string ? 1 : 0; + in_buffer.write((uint8_t *)&p_size, 4); + in_buffer.write((uint8_t *)&is_string, 1); + in_buffer.write(p_data, p_size); + queue_count++; +} + +void WebRTCPeerJS::close() { + in_buffer.resize(0); + queue_count = 0; + _was_string = false; + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + if (!dict) return; + if (dict["channel"]) { + dict["channel"].close(); + dict["channel"] = null; + } + if (dict["conn"]) { + dict["conn"].close(); + } + }, _js_id); + /* clang-format on */ + _conn_state = STATE_CLOSED; +} + +Error WebRTCPeerJS::create_offer() { + ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED); + + _conn_state = STATE_CONNECTING; + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var onError = function(error) { + console.log(error); + ccall("_emrtc_on_error", + "void", + ["number"], + [c_ptr] + ); + }; + var onCreated = function(offer) { + ccall("_emrtc_offer_created", + "void", + ["number", "string", "string"], + [c_ptr, offer.type, offer.sdp] + ); + }; + + var channel = conn.createDataChannel("default"); + dict["channel"] = channel; + ccall("_emrtc_bind_channel", + "void", + ["number"], + [$0] + ); + conn.createOffer().then(onCreated).catch(onError); + }, _js_id); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerJS::set_local_description(String type, String sdp) { + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var type = UTF8ToString($1); + var sdp = UTF8ToString($2); + var onError = function(error) { + console.log(error); + ccall("_emrtc_on_error", + "void", + ["number"], + [c_ptr] + ); + }; + conn.setLocalDescription({ + "sdp": sdp, + "type": type + }).catch(onError); + }, _js_id, type.utf8().get_data(), sdp.utf8().get_data()); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerJS::set_remote_description(String type, String sdp) { + if (type == "offer") { + ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED); + _conn_state = STATE_CONNECTING; + } + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var type = UTF8ToString($1); + var sdp = UTF8ToString($2); + + var onError = function(error) { + console.log(error); + ccall("_emrtc_on_error", + "void", + ["number"], + [c_ptr] + ); + }; + var onCreated = function(offer) { + ccall("_emrtc_offer_created", + "void", + ["number", "string", "string"], + [c_ptr, offer.type, offer.sdp] + ); + }; + var onSet = function() { + if (type != "offer") { + return; + } + conn.createAnswer().then(onCreated); + }; + conn.setRemoteDescription({ + "sdp": sdp, + "type": type + }).then(onSet).catch(onError); + }, _js_id, type.utf8().get_data(), sdp.utf8().get_data()); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerJS::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) { + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var sdpMidName = UTF8ToString($1); + var sdpMlineIndexName = UTF8ToString($2); + var sdpName = UTF8ToString($3); + conn.addIceCandidate(new RTCIceCandidate({ + "candidate": sdpName, + "sdpMid": sdpMidName, + "sdpMlineIndex": sdpMlineIndexName + })); + }, _js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data()); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerJS::poll() { + return OK; +} + +WebRTCPeer::ConnectionState WebRTCPeerJS::get_connection_state() const { + return _conn_state; +} + +int WebRTCPeerJS::get_available_packet_count() const { + return queue_count; +} + +Error WebRTCPeerJS::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + ERR_FAIL_COND_V(_conn_state != STATE_CONNECTED, ERR_UNCONFIGURED); + + if (queue_count == 0) + return ERR_UNAVAILABLE; + + uint32_t to_read = 0; + uint32_t left = 0; + uint8_t is_string = 0; + r_buffer_size = 0; + + in_buffer.read((uint8_t *)&to_read, 4); + --queue_count; + left = in_buffer.data_left(); + + if (left < to_read + 1) { + in_buffer.advance_read(left); + return FAILED; + } + + in_buffer.read(&is_string, 1); + _was_string = is_string == 1; + in_buffer.read(packet_buffer, to_read); + *r_buffer = packet_buffer; + r_buffer_size = to_read; + + return OK; +} + +Error WebRTCPeerJS::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_FAIL_COND_V(_conn_state != STATE_CONNECTED, ERR_UNCONFIGURED); + + int is_bin = _write_mode == WebRTCPeer::WRITE_MODE_BINARY ? 1 : 0; + + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var channel = dict["channel"]; + var bytes_array = new Uint8Array($2); + var i = 0; + + for(i=0; i<$2; i++) { + bytes_array[i] = getValue($1+i, 'i8'); + } + + if ($3) { + channel.send(bytes_array.buffer); + } else { + var string = new TextDecoder("utf-8").decode(bytes_array); + channel.send(string); + } + }, _js_id, p_buffer, p_buffer_size, is_bin); + /* clang-format on */ + + return OK; +} + +int WebRTCPeerJS::get_max_packet_size() const { + return 1200; +} + +void WebRTCPeerJS::set_write_mode(WriteMode p_mode) { + _write_mode = p_mode; +} + +WebRTCPeer::WriteMode WebRTCPeerJS::get_write_mode() const { + return _write_mode; +} + +bool WebRTCPeerJS::was_string_packet() const { + return _was_string; +} + +WebRTCPeerJS::WebRTCPeerJS() { + queue_count = 0; + _was_string = false; + _write_mode = WRITE_MODE_BINARY; + _conn_state = STATE_NEW; + + /* clang-format off */ + _js_id = EM_ASM_INT({ + return Module.IDHandler.add({"conn": null, "ptr": $0, "channel": null}); + }, this); + /* clang-format on */ + _emrtc_create_pc(_js_id); +} + +WebRTCPeerJS::~WebRTCPeerJS() { + close(); + /* clang-format off */ + EM_ASM({ + Module.IDHandler.remove($0); + }, _js_id); + /* clang-format on */ +}; +#endif diff --git a/modules/webrtc/webrtc_peer_js.h b/modules/webrtc/webrtc_peer_js.h new file mode 100644 index 0000000000..02f0c9b55d --- /dev/null +++ b/modules/webrtc/webrtc_peer_js.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* webrtc_peer_js.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 WEBRTC_PEER_JS_H +#define WEBRTC_PEER_JS_H + +#ifdef JAVASCRIPT_ENABLED + +#include "webrtc_peer.h" + +class WebRTCPeerJS : public WebRTCPeer { + +private: + enum { + PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type + }; + + bool _was_string; + WriteMode _write_mode; + + int _js_id; + RingBuffer<uint8_t> in_buffer; + int queue_count; + uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + ConnectionState _conn_state; + +public: + static WebRTCPeer *_create() { return memnew(WebRTCPeerJS); } + static void make_default() { WebRTCPeer::_create = WebRTCPeerJS::_create; } + + virtual void set_write_mode(WriteMode mode); + virtual WriteMode get_write_mode() const; + virtual bool was_string_packet() const; + virtual ConnectionState get_connection_state() const; + + virtual Error create_offer(); + virtual Error set_remote_description(String type, String sdp); + virtual Error set_local_description(String type, String sdp); + virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName); + virtual Error poll(); + + /** Inherited from PacketPeer: **/ + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + + virtual int get_max_packet_size() const; + + void close(); + void _on_open(); + void _on_close(); + void _on_error(); + void _on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string); + + WebRTCPeerJS(); + ~WebRTCPeerJS(); +}; + +#endif + +#endif // WEBRTC_PEER_JS_H diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index cb85fe864d..26122dfad4 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -11,8 +11,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="connect_to_url"> <return type="int" enum="Error"> diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index 139480c31f..8548d21323 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_peer" qualifiers="const"> <return type="WebSocketPeer"> diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index 3b3692dd87..8a6868d311 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="close"> <return type="void"> diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index 4740bd6dcf..51945d410a 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="disconnect_peer"> <return type="void"> diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp index ed6cc5638e..39bf3de982 100644 --- a/modules/websocket/register_types.cpp +++ b/modules/websocket/register_types.cpp @@ -60,25 +60,6 @@ void register_websocket_types() { _SET_HINT(WSS_OUT_PKT, 1024, 16384); #ifdef JAVASCRIPT_ENABLED - EM_ASM({ - var IDHandler = {}; - IDHandler["ids"] = {}; - IDHandler["has"] = function(id) { - return IDHandler.ids.hasOwnProperty(id); - }; - IDHandler["add"] = function(obj) { - var id = crypto.getRandomValues(new Int32Array(32))[0]; - IDHandler.ids[id] = obj; - return id; - }; - IDHandler["get"] = function(id) { - return IDHandler.ids[id]; - }; - IDHandler["remove"] = function(id) { - delete IDHandler.ids[id]; - }; - Module["IDHandler"] = IDHandler; - }); EMWSPeer::make_default(); EMWSClient::make_default(); EMWSServer::make_default(); diff --git a/modules/xatlas_unwrap/config.py b/modules/xatlas_unwrap/config.py index 2dda5db7e0..bd092bdc16 100644 --- a/modules/xatlas_unwrap/config.py +++ b/modules/xatlas_unwrap/config.py @@ -1,5 +1,4 @@ def can_build(env, platform): - #return False #xatlas is buggy return (env['tools'] and platform not in ["android", "ios"]) def configure(env): diff --git a/platform/android/SCsub b/platform/android/SCsub index 47d5035224..d494372bcd 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -2,7 +2,6 @@ Import('env') -import shutil from compat import open_utf8 from distutils.version import LooseVersion from detect import get_ndk_version @@ -16,8 +15,10 @@ android_files = [ 'dir_access_jandroid.cpp', 'thread_jandroid.cpp', 'audio_driver_jandroid.cpp', - 'java_glue.cpp', + 'java_godot_lib_jni.cpp', 'java_class_wrapper.cpp', + 'java_godot_wrapper.cpp', + 'java_godot_io_wrapper.cpp', # 'power_android.cpp' ] diff --git a/platform/android/audio_driver_jandroid.h b/platform/android/audio_driver_jandroid.h index a5b49e9077..f92ef06052 100644 --- a/platform/android/audio_driver_jandroid.h +++ b/platform/android/audio_driver_jandroid.h @@ -33,7 +33,7 @@ #include "servers/audio_server.h" -#include "java_glue.h" +#include "java_godot_lib_jni.h" class AudioDriverAndroid : public AudioDriver { diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp index cb25575b74..1232fc7453 100644 --- a/platform/android/audio_driver_opensl.cpp +++ b/platform/android/audio_driver_opensl.cpp @@ -326,7 +326,7 @@ Error AudioDriverOpenSL::capture_stop() { int AudioDriverOpenSL::get_mix_rate() const { - return 44100; + return 44100; // hardcoded for Android, as selected by SL_SAMPLINGRATE_44_1 } AudioDriver::SpeakerMode AudioDriverOpenSL::get_speaker_mode() const { diff --git a/platform/android/detect.py b/platform/android/detect.py index 80cda68a9e..5623274050 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -1,6 +1,5 @@ import os import sys -import string import platform from distutils.version import LooseVersion diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h index e7a2d5ada1..cdea93ff4c 100644 --- a/platform/android/dir_access_jandroid.h +++ b/platform/android/dir_access_jandroid.h @@ -32,7 +32,7 @@ #define DIR_ACCESS_JANDROID_H #include "core/os/dir_access.h" -#include "java_glue.h" +#include "java_godot_lib_jni.h" #include <stdio.h> class DirAccessJAndroid : public DirAccess { diff --git a/platform/android/file_access_jandroid.h b/platform/android/file_access_jandroid.h index 304c33ecac..4f02fea81d 100644 --- a/platform/android/file_access_jandroid.h +++ b/platform/android/file_access_jandroid.h @@ -32,7 +32,7 @@ #define FILE_ACCESS_JANDROID_H #include "core/os/file_access.h" -#include "java_glue.h" +#include "java_godot_lib_jni.h" class FileAccessJAndroid : public FileAccess { static jobject io; diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index e42099ba0b..374d40463a 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -32,6 +32,7 @@ package org.godotengine.godot; //import android.R; +import android.Manifest; import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; @@ -53,7 +54,6 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; -import android.Manifest; import android.os.Build; import android.os.Bundle; import android.os.Environment; @@ -75,7 +75,6 @@ import android.widget.FrameLayout; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; - import com.google.android.vending.expansion.downloader.DownloadProgressInfo; import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller; @@ -83,10 +82,6 @@ import com.google.android.vending.expansion.downloader.Helpers; import com.google.android.vending.expansion.downloader.IDownloaderClient; import com.google.android.vending.expansion.downloader.IDownloaderService; import com.google.android.vending.expansion.downloader.IStub; - -import org.godotengine.godot.input.GodotEditText; -import org.godotengine.godot.payments.PaymentsManager; - import java.io.File; import java.io.FileInputStream; import java.io.InputStream; @@ -96,13 +91,15 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Locale; - import javax.microedition.khronos.opengles.GL10; +import org.godotengine.godot.input.GodotEditText; +import org.godotengine.godot.payments.PaymentsManager; public class Godot extends Activity implements SensorEventListener, IDownloaderClient { static final int MAX_SINGLETONS = 64; static final int REQUEST_RECORD_AUDIO_PERMISSION = 1; + static final int REQUEST_CAMERA_PERMISSION = 2; private IStub mDownloaderClientStub; private IDownloaderService mRemoteService; private TextView mStatusText; @@ -429,7 +426,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } io = new GodotIO(this); - io.unique_id = Secure.ANDROID_ID; + io.unique_id = Secure.getString(getContentResolver(), Secure.ANDROID_ID); GodotLib.io = io; mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); @@ -606,6 +603,9 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC for (int i = 0; i < singleton_count; i++) { singletons[i].onMainDestroy(); } + + GodotLib.ondestroy(this); + super.onDestroy(); } @@ -957,6 +957,12 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } } + if (p_name.equals("CAMERA")) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION); + return false; + } + } return true; } diff --git a/platform/android/java/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java b/platform/android/java/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java index fb7477c09c..e7e2a3f808 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java +++ b/platform/android/java/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java @@ -30,13 +30,12 @@ package org.godotengine.godot; -import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.util.Log; +import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; /** * You should start your derived downloader class when this receiver gets the message diff --git a/platform/android/java/src/org/godotengine/godot/GodotDownloaderService.java b/platform/android/java/src/org/godotengine/godot/GodotDownloaderService.java index 91a7b99c36..8e10710c9f 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotDownloaderService.java +++ b/platform/android/java/src/org/godotengine/godot/GodotDownloaderService.java @@ -33,7 +33,6 @@ package org.godotengine.godot; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; - import com.google.android.vending.expansion.downloader.impl.DownloaderService; /** diff --git a/platform/android/java/src/org/godotengine/godot/GodotIO.java b/platform/android/java/src/org/godotengine/godot/GodotIO.java index 1b2ff61314..98174157ec 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/src/org/godotengine/godot/GodotIO.java @@ -29,28 +29,27 @@ /*************************************************************************/ package org.godotengine.godot; -import java.util.HashMap; -import java.util.Locale; -import android.net.Uri; -import android.content.Intent; -import android.content.res.AssetManager; -import java.io.InputStream; -import java.io.IOException; import android.app.*; import android.content.*; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.res.AssetManager; +import android.graphics.*; +import android.hardware.*; +import android.media.*; +import android.net.Uri; +import android.os.*; +import android.text.*; +import android.text.method.*; +import android.util.DisplayMetrics; +import android.util.Log; import android.util.SparseArray; import android.view.*; import android.view.inputmethod.InputMethodManager; -import android.os.*; -import android.util.Log; -import android.util.DisplayMetrics; -import android.graphics.*; -import android.text.method.*; -import android.text.*; -import android.media.*; -import android.hardware.*; -import android.content.*; -import android.content.pm.ActivityInfo; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Locale; import org.godotengine.godot.input.*; //android.os.Build diff --git a/platform/android/java/src/org/godotengine/godot/GodotLib.java b/platform/android/java/src/org/godotengine/godot/GodotLib.java index 6e6b398a5d..31ca9a8500 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/src/org/godotengine/godot/GodotLib.java @@ -46,6 +46,7 @@ public class GodotLib { */ public static native void initialize(Godot p_instance, Object p_asset_manager, boolean use_apk_expansion); + public static native void ondestroy(Godot p_instance); public static native void setup(String[] p_cmdline); public static native void resize(int width, int height); public static native void newcontext(boolean p_32_bits); diff --git a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java index c3d81c5c1d..1432cd3a67 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java +++ b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java @@ -32,14 +32,12 @@ package org.godotengine.godot; import android.app.Activity; import android.util.Log; - -import org.godotengine.godot.payments.PaymentsManager; -import org.json.JSONException; -import org.json.JSONObject; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.godotengine.godot.payments.PaymentsManager; +import org.json.JSONException; +import org.json.JSONObject; public class GodotPaymentV3 extends Godot.SingletonBase { diff --git a/platform/android/java/src/org/godotengine/godot/GodotView.java b/platform/android/java/src/org/godotengine/godot/GodotView.java index ccf78f26f3..d7cd5b4360 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/src/org/godotengine/godot/GodotView.java @@ -31,16 +31,15 @@ package org.godotengine.godot; import android.annotation.SuppressLint; import android.content.Context; +import android.content.ContextWrapper; import android.graphics.PixelFormat; +import android.hardware.input.InputManager; import android.opengl.GLSurfaceView; import android.util.AttributeSet; import android.util.Log; +import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; -import android.content.ContextWrapper; -import android.view.InputDevice; -import android.hardware.input.InputManager; - import java.io.File; import java.util.ArrayList; import java.util.Collections; @@ -51,7 +50,6 @@ import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.opengles.GL10; - import org.godotengine.godot.input.InputManagerCompat; import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener; /** diff --git a/platform/android/java/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/src/org/godotengine/godot/input/GodotEditText.java index 44bd462ed0..45b739baa0 100644 --- a/platform/android/java/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/src/org/godotengine/godot/input/GodotEditText.java @@ -30,16 +30,15 @@ package org.godotengine.godot.input; import android.content.Context; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.widget.EditText; -import org.godotengine.godot.*; import android.os.Handler; import android.os.Message; -import android.view.inputmethod.InputMethodManager; +import android.util.AttributeSet; +import android.view.KeyEvent; import android.view.inputmethod.EditorInfo; - +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; import java.lang.ref.WeakReference; +import org.godotengine.godot.*; public class GodotEditText extends EditText { // =========================================================== diff --git a/platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java b/platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java index 3b88609cc9..e4bafa7ff9 100644 --- a/platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java +++ b/platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java @@ -23,7 +23,6 @@ import android.os.Build; import android.os.Handler; import android.view.InputDevice; import android.view.MotionEvent; - import java.util.HashMap; import java.util.Map; diff --git a/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java b/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java index 1d42aa4464..f872e7af56 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java @@ -30,13 +30,11 @@ package org.godotengine.godot.payments; -import com.android.vending.billing.IInAppBillingService; - import android.content.Context; import android.os.AsyncTask; import android.os.RemoteException; import android.util.Log; - +import com.android.vending.billing.IInAppBillingService; import java.lang.ref.WeakReference; abstract public class ConsumeTask { diff --git a/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java b/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java index 835779ba00..5424ebb49d 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java @@ -30,13 +30,6 @@ package org.godotengine.godot.payments; -import org.json.JSONException; -import org.json.JSONObject; - -import org.godotengine.godot.GodotLib; -import org.godotengine.godot.utils.Crypt; -import com.android.vending.billing.IInAppBillingService; - import android.app.Activity; import android.app.PendingIntent; import android.app.ProgressDialog; @@ -47,6 +40,11 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.RemoteException; import android.util.Log; +import com.android.vending.billing.IInAppBillingService; +import org.godotengine.godot.GodotLib; +import org.godotengine.godot.utils.Crypt; +import org.json.JSONException; +import org.json.JSONObject; abstract public class HandlePurchaseTask { diff --git a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java index 747a4ffd45..a0dbc432c1 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java +++ b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java @@ -40,17 +40,14 @@ import android.os.IBinder; import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; - import com.android.vending.billing.IInAppBillingService; - +import java.util.ArrayList; +import java.util.Arrays; import org.godotengine.godot.Godot; import org.godotengine.godot.GodotPaymentV3; import org.json.JSONException; import org.json.JSONObject; -import java.util.ArrayList; -import java.util.Arrays; - public class PaymentsManager { public static final int BILLING_RESPONSE_RESULT_OK = 0; diff --git a/platform/android/java/src/org/godotengine/godot/payments/PurchaseTask.java b/platform/android/java/src/org/godotengine/godot/payments/PurchaseTask.java index 000aaa9456..650c5178f0 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/PurchaseTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/PurchaseTask.java @@ -30,13 +30,6 @@ package org.godotengine.godot.payments; -import org.json.JSONException; -import org.json.JSONObject; - -import org.godotengine.godot.GodotLib; -import org.godotengine.godot.utils.Crypt; -import com.android.vending.billing.IInAppBillingService; - import android.app.Activity; import android.app.PendingIntent; import android.app.ProgressDialog; @@ -47,6 +40,11 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.RemoteException; import android.util.Log; +import com.android.vending.billing.IInAppBillingService; +import org.godotengine.godot.GodotLib; +import org.godotengine.godot.utils.Crypt; +import org.json.JSONException; +import org.json.JSONObject; abstract public class PurchaseTask { diff --git a/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java b/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java index cf750872d5..daca6ef5ae 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java @@ -34,14 +34,11 @@ import android.content.Context; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; - import com.android.vending.billing.IInAppBillingService; - -import org.json.JSONException; -import org.json.JSONObject; - import java.lang.ref.WeakReference; import java.util.ArrayList; +import org.json.JSONException; +import org.json.JSONObject; abstract public class ReleaseAllConsumablesTask { diff --git a/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java b/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java index 6701f9396a..d32c80e8e0 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java @@ -30,17 +30,6 @@ package org.godotengine.godot.payments; -import org.json.JSONException; -import org.json.JSONObject; - -import org.godotengine.godot.Godot; -import org.godotengine.godot.GodotLib; -import org.godotengine.godot.GodotPaymentV3; -import org.godotengine.godot.utils.Crypt; -import org.godotengine.godot.utils.HttpRequester; -import org.godotengine.godot.utils.RequestParams; -import com.android.vending.billing.IInAppBillingService; - import android.app.Activity; import android.app.PendingIntent; import android.app.ProgressDialog; @@ -51,8 +40,16 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.RemoteException; import android.util.Log; - +import com.android.vending.billing.IInAppBillingService; import java.lang.ref.WeakReference; +import org.godotengine.godot.Godot; +import org.godotengine.godot.GodotLib; +import org.godotengine.godot.GodotPaymentV3; +import org.godotengine.godot.utils.Crypt; +import org.godotengine.godot.utils.HttpRequester; +import org.godotengine.godot.utils.RequestParams; +import org.json.JSONException; +import org.json.JSONObject; abstract public class ValidateTask { diff --git a/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java b/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java index 3806d4bcad..b61007faa3 100644 --- a/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java +++ b/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java @@ -37,10 +37,8 @@ import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; - import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; - import org.apache.http.conn.ssl.SSLSocketFactory; /** diff --git a/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java b/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java index e9c81eb2e5..e98f533c23 100644 --- a/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java +++ b/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java @@ -30,6 +30,9 @@ package org.godotengine.godot.utils; +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -39,7 +42,6 @@ import java.security.KeyStore; import java.util.ArrayList; import java.util.Date; import java.util.List; - import org.apache.http.HttpResponse; import org.apache.http.HttpVersion; import org.apache.http.NameValuePair; @@ -64,10 +66,6 @@ import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Log; - /** * * @author Luis Linietsky <luis.linietsky@gmail.com> diff --git a/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java b/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java index c5778102f6..b9fe0dd0c9 100644 --- a/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java +++ b/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java @@ -34,7 +34,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; - import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp new file mode 100644 index 0000000000..0c41b85939 --- /dev/null +++ b/platform/android/java_godot_io_wrapper.cpp @@ -0,0 +1,207 @@ +/*************************************************************************/ +/* java_godot_io_wrapper.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "java_godot_io_wrapper.h" +#include "core/error_list.h" + +// JNIEnv is only valid within the thread it belongs to, in a multi threading environment +// we can't cache it. +// For GodotIO we call all access methods from our thread and we thus get a valid JNIEnv +// from ThreadAndroid. + +GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instance) { + godot_io_instance = p_env->NewGlobalRef(p_godot_io_instance); + if (godot_io_instance) { + cls = p_env->GetObjectClass(godot_io_instance); + if (cls) { + cls = (jclass)p_env->NewGlobalRef(cls); + } else { + // this is a pretty serious fail.. bail... pointers will stay 0 + return; + } + + _open_URI = p_env->GetMethodID(cls, "openURI", "(Ljava/lang/String;)I"); + _get_data_dir = p_env->GetMethodID(cls, "getDataDir", "()Ljava/lang/String;"); + _get_locale = p_env->GetMethodID(cls, "getLocale", "()Ljava/lang/String;"); + _get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;"); + _get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I"); + _get_unique_ID = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;"); + _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;)V"); + _hide_keyboard = p_env->GetMethodID(cls, "hideKeyboard", "()V"); + _set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V"); + _get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(I)Ljava/lang/String;"); + _play_video = p_env->GetMethodID(cls, "playVideo", "(Ljava/lang/String;)V"); + _is_video_playing = p_env->GetMethodID(cls, "isVideoPlaying", "()Z"); + _pause_video = p_env->GetMethodID(cls, "pauseVideo", "()V"); + _stop_video = p_env->GetMethodID(cls, "stopVideo", "()V"); + } +} + +GodotIOJavaWrapper::~GodotIOJavaWrapper() { + // nothing to do here for now +} + +jobject GodotIOJavaWrapper::get_instance() { + return godot_io_instance; +} + +Error GodotIOJavaWrapper::open_uri(const String &p_uri) { + if (_open_URI) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStr = env->NewStringUTF(p_uri.utf8().get_data()); + return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK; + } else { + return ERR_UNAVAILABLE; + } +} + +String GodotIOJavaWrapper::get_user_data_dir() { + if (_get_data_dir) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir); + return jstring_to_string(s, env); + } else { + return String(); + } +} + +String GodotIOJavaWrapper::get_locale() { + if (_get_locale) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale); + return jstring_to_string(s, env); + } else { + return String(); + } +} + +String GodotIOJavaWrapper::get_model() { + if (_get_model) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model); + return jstring_to_string(s, env); + } else { + return String(); + } +} + +int GodotIOJavaWrapper::get_screen_dpi() { + if (_get_screen_DPI) { + JNIEnv *env = ThreadAndroid::get_env(); + return env->CallIntMethod(godot_io_instance, _get_screen_DPI); + } else { + return 160; + } +} + +String GodotIOJavaWrapper::get_unique_id() { + if (_get_unique_ID) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_ID); + return jstring_to_string(s, env); + } else { + return String(); + } +} + +bool GodotIOJavaWrapper::has_vk() { + return (_show_keyboard != 0) && (_hide_keyboard != 0); +} + +void GodotIOJavaWrapper::show_vk(const String &p_existing) { + if (_show_keyboard) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStr = env->NewStringUTF(p_existing.utf8().get_data()); + env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr); + } +} + +void GodotIOJavaWrapper::hide_vk() { + if (_hide_keyboard) { + JNIEnv *env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_io_instance, _hide_keyboard); + } +} + +void GodotIOJavaWrapper::set_screen_orientation(int p_orient) { + if (_set_screen_orientation) { + JNIEnv *env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient); + } +} + +String GodotIOJavaWrapper::get_system_dir(int p_dir) { + if (_get_system_dir) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir); + return jstring_to_string(s, env); + } else { + return String("."); + } +} + +void GodotIOJavaWrapper::play_video(const String &p_path) { + // Why is this not here?!?! +} + +bool GodotIOJavaWrapper::is_video_playing() { + if (_is_video_playing) { + JNIEnv *env = ThreadAndroid::get_env(); + return env->CallBooleanMethod(godot_io_instance, _is_video_playing); + } else { + return false; + } +} + +void GodotIOJavaWrapper::pause_video() { + if (_pause_video) { + JNIEnv *env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_io_instance, _pause_video); + } +} + +void GodotIOJavaWrapper::stop_video() { + if (_stop_video) { + JNIEnv *env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_io_instance, _stop_video); + } +} + +// volatile because it can be changed from non-main thread and we need to +// ensure the change is immediately visible to other threads. +static volatile int virtual_keyboard_height; + +int GodotIOJavaWrapper::get_vk_height() { + return virtual_keyboard_height; +} + +void GodotIOJavaWrapper::set_vk_height(int p_height) { + virtual_keyboard_height = p_height; +} diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h new file mode 100644 index 0000000000..920c433b08 --- /dev/null +++ b/platform/android/java_godot_io_wrapper.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* java_godot_io_wrapper.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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. */ +/*************************************************************************/ + +// note, swapped java and godot around in the file name so all the java +// wrappers are together + +#ifndef JAVA_GODOT_IO_WRAPPER_H +#define JAVA_GODOT_IO_WRAPPER_H + +#include <android/log.h> +#include <jni.h> + +#include "string_android.h" + +// Class that makes functions in java/src/org/godotengine/godot/GodotIO.java callable from C++ +class GodotIOJavaWrapper { +private: + jobject godot_io_instance; + jclass cls; + + jmethodID _open_URI = 0; + jmethodID _get_data_dir = 0; + jmethodID _get_locale = 0; + jmethodID _get_model = 0; + jmethodID _get_screen_DPI = 0; + jmethodID _get_unique_ID = 0; + jmethodID _show_keyboard = 0; + jmethodID _hide_keyboard = 0; + jmethodID _set_screen_orientation = 0; + jmethodID _get_system_dir = 0; + jmethodID _play_video = 0; + jmethodID _is_video_playing = 0; + jmethodID _pause_video = 0; + jmethodID _stop_video = 0; + +public: + GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instance); + ~GodotIOJavaWrapper(); + + jobject get_instance(); + + Error open_uri(const String &p_uri); + String get_user_data_dir(); + String get_locale(); + String get_model(); + int get_screen_dpi(); + String get_unique_id(); + bool has_vk(); + void show_vk(const String &p_existing); + void hide_vk(); + int get_vk_height(); + void set_vk_height(int p_height); + void set_screen_orientation(int p_orient); + String get_system_dir(int p_dir); + void play_video(const String &p_path); + bool is_video_playing(); + void pause_video(); + void stop_video(); +}; + +#endif /* !JAVA_GODOT_IO_WRAPPER_H */ diff --git a/platform/android/java_glue.cpp b/platform/android/java_godot_lib_jni.cpp index e9c0e5564f..466f79c215 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* java_glue.cpp */ +/* java_godot_lib_jni.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "java_glue.h" +#include "java_godot_lib_jni.h" +#include "java_godot_io_wrapper.h" +#include "java_godot_wrapper.h" + #include "android/asset_manager_jni.h" #include "audio_driver_jandroid.h" #include "core/engine.h" @@ -47,6 +50,8 @@ static JavaClassWrapper *java_class_wrapper = NULL; static OS_Android *os_android = NULL; +static GodotJavaWrapper *godot_java = NULL; +static GodotIOJavaWrapper *godot_io_java = NULL; struct jvalret { @@ -588,181 +593,23 @@ TST tst; static bool initialized = false; static int step = 0; + static Size2 new_size; static Vector3 accelerometer; static Vector3 gravity; static Vector3 magnetometer; static Vector3 gyroscope; static HashMap<String, JNISingleton *> jni_singletons; -static jobject godot_io; - -typedef void (*GFXInitFunc)(void *ud, bool gl2); - -static jmethodID _on_video_init = 0; -static jmethodID _restart = 0; -static jobject _godot_instance; - -static jmethodID _openURI = 0; -static jmethodID _getDataDir = 0; -static jmethodID _getLocale = 0; -static jmethodID _getClipboard = 0; -static jmethodID _setClipboard = 0; -static jmethodID _getModel = 0; -static jmethodID _getScreenDPI = 0; -static jmethodID _showKeyboard = 0; -static jmethodID _hideKeyboard = 0; -static jmethodID _setScreenOrientation = 0; -static jmethodID _getUniqueID = 0; -static jmethodID _getSystemDir = 0; -static jmethodID _getGLESVersionCode = 0; -static jmethodID _playVideo = 0; -static jmethodID _isVideoPlaying = 0; -static jmethodID _pauseVideo = 0; -static jmethodID _stopVideo = 0; -static jmethodID _setKeepScreenOn = 0; -static jmethodID _alertDialog = 0; -static jmethodID _requestPermission = 0; - -static void _gfx_init_func(void *ud, bool gl2) { -} - -static int _open_uri(const String &p_uri) { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring jStr = env->NewStringUTF(p_uri.utf8().get_data()); - return env->CallIntMethod(godot_io, _openURI, jStr); -} - -static String _get_user_data_dir() { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(godot_io, _getDataDir); - return jstring_to_string(s, env); -} - -static String _get_locale() { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(godot_io, _getLocale); - return jstring_to_string(s, env); -} - -static String _get_clipboard() { - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(_godot_instance, _getClipboard); - return jstring_to_string(s, env); -} - -static void _set_clipboard(const String &p_text) { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring jStr = env->NewStringUTF(p_text.utf8().get_data()); - env->CallVoidMethod(_godot_instance, _setClipboard, jStr); -} - -static String _get_model() { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(godot_io, _getModel); - return jstring_to_string(s, env); -} - -static int _get_screen_dpi() { - - JNIEnv *env = ThreadAndroid::get_env(); - return env->CallIntMethod(godot_io, _getScreenDPI); -} - -static String _get_unique_id() { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(godot_io, _getUniqueID); - return jstring_to_string(s, env); -} - -static void _show_vk(const String &p_existing) { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring jStr = env->NewStringUTF(p_existing.utf8().get_data()); - env->CallVoidMethod(godot_io, _showKeyboard, jStr); -} - -static void _set_screen_orient(int p_orient) { - - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(godot_io, _setScreenOrientation, p_orient); -} - -static String _get_system_dir(int p_dir) { - - JNIEnv *env = ThreadAndroid::get_env(); - jstring s = (jstring)env->CallObjectMethod(godot_io, _getSystemDir, p_dir); - return jstring_to_string(s, env); -} - -static int _get_gles_version_code() { - JNIEnv *env = ThreadAndroid::get_env(); - return env->CallIntMethod(_godot_instance, _getGLESVersionCode); -} - -static void _hide_vk() { - - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(godot_io, _hideKeyboard); -} // virtual Error native_video_play(String p_path); // virtual bool native_video_is_playing(); // virtual void native_video_pause(); // virtual void native_video_stop(); -static void _play_video(const String &p_path) { -} - -static bool _is_video_playing() { - JNIEnv *env = ThreadAndroid::get_env(); - return env->CallBooleanMethod(godot_io, _isVideoPlaying); - //return false; -} - -static void _pause_video() { - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(godot_io, _pauseVideo); -} - -static void _stop_video() { - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(godot_io, _stopVideo); -} - -static void _set_keep_screen_on(bool p_enabled) { - JNIEnv *env = ThreadAndroid::get_env(); - env->CallVoidMethod(_godot_instance, _setKeepScreenOn, p_enabled); -} - -static void _alert(const String &p_message, const String &p_title) { - JNIEnv *env = ThreadAndroid::get_env(); - jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data()); - jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data()); - env->CallVoidMethod(_godot_instance, _alertDialog, jStrMessage, jStrTitle); -} - -static bool _request_permission(const String &p_name) { - JNIEnv *env = ThreadAndroid::get_env(); - jstring jStrName = env->NewStringUTF(p_name.utf8().get_data()); - return env->CallBooleanMethod(_godot_instance, _requestPermission, jStrName); -} - -// volatile because it can be changed from non-main thread and we need to -// ensure the change is immediately visible to other threads. -static volatile int virtual_keyboard_height; - -static int _get_vk_height() { - return virtual_keyboard_height; -} - JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jobject obj, jint p_height) { - virtual_keyboard_height = p_height; + if (godot_io_java) { + godot_io_java->set_vk_height(p_height); + } } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion) { @@ -772,70 +619,42 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en JavaVM *jvm; env->GetJavaVM(&jvm); - _godot_instance = env->NewGlobalRef(activity); - //_godot_instance=activity; - - { - //setup IO Object - - jclass cls = env->FindClass("org/godotengine/godot/Godot"); - if (cls) { - - cls = (jclass)env->NewGlobalRef(cls); - } - - jfieldID fid = env->GetStaticFieldID(cls, "io", "Lorg/godotengine/godot/GodotIO;"); - jobject ob = env->GetStaticObjectField(cls, fid); - jobject gob = env->NewGlobalRef(ob); - - godot_io = gob; - - _on_video_init = env->GetMethodID(cls, "onVideoInit", "()V"); - _restart = env->GetMethodID(cls, "restart", "()V"); - _setKeepScreenOn = env->GetMethodID(cls, "setKeepScreenOn", "(Z)V"); - _alertDialog = env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); - _getGLESVersionCode = env->GetMethodID(cls, "getGLESVersionCode", "()I"); - _getClipboard = env->GetMethodID(cls, "getClipboard", "()Ljava/lang/String;"); - _setClipboard = env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V"); - _requestPermission = env->GetMethodID(cls, "requestPermission", "(Ljava/lang/String;)Z"); - - if (cls) { - jclass c = env->GetObjectClass(gob); - _openURI = env->GetMethodID(c, "openURI", "(Ljava/lang/String;)I"); - _getDataDir = env->GetMethodID(c, "getDataDir", "()Ljava/lang/String;"); - _getLocale = env->GetMethodID(c, "getLocale", "()Ljava/lang/String;"); - _getModel = env->GetMethodID(c, "getModel", "()Ljava/lang/String;"); - _getScreenDPI = env->GetMethodID(c, "getScreenDPI", "()I"); - _getUniqueID = env->GetMethodID(c, "getUniqueID", "()Ljava/lang/String;"); - _showKeyboard = env->GetMethodID(c, "showKeyboard", "(Ljava/lang/String;)V"); - _hideKeyboard = env->GetMethodID(c, "hideKeyboard", "()V"); - _setScreenOrientation = env->GetMethodID(c, "setScreenOrientation", "(I)V"); - _getSystemDir = env->GetMethodID(c, "getSystemDir", "(I)Ljava/lang/String;"); - _playVideo = env->GetMethodID(c, "playVideo", "(Ljava/lang/String;)V"); - _isVideoPlaying = env->GetMethodID(c, "isVideoPlaying", "()Z"); - _pauseVideo = env->GetMethodID(c, "pauseVideo", "()V"); - _stopVideo = env->GetMethodID(c, "stopVideo", "()V"); - } + // create our wrapper classes + godot_java = new GodotJavaWrapper(env, activity); // our activity is our godot instance is our activity.. + godot_io_java = new GodotIOJavaWrapper(env, godot_java->get_member_object("io", "Lorg/godotengine/godot/GodotIO;", env)); - ThreadAndroid::make_default(jvm); + ThreadAndroid::make_default(jvm); #ifdef USE_JAVA_FILE_ACCESS - FileAccessJAndroid::setup(gob); + FileAccessJAndroid::setup(godot_io_java->get_instance()); #else - jobject amgr = env->NewGlobalRef(p_asset_manager); + jobject amgr = env->NewGlobalRef(p_asset_manager); - FileAccessAndroid::asset_manager = AAssetManager_fromJava(env, amgr); + FileAccessAndroid::asset_manager = AAssetManager_fromJava(env, amgr); #endif - DirAccessJAndroid::setup(gob); - AudioDriverAndroid::setup(gob); - } - os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_user_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _get_gles_version_code, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, _set_clipboard, _get_clipboard, _request_permission, p_use_apk_expansion); + DirAccessJAndroid::setup(godot_io_java->get_instance()); + AudioDriverAndroid::setup(godot_io_java->get_instance()); + + os_android = new OS_Android(godot_java, godot_io_java, p_use_apk_expansion); char wd[500]; getcwd(wd, 500); - env->CallVoidMethod(_godot_instance, _on_video_init); + godot_java->on_video_init(env); +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env) { + // lets cleanup + if (godot_io_java) { + delete godot_io_java; + } + if (godot_java) { + delete godot_java; + } + if (os_android) { + delete os_android; + } } static void _initialize_java_modules() { @@ -852,17 +671,12 @@ static void _initialize_java_modules() { Vector<String> mods = modules.split(",", false); if (mods.size()) { + jobject cls = godot_java->get_class_loader(); - JNIEnv *env = ThreadAndroid::get_env(); - - jclass activityClass = env->FindClass("org/godotengine/godot/Godot"); - - jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;"); - - jobject cls = env->CallObjectMethod(_godot_instance, getClassLoader); + // TODO create wrapper for class loader + JNIEnv *env = ThreadAndroid::get_env(); jclass classLoader = env->FindClass("java/lang/ClassLoader"); - jmethodID findClass = env->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); for (int i = 0; i < mods.size(); i++) { @@ -886,7 +700,7 @@ static void _initialize_java_modules() { ERR_EXPLAIN("Couldn't find proper initialize function 'public static Godot.SingletonBase Class::initialize(Activity p_activity)' initializer for singleton class: " + m); ERR_CONTINUE(!initialize); } - jobject obj = env->CallStaticObjectMethod(singletonClass, initialize, _godot_instance); + jobject obj = env->CallStaticObjectMethod(singletonClass, initialize, godot_java->get_activity()); env->NewGlobalRef(obj); } } @@ -931,7 +745,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo return; //should exit instead and print the error } - java_class_wrapper = memnew(JavaClassWrapper(_godot_instance)); + java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity())); Engine::get_singleton()->add_singleton(Engine::Singleton("JavaClassWrapper", java_class_wrapper)); _initialize_java_modules(); } @@ -951,7 +765,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en } else { // GL context recreated because it was lost; restart app to let it reload everything os_android->main_loop_end(); - env->CallVoidMethod(_godot_instance, _restart); + godot_java->restart(env); step = -1; // Ensure no further steps are attempted } } @@ -987,18 +801,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job } os_android->process_accelerometer(accelerometer); - os_android->process_gravity(gravity); - os_android->process_magnetometer(magnetometer); - os_android->process_gyroscope(gyroscope); if (os_android->main_loop_iterate()) { - jclass cls = env->FindClass("org/godotengine/godot/Godot"); - jmethodID _finish = env->GetMethodID(cls, "forceQuit", "()V"); - env->CallVoidMethod(_godot_instance, _finish); + godot_java->force_quit(env); } } diff --git a/platform/android/java_glue.h b/platform/android/java_godot_lib_jni.h index 3b93c9b42a..3a03294b08 100644 --- a/platform/android/java_glue.h +++ b/platform/android/java_godot_lib_jni.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* java_glue.h */ +/* java_godot_lib_jni.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,14 +28,17 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef JAVA_GLUE_H -#define JAVA_GLUE_H +#ifndef JAVA_GODOT_LIB_JNI_H +#define JAVA_GODOT_LIB_JNI_H #include <android/log.h> #include <jni.h> +// These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code. +// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes thats why we have the long names) extern "C" { JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits); @@ -63,4 +66,4 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jobject p_obj, jstring p_permission, jboolean p_result); } -#endif // JAVA_GLUE_H +#endif /* !JAVA_GODOT_LIB_JNI_H */ diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp new file mode 100644 index 0000000000..101a1d76c6 --- /dev/null +++ b/platform/android/java_godot_wrapper.cpp @@ -0,0 +1,185 @@ +/*************************************************************************/ +/* java_godot_wrapper.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "java_godot_wrapper.h" + +// JNIEnv is only valid within the thread it belongs to, in a multi threading environment +// we can't cache it. +// For Godot we call most access methods from our thread and we thus get a valid JNIEnv +// from ThreadAndroid. For one or two we expect to pass the environment + +// TODO we could probably create a base class for this... + +GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) { + godot_instance = p_env->NewGlobalRef(p_godot_instance); + + // get info about our Godot class so we can get pointers and stuff... + cls = p_env->FindClass("org/godotengine/godot/Godot"); + if (cls) { + cls = (jclass)p_env->NewGlobalRef(cls); + } else { + // this is a pretty serious fail.. bail... pointers will stay 0 + return; + } + + // get some method pointers... + _on_video_init = p_env->GetMethodID(cls, "onVideoInit", "()V"); + _restart = p_env->GetMethodID(cls, "restart", "()V"); + _finish = p_env->GetMethodID(cls, "forceQuit", "()V"); + _set_keep_screen_on = p_env->GetMethodID(cls, "setKeepScreenOn", "(Z)V"); + _alert = p_env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); + _get_GLES_version_code = p_env->GetMethodID(cls, "getGLESVersionCode", "()I"); + _get_clipboard = p_env->GetMethodID(cls, "getClipboard", "()Ljava/lang/String;"); + _set_clipboard = p_env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V"); + _request_permission = p_env->GetMethodID(cls, "requestPermission", "(Ljava/lang/String;)Z"); +} + +GodotJavaWrapper::~GodotJavaWrapper() { + // nothing to do here for now +} + +jobject GodotJavaWrapper::get_activity() { + // our godot instance is our activity + return godot_instance; +} + +jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) { + if (cls) { + if (p_env == NULL) + p_env = ThreadAndroid::get_env(); + + jfieldID fid = p_env->GetStaticFieldID(cls, p_name, p_class); + return p_env->GetStaticObjectField(cls, fid); + } else { + return NULL; + } +} + +jobject GodotJavaWrapper::get_class_loader() { + if (cls) { + JNIEnv *env = ThreadAndroid::get_env(); + jmethodID getClassLoader = env->GetMethodID(cls, "getClassLoader", "()Ljava/lang/ClassLoader;"); + return env->CallObjectMethod(godot_instance, getClassLoader); + } else { + return NULL; + } +} + +void GodotJavaWrapper::gfx_init(bool gl2) { + // beats me what this once did, there was no code, + // but we're getting false if our GLES3 driver is initialised + // and true for our GLES2 driver + // Maybe we're supposed to communicate this back or store it? +} + +void GodotJavaWrapper::on_video_init(JNIEnv *p_env) { + if (_on_video_init) + if (p_env == NULL) + p_env = ThreadAndroid::get_env(); + + p_env->CallVoidMethod(godot_instance, _on_video_init); +} + +void GodotJavaWrapper::restart(JNIEnv *p_env) { + if (_restart) + if (p_env == NULL) + p_env = ThreadAndroid::get_env(); + + p_env->CallVoidMethod(godot_instance, _restart); +} + +void GodotJavaWrapper::force_quit(JNIEnv *p_env) { + if (_finish) + if (p_env == NULL) + p_env = ThreadAndroid::get_env(); + + p_env->CallVoidMethod(godot_instance, _finish); +} + +void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) { + if (_set_keep_screen_on) { + JNIEnv *env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled); + } +} + +void GodotJavaWrapper::alert(const String &p_message, const String &p_title) { + if (_alert) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data()); + jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data()); + env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle); + } +} + +int GodotJavaWrapper::get_gles_version_code() { + JNIEnv *env = ThreadAndroid::get_env(); + if (_get_GLES_version_code) { + return env->CallIntMethod(godot_instance, _get_GLES_version_code); + } + + return 0; +} + +bool GodotJavaWrapper::has_get_clipboard() { + return _get_clipboard != 0; +} + +String GodotJavaWrapper::get_clipboard() { + if (_get_clipboard) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard); + return jstring_to_string(s, env); + } else { + return String(); + } +} + +bool GodotJavaWrapper::has_set_clipboard() { + return _set_clipboard != 0; +} + +void GodotJavaWrapper::set_clipboard(const String &p_text) { + if (_set_clipboard) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStr = env->NewStringUTF(p_text.utf8().get_data()); + env->CallVoidMethod(godot_instance, _set_clipboard, jStr); + } +} + +bool GodotJavaWrapper::request_permission(const String &p_name) { + if (_request_permission) { + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStrName = env->NewStringUTF(p_name.utf8().get_data()); + return env->CallBooleanMethod(godot_instance, _request_permission, jStrName); + } else { + return false; + } +} diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h new file mode 100644 index 0000000000..438aee019b --- /dev/null +++ b/platform/android/java_godot_wrapper.h @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* java_godot_wrapper.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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. */ +/*************************************************************************/ + +// note, swapped java and godot around in the file name so all the java +// wrappers are together + +#ifndef JAVA_GODOT_WRAPPER_H +#define JAVA_GODOT_WRAPPER_H + +#include <android/log.h> +#include <jni.h> + +#include "string_android.h" + +// Class that makes functions in java/src/org/godotengine/godot/Godot.java callable from C++ +class GodotJavaWrapper { +private: + jobject godot_instance; + jclass cls; + + jmethodID _on_video_init = 0; + jmethodID _restart = 0; + jmethodID _finish = 0; + jmethodID _set_keep_screen_on = 0; + jmethodID _alert = 0; + jmethodID _get_GLES_version_code = 0; + jmethodID _get_clipboard = 0; + jmethodID _set_clipboard = 0; + jmethodID _request_permission = 0; + +public: + GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance); + ~GodotJavaWrapper(); + + jobject get_activity(); + jobject get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env = NULL); + + jobject get_class_loader(); + + void gfx_init(bool gl2); + void on_video_init(JNIEnv *p_env = NULL); + void restart(JNIEnv *p_env = NULL); + void force_quit(JNIEnv *p_env = NULL); + void set_keep_screen_on(bool p_enabled); + void alert(const String &p_message, const String &p_title); + int get_gles_version_code(); + bool has_get_clipboard(); + String get_clipboard(); + bool has_set_clipboard(); + void set_clipboard(const String &p_text); + bool request_permission(const String &p_name); +}; + +#endif /* !JAVA_GODOT_WRAPPER_H */ diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 837713f9c9..93d39859f2 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -46,6 +46,9 @@ #include <dlfcn.h> +#include "java_godot_io_wrapper.h" +#include "java_godot_wrapper.h" + class AndroidLogger : public Logger { public: virtual void logv(const char *p_format, va_list p_list, bool p_err) { @@ -118,15 +121,14 @@ int OS_Android::get_current_video_driver() const { Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { - bool use_gl3 = get_gl_version_code_func() >= 0x00030000; + bool use_gl3 = godot_java->get_gles_version_code() >= 0x00030000; use_gl3 = use_gl3 && (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3"); bool gl_initialization_error = false; while (true) { if (use_gl3) { if (RasterizerGLES3::is_viable() == OK) { - if (gfx_init_func) - gfx_init_func(gfx_init_ud, false); + godot_java->gfx_init(false); RasterizerGLES3::register_config(); RasterizerGLES3::make_current(); break; @@ -142,8 +144,7 @@ Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int } } else { if (RasterizerGLES2::is_viable() == OK) { - if (gfx_init_func) - gfx_init_func(gfx_init_ud, true); + godot_java->gfx_init(true); RasterizerGLES2::register_config(); RasterizerGLES2::make_current(); break; @@ -195,17 +196,23 @@ void OS_Android::finalize() { memdelete(input); } +GodotJavaWrapper *OS_Android::get_godot_java() { + return godot_java; +} + +GodotIOJavaWrapper *OS_Android::get_godot_io_java() { + return godot_io_java; +} + void OS_Android::alert(const String &p_alert, const String &p_title) { //print("ALERT: %s\n", p_alert.utf8().get_data()); - if (alert_func) - alert_func(p_alert, p_title); + godot_java->alert(p_alert, p_title); } bool OS_Android::request_permission(const String &p_name) { - if (request_permission_func) - return request_permission_func(p_name); - return false; + + return godot_java->request_permission(p_name); } Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { @@ -262,9 +269,7 @@ void OS_Android::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) void OS_Android::set_keep_screen_on(bool p_enabled) { OS::set_keep_screen_on(p_enabled); - if (set_keep_screen_on_func) { - set_keep_screen_on_func(p_enabled); - } + godot_java->set_keep_screen_on(p_enabled); } Size2 OS_Android::get_window_size() const { @@ -287,14 +292,6 @@ bool OS_Android::can_draw() const { return true; //always? } -void OS_Android::set_cursor_shape(CursorShape p_shape) { - - //android really really really has no mouse.. how amazing.. -} - -void OS_Android::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { -} - void OS_Android::main_loop_begin() { if (main_loop) @@ -505,18 +502,16 @@ bool OS_Android::has_virtual_keyboard() const { } int OS_Android::get_virtual_keyboard_height() const { - if (get_virtual_keyboard_height_func) { - return get_virtual_keyboard_height_func(); - } + return godot_io_java->get_vk_height(); - ERR_PRINT("Cannot obtain virtual keyboard height."); - return 0; + // ERR_PRINT("Cannot obtain virtual keyboard height."); + // return 0; } void OS_Android::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect) { - if (show_virtual_keyboard_func) { - show_virtual_keyboard_func(p_existing_text); + if (godot_io_java->has_vk()) { + godot_io_java->show_vk(p_existing_text); } else { ERR_PRINT("Virtual keyboard not available"); @@ -525,9 +520,9 @@ void OS_Android::show_virtual_keyboard(const String &p_existing_text, const Rect void OS_Android::hide_virtual_keyboard() { - if (hide_virtual_keyboard_func) { + if (godot_io_java->has_vk()) { - hide_virtual_keyboard_func(); + godot_io_java->hide_vk(); } else { ERR_PRINT("Virtual keyboard not available"); @@ -556,9 +551,7 @@ void OS_Android::set_display_size(Size2 p_size) { Error OS_Android::shell_open(String p_uri) { - if (open_uri_func) - return open_uri_func(p_uri) ? ERR_CANT_OPEN : OK; - return ERR_UNAVAILABLE; + return godot_io_java->open_uri(p_uri); } String OS_Android::get_resource_dir() const { @@ -568,23 +561,29 @@ String OS_Android::get_resource_dir() const { String OS_Android::get_locale() const { - if (get_locale_func) - return get_locale_func(); + String locale = godot_io_java->get_locale(); + if (locale != "") { + return locale; + } + return OS_Unix::get_locale(); } void OS_Android::set_clipboard(const String &p_text) { - if (set_clipboard_func) { - set_clipboard_func(p_text); + // DO we really need the fallback to OS_Unix here?! + if (godot_java->has_set_clipboard()) { + godot_java->set_clipboard(p_text); } else { OS_Unix::set_clipboard(p_text); } } String OS_Android::get_clipboard() const { - if (get_clipboard_func) { - return get_clipboard_func(); + + // DO we really need the fallback to OS_Unix here?! + if (godot_java->has_get_clipboard()) { + return godot_java->get_clipboard(); } return OS_Unix::get_clipboard(); @@ -592,17 +591,16 @@ String OS_Android::get_clipboard() const { String OS_Android::get_model_name() const { - if (get_model_func) - return get_model_func(); + String model = godot_io_java->get_model(); + if (model != "") + return model; + return OS_Unix::get_model_name(); } int OS_Android::get_screen_dpi(int p_screen) const { - if (get_screen_dpi_func) { - return get_screen_dpi_func(); - } - return 160; + return godot_io_java->get_screen_dpi(); } String OS_Android::get_user_data_dir() const { @@ -610,8 +608,8 @@ String OS_Android::get_user_data_dir() const { if (data_dir_cache != String()) return data_dir_cache; - if (get_user_data_dir_func) { - String data_dir = get_user_data_dir_func(); + String data_dir = godot_io_java->get_user_data_dir(); + if (data_dir != "") { //store current dir char real_current_dir_name[2048]; @@ -638,45 +636,43 @@ String OS_Android::get_user_data_dir() const { void OS_Android::set_screen_orientation(ScreenOrientation p_orientation) { - if (set_screen_orientation_func) - set_screen_orientation_func(p_orientation); + godot_io_java->set_screen_orientation(p_orientation); } String OS_Android::get_unique_id() const { - if (get_unique_id_func) - return get_unique_id_func(); + String unique_id = godot_io_java->get_unique_id(); + if (unique_id != "") + return unique_id; + return OS::get_unique_id(); } Error OS_Android::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) { // FIXME: Add support for volume, audio and subtitle tracks - if (video_play_func) - video_play_func(p_path); + + godot_io_java->play_video(p_path); return OK; } bool OS_Android::native_video_is_playing() const { - if (video_is_playing_func) - return video_is_playing_func(); - return false; + + return godot_io_java->is_video_playing(); } void OS_Android::native_video_pause() { - if (video_pause_func) - video_pause_func(); + + godot_io_java->pause_video(); } String OS_Android::get_system_dir(SystemDir p_dir) const { - if (get_system_dir_func) - return get_system_dir_func(p_dir); - return String("."); + return godot_io_java->get_system_dir(p_dir); } void OS_Android::native_video_stop() { - if (video_stop_func) - video_stop_func(); + + godot_io_java->stop_video(); } void OS_Android::set_context_is_16_bits(bool p_is_16) { @@ -719,7 +715,7 @@ bool OS_Android::_check_internal_feature_support(const String &p_feature) { return false; } -OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, SetClipboardFunc p_set_clipboard_func, GetClipboardFunc p_get_clipboard_func, RequestPermissionFunc p_request_permission, bool p_use_apk_expansion) { +OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion) { use_apk_expansion = p_use_apk_expansion; default_videomode.width = 800; @@ -727,38 +723,13 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURI default_videomode.fullscreen = true; default_videomode.resizable = false; - gfx_init_func = p_gfx_init_func; - gfx_init_ud = p_gfx_init_ud; main_loop = NULL; gl_extensions = NULL; //rasterizer = NULL; use_gl2 = false; - open_uri_func = p_open_uri_func; - get_user_data_dir_func = p_get_user_data_dir_func; - get_locale_func = p_get_locale_func; - get_model_func = p_get_model_func; - get_screen_dpi_func = p_get_screen_dpi_func; - get_unique_id_func = p_get_unique_id; - get_system_dir_func = p_get_sdir_func; - get_gl_version_code_func = p_get_gl_version_func; - - video_play_func = p_video_play_func; - video_is_playing_func = p_video_is_playing_func; - video_pause_func = p_video_pause_func; - video_stop_func = p_video_stop_func; - - show_virtual_keyboard_func = p_show_vk; - hide_virtual_keyboard_func = p_hide_vk; - get_virtual_keyboard_height_func = p_vk_height_func; - - set_clipboard_func = p_set_clipboard_func; - get_clipboard_func = p_get_clipboard_func; - - set_screen_orientation_func = p_screen_orient; - set_keep_screen_on_func = p_set_keep_screen_on_func; - alert_func = p_alert_func; - request_permission_func = p_request_permission; + godot_java = p_godot_java; + godot_io_java = p_godot_io_java; Vector<Logger *> loggers; loggers.push_back(memnew(AndroidLogger)); diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 44c5a206d4..d2198b0579 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -41,29 +41,8 @@ #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" -typedef void (*GFXInitFunc)(void *ud, bool gl2); -typedef int (*OpenURIFunc)(const String &); -typedef String (*GetUserDataDirFunc)(); -typedef String (*GetLocaleFunc)(); -typedef void (*SetClipboardFunc)(const String &); -typedef String (*GetClipboardFunc)(); -typedef String (*GetModelFunc)(); -typedef int (*GetScreenDPIFunc)(); -typedef String (*GetUniqueIDFunc)(); -typedef void (*ShowVirtualKeyboardFunc)(const String &); -typedef void (*HideVirtualKeyboardFunc)(); -typedef void (*SetScreenOrientationFunc)(int); -typedef String (*GetSystemDirFunc)(int); -typedef int (*GetGLVersionCodeFunc)(); - -typedef void (*VideoPlayFunc)(const String &); -typedef bool (*VideoIsPlayingFunc)(); -typedef void (*VideoPauseFunc)(); -typedef void (*VideoStopFunc)(); -typedef void (*SetKeepScreenOnFunc)(bool p_enabled); -typedef void (*AlertFunc)(const String &, const String &); -typedef int (*VirtualKeyboardHeightFunc)(); -typedef bool (*RequestPermissionFunc)(const String &); +class GodotJavaWrapper; +class GodotIOJavaWrapper; class OS_Android : public OS_Unix { public: @@ -91,9 +70,6 @@ public: private: Vector<TouchPos> touch; - GFXInitFunc gfx_init_func; - void *gfx_init_ud; - bool use_gl2; bool use_apk_expansion; @@ -112,30 +88,11 @@ private: VideoMode default_videomode; MainLoop *main_loop; - OpenURIFunc open_uri_func; - GetUserDataDirFunc get_user_data_dir_func; - GetLocaleFunc get_locale_func; - SetClipboardFunc set_clipboard_func; - GetClipboardFunc get_clipboard_func; - GetModelFunc get_model_func; - GetScreenDPIFunc get_screen_dpi_func; - ShowVirtualKeyboardFunc show_virtual_keyboard_func; - HideVirtualKeyboardFunc hide_virtual_keyboard_func; - VirtualKeyboardHeightFunc get_virtual_keyboard_height_func; - SetScreenOrientationFunc set_screen_orientation_func; - GetUniqueIDFunc get_unique_id_func; - GetSystemDirFunc get_system_dir_func; - GetGLVersionCodeFunc get_gl_version_code_func; - - VideoPlayFunc video_play_func; - VideoIsPlayingFunc video_is_playing_func; - VideoPauseFunc video_pause_func; - VideoStopFunc video_stop_func; - SetKeepScreenOnFunc set_keep_screen_on_func; - AlertFunc alert_func; - RequestPermissionFunc request_permission_func; - - //PowerAndroid *power_manager; + GodotJavaWrapper *godot_java; + GodotIOJavaWrapper *godot_io_java; + + //PowerAndroid *power_manager_func; + int video_driver_index; public: @@ -159,6 +116,8 @@ public: typedef int64_t ProcessID; static OS *get_singleton(); + GodotJavaWrapper *get_godot_java(); + GodotIOJavaWrapper *get_godot_io_java(); virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); virtual bool request_permission(const String &p_name); @@ -185,9 +144,6 @@ public: virtual bool can_draw() const; - virtual void set_cursor_shape(CursorShape p_shape); - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); - void main_loop_begin(); bool main_loop_iterate(); void main_loop_request_go_back(); @@ -241,7 +197,7 @@ public: void joy_connection_changed(int p_device, bool p_connected, String p_name); virtual bool _check_internal_feature_support(const String &p_feature); - OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, SetClipboardFunc p_set_clipboard, GetClipboardFunc p_get_clipboard, RequestPermissionFunc p_request_permission, bool p_use_apk_expansion); + OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion); ~OS_Android(); }; diff --git a/platform/haiku/audio_driver_media_kit.cpp b/platform/haiku/audio_driver_media_kit.cpp index 8b7dd08bb7..3c4e31da36 100644 --- a/platform/haiku/audio_driver_media_kit.cpp +++ b/platform/haiku/audio_driver_media_kit.cpp @@ -39,11 +39,11 @@ int32_t *AudioDriverMediaKit::samples_in = NULL; Error AudioDriverMediaKit::init() { active = false; - mix_rate = 44100; + mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); speaker_mode = SPEAKER_MODE_STEREO; channels = 2; - int latency = GLOBAL_DEF_RST("audio/output_latency", 25); + int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); buffer_size = next_power_of_2(latency * mix_rate / 1000); samples_in = memnew_arr(int32_t, buffer_size * channels); diff --git a/platform/haiku/context_gl_haiku.h b/platform/haiku/context_gl_haiku.h index 6eb67fea70..8452f5fbfb 100644 --- a/platform/haiku/context_gl_haiku.h +++ b/platform/haiku/context_gl_haiku.h @@ -33,12 +33,10 @@ #if defined(OPENGL_ENABLED) -#include "drivers/gl_context/context_gl.h" - #include "haiku_direct_window.h" #include "haiku_gl_view.h" -class ContextGL_Haiku : public ContextGL { +class ContextGL_Haiku { private: HaikuGLView *view; HaikuDirectWindow *window; @@ -46,18 +44,18 @@ private: bool use_vsync; public: - virtual Error initialize(); - virtual void release_current(); - virtual void make_current(); - virtual void swap_buffers(); - virtual int get_window_width(); - virtual int get_window_height(); + Error initialize(); + void release_current(); + void make_current(); + void swap_buffers(); + int get_window_width(); + int get_window_height(); - virtual void set_use_vsync(bool p_use); - virtual bool is_using_vsync() const; + void set_use_vsync(bool p_use); + bool is_using_vsync() const; ContextGL_Haiku(HaikuDirectWindow *p_window); - virtual ~ContextGL_Haiku(); + ~ContextGL_Haiku(); }; #endif diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp index a6d5a00852..f3fed6669b 100644 --- a/platform/haiku/os_haiku.cpp +++ b/platform/haiku/os_haiku.cpp @@ -203,6 +203,10 @@ void OS_Haiku::set_cursor_shape(CursorShape p_shape) { //ERR_PRINT("set_cursor_shape() NOT IMPLEMENTED"); } +OS::CursorShape OS_Haiku::get_cursor_shape() const { + // TODO: implement get_cursor_shape +} + void OS_Haiku::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { // TODO } diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h index d7eac10635..6ab006843a 100644 --- a/platform/haiku/os_haiku.h +++ b/platform/haiku/os_haiku.h @@ -86,6 +86,7 @@ public: virtual Point2 get_mouse_position() const; virtual int get_mouse_button_state() const; virtual void set_cursor_shape(CursorShape p_shape); + virtual CursorShape get_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); virtual int get_screen_count() const; diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index 41991bce86..fa1b124561 100644 --- a/platform/iphone/SCsub +++ b/platform/iphone/SCsub @@ -2,8 +2,6 @@ Import('env') -import os - iphone_lib = [ 'godot_iphone.cpp', 'os_iphone.cpp', diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index 172572bcb4..853f24379e 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -1,5 +1,4 @@ import os -import string import sys from methods import detect_darwin_sdk_path diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 7d0fdd2078..e25efc813b 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -490,17 +490,11 @@ void OSIPhone::set_keep_screen_on(bool p_enabled) { _set_keep_screen_on(p_enabled); }; -void OSIPhone::set_cursor_shape(CursorShape p_shape){ - -}; - String OSIPhone::get_user_data_dir() const { return data_dir; }; -void OSIPhone::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot){}; - String OSIPhone::get_name() { return "iOS"; diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index 30d7a1ba41..49c6475cf9 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -167,9 +167,6 @@ public: virtual void hide_virtual_keyboard(); virtual int get_virtual_keyboard_height() const; - virtual void set_cursor_shape(CursorShape p_shape); - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); - virtual Size2 get_window_size() const; virtual Rect2 get_window_safe_area() const; diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index a93c98a89f..85a633442e 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -20,6 +20,13 @@ for lib in js_libraries: env.Append(LINKFLAGS=['--js-library', env.File(lib).path]) env.Depends(build, js_libraries) +js_modules = [ + 'id_handler.js', +] +for module in js_modules: + env.Append(LINKFLAGS=['--pre-js', env.File(module).path]) +env.Depends(build, js_modules) + wrapper_start = env.File('pre.js') wrapper_end = env.File('engine.js') js_wrapped = env.Textfile('#bin/godot', [wrapper_start, js, wrapper_end], TEXTFILESUFFIX='${PROGSUFFIX}.wrapped.js') diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 47da8de5df..c7acbde3f7 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -1,5 +1,4 @@ import os -import sys def is_active(): diff --git a/platform/javascript/id_handler.js b/platform/javascript/id_handler.js new file mode 100644 index 0000000000..36ef5aa8ef --- /dev/null +++ b/platform/javascript/id_handler.js @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* id_handler.js */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/*************************************************************************/ + +var IDHandler = function() { + + var ids = {}; + var size = 0; + + this.has = function(id) { + return ids.hasOwnProperty(id); + } + + this.add = function(obj) { + size += 1; + var id = crypto.getRandomValues(new Int32Array(32))[0]; + ids[id] = obj; + return id; + } + + this.get = function(id) { + return ids[id]; + } + + this.remove = function(id) { + size -= 1; + delete ids[id]; + } + + this.size = function() { + return size; + } + + this.ids = ids; +}; + +Module.IDHandler = new IDHandler; diff --git a/platform/osx/SCsub b/platform/osx/SCsub index d2952ebdc0..e15b4339a7 100644 --- a/platform/osx/SCsub +++ b/platform/osx/SCsub @@ -2,7 +2,6 @@ Import('env') -import os from platform_methods import run_in_subprocess import platform_osx_builders diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index dfe7b27bd0..125a88ab6d 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -172,6 +172,7 @@ public: virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); virtual void set_cursor_shape(CursorShape p_shape); + virtual CursorShape get_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); virtual void set_mouse_show(bool p_show); diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 7d2116ba7e..5a4c0593bc 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -271,6 +271,8 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt - (void)windowDidExitFullScreen:(NSNotification *)notification { OS_OSX::singleton->zoomed = false; + if (!OS_OSX::singleton->resizable) + [OS_OSX::singleton->window_object setStyleMask:[OS_OSX::singleton->window_object styleMask] & ~NSWindowStyleMaskResizable]; } - (void)windowDidChangeBackingProperties:(NSNotification *)notification { @@ -710,8 +712,6 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED) OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT); - if (OS_OSX::singleton->input) - OS_OSX::singleton->input->set_mouse_in_window(false); } - (void)mouseEntered:(NSEvent *)event { @@ -719,8 +719,6 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { return; if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED) OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); - if (OS_OSX::singleton->input) - OS_OSX::singleton->input->set_mouse_in_window(true); OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape; OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX; @@ -1694,6 +1692,11 @@ void OS_OSX::set_cursor_shape(CursorShape p_shape) { cursor_shape = p_shape; } +OS::CursorShape OS_OSX::get_cursor_shape() const { + + return cursor_shape; +} + void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { if (p_cursor.is_valid()) { Ref<Texture> texture = p_cursor; @@ -2300,6 +2303,8 @@ void OS_OSX::set_window_fullscreen(bool p_enabled) { if (zoomed != p_enabled) { if (layered_window) set_window_per_pixel_transparency_enabled(false); + if (!resizable) + [window_object setStyleMask:[window_object styleMask] | NSWindowStyleMaskResizable]; [window_object toggleFullScreen:nil]; } zoomed = p_enabled; @@ -2314,7 +2319,7 @@ void OS_OSX::set_window_resizable(bool p_enabled) { if (p_enabled) [window_object setStyleMask:[window_object styleMask] | NSWindowStyleMaskResizable]; - else + else if (!zoomed) [window_object setStyleMask:[window_object styleMask] & ~NSWindowStyleMaskResizable]; resizable = p_enabled; diff --git a/platform/server/SCsub b/platform/server/SCsub index 62d45efbc0..f977275595 100644 --- a/platform/server/SCsub +++ b/platform/server/SCsub @@ -1,7 +1,5 @@ #!/usr/bin/env python -import os -import platform import sys Import('env') diff --git a/platform/server/detect.py b/platform/server/detect.py index 39f72db0ad..f13ee72fd2 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -1,7 +1,6 @@ import os import platform import sys -from methods import get_compiler_version, use_gcc # This file is mostly based on platform/x11/detect.py. # If editing this file, make sure to apply relevant changes here too. diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp index e643d3e8bb..53f2a65c8e 100644 --- a/platform/server/os_server.cpp +++ b/platform/server/os_server.cpp @@ -198,12 +198,6 @@ String OS_Server::get_name() { void OS_Server::move_window_to_foreground() { } -void OS_Server::set_cursor_shape(CursorShape p_shape) { -} - -void OS_Server::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { -} - OS::PowerState OS_Server::get_power_state() { return power_manager->get_power_state(); } diff --git a/platform/server/os_server.h b/platform/server/os_server.h index eebe8ae777..7441064790 100644 --- a/platform/server/os_server.h +++ b/platform/server/os_server.h @@ -95,9 +95,6 @@ protected: public: virtual String get_name(); - virtual void set_cursor_shape(CursorShape p_shape); - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); - virtual void set_mouse_show(bool p_show); virtual void set_mouse_grab(bool p_grab); virtual bool is_mouse_grab_enabled() const; diff --git a/platform/uwp/context_egl_uwp.h b/platform/uwp/context_egl_uwp.h index 812bdfb688..0c62fe7456 100644 --- a/platform/uwp/context_egl_uwp.h +++ b/platform/uwp/context_egl_uwp.h @@ -37,11 +37,10 @@ #include "core/error_list.h" #include "core/os/os.h" -#include "drivers/gl_context/context_gl.h" using namespace Windows::UI::Core; -class ContextEGL_UWP : public ContextGL { +class ContextEGL_UWP { public: enum Driver { @@ -64,24 +63,24 @@ private: Driver driver; public: - virtual void release_current(); + void release_current(); - virtual void make_current(); + void make_current(); - virtual int get_window_width(); - virtual int get_window_height(); - virtual void swap_buffers(); + int get_window_width(); + int get_window_height(); + void swap_buffers(); - virtual void set_use_vsync(bool use) { vsync = use; } - virtual bool is_using_vsync() const { return vsync; } + void set_use_vsync(bool use) { vsync = use; } + bool is_using_vsync() const { return vsync; } - virtual Error initialize(); + Error initialize(); void reset(); void cleanup(); ContextEGL_UWP(CoreWindow ^ p_window, Driver p_driver); - virtual ~ContextEGL_UWP(); + ~ContextEGL_UWP(); }; #endif // CONTEXT_EGL_UWP_H diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index c32a11b396..f8f1b066f6 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -1,6 +1,5 @@ import methods import os -import string import sys @@ -25,8 +24,6 @@ def can_build(): def get_opts(): - from SCons.Variables import BoolVariable - return [ ('msvc_version', 'MSVC version to use (ignored if the VCINSTALLDIR environment variable is set)', None), ] diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index bc74da8a1b..82f09032f5 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -704,6 +704,11 @@ void OS_UWP::set_cursor_shape(CursorShape p_shape) { cursor_shape = p_shape; } +OS::CursorShape OS_UWP::get_cursor_shape() const { + + return cursor_shape; +} + void OS_UWP::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { // TODO } diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index fd78b3cdf7..00f79efb04 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -219,6 +219,7 @@ public: virtual String get_clipboard() const; void set_cursor_shape(CursorShape p_shape); + CursorShape get_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); void set_icon(const Ref<Image> &p_icon); diff --git a/platform/windows/context_gl_windows.h b/platform/windows/context_gl_windows.h index 09801b9146..d23fba50e1 100644 --- a/platform/windows/context_gl_windows.h +++ b/platform/windows/context_gl_windows.h @@ -37,13 +37,12 @@ #include "core/error_list.h" #include "core/os/os.h" -#include "drivers/gl_context/context_gl.h" #include <windows.h> typedef bool(APIENTRY *PFNWGLSWAPINTERVALEXTPROC)(int interval); -class ContextGL_Windows : public ContextGL { +class ContextGL_Windows { HDC hDC; HGLRC hRC; @@ -55,21 +54,21 @@ class ContextGL_Windows : public ContextGL { PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; public: - virtual void release_current(); + void release_current(); - virtual void make_current(); + void make_current(); - virtual int get_window_width(); - virtual int get_window_height(); - virtual void swap_buffers(); + int get_window_width(); + int get_window_height(); + void swap_buffers(); - virtual Error initialize(); + Error initialize(); - virtual void set_use_vsync(bool p_use); - virtual bool is_using_vsync() const; + void set_use_vsync(bool p_use); + bool is_using_vsync() const; ContextGL_Windows(HWND hwnd, bool p_opengl_3_context); - virtual ~ContextGL_Windows(); + ~ContextGL_Windows(); }; #endif diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 426a5e9e61..2f7334657f 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -1,6 +1,5 @@ import methods import os -import sys def is_active(): @@ -197,6 +196,8 @@ def configure_msvc(env, manual_msvc_config): ## Compile/link flags env.AppendUnique(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo']) + if int(env['MSVC_VERSION'].split('.')[0]) >= 14: #vs2015 and later + env.AppendUnique(CCFLAGS=['/utf-8']) env.AppendUnique(CXXFLAGS=['/TP']) # assume all sources are C++ if manual_msvc_config: # should be automatic if SCons found it if os.getenv("WindowsSdkDir") is not None: diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 6125455e74..1a5050a15a 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -384,8 +384,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) outside = true; if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED) main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT); - if (input) - input->set_mouse_in_window(false); } break; case WM_INPUT: { @@ -480,8 +478,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED) main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); - if (input) - input->set_mouse_in_window(true); CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; @@ -670,7 +666,9 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mb->set_button_index(BUTTON_XBUTTON2); mb->set_doubleclick(true); } break; - default: { return 0; } + default: { + return 0; + } } mb->set_control((wParam & MK_CONTROL) != 0); @@ -2297,6 +2295,11 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) { cursor_shape = p_shape; } +OS::CursorShape OS_Windows::get_cursor_shape() const { + + return cursor_shape; +} + void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { if (p_cursor.is_valid()) { Ref<Texture> texture = p_cursor; diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 2d03532c69..c15e1cabc3 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -273,6 +273,7 @@ public: virtual String get_clipboard() const; void set_cursor_shape(CursorShape p_shape); + CursorShape get_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap); void set_icon(const Ref<Image> &p_icon); diff --git a/platform/x11/SCsub b/platform/x11/SCsub index e302f09c88..3d5aa15208 100644 --- a/platform/x11/SCsub +++ b/platform/x11/SCsub @@ -2,7 +2,6 @@ Import('env') -import os from platform_methods import run_in_subprocess import platform_x11_builders diff --git a/platform/x11/context_gl_x11.h b/platform/x11/context_gl_x11.h index 02455ce005..46420df48b 100644 --- a/platform/x11/context_gl_x11.h +++ b/platform/x11/context_gl_x11.h @@ -39,13 +39,12 @@ #if defined(OPENGL_ENABLED) #include "core/os/os.h" -#include "drivers/gl_context/context_gl.h" #include <X11/Xlib.h> #include <X11/extensions/Xrender.h> struct ContextGL_X11_Private; -class ContextGL_X11 : public ContextGL { +class ContextGL_X11 { public: enum ContextType { @@ -67,19 +66,19 @@ private: ContextType context_type; public: - virtual void release_current(); - virtual void make_current(); - virtual void swap_buffers(); - virtual int get_window_width(); - virtual int get_window_height(); + void release_current(); + void make_current(); + void swap_buffers(); + int get_window_width(); + int get_window_height(); - virtual Error initialize(); + Error initialize(); - virtual void set_use_vsync(bool p_use); - virtual bool is_using_vsync() const; + void set_use_vsync(bool p_use); + bool is_using_vsync() const; ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, ContextType p_context_type); - virtual ~ContextGL_X11(); + ~ContextGL_X11(); }; #endif diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 163b9999a9..5f7b825f5e 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -1,8 +1,8 @@ import os import platform import sys -from compat import decode_utf8 -from methods import get_compiler_version, use_gcc +from methods import get_compiler_version, using_gcc + def is_active(): return True @@ -160,7 +160,7 @@ def configure(env): env.Append(LINKFLAGS=['-pipe']) # Check for gcc version >= 6 before adding -no-pie - if use_gcc(env): + if using_gcc(env): version = get_compiler_version(env) if version != None and version[0] >= '6': env.Append(CCFLAGS=['-fpie']) diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 0fe91f3d00..c96047dfbd 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -2041,15 +2041,11 @@ void OS_X11::process_xevents() { case LeaveNotify: { if (main_loop && !mouse_mode_grab) main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT); - if (input) - input->set_mouse_in_window(false); } break; case EnterNotify: { if (main_loop && !mouse_mode_grab) main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); - if (input) - input->set_mouse_in_window(true); } break; case FocusIn: minimized = false; @@ -2722,6 +2718,11 @@ void OS_X11::set_cursor_shape(CursorShape p_shape) { current_cursor = p_shape; } +OS::CursorShape OS_X11::get_cursor_shape() const { + + return current_cursor; +} + void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { if (p_cursor.is_valid()) { Ref<Texture> texture = p_cursor; @@ -3238,6 +3239,8 @@ OS_X11::OS_X11() { AudioDriverManager::add_driver(&driver_alsa); #endif + xi.opcode = 0; + xi.last_relative_time = 0; layered_window = false; minimized = false; xim_style = 0L; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 6d1a66af84..a54851d4e7 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -219,6 +219,7 @@ public: virtual String get_name(); virtual void set_cursor_shape(CursorShape p_shape); + virtual CursorShape get_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); void set_mouse_mode(MouseMode p_mode); diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index f8384bd1e4..932db8f001 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -393,19 +393,30 @@ void AnimatedSprite::_notification(int p_what) { timeout = _get_frame_duration(); int fc = frames->get_frame_count(animation); - if (frame >= fc - 1) { + if ((!backwards && frame >= fc - 1) || (backwards && frame <= 0)) { if (frames->get_animation_loop(animation)) { - frame = 0; + if (backwards) + frame = fc - 1; + else + frame = 0; + emit_signal(SceneStringNames::get_singleton()->animation_finished); } else { - frame = fc - 1; + if (backwards) + frame = 0; + else + frame = fc - 1; + if (!is_over) { is_over = true; emit_signal(SceneStringNames::get_singleton()->animation_finished); } } } else { - frame++; + if (backwards) + frame--; + else + frame++; } update(); @@ -594,10 +605,12 @@ bool AnimatedSprite::_is_playing() const { return playing; } -void AnimatedSprite::play(const StringName &p_animation) { +void AnimatedSprite::play(const StringName &p_animation, const bool p_backwards) { if (p_animation) set_animation(p_animation); + + backwards = p_backwards; _set_playing(true); } @@ -666,7 +679,7 @@ void AnimatedSprite::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_playing", "playing"), &AnimatedSprite::_set_playing); ClassDB::bind_method(D_METHOD("_is_playing"), &AnimatedSprite::_is_playing); - ClassDB::bind_method(D_METHOD("play", "anim"), &AnimatedSprite::play, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("play", "anim", "backwards"), &AnimatedSprite::play, DEFVAL(StringName()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("stop"), &AnimatedSprite::stop); ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite::is_playing); @@ -713,6 +726,7 @@ AnimatedSprite::AnimatedSprite() { frame = 0; speed_scale = 1.0f; playing = false; + backwards = false; animation = "default"; timeout = 0; is_over = false; diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h index 8753f88799..2cc372bd93 100644 --- a/scene/2d/animated_sprite.h +++ b/scene/2d/animated_sprite.h @@ -128,6 +128,7 @@ class AnimatedSprite : public Node2D { Ref<SpriteFrames> frames; bool playing; + bool backwards; StringName animation; int frame; float speed_scale; @@ -169,7 +170,7 @@ public: void set_sprite_frames(const Ref<SpriteFrames> &p_frames); Ref<SpriteFrames> get_sprite_frames() const; - void play(const StringName &p_animation = StringName()); + void play(const StringName &p_animation = StringName(), const bool p_backwards = false); void stop(); bool is_playing() const; diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 2a225e5797..b322cfe8f1 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -261,8 +261,9 @@ void Area2D::_area_inout(int p_status, const RID &p_area, int p_instance, int p_ Map<ObjectID, AreaState>::Element *E = area_map.find(objid); - ERR_FAIL_COND(!area_in && !E); - + if (!area_in && !E) { + return; //likely removed from the tree + } locked = true; if (area_in) { diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 7b28ad2c12..faefd85968 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -473,6 +473,10 @@ bool AudioStreamPlayer2D::get_stream_paused() const { return stream_paused; } +Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() { + return stream_playback; +} + void AudioStreamPlayer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer2D::set_stream); @@ -512,6 +516,8 @@ void AudioStreamPlayer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer2D::set_stream_paused); ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer2D::get_stream_paused); + ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback); + ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer2D::_bus_layout_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index caf5c6ee49..0cd18fb93b 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -130,6 +130,8 @@ public: void set_stream_paused(bool p_pause); bool get_stream_paused() const; + Ref<AudioStreamPlayback> get_stream_playback(); + AudioStreamPlayer2D(); ~AudioStreamPlayer2D(); }; diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index 2739f71543..78e98deb93 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -891,13 +891,13 @@ void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Colo VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid, rid_normal, p_antialiased); } -void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) { +void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, const Transform2D &p_transform, const Color &p_modulate) { ERR_FAIL_COND(p_mesh.is_null()); RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); - VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), texture_rid, normal_map_rid); + VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), p_transform, p_modulate, texture_rid, normal_map_rid); } void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) { @@ -1168,7 +1168,7 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture", "normal_map", "antialiased"), &CanvasItem::draw_colored_polygon, DEFVAL(PoolVector2Array()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_string", "font", "position", "text", "modulate", "clip_w"), &CanvasItem::draw_string, DEFVAL(Color(1, 1, 1)), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("draw_char", "font", "position", "char", "next", "modulate"), &CanvasItem::draw_char, DEFVAL(Color(1, 1, 1))); - ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>())); + ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map", "transform", "modulate"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>()), DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture", "normal_map"), &CanvasItem::draw_multimesh, DEFVAL(Ref<Texture>())); ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform); diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index bf7cfa8e75..a020ab5029 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -317,7 +317,7 @@ public: void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false); void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false); - void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map); + void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1)); void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map); void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1); diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index d54070df8d..f43d97eb2a 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "collision_object_2d.h" + #include "scene/scene_string_names.h" #include "servers/physics_2d_server.h" @@ -56,7 +57,7 @@ void CollisionObject2D::_notification(int p_what) { _update_pickable(); //get space - } + } break; case NOTIFICATION_ENTER_CANVAS: { @@ -64,7 +65,7 @@ void CollisionObject2D::_notification(int p_what) { Physics2DServer::get_singleton()->area_attach_canvas_instance_id(rid, get_canvas_layer_instance_id()); else Physics2DServer::get_singleton()->body_attach_canvas_instance_id(rid, get_canvas_layer_instance_id()); - } + } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -101,7 +102,7 @@ void CollisionObject2D::_notification(int p_what) { Physics2DServer::get_singleton()->area_attach_canvas_instance_id(rid, 0); else Physics2DServer::get_singleton()->body_attach_canvas_instance_id(rid, 0); - } + } break; } } diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 5334caed67..3a3f90ac4b 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -32,9 +32,59 @@ #include "core/engine.h" +#define LINE_GRAB_WIDTH 8 +Rect2 OccluderPolygon2D::_edit_get_rect() const { + + if (rect_cache_dirty) { + if (closed) { + PoolVector<Vector2>::Read r = polygon.read(); + item_rect = Rect2(); + for (int i = 0; i < polygon.size(); i++) { + Vector2 pos = r[i]; + if (i == 0) + item_rect.position = pos; + else + item_rect.expand_to(pos); + } + rect_cache_dirty = false; + } else { + if (polygon.size() == 0) { + item_rect = Rect2(); + } else { + Vector2 d = Vector2(LINE_GRAB_WIDTH, LINE_GRAB_WIDTH); + item_rect = Rect2(polygon[0] - d, 2 * d); + for (int i = 1; i < polygon.size(); i++) { + item_rect.expand_to(polygon[i] - d); + item_rect.expand_to(polygon[i] + d); + } + } + } + } + + return item_rect; +} + +bool OccluderPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + if (closed) { + return Geometry::is_point_in_polygon(p_point, Variant(polygon)); + } else { + const real_t d = LINE_GRAB_WIDTH / 2 + p_tolerance; + PoolVector<Vector2>::Read points = polygon.read(); + for (int i = 0; i < polygon.size() - 1; i++) { + Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, &points[i]); + if (p.distance_to(p_point) <= d) + return true; + } + + return false; + } +} + void OccluderPolygon2D::set_polygon(const PoolVector<Vector2> &p_polygon) { polygon = p_polygon; + rect_cache_dirty = true; VS::get_singleton()->canvas_occluder_polygon_set_shape(occ_polygon, p_polygon, closed); emit_changed(); } @@ -100,6 +150,7 @@ OccluderPolygon2D::OccluderPolygon2D() { occ_polygon = VS::get_singleton()->canvas_occluder_polygon_create(); closed = true; cull = CULL_DISABLED; + rect_cache_dirty = true; } OccluderPolygon2D::~OccluderPolygon2D() { @@ -164,6 +215,16 @@ void LightOccluder2D::_notification(int p_what) { } } +Rect2 LightOccluder2D::_edit_get_rect() const { + + return occluder_polygon.is_valid() ? occluder_polygon->_edit_get_rect() : Rect2(); +} + +bool LightOccluder2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return occluder_polygon.is_valid() ? occluder_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false; +} + void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon) { #ifdef DEBUG_ENABLED diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h index 498d65d764..7931cd0b30 100644 --- a/scene/2d/light_occluder_2d.h +++ b/scene/2d/light_occluder_2d.h @@ -50,10 +50,16 @@ private: bool closed; CullMode cull; + mutable Rect2 item_rect; + mutable bool rect_cache_dirty; + protected: static void _bind_methods(); public: + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_polygon(const PoolVector<Vector2> &p_polygon); PoolVector<Vector2> get_polygon() const; @@ -85,6 +91,9 @@ protected: static void _bind_methods(); public: + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon); Ref<OccluderPolygon2D> get_occluder_polygon() const; diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 105eb82afb..73692e0535 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -112,6 +112,14 @@ int Line2D::get_point_count() const { return _points.size(); } +void Line2D::clear_points() { + int count = _points.size(); + if (count > 0) { + _points.resize(0); + update(); + } +} + void Line2D::add_point(Vector2 pos) { _points.append(pos); update(); @@ -313,6 +321,8 @@ void Line2D::_bind_methods() { ClassDB::bind_method(D_METHOD("add_point", "position"), &Line2D::add_point); ClassDB::bind_method(D_METHOD("remove_point", "i"), &Line2D::remove_point); + ClassDB::bind_method(D_METHOD("clear_points"), &Line2D::clear_points); + ClassDB::bind_method(D_METHOD("set_width", "width"), &Line2D::set_width); ClassDB::bind_method(D_METHOD("get_width"), &Line2D::get_width); diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h index 5bbd38e460..32befab2d3 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -70,6 +70,8 @@ public: int get_point_count() const; + void clear_points(); + void add_point(Vector2 pos); void remove_point(int i); diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp index 96e13396c5..4ead1bbd1e 100644 --- a/scene/2d/parallax_background.cpp +++ b/scene/2d/parallax_background.cpp @@ -207,7 +207,7 @@ void ParallaxBackground::_bind_methods() { ParallaxBackground::ParallaxBackground() { scale = 1.0; - set_layer(-1); //behind all by default + set_layer(-100); //behind all by default base_scale = Vector2(1, 1); ignore_camera_zoom = false; diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index fec861ad2f..4097006b33 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -95,7 +95,7 @@ void Path2D::_notification(int p_what) { return; } -#if TOOLS_ENABLED +#ifdef TOOLS_ENABLED const float line_width = 2 * EDSCALE; #else const float line_width = 2; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 46b4f0a5d2..885c9ea8bc 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1392,18 +1392,21 @@ Vector2 TileMap::_map_to_world(int p_x, int p_y, bool p_ignore_ofs) const { if (!p_ignore_ofs) { switch (half_offset) { - case HALF_OFFSET_X: { + case HALF_OFFSET_X: + case HALF_OFFSET_NEGATIVE_X: { if (ABS(p_y) & 1) { - ret += get_cell_transform()[0] * 0.5; + ret += get_cell_transform()[0] * (half_offset == HALF_OFFSET_X ? 0.5 : -0.5); } } break; - case HALF_OFFSET_Y: { + case HALF_OFFSET_Y: + case HALF_OFFSET_NEGATIVE_Y: { if (ABS(p_x) & 1) { - ret += get_cell_transform()[1] * 0.5; + ret += get_cell_transform()[1] * (half_offset == HALF_OFFSET_Y ? 0.5 : -0.5); } } break; - default: {} + default: { + } } } return ret; @@ -1463,12 +1466,23 @@ Vector2 TileMap::world_to_map(const Vector2 &p_pos) const { ret.x -= 0.5; } } break; + case HALF_OFFSET_NEGATIVE_X: { + if (ret.y > 0 ? int(ret.y) & 1 : (int(ret.y) - 1) & 1) { + ret.x += 0.5; + } + } break; case HALF_OFFSET_Y: { if (ret.x > 0 ? int(ret.x) & 1 : (int(ret.x) - 1) & 1) { ret.y -= 0.5; } } break; - default: {} + case HALF_OFFSET_NEGATIVE_Y: { + if (ret.x > 0 ? int(ret.x) & 1 : (int(ret.x) - 1) & 1) { + ret.y += 0.5; + } + } break; + default: { + } } // Account for precision errors on the border (GH-23250). @@ -1678,7 +1692,7 @@ void TileMap::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cell_size", PROPERTY_HINT_RANGE, "1,8192,1"), "set_cell_size", "get_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "cell_custom_transform"), "set_custom_transform", "get_custom_transform"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_half_offset", PROPERTY_HINT_ENUM, "Offset X,Offset Y,Disabled"), "set_half_offset", "get_half_offset"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_half_offset", PROPERTY_HINT_ENUM, "Offset X,Offset Y,Disabled,Offset Negative X,Offset Negative Y"), "set_half_offset", "get_half_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_tile_origin", PROPERTY_HINT_ENUM, "Top Left,Center,Bottom Left"), "set_tile_origin", "get_tile_origin"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_y_sort"), "set_y_sort_mode", "is_y_sort_mode_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_clip_uv"), "set_clip_uv", "get_clip_uv"); @@ -1704,6 +1718,8 @@ void TileMap::_bind_methods() { BIND_ENUM_CONSTANT(HALF_OFFSET_X); BIND_ENUM_CONSTANT(HALF_OFFSET_Y); BIND_ENUM_CONSTANT(HALF_OFFSET_DISABLED); + BIND_ENUM_CONSTANT(HALF_OFFSET_NEGATIVE_X); + BIND_ENUM_CONSTANT(HALF_OFFSET_NEGATIVE_Y); BIND_ENUM_CONSTANT(TILE_ORIGIN_TOP_LEFT); BIND_ENUM_CONSTANT(TILE_ORIGIN_CENTER); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index e450e1e256..6a1467aa48 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -52,6 +52,8 @@ public: HALF_OFFSET_X, HALF_OFFSET_Y, HALF_OFFSET_DISABLED, + HALF_OFFSET_NEGATIVE_X, + HALF_OFFSET_NEGATIVE_Y, }; enum TileOrigin { diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp index 13d9181082..3557f0425c 100644 --- a/scene/3d/area.cpp +++ b/scene/3d/area.cpp @@ -356,7 +356,9 @@ void Area::_area_inout(int p_status, const RID &p_area, int p_instance, int p_ar Map<ObjectID, AreaState>::Element *E = area_map.find(objid); - ERR_FAIL_COND(!area_in && !E); + if (!area_in && !E) { + return; //likely removed from the tree + } locked = true; diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp index 17b698c1b8..e5346c4c53 100644 --- a/scene/3d/arvr_nodes.cpp +++ b/scene/3d/arvr_nodes.cpp @@ -233,6 +233,13 @@ void ARVRController::_notification(int p_what) { } else { button_states = 0; }; + + // check for an updated mesh + Ref<Mesh> trackerMesh = tracker->get_mesh(); + if (mesh != trackerMesh) { + mesh = trackerMesh; + emit_signal("mesh_updated", mesh); + } }; }; break; default: @@ -258,8 +265,11 @@ void ARVRController::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &ARVRController::set_rumble); ADD_PROPERTY(PropertyInfo(Variant::REAL, "rumble", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_rumble", "get_rumble"); + ClassDB::bind_method(D_METHOD("get_mesh"), &ARVRController::get_mesh); + ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::INT, "button"))); ADD_SIGNAL(MethodInfo("button_release", PropertyInfo(Variant::INT, "button"))); + ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"))); }; void ARVRController::set_controller_id(int p_controller_id) { @@ -341,6 +351,10 @@ void ARVRController::set_rumble(real_t p_rumble) { }; }; +Ref<Mesh> ARVRController::get_mesh() const { + return mesh; +} + bool ARVRController::get_is_active() const { return is_active; }; @@ -423,6 +437,13 @@ void ARVRAnchor::_notification(int p_what) { // apply our reference frame and set our transform set_transform(arvr_server->get_reference_frame() * transform); + + // check for an updated mesh + Ref<Mesh> trackerMesh = tracker->get_mesh(); + if (mesh != trackerMesh) { + mesh = trackerMesh; + emit_signal("mesh_updated", mesh); + } }; }; break; default: @@ -441,6 +462,9 @@ void ARVRAnchor::_bind_methods() { ClassDB::bind_method(D_METHOD("get_size"), &ARVRAnchor::get_size); ClassDB::bind_method(D_METHOD("get_plane"), &ARVRAnchor::get_plane); + + ClassDB::bind_method(D_METHOD("get_mesh"), &ARVRAnchor::get_mesh); + ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"))); }; void ARVRAnchor::set_anchor_id(int p_anchor_id) { @@ -501,6 +525,10 @@ Plane ARVRAnchor::get_plane() const { return plane; }; +Ref<Mesh> ARVRAnchor::get_mesh() const { + return mesh; +} + ARVRAnchor::ARVRAnchor() { anchor_id = 0; is_active = true; diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h index 523bc112c1..0833e18d48 100644 --- a/scene/3d/arvr_nodes.h +++ b/scene/3d/arvr_nodes.h @@ -33,6 +33,7 @@ #include "scene/3d/camera.h" #include "scene/3d/spatial.h" +#include "scene/resources/mesh.h" #include "servers/arvr/arvr_positional_tracker.h" /** @@ -75,6 +76,7 @@ private: int controller_id; bool is_active; int button_states; + Ref<Mesh> mesh; protected: void _notification(int p_what); @@ -95,6 +97,8 @@ public: bool get_is_active() const; ARVRPositionalTracker::TrackerHand get_hand() const; + Ref<Mesh> get_mesh(void) const; + String get_configuration_warning() const; ARVRController(); @@ -113,6 +117,7 @@ private: int anchor_id; bool is_active; Vector3 size; + Ref<Mesh> mesh; protected: void _notification(int p_what); @@ -128,6 +133,8 @@ public: Plane get_plane() const; + Ref<Mesh> get_mesh(void) const; + String get_configuration_warning() const; ARVRAnchor(); diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 4b3934c4ea..4d5ffc31b3 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -887,6 +887,10 @@ bool AudioStreamPlayer3D::get_stream_paused() const { return stream_paused; } +Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() { + return stream_playback; +} + void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream); @@ -953,6 +957,8 @@ void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer3D::set_stream_paused); ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer3D::get_stream_paused); + ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback); + ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer3D::_bus_layout_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index e467c170fb..1175547028 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -206,6 +206,8 @@ public: void set_stream_paused(bool p_pause); bool get_stream_paused() const; + Ref<AudioStreamPlayback> get_stream_playback(); + AudioStreamPlayer3D(); ~AudioStreamPlayer3D(); }; diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp index d8c2042c88..f542b021be 100644 --- a/scene/3d/collision_object.cpp +++ b/scene/3d/collision_object.cpp @@ -52,7 +52,7 @@ void CollisionObject::_notification(int p_what) { _update_pickable(); //get space - }; + } break; case NOTIFICATION_TRANSFORM_CHANGED: { diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp index cf1af918f7..ed533ee7a4 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -293,7 +293,8 @@ Light::Light(VisualServer::LightType p_type) { case VS::LIGHT_DIRECTIONAL: light = VisualServer::get_singleton()->directional_light_create(); break; case VS::LIGHT_OMNI: light = VisualServer::get_singleton()->omni_light_create(); break; case VS::LIGHT_SPOT: light = VisualServer::get_singleton()->spot_light_create(); break; - default: {}; + default: { + }; } VS::get_singleton()->instance_set_base(get_instance(), light); diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index 848889155b..89072519d5 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -96,7 +96,7 @@ void MeshInstance::_get_property_list(List<PropertyInfo> *p_list) const { ls.sort(); for (List<String>::Element *E = ls.front(); E; E = E->next()) { - p_list->push_back(PropertyInfo(Variant::REAL, E->get(), PROPERTY_HINT_RANGE, "0,1,0.01")); + p_list->push_back(PropertyInfo(Variant::REAL, E->get(), PROPERTY_HINT_RANGE, "0,1,0.00001")); } if (mesh.is_valid()) { diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index b7279e4d4f..dafcf95b1f 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -540,10 +540,11 @@ void Skeleton::clear_bones() { void Skeleton::set_bone_pose(int p_bone, const Transform &p_pose) { ERR_FAIL_INDEX(p_bone, bones.size()); - ERR_FAIL_COND(!is_inside_tree()); bones.write[p_bone].pose = p_pose; - _make_dirty(); + if (is_inside_tree()) { + _make_dirty(); + } } Transform Skeleton::get_bone_pose(int p_bone) const { diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp index ac20609c21..d6a0595519 100644 --- a/scene/3d/soft_body.cpp +++ b/scene/3d/soft_body.cpp @@ -104,6 +104,14 @@ SoftBody::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) { offset = obj_tocopy.offset; } +SoftBody::PinnedPoint SoftBody::PinnedPoint::operator=(const PinnedPoint &obj) { + point_index = obj.point_index; + spatial_attachment_path = obj.spatial_attachment_path; + spatial_attachment = obj.spatial_attachment; + offset = obj.offset; + return *this; +} + void SoftBody::_update_pickable() { if (!is_inside_tree()) return; diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h index 2516d39552..ee455f8dab 100644 --- a/scene/3d/soft_body.h +++ b/scene/3d/soft_body.h @@ -75,6 +75,7 @@ public: PinnedPoint(); PinnedPoint(const PinnedPoint &obj_tocopy); + PinnedPoint operator=(const PinnedPoint &obj); }; private: diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index 83f99a2e3c..395f7b9b35 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -224,7 +224,8 @@ void Spatial::_notification(int p_what) { #endif } break; - default: {} + default: { + } } } diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 0daa574f72..e9b38ae990 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -363,6 +363,7 @@ AnimationNodeOneShot::AnimationNodeOneShot() { fade_out = 0.1; autorestart = false; autorestart_delay = 1; + autorestart_random_delay = 0; mix = MIX_MODE_BLEND; sync = false; @@ -857,6 +858,7 @@ AnimationNodeTransition::AnimationNodeTransition() { time = "time"; current = "current"; prev_current = "prev_current"; + xfade = 0.0; enabled_inputs = 0; for (int i = 0; i < MAX_INPUTS; i++) { diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp index feefe3ddd4..84b3f103c5 100644 --- a/scene/animation/animation_cache.cpp +++ b/scene/animation/animation_cache.cpp @@ -309,7 +309,8 @@ void AnimationCache::set_all(float p_time, float p_delta) { } } break; - default: {} + default: { + } } } } diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 68ad71d01c..1e3470cd90 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -960,7 +960,7 @@ void AnimationNodeStateMachine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_end_node", "name"), &AnimationNodeStateMachine::set_end_node); ClassDB::bind_method(D_METHOD("get_end_node"), &AnimationNodeStateMachine::get_end_node); - ClassDB::bind_method(D_METHOD("set_graph_offset", "name"), &AnimationNodeStateMachine::set_graph_offset); + ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeStateMachine::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset); ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeStateMachine::_tree_changed); diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index fe1b8247ff..13b8ebe12d 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -1276,7 +1276,8 @@ void AnimationTree::_process_graph(float p_delta) { t->object->set_indexed(t->subpath, t->value); } break; - default: {} //the rest don't matter + default: { + } //the rest don't matter } } } @@ -1301,9 +1302,17 @@ void AnimationTree::_notification(int p_what) { _clear_caches(); if (last_animation_player) { - Object *old_player = ObjectDB::get_instance(last_animation_player); - if (old_player) { - old_player->disconnect("caches_cleared", this, "_clear_caches"); + Object *player = ObjectDB::get_instance(last_animation_player); + if (player) { + player->disconnect("caches_cleared", this, "_clear_caches"); + } + } + } else if (p_what == NOTIFICATION_ENTER_TREE) { + if (last_animation_player) { + + Object *player = ObjectDB::get_instance(last_animation_player); + if (player) { + player->connect("caches_cleared", this, "_clear_caches"); } } } diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 3420b48ae3..3cc90c2ad6 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -193,7 +193,8 @@ bool AnimationTreePlayer::_set(const StringName &p_name, const Variant &p_value) } } break; - default: {}; + default: { + }; } } @@ -352,7 +353,8 @@ bool AnimationTreePlayer::_get(const StringName &p_name, Variant &r_ret) const { node["transitions"] = transitions; } break; - default: {}; + default: { + }; } nodes.push_back(node); @@ -772,7 +774,8 @@ float AnimationTreePlayer::_process_node(const StringName &p_node, AnimationNode } } break; - default: {} + default: { + } } return 0; @@ -882,7 +885,8 @@ void AnimationTreePlayer::_process_animation(float p_delta) { tr.track->object->call(method, args[0], args[1], args[2], args[3], args[4]); } } break; - default: {} + default: { + } } } } @@ -969,7 +973,8 @@ void AnimationTreePlayer::add_node(NodeType p_type, const StringName &p_node) { n = memnew(TransitionNode); } break; - default: {} + default: { + } } //n->name+=" "+itos(p_node); diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp index 88fb2d5bfc..4da3e6ee28 100644 --- a/scene/animation/skeleton_ik.cpp +++ b/scene/animation/skeleton_ik.cpp @@ -335,7 +335,7 @@ void SkeletonIK::_validate_property(PropertyInfo &property) const { if (skeleton) { - String names; + String names("--,"); for (int i = 0; i < skeleton->get_bone_count(); i++) { if (i > 0) names += ","; diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 6a5d7839f4..23998183b8 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -223,6 +223,7 @@ void Tween::_bind_methods() { ADD_SIGNAL(MethodInfo("tween_started", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"))); ADD_SIGNAL(MethodInfo("tween_step", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"), PropertyInfo(Variant::REAL, "elapsed"), PropertyInfo(Variant::OBJECT, "value"))); ADD_SIGNAL(MethodInfo("tween_completed", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"))); + ADD_SIGNAL(MethodInfo("tween_all_completed")); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "repeat"), "set_repeat", "is_repeat"); ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_tween_process_mode", "get_tween_process_mode"); @@ -628,8 +629,10 @@ void Tween::_tween_process(float p_delta) { } pending_update--; - if (all_finished) + if (all_finished) { set_active(false); + emit_signal("tween_all_completed"); + } } void Tween::set_tween_process_mode(TweenProcessMode p_mode) { diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index 2688041d18..47278b8fc1 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -331,6 +331,10 @@ void AudioStreamPlayer::_bus_layout_changed() { _change_notify(); } +Ref<AudioStreamPlayback> AudioStreamPlayer::get_stream_playback() { + return stream_playback; +} + void AudioStreamPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer::set_stream); @@ -366,6 +370,8 @@ void AudioStreamPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer::set_stream_paused); ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer::get_stream_paused); + ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer::get_stream_playback); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h index fba8ce7dd3..590bef95b0 100644 --- a/scene/audio/audio_stream_player.h +++ b/scene/audio/audio_stream_player.h @@ -106,6 +106,9 @@ public: void set_stream_paused(bool p_pause); bool get_stream_paused() const; + Ref<AudioStreamPlayback> get_stream_playback(); + + AudioStreamPlayer(); ~AudioStreamPlayer(); }; diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index c734136895..65e9cccd05 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "button.h" + #include "core/translation.h" #include "servers/visual_server.h" @@ -102,6 +103,7 @@ void Button::_notification(int p_what) { break; } + FALLTHROUGH; } case DRAW_PRESSED: { diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 059e59ea21..5671b41de8 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -86,7 +86,9 @@ void FileDialog::_unhandled_input(const Ref<InputEvent> &p_event) { _dir_entered(".."); } break; - default: { handled = false; } + default: { + handled = false; + } } if (handled) diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 874246586d..0c5dc39273 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "line_edit.h" + #include "core/message_queue.h" #include "core/os/keyboard.h" #include "core/os/os.h" @@ -320,7 +321,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { handled = false; break; } - // numlock disabled. fallthrough to key_left + FALLTHROUGH; } case KEY_LEFT: { @@ -367,7 +368,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { handled = false; break; } - // numlock disabled. fallthrough to key_right + FALLTHROUGH; } case KEY_RIGHT: { @@ -474,7 +475,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { handled = false; break; } - // numlock disabled. fallthrough to key_home + FALLTHROUGH; } case KEY_HOME: { @@ -487,7 +488,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { handled = false; break; } - // numlock disabled. fallthrough to key_end + FALLTHROUGH; } case KEY_END: { diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 28b124d143..94c73b2e42 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -594,7 +594,7 @@ void PopupMenu::add_item(const String &p_label, int p_ID, uint32_t p_accel) { item.text = p_label; item.xl_text = tr(p_label); item.accel = p_accel; - item.ID = p_ID; + item.ID = p_ID == -1 ? items.size() : p_ID; items.push_back(item); update(); minimum_size_changed(); @@ -632,7 +632,7 @@ void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel item.text = p_label; item.xl_text = tr(p_label); item.accel = p_accel; - item.ID = p_ID; + item.ID = p_ID == -1 ? items.size() : p_ID; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); update(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 00d6ac3b94..3fbf809012 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -711,7 +711,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & } break; - default: {} + default: { + } } Item *itp = it; diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index eb04b85931..028ca41cbf 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -34,8 +34,15 @@ Size2 Slider::get_minimum_size() const { Ref<StyleBox> style = get_stylebox("slider"); - Size2i ms = style->get_minimum_size() + style->get_center_size(); - return ms; + Size2i ss = style->get_minimum_size() + style->get_center_size(); + + Ref<Texture> grabber = get_icon("grabber"); + Size2i rs = grabber->get_size(); + + if (orientation == HORIZONTAL) + return Size2i(ss.width, MAX(ss.height, rs.height)); + else + return Size2i(MAX(ss.width, rs.width), ss.height); } void Slider::_gui_input(Ref<InputEvent> p_event) { @@ -134,7 +141,11 @@ void Slider::_gui_input(Ref<InputEvent> p_event) { void Slider::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + minimum_size_changed(); + update(); + } break; case NOTIFICATION_MOUSE_ENTER: { mouse_inside = true; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 212efa4976..ad41cc4167 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -127,6 +127,9 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { // Activate the clicked tab. pos.x -= tabs_ofs_cache; for (int i = first_tab_cache; i <= last_tab_cache; i++) { + if (get_tab_hidden(i)) { + continue; + } int tab_width = _get_tab_width(i); if (pos.x < tab_width) { if (!get_tab_disabled(i)) { @@ -216,6 +219,9 @@ void TabContainer::_notification(int p_what) { // Check if all tabs would fit into the header area. int all_tabs_width = 0; for (int i = 0; i < tabs.size(); i++) { + if (get_tab_hidden(i)) { + continue; + } int tab_width = _get_tab_width(i); all_tabs_width += tab_width; @@ -241,6 +247,9 @@ void TabContainer::_notification(int p_what) { all_tabs_width = 0; Vector<int> tab_widths; for (int i = first_tab_cache; i < tabs.size(); i++) { + if (get_tab_hidden(i)) { + continue; + } int tab_width = _get_tab_width(i); if (all_tabs_width + tab_width > header_width && tab_widths.size() > 0) break; @@ -267,6 +276,9 @@ void TabContainer::_notification(int p_what) { // Draw all visible tabs. int x = 0; for (int i = 0; i < tab_widths.size(); i++) { + if (get_tab_hidden(i)) { + continue; + } Ref<StyleBox> tab_style; Color font_color; if (get_tab_disabled(i + first_tab_cache)) { @@ -354,7 +366,7 @@ int TabContainer::_get_tab_width(int p_index) const { ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0); Control *control = Object::cast_to<Control>(_get_tabs()[p_index]); - if (!control || control->is_set_as_toplevel()) + if (!control || control->is_set_as_toplevel() || get_tab_hidden(p_index)) return 0; // Get the width of the text displayed on the tab. @@ -765,6 +777,36 @@ bool TabContainer::get_tab_disabled(int p_tab) const { return false; } +void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) { + + Control *child = _get_tab(p_tab); + ERR_FAIL_COND(!child); + child->set_meta("_tab_hidden", p_hidden); + update(); + for (int i = 0; i < get_tab_count(); i++) { + int try_tab = (p_tab + 1 + i) % get_tab_count(); + if (get_tab_disabled(try_tab) || get_tab_hidden(try_tab)) { + continue; + } + + set_current_tab(try_tab); + return; + } + + //assumed no other tab can be switched to, just hide + child->hide(); +} + +bool TabContainer::get_tab_hidden(int p_tab) const { + + Control *child = _get_tab(p_tab); + ERR_FAIL_COND_V(!child, false); + if (child->has_meta("_tab_hidden")) + return child->get_meta("_tab_hidden"); + else + return false; +} + void TabContainer::get_translatable_strings(List<String> *p_strings) const { Vector<Control *> tabs = _get_tabs(); diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index c110f041d0..f7a9fb64fd 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -96,6 +96,9 @@ public: void set_tab_disabled(int p_tab, bool p_disabled); bool get_tab_disabled(int p_tab) const; + void set_tab_hidden(int p_tab, bool p_hidden); + bool get_tab_hidden(int p_tab) const; + int get_tab_count() const; void set_current_tab(int p_current); int get_current_tab() const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 2bf2364873..39a0b0aaf2 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -590,6 +590,12 @@ void TextEdit::_notification(int p_what) { } } break; case NOTIFICATION_DRAW: { + + if (first_draw) { + //size may not be the final one, so attempts to ensure cursor was visible may have failed + adjust_viewport_to_cursor(); + first_draw = false; + } Size2 size = get_size(); if ((!has_focus() && !menu->has_focus()) || !window_has_focus) { draw_caret = false; @@ -2517,7 +2523,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - // numlock disabled. fallthrough to key_left + FALLTHROUGH; } case KEY_LEFT: { @@ -2580,7 +2586,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - // numlock disabled. fallthrough to key_right + FALLTHROUGH; } case KEY_RIGHT: { @@ -2641,7 +2647,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - // numlock disabled. fallthrough to key_up + FALLTHROUGH; } case KEY_UP: { @@ -2694,7 +2700,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - // numlock disabled. fallthrough to key_down + FALLTHROUGH; } case KEY_DOWN: { @@ -2817,11 +2823,10 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - // numlock disabled. fallthrough to key_home + FALLTHROUGH; } -#ifdef APPLE_STYLE_KEYS case KEY_HOME: { - +#ifdef APPLE_STYLE_KEYS if (k->get_shift()) _pre_shift_selection(); @@ -2831,11 +2836,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { _post_shift_selection(); else if (k->get_command() || k->get_control()) deselect(); - - } break; #else - case KEY_HOME: { - if (k->get_shift()) _pre_shift_selection(); @@ -2876,19 +2877,17 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { deselect(); _cancel_completion(); completion_hint = ""; - - } break; #endif + } break; case KEY_KP_1: { if (k->get_unicode() != 0) { scancode_handled = false; break; } - // numlock disabled. fallthrough to key_end + FALLTHROUGH; } -#ifdef APPLE_STYLE_KEYS case KEY_END: { - +#ifdef APPLE_STYLE_KEYS if (k->get_shift()) _pre_shift_selection(); @@ -2898,11 +2897,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { _post_shift_selection(); else if (k->get_command() || k->get_control()) deselect(); - - } break; #else - case KEY_END: { - if (k->get_shift()) _pre_shift_selection(); @@ -2929,15 +2924,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { _cancel_completion(); completion_hint = ""; - - } break; #endif + } break; case KEY_KP_9: { if (k->get_unicode() != 0) { scancode_handled = false; break; } - // numlock disabled. fallthrough to key_pageup + FALLTHROUGH; } case KEY_PAGEUP: { @@ -2960,7 +2954,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - // numlock disabled. fallthrough to key_pagedown + FALLTHROUGH; } case KEY_PAGEDOWN: { @@ -3139,21 +3133,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (scancode_handled) accept_event(); - /* - if (!scancode_handled && !k->get_command() && !k->get_alt()) { - - if (k->get_unicode()>=32) { - if (readonly) - break; - - accept_event(); - } else { - - break; - } - } -*/ if (k->get_scancode() == KEY_INSERT) { set_insert_mode(!insert_mode); accept_event(); @@ -3196,7 +3176,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { end_complex_operation(); } accept_event(); - } else { } } @@ -6383,6 +6362,7 @@ TextEdit::TextEdit() { menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z); menu->connect("id_pressed", this, "menu_option"); + first_draw = true; } TextEdit::~TextEdit() { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 33f0a3f45d..96b409c6d4 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -275,6 +275,7 @@ private: int wrap_at; int wrap_right_offset; + bool first_draw; bool setting_row; bool draw_tabs; bool override_selected_font_color; diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp index caae48336b..2195de9694 100644 --- a/scene/gui/texture_rect.cpp +++ b/scene/gui/texture_rect.cpp @@ -38,30 +38,32 @@ void TextureRect::_notification(int p_what) { if (texture.is_null()) return; + Size2 size; + Point2 offset; + Rect2 region; + bool tile = false; + switch (stretch_mode) { case STRETCH_SCALE_ON_EXPAND: { - Size2 s = expand ? get_size() : texture->get_size(); - draw_texture_rect(texture, Rect2(Point2(), s), false); + size = expand ? get_size() : texture->get_size(); } break; case STRETCH_SCALE: { - draw_texture_rect(texture, Rect2(Point2(), get_size()), false); + size = get_size(); } break; case STRETCH_TILE: { - draw_texture_rect(texture, Rect2(Point2(), get_size()), true); + size = get_size(); + tile = true; } break; case STRETCH_KEEP: { - draw_texture_rect(texture, Rect2(Point2(), texture->get_size()), false); - + size = texture->get_size(); } break; case STRETCH_KEEP_CENTERED: { - - Vector2 ofs = (get_size() - texture->get_size()) / 2; - draw_texture_rect(texture, Rect2(ofs, texture->get_size()), false); + offset = (get_size() - texture->get_size()) / 2; + size = texture->get_size(); } break; case STRETCH_KEEP_ASPECT_CENTERED: case STRETCH_KEEP_ASPECT: { - - Size2 size = get_size(); + size = get_size(); int tex_width = texture->get_width() * size.height / texture->get_height(); int tex_height = size.height; @@ -70,26 +72,35 @@ void TextureRect::_notification(int p_what) { tex_height = texture->get_height() * tex_width / texture->get_width(); } - int ofs_x = 0; - int ofs_y = 0; - if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) { - ofs_x += (size.width - tex_width) / 2; - ofs_y += (size.height - tex_height) / 2; + offset.x += (size.width - tex_width) / 2; + offset.y += (size.height - tex_height) / 2; } - draw_texture_rect(texture, Rect2(ofs_x, ofs_y, tex_width, tex_height)); + size.width = tex_width; + size.height = tex_height; } break; case STRETCH_KEEP_ASPECT_COVERED: { - Size2 size = get_size(); + size = get_size(); + Size2 tex_size = texture->get_size(); Size2 scaleSize(size.width / tex_size.width, size.height / tex_size.height); float scale = scaleSize.width > scaleSize.height ? scaleSize.width : scaleSize.height; Size2 scaledTexSize = tex_size * scale; - Point2 ofs = ((scaledTexSize - size) / scale).abs() / 2.0f; - draw_texture_rect_region(texture, Rect2(Point2(), size), Rect2(ofs, size / scale)); + + region.position = ((scaledTexSize - size) / scale).abs() / 2.0f; + region.size = size / scale; } break; } + + size.width *= hflip ? -1.0f : 1.0f; + size.height *= vflip ? -1.0f : 1.0f; + + if (region.no_area()) { + draw_texture_rect(texture, Rect2(offset, size), tile); + } else { + draw_texture_rect_region(texture, Rect2(offset, size), region); + } } } @@ -106,12 +117,18 @@ void TextureRect::_bind_methods() { ClassDB::bind_method(D_METHOD("get_texture"), &TextureRect::get_texture); ClassDB::bind_method(D_METHOD("set_expand", "enable"), &TextureRect::set_expand); ClassDB::bind_method(D_METHOD("has_expand"), &TextureRect::has_expand); + ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &TextureRect::set_flip_h); + ClassDB::bind_method(D_METHOD("is_flipped_h"), &TextureRect::is_flipped_h); + ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &TextureRect::set_flip_v); + ClassDB::bind_method(D_METHOD("is_flipped_v"), &TextureRect::is_flipped_v); ClassDB::bind_method(D_METHOD("set_stretch_mode", "stretch_mode"), &TextureRect::set_stretch_mode); ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureRect::get_stretch_mode); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand"), "set_expand", "has_expand"); ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale On Expand (Compat),Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v"); BIND_ENUM_CONSTANT(STRETCH_SCALE_ON_EXPAND); BIND_ENUM_CONSTANT(STRETCH_SCALE); @@ -161,9 +178,31 @@ TextureRect::StretchMode TextureRect::get_stretch_mode() const { return stretch_mode; } +void TextureRect::set_flip_h(bool p_flip) { + + hflip = p_flip; + update(); +} +bool TextureRect::is_flipped_h() const { + + return hflip; +} + +void TextureRect::set_flip_v(bool p_flip) { + + vflip = p_flip; + update(); +} +bool TextureRect::is_flipped_v() const { + + return vflip; +} + TextureRect::TextureRect() { expand = false; + hflip = false; + vflip = false; set_mouse_filter(MOUSE_FILTER_PASS); stretch_mode = STRETCH_SCALE_ON_EXPAND; } diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h index ddd101573b..3ab35324e5 100644 --- a/scene/gui/texture_rect.h +++ b/scene/gui/texture_rect.h @@ -53,6 +53,8 @@ public: private: bool expand; + bool hflip; + bool vflip; Ref<Texture> texture; StretchMode stretch_mode; @@ -71,6 +73,12 @@ public: void set_stretch_mode(StretchMode p_mode); StretchMode get_stretch_mode() const; + void set_flip_h(bool p_flip); + bool is_flipped_h() const; + + void set_flip_v(bool p_flip); + bool is_flipped_v() const; + TextureRect(); ~TextureRect(); }; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 49cad6fccf..7e59606d2b 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -29,7 +29,6 @@ /*************************************************************************/ #include "tree.h" -#include <limits.h> #include "core/math/math_funcs.h" #include "core/os/input.h" @@ -43,6 +42,8 @@ #include "editor/editor_node.h" #endif +#include <limits.h> + void TreeItem::move_to_top() { if (!parent || parent->children == this) @@ -940,6 +941,7 @@ int Tree::compute_item_height(TreeItem *p_item) const { int check_icon_h = cache.checked->get_height(); if (height < check_icon_h) height = check_icon_h; + FALLTHROUGH; } case TreeItem::CELL_MODE_STRING: case TreeItem::CELL_MODE_CUSTOM: @@ -960,7 +962,8 @@ int Tree::compute_item_height(TreeItem *p_item) const { } } break; - default: {} + default: { + } } } int item_min_height = p_item->get_custom_minimum_height(); @@ -2018,7 +2021,9 @@ void Tree::text_editor_enter(String p_text) { //popup_edited_item->edited_signal.call( popup_edited_item_col ); } break; - default: { ERR_FAIL(); } + default: { + ERR_FAIL(); + } } item_edited(popup_edited_item_col, popup_edited_item); @@ -2845,7 +2850,7 @@ void Tree::_notification(int p_what) { if (p_what == NOTIFICATION_DRAG_BEGIN) { single_select_defer = NULL; - if (cache.scroll_speed > 0 && get_rect().has_point(get_viewport()->get_mouse_position() - get_global_position())) { + if (cache.scroll_speed > 0) { scrolling = true; set_physics_process_internal(true); } @@ -2892,22 +2897,22 @@ void Tree::_notification(int p_what) { } } - if (scrolling) { - Point2 point = get_viewport()->get_mouse_position() - get_global_position(); - if (point.x < cache.scroll_border) { - point.x -= cache.scroll_border; - } else if (point.x > get_size().width - cache.scroll_border) { - point.x -= get_size().width - cache.scroll_border; - } else { - point.x = 0; + Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position(); + if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) { + Point2 point; + + if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < cache.scroll_border)) { + point.x = mouse_position.x - cache.scroll_border; + } else if (ABS(mouse_position.x - get_size().width) < cache.scroll_border) { + point.x = mouse_position.x - (get_size().width - cache.scroll_border); } - if (point.y < cache.scroll_border) { - point.y -= cache.scroll_border; - } else if (point.y > get_size().height - cache.scroll_border) { - point.y -= get_size().height - cache.scroll_border; - } else { - point.y = 0; + + if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < cache.scroll_border)) { + point.y = mouse_position.y - cache.scroll_border; + } else if (ABS(mouse_position.y - get_size().height) < cache.scroll_border) { + point.y = mouse_position.y - (get_size().height - cache.scroll_border); } + point *= cache.scroll_speed * get_physics_process_delta_time(); point += get_scroll(); h_scroll->set_value(point.x); @@ -3383,10 +3388,13 @@ void Tree::ensure_cursor_is_visible() { int h = compute_item_height(selected) + cache.vseparation; int screenh = get_size().height - h_scroll->get_combined_minimum_size().height; - if (ofs + h > v_scroll->get_value() + screenh) + if (h > screenh) { //screen size is too small, maybe it was not resized yet. + v_scroll->set_value(ofs); + } else if (ofs + h > v_scroll->get_value() + screenh) { v_scroll->call_deferred("set_value", ofs - screenh + h); - else if (ofs < v_scroll->get_value()) + } else if (ofs < v_scroll->get_value()) { v_scroll->set_value(ofs); + } } int Tree::get_pressed_button() const { diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 2b1991ebb0..8cab1b1280 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -153,6 +153,7 @@ void CanvasLayer::_notification(int p_what) { VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas); VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent()); VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform); + _update_follow_viewport(); } break; case NOTIFICATION_EXIT_TREE: { @@ -160,6 +161,7 @@ void CanvasLayer::_notification(int p_what) { vp->_canvas_layer_remove(this); VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas); viewport = RID(); + _update_follow_viewport(false); } break; case NOTIFICATION_MOVED_IN_PARENT: { @@ -235,6 +237,41 @@ RID CanvasLayer::get_canvas() const { return canvas; } + +void CanvasLayer::set_follow_viewport(bool p_enable) { + if (follow_viewport == p_enable) { + return; + } + + follow_viewport = p_enable; + _update_follow_viewport(); +} + +bool CanvasLayer::is_following_viewport() const { + return follow_viewport; +} + +void CanvasLayer::set_follow_viewport_scale(float p_ratio) { + follow_viewport_scale = p_ratio; + _update_follow_viewport(); +} + +float CanvasLayer::get_follow_viewport_scale() const { + return follow_viewport_scale; +} + +void CanvasLayer::_update_follow_viewport(bool p_force_exit) { + + if (!is_inside_tree()) { + return; + } + if (p_force_exit || !follow_viewport) { + VS::get_singleton()->canvas_set_parent(canvas, RID(), 1.0); + } else { + VS::get_singleton()->canvas_set_parent(canvas, vp->get_world_2d()->get_canvas(), follow_viewport_scale); + } +} + void CanvasLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer); @@ -255,18 +292,30 @@ void CanvasLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_scale", "scale"), &CanvasLayer::set_scale); ClassDB::bind_method(D_METHOD("get_scale"), &CanvasLayer::get_scale); + ClassDB::bind_method(D_METHOD("set_follow_viewport", "enable"), &CanvasLayer::set_follow_viewport); + ClassDB::bind_method(D_METHOD("is_following_viewport"), &CanvasLayer::is_following_viewport); + + ClassDB::bind_method(D_METHOD("set_follow_viewport_scale", "scale"), &CanvasLayer::set_follow_viewport_scale); + ClassDB::bind_method(D_METHOD("get_follow_viewport_scale"), &CanvasLayer::get_follow_viewport_scale); + ClassDB::bind_method(D_METHOD("set_custom_viewport", "viewport"), &CanvasLayer::set_custom_viewport); ClassDB::bind_method(D_METHOD("get_custom_viewport"), &CanvasLayer::get_custom_viewport); ClassDB::bind_method(D_METHOD("get_canvas"), &CanvasLayer::get_canvas); + ADD_GROUP("Layer", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "layer", PROPERTY_HINT_RANGE, "-128,128,1"), "set_layer", "get_layer"); + ADD_GROUP("Transform", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation_degrees", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform"), "set_transform", "get_transform"); + ADD_GROUP("", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", 0), "set_custom_viewport", "get_custom_viewport"); + ADD_GROUP("Follow Viewport", "follow_viewport"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport_enable"), "set_follow_viewport", "is_following_viewport"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_lesser"), "set_follow_viewport_scale", "get_follow_viewport_scale"); } CanvasLayer::CanvasLayer() { @@ -280,6 +329,8 @@ CanvasLayer::CanvasLayer() { custom_viewport = NULL; custom_viewport_id = 0; sort_index = 0; + follow_viewport = false; + follow_viewport_scale = 1.0; } CanvasLayer::~CanvasLayer() { diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h index 5d67245102..fa2558556c 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -55,8 +55,12 @@ class CanvasLayer : public Node { int sort_index; + bool follow_viewport; + float follow_viewport_scale; + void _update_xform(); void _update_locrotscale(); + void _update_follow_viewport(bool p_force_exit = false); protected: void _notification(int p_what); @@ -91,6 +95,12 @@ public: void reset_sort_index(); int get_sort_index(); + void set_follow_viewport(bool p_enable); + bool is_following_viewport() const; + + void set_follow_viewport_scale(float p_ratio); + float get_follow_viewport_scale() const; + RID get_canvas() const; CanvasLayer(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 96e286602a..5f9c187e0b 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1313,6 +1313,10 @@ Node *Node::_get_child_by_name(const StringName &p_name) const { Node *Node::get_node_or_null(const NodePath &p_path) const { + if (p_path.is_empty()) { + return NULL; + } + if (!data.inside_tree && p_path.is_absolute()) { ERR_EXPLAIN("Can't use get_node() with absolute paths from outside the active scene tree."); ERR_FAIL_V(NULL); @@ -2755,6 +2759,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("can_process"), &Node::can_process); ClassDB::bind_method(D_METHOD("print_stray_nodes"), &Node::_print_stray_nodes); ClassDB::bind_method(D_METHOD("get_position_in_parent"), &Node::get_position_in_parent); + ClassDB::bind_method(D_METHOD("set_display_folded", "fold"), &Node::set_display_folded); ClassDB::bind_method(D_METHOD("is_displayed_folded"), &Node::is_displayed_folded); @@ -2867,7 +2872,12 @@ void Node::_bind_methods() { //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), "set_process_unhandled_input","is_processing_unhandled_input" ) ; ADD_GROUP("Pause", "pause_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_display_folded", "is_displayed_folded"); + +#ifdef ENABLE_DEPRECATED + //no longer exists, but remains for compatibility (keep previous scenes folded + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", 0), "set_display_folded", "is_displayed_folded"); +#endif + ADD_PROPERTY(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner"); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 2524af9cfb..05c183275a 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3222,7 +3222,7 @@ Viewport::Viewport() { gui.tooltip_timer = -1; //gui.tooltip_timer->force_parent_owned(); - gui.tooltip_delay = GLOBAL_DEF("gui/timers/tooltip_delay_sec", 0.7); + gui.tooltip_delay = GLOBAL_DEF("gui/timers/tooltip_delay_sec", 0.5); ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::REAL, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers gui.tooltip = NULL; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 49c9c4c23c..9face3e476 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -139,6 +139,7 @@ #include "scene/resources/dynamic_font.h" #include "scene/resources/dynamic_font_stb.h" #include "scene/resources/gradient.h" +#include "scene/resources/height_map_shape.h" #include "scene/resources/line_shape_2d.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" @@ -476,6 +477,7 @@ void register_scene_types() { ClassDB::register_class<VisualShaderNodeInput>(); ClassDB::register_virtual_class<VisualShaderNodeOutput>(); ClassDB::register_class<VisualShaderNodeScalarConstant>(); + ClassDB::register_class<VisualShaderNodeBooleanConstant>(); ClassDB::register_class<VisualShaderNodeColorConstant>(); ClassDB::register_class<VisualShaderNodeVec3Constant>(); ClassDB::register_class<VisualShaderNodeTransformConstant>(); @@ -486,8 +488,23 @@ void register_scene_types() { ClassDB::register_class<VisualShaderNodeTransformVecMult>(); ClassDB::register_class<VisualShaderNodeScalarFunc>(); ClassDB::register_class<VisualShaderNodeVectorFunc>(); + ClassDB::register_class<VisualShaderNodeColorFunc>(); + ClassDB::register_class<VisualShaderNodeTransformFunc>(); ClassDB::register_class<VisualShaderNodeDotProduct>(); ClassDB::register_class<VisualShaderNodeVectorLen>(); + ClassDB::register_class<VisualShaderNodeDeterminant>(); + ClassDB::register_class<VisualShaderNodeScalarDerivativeFunc>(); + ClassDB::register_class<VisualShaderNodeVectorDerivativeFunc>(); + ClassDB::register_class<VisualShaderNodeScalarClamp>(); + ClassDB::register_class<VisualShaderNodeVectorClamp>(); + ClassDB::register_class<VisualShaderNodeFaceForward>(); + ClassDB::register_class<VisualShaderNodeOuterProduct>(); + ClassDB::register_class<VisualShaderNodeVectorScalarStep>(); + ClassDB::register_class<VisualShaderNodeScalarSmoothStep>(); + ClassDB::register_class<VisualShaderNodeVectorSmoothStep>(); + ClassDB::register_class<VisualShaderNodeVectorScalarSmoothStep>(); + ClassDB::register_class<VisualShaderNodeVectorDistance>(); + ClassDB::register_class<VisualShaderNodeVectorRefract>(); ClassDB::register_class<VisualShaderNodeScalarInterp>(); ClassDB::register_class<VisualShaderNodeVectorInterp>(); ClassDB::register_class<VisualShaderNodeVectorCompose>(); @@ -498,11 +515,14 @@ void register_scene_types() { ClassDB::register_class<VisualShaderNodeCubeMap>(); ClassDB::register_virtual_class<VisualShaderNodeUniform>(); ClassDB::register_class<VisualShaderNodeScalarUniform>(); + ClassDB::register_class<VisualShaderNodeBooleanUniform>(); ClassDB::register_class<VisualShaderNodeColorUniform>(); ClassDB::register_class<VisualShaderNodeVec3Uniform>(); ClassDB::register_class<VisualShaderNodeTransformUniform>(); ClassDB::register_class<VisualShaderNodeTextureUniform>(); ClassDB::register_class<VisualShaderNodeCubeMapUniform>(); + ClassDB::register_class<VisualShaderNodeIf>(); + ClassDB::register_class<VisualShaderNodeSwitch>(); ClassDB::register_class<ShaderMaterial>(); ClassDB::register_virtual_class<CanvasItem>(); @@ -591,6 +611,7 @@ void register_scene_types() { ClassDB::register_class<BoxShape>(); ClassDB::register_class<CapsuleShape>(); ClassDB::register_class<CylinderShape>(); + ClassDB::register_class<HeightMapShape>(); ClassDB::register_class<PlaneShape>(); ClassDB::register_class<ConvexPolygonShape>(); ClassDB::register_class<ConcavePolygonShape>(); @@ -614,6 +635,7 @@ void register_scene_types() { ClassDB::register_class<StreamTexture>(); ClassDB::register_class<ImageTexture>(); ClassDB::register_class<AtlasTexture>(); + ClassDB::register_class<MeshTexture>(); ClassDB::register_class<LargeTexture>(); ClassDB::register_class<CurveTexture>(); ClassDB::register_class<GradientTexture>(); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index f73914b186..e58ec9d71e 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -1466,7 +1466,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const { int high = len - 1; int middle = 0; -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (low > high) ERR_PRINT("low > high, this may be a bug"); #endif diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index d2e4d28b44..a9d85be0dc 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -96,7 +96,7 @@ int BitMap::get_true_bit_count() const { const uint8_t *d = bitmask.ptr(); int c = 0; - //fast, almot branchless version + //fast, almost branchless version for (int i = 0; i < ds; i++) { @@ -106,6 +106,7 @@ int BitMap::get_true_bit_count() const { c += (d[i] & (1 << 4)) >> 4; c += (d[i] & (1 << 3)) >> 3; c += (d[i] & (1 << 2)) >> 2; + c += (d[i] & (1 << 1)) >> 1; c += d[i] & 1; } @@ -594,6 +595,64 @@ Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) con return result_array; } +void BitMap::resize(const Size2& p_new_size) { + + Ref<BitMap> new_bitmap; + new_bitmap.instance(); + new_bitmap->create(p_new_size); + int lw = MIN(width,p_new_size.width); + int lh = MIN(height,p_new_size.height); + for(int x=0;x<lw;x++) { + for(int y=0;y<lh;y++) { + new_bitmap->set_bit(Vector2(x,y),get_bit(Vector2(x,y))); + } + } + + width = new_bitmap->width; + height = new_bitmap->height; + bitmask = new_bitmap->bitmask; +} + +Ref<Image> BitMap::convert_to_image() const { + + Ref<Image> image; + image.instance(); + image->create(width,height,false,Image::FORMAT_L8); + image->lock(); + for(int i=0;i<width;i++) { + for(int j=0;j<height;j++) { + image->set_pixel( i,j,get_bit(Point2(i,j)) ? Color(1,1,1) : Color(0,0,0)); + } + } + + image->unlock(); + + return image; +} +void BitMap::blit(const Vector2& p_pos,const Ref<BitMap>& p_bitmap) { + + int x = p_pos.x; + int y = p_pos.y; + int w = p_bitmap->get_size().width; + int h = p_bitmap->get_size().height; + + for(int i=0;i<w;i++) { + for (int j=0;j<h;j++) { + int px = x+i; + int py = y+j; + if (px<0 || px>=width) + continue; + if (py<0 || py>=height) + continue; + if (p_bitmap->get_bit(Vector2(i,j))) { + set_bit(Vector2(x,y),true); + } + } + } + +} + + void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create); diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index b3c86afd38..6e1171b8a9 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -64,9 +64,13 @@ public: int get_true_bit_count() const; Size2 get_size() const; + void resize(const Size2& p_new_size); void grow_mask(int p_pixels, const Rect2 &p_rect); + void blit(const Vector2& p_pos,const Ref<BitMap>& p_bitmap); + Ref<Image> convert_to_image() const; + Vector<Vector<Vector2> > clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon = 2.0) const; BitMap(); diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp index 98d3460ed2..5845e4be50 100644 --- a/scene/resources/convex_polygon_shape.cpp +++ b/scene/resources/convex_polygon_shape.cpp @@ -83,6 +83,4 @@ void ConvexPolygonShape::_bind_methods() { ConvexPolygonShape::ConvexPolygonShape() : Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CONVEX_POLYGON)) { - - //set_points(Vector3(1,1,1)); } diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index f275405de2..d424fb2814 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -99,10 +99,4 @@ Rect2 ConvexPolygonShape2D::get_rect() const { ConvexPolygonShape2D::ConvexPolygonShape2D() : Shape2D(Physics2DServer::get_singleton()->convex_polygon_shape_create()) { - - int pcount = 3; - for (int i = 0; i < pcount; i++) - points.push_back(Vector2(Math::sin(i * Math_PI * 2 / pcount), -Math::cos(i * Math_PI * 2 / pcount)) * 10); - - _update_shape(); } diff --git a/scene/resources/default_theme/make_header.py b/scene/resources/default_theme/make_header.py index 73b1ae0b0b..bd5a723b23 100755 --- a/scene/resources/default_theme/make_header.py +++ b/scene/resources/default_theme/make_header.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -import os import glob -import string enc = "utf-8" diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h index a51a0ca0d0..7e7fc99a5d 100644 --- a/scene/resources/gradient.h +++ b/scene/resources/gradient.h @@ -91,7 +91,7 @@ public: int high = points.size() - 1; int middle = 0; -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (low > high) ERR_PRINT("low > high, this may be a bug"); #endif diff --git a/scene/resources/height_map_shape.cpp b/scene/resources/height_map_shape.cpp new file mode 100644 index 0000000000..32e9c527ef --- /dev/null +++ b/scene/resources/height_map_shape.cpp @@ -0,0 +1,196 @@ +/*************************************************************************/ +/* height_map_shape.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "height_map_shape.h" +#include "servers/physics_server.h" + +Vector<Vector3> HeightMapShape::_gen_debug_mesh_lines() { + Vector<Vector3> points; + + // This will be slow for large maps... + // also we'll have to figure out how well bullet centers this shape... + + Vector2 size(map_width - 1, map_depth - 1); + Vector2 start = size * -0.5; + int offset = 0; + + PoolRealArray::Read r = map_data.read(); + + for (int d = 0; d < map_depth; d++) { + Vector3 height(start.x, 0.0, start.y); + + for (int w = 0; w < map_width; w++) { + height.y = r[offset++]; + + if (w != map_width - 1) { + points.push_back(height); + points.push_back(Vector3(height.x + 1.0, r[offset], height.z)); + } + + if (d != map_depth - 1) { + points.push_back(height); + points.push_back(Vector3(height.x, r[offset + map_width - 1], height.z + 1.0)); + } + + height.x += 1.0; + } + + start.y += 1.0; + } + + return points; +} + +void HeightMapShape::_update_shape() { + + Dictionary d; + d["width"] = map_width; + d["depth"] = map_depth; + d["heights"] = map_data; + d["min_height"] = min_height; + d["max_height"] = max_height; + PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); +} + +void HeightMapShape::set_map_width(int p_new) { + if (p_new < 1) { + // ignore + } else if (map_width != p_new) { + int was_size = map_width * map_depth; + map_width = p_new; + + int new_size = map_width * map_depth; + map_data.resize(map_width * map_depth); + + PoolRealArray::Write w = map_data.write(); + while (was_size < new_size) { + w[was_size++] = 0.0; + } + + _update_shape(); + notify_change_to_owners(); + _change_notify("map_width"); + _change_notify("map_data"); + } +} + +int HeightMapShape::get_map_width() const { + return map_width; +} + +void HeightMapShape::set_map_depth(int p_new) { + if (p_new < 1) { + // ignore + } else if (map_depth != p_new) { + int was_size = map_width * map_depth; + map_depth = p_new; + + int new_size = map_width * map_depth; + map_data.resize(new_size); + + PoolRealArray::Write w = map_data.write(); + while (was_size < new_size) { + w[was_size++] = 0.0; + } + + _update_shape(); + notify_change_to_owners(); + _change_notify("map_depth"); + _change_notify("map_data"); + } +} + +int HeightMapShape::get_map_depth() const { + return map_depth; +} + +void HeightMapShape::set_map_data(PoolRealArray p_new) { + int size = (map_width * map_depth); + if (p_new.size() != size) { + // fail + return; + } + + // copy + PoolRealArray::Write w = map_data.write(); + PoolRealArray::Read r = p_new.read(); + for (int i = 0; i < size; i++) { + float val = r[i]; + w[i] = val; + if (i == 0) { + min_height = val; + max_height = val; + } else { + if (min_height > val) + min_height = val; + + if (max_height < val) + max_height = val; + } + } + + _update_shape(); + notify_change_to_owners(); + _change_notify("map_data"); +} + +PoolRealArray HeightMapShape::get_map_data() const { + return map_data; +} + +void HeightMapShape::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_map_width", "width"), &HeightMapShape::set_map_width); + ClassDB::bind_method(D_METHOD("get_map_width"), &HeightMapShape::get_map_width); + ClassDB::bind_method(D_METHOD("set_map_depth", "height"), &HeightMapShape::set_map_depth); + ClassDB::bind_method(D_METHOD("get_map_depth"), &HeightMapShape::get_map_depth); + ClassDB::bind_method(D_METHOD("set_map_data", "data"), &HeightMapShape::set_map_data); + ClassDB::bind_method(D_METHOD("get_map_data"), &HeightMapShape::get_map_data); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_width", "get_map_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_depth", "get_map_depth"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_REAL_ARRAY, "map_data"), "set_map_data", "get_map_data"); +} + +HeightMapShape::HeightMapShape() : + Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_HEIGHTMAP)) { + + map_width = 2; + map_depth = 2; + map_data.resize(map_width * map_depth); + PoolRealArray::Write w = map_data.write(); + w[0] = 0.0; + w[1] = 0.0; + w[2] = 0.0; + w[3] = 0.0; + min_height = 0.0; + max_height = 0.0; + + _update_shape(); +} diff --git a/scene/resources/height_map_shape.h b/scene/resources/height_map_shape.h new file mode 100644 index 0000000000..b062f4e893 --- /dev/null +++ b/scene/resources/height_map_shape.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* height_map_shape.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 HEIGHT_MAP_SHAPE_H +#define HEIGHT_MAP_SHAPE_H + +#include "scene/resources/shape.h" + +class HeightMapShape : public Shape { + GDCLASS(HeightMapShape, Shape); + + int map_width; + int map_depth; + PoolRealArray map_data; + float min_height; + float max_height; + +protected: + static void _bind_methods(); + virtual void _update_shape(); + + virtual Vector<Vector3> _gen_debug_mesh_lines(); + +public: + void set_map_width(int p_new); + int get_map_width() const; + void set_map_depth(int p_new); + int get_map_depth() const; + void set_map_data(PoolRealArray p_new); + PoolRealArray get_map_data() const; + + HeightMapShape(); +}; + +#endif /* !HEIGHT_MAP_SHAPE_H */ diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 85018f38d7..67639858ce 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -37,6 +37,8 @@ #include <stdlib.h> +Mesh::ConvexDecompositionFunc Mesh::convex_composition_function = NULL; + Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { if (triangle_mesh.is_valid()) @@ -543,6 +545,49 @@ void Mesh::clear_cache() const { debug_lines.clear(); } +Vector<Ref<Shape> > Mesh::convex_decompose() const { + + ERR_FAIL_COND_V(!convex_composition_function, Vector<Ref<Shape> >()); + + PoolVector<Face3> faces = get_faces(); + Vector<Face3> f3; + f3.resize(faces.size()); + PoolVector<Face3>::Read f = faces.read(); + for (int i = 0; i < f3.size(); i++) { + f3.write[i] = f[i]; + } + + Vector<Vector<Face3> > decomposed = convex_composition_function(f3); + + Vector<Ref<Shape> > ret; + + for (int i = 0; i < decomposed.size(); i++) { + Set<Vector3> points; + for (int j = 0; j < decomposed[i].size(); j++) { + points.insert(decomposed[i][j].vertex[0]); + points.insert(decomposed[i][j].vertex[1]); + points.insert(decomposed[i][j].vertex[2]); + } + + PoolVector<Vector3> convex_points; + convex_points.resize(points.size()); + { + PoolVector<Vector3>::Write w = convex_points.write(); + int idx = 0; + for (Set<Vector3>::Element *E = points.front(); E; E = E->next()) { + w[idx++] = E->get(); + } + } + + Ref<ConvexPolygonShape> shape; + shape.instance(); + shape->set_points(convex_points); + ret.push_back(shape); + } + + return ret; +} + Mesh::Mesh() { } diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index dabfc6ea60..1457d283bd 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -31,6 +31,7 @@ #ifndef MESH_H #define MESH_H +#include "core/math/face3.h" #include "core/math/triangle_mesh.h" #include "core/resource.h" #include "scene/resources/material.h" @@ -147,6 +148,12 @@ public: Size2 get_lightmap_size_hint() const; void clear_cache() const; + typedef Vector<Vector<Face3> > (*ConvexDecompositionFunc)(const Vector<Face3> &); + + static ConvexDecompositionFunc convex_composition_function; + + Vector<Ref<Shape> > convex_decompose() const; + Mesh(); }; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index b6efca9acc..e9f90fc85f 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -445,6 +445,11 @@ Error ResourceInteractiveLoaderText::poll() { } else { resource_cache.push_back(res); +#ifdef TOOLS_ENABLED + //remember ID for saving + res->set_id_for_path(local_path,index); +#endif + } ExtResource er; @@ -1355,7 +1360,7 @@ String ResourceFormatSaverTextInstance::_write_resource(const RES &res) { if (external_resources.has(res)) { - return "ExtResource( " + itos(external_resources[res] + 1) + " )"; + return "ExtResource( " + itos(external_resources[res]) + " )"; } else { if (internal_resources.has(res)) { @@ -1459,7 +1464,8 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, _find_resources(v); } } break; - default: {} + default: { + } } } @@ -1538,18 +1544,65 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_line("]\n"); //one empty line } - Vector<RES> sorted_er; - sorted_er.resize(external_resources.size()); + { + + + } + +#ifdef TOOLS_ENABLED + //keep order from cached ids + Set<int> cached_ids_found; + for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { + int cached_id = E->key()->get_id_for_path(local_path); + if (cached_id < 0 || cached_ids_found.has(cached_id)) { + E->get() = -1; //reset + } else { + E->get() = cached_id; + cached_ids_found.insert(cached_id); + } + } + //create IDs for non cached resources for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { + if (cached_ids_found.has(E->get())) { //already cached, go on + continue; + } + + int attempt = 1; //start from one, more readable format + while(cached_ids_found.has(attempt)) { + attempt++; + } - sorted_er.write[E->get()] = E->key(); + cached_ids_found.insert(attempt); + E->get() = attempt; + //update also in resource + Ref<Resource> res = E->key(); + res->set_id_for_path(local_path,attempt); } +#else + //make sure to start from one, as it makes format more readable + for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { + E->get() = E->get() + 1; + } +#endif + + Vector<ResourceSort> sorted_er; + + for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { + + ResourceSort rs; + rs.resource = E->key(); + rs.index = E->get(); + sorted_er.push_back(rs); + } + + sorted_er.sort(); + for (int i = 0; i < sorted_er.size(); i++) { - String p = sorted_er[i]->get_path(); + String p = sorted_er[i].resource->get_path(); - f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i]->get_save_class() + "\" id=" + itos(i + 1) + "]\n"); //bundled + f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i].resource->get_save_class() + "\" id=" + itos(sorted_er[i].index) + "]\n"); //bundled } if (external_resources.size()) diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index 8d78ab33b0..ab6f94986c 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -169,6 +169,15 @@ class ResourceFormatSaverTextInstance { Map<RES, int> external_resources; Map<RES, int> internal_resources; + struct ResourceSort { + RES resource; + int index; + bool operator<(const ResourceSort& p_right) const { + return index < p_right.index; + } + + }; + void _find_resources(const Variant &p_variant, bool p_main = false); static String _write_resources(void *ud, const RES &p_resource); diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp index f9f8ff19e4..48945d4e63 100644 --- a/scene/resources/sky.cpp +++ b/scene/resources/sky.cpp @@ -553,10 +553,10 @@ ProceduralSky::ProceduralSky(bool p_desaturate) { ground_energy = 1; if (p_desaturate) { - sky_top_color.set_hsv(sky_top_color.get_h(),0,sky_top_color.get_v()); - sky_horizon_color.set_hsv(sky_horizon_color.get_h(),0,sky_horizon_color.get_v()); - ground_bottom_color.set_hsv(ground_bottom_color.get_h(),0,ground_bottom_color.get_v()); - ground_horizon_color.set_hsv(ground_horizon_color.get_h(),0,ground_horizon_color.get_v()); + sky_top_color.set_hsv(sky_top_color.get_h(), 0, sky_top_color.get_v()); + sky_horizon_color.set_hsv(sky_horizon_color.get_h(), 0, sky_horizon_color.get_v()); + ground_bottom_color.set_hsv(ground_bottom_color.get_h(), 0, ground_bottom_color.get_v()); + ground_horizon_color.set_hsv(ground_horizon_color.get_h(), 0, ground_horizon_color.get_v()); } sun_color = Color(1, 1, 1); sun_latitude = 35; diff --git a/scene/resources/sky.h b/scene/resources/sky.h index 7327b2a627..3a6f44e11b 100644 --- a/scene/resources/sky.h +++ b/scene/resources/sky.h @@ -191,7 +191,7 @@ public: virtual RID get_rid() const; - ProceduralSky(bool p_desaturate=false); + ProceduralSky(bool p_desaturate = false); ~ProceduralSky(); }; diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index cdd65c7642..5dd429fa75 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -368,25 +368,14 @@ Color StyleBoxFlat::get_bg_color() const { return bg_color; } -void StyleBoxFlat::set_border_color_all(const Color &p_color) { - for (int i = 0; i < 4; i++) { +void StyleBoxFlat::set_border_color(const Color &p_color) { - border_color.write()[i] = p_color; - } + border_color = p_color; emit_changed(); } -Color StyleBoxFlat::get_border_color_all() const { +Color StyleBoxFlat::get_border_color() const { - return border_color[MARGIN_TOP]; -} -void StyleBoxFlat::set_border_color(Margin p_border, const Color &p_color) { - - border_color.write()[p_border] = p_color; - emit_changed(); -} -Color StyleBoxFlat::get_border_color(Margin p_border) const { - - return border_color[p_border]; + return border_color; } void StyleBoxFlat::set_border_width_all(int p_size) { @@ -511,6 +500,16 @@ int StyleBoxFlat::get_shadow_size() const { return shadow_size; } +void StyleBoxFlat::set_shadow_offset(const Point2 &p_offset) { + + shadow_offset = p_offset; + emit_changed(); +} +Point2 StyleBoxFlat::get_shadow_offset() const { + + return shadow_offset; +} + void StyleBoxFlat::set_anti_aliased(const bool &p_anti_aliased) { anti_aliased = p_anti_aliased; emit_changed(); @@ -552,7 +551,7 @@ inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_re inner_corner_radius[0] = MAX(corner_radius[0] - rad, 0); //tr - rad = MIN(border_top, border_bottom); + rad = MIN(border_top, border_right); inner_corner_radius[1] = MAX(corner_radius[1] - rad, 0); //br @@ -565,12 +564,13 @@ inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_re } inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 style_rect, const int corner_radius[4], - const Rect2 ring_rect, const int border_width[4], const Color inner_color[4], const Color outer_color[4], const int corner_detail) { + const Rect2 ring_rect, const int border_width[4], const Color &inner_color, const Color &outer_color, const int corner_detail, const bool fill_center = false) { int vert_offset = verts.size(); if (!vert_offset) { vert_offset = 0; } + int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail; int ring_corner_radius[4]; @@ -585,10 +585,11 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color Rect2 inner_rect; inner_rect = ring_rect.grow_individual(-border_width[MARGIN_LEFT], -border_width[MARGIN_TOP], -border_width[MARGIN_RIGHT], -border_width[MARGIN_BOTTOM]); + int inner_corner_radius[4]; + set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius); Vector<Point2> inner_points; - set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius); inner_points.push_back(inner_rect.position + Vector2(inner_corner_radius[0], inner_corner_radius[0])); //tl inner_points.push_back(Point2(inner_rect.position.x + inner_rect.size.x - inner_corner_radius[1], inner_rect.position.y + inner_corner_radius[1])); //tr inner_points.push_back(inner_rect.position + inner_rect.size - Vector2(inner_corner_radius[2], inner_corner_radius[2])); //br @@ -603,11 +604,11 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color Point2 corner_point; if (inner_outer == 0) { radius = inner_corner_radius[corner_index]; - color = *inner_color; + color = inner_color; corner_point = inner_points[corner_index]; } else { radius = ring_corner_radius[corner_index]; - color = *outer_color; + color = outer_color; corner_point = outer_points[corner_index]; } float x = radius * (float)cos((double)corner_index * Math_PI / 2.0 + (double)detail / (double)adapted_corner_detail * Math_PI / 2.0 + Math_PI) + corner_point.x; @@ -618,17 +619,28 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color } } - int vert_count = (adapted_corner_detail + 1) * 4 * 2; + int ring_vert_count = verts.size() - vert_offset; + //fill the indices and the colors for the border - for (int i = 0; i < vert_count; i++) { - //poly 1 - indices.push_back(vert_offset + ((i + 0) % vert_count)); - indices.push_back(vert_offset + ((i + 2) % vert_count)); - indices.push_back(vert_offset + ((i + 1) % vert_count)); - //poly 2 - indices.push_back(vert_offset + ((i + 1) % vert_count)); - indices.push_back(vert_offset + ((i + 2) % vert_count)); - indices.push_back(vert_offset + ((i + 3) % vert_count)); + for (int i = 0; i < ring_vert_count; i++) { + indices.push_back(vert_offset + ((i + 0) % ring_vert_count)); + indices.push_back(vert_offset + ((i + 2) % ring_vert_count)); + indices.push_back(vert_offset + ((i + 1) % ring_vert_count)); + } + + if (fill_center) { + //fill the indices and the colors for the center + for (int index = 0; index < ring_vert_count / 2; index += 2) { + int i = index; + //poly 1 + indices.push_back(vert_offset + i); + indices.push_back(vert_offset + ring_vert_count - 4 - i); + indices.push_back(vert_offset + i + 2); + //poly 2 + indices.push_back(vert_offset + i); + indices.push_back(vert_offset + ring_vert_count - 2 - i); + indices.push_back(vert_offset + ring_vert_count - 4 - i); + } } } @@ -657,13 +669,29 @@ inline void adapt_values(int p_index_a, int p_index_b, int *adapted_values, cons void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { //PREPARATIONS + bool draw_border = (border_width[0] > 0) || (border_width[1] > 0) || (border_width[2] > 0) || (border_width[3] > 0); + bool draw_shadow = (shadow_size > 0); + if (!draw_border && !draw_center && !draw_shadow) { + return; + } bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); bool aa_on = rounded_corners && anti_aliased; + Color border_color_alpha = Color(border_color.r, border_color.g, border_color.b, 0); + + bool blend_on = blend_border && draw_border; + Rect2 style_rect = p_rect.grow_individual(expand_margin[MARGIN_LEFT], expand_margin[MARGIN_TOP], expand_margin[MARGIN_RIGHT], expand_margin[MARGIN_BOTTOM]); - if (aa_on) { - style_rect = style_rect.grow(-((aa_size + 1) / 2)); + Rect2 border_style_rect = style_rect; + if (aa_on && !blend_on) { + float aa_size_grow = 0.5 * ((aa_size + 1) / 2); + style_rect = style_rect.grow(-aa_size_grow); + for (int i = 0; i < 4; i++) { + if (border_width[i] > 0) { + border_style_rect = border_style_rect.grow_margin((Margin)i, -aa_size_grow); + } + } } //adapt borders (prevent weird overlapping/glitchy drawings) @@ -686,79 +714,93 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { Vector<int> indices; Vector<Color> colors; - //DRAWING - VisualServer *vs = VisualServer::get_singleton(); - //DRAW SHADOW - if (shadow_size > 0) { + if (draw_shadow) { int shadow_width[4] = { shadow_size, shadow_size, shadow_size, shadow_size }; - Color shadow_colors[4] = { shadow_color, shadow_color, shadow_color, shadow_color }; - Color shadow_colors_transparent[4]; - for (int i = 0; i < 4; i++) { - shadow_colors_transparent[i] = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0); + + Rect2 shadow_inner_rect = style_rect; + shadow_inner_rect.position += shadow_offset; + + Rect2 shadow_rect = style_rect.grow(shadow_size); + shadow_rect.position += shadow_offset; + + Color shadow_color_transparent = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0); + + draw_ring(verts, indices, colors, shadow_inner_rect, adapted_corner, + shadow_rect, shadow_width, shadow_color, shadow_color_transparent, corner_detail); + + if (draw_center) { + int no_border[4] = { 0, 0, 0, 0 }; + draw_ring(verts, indices, colors, shadow_inner_rect, adapted_corner, + shadow_inner_rect, no_border, shadow_color, shadow_color, corner_detail, true); } - draw_ring(verts, indices, colors, style_rect, adapted_corner, - style_rect.grow(shadow_size), shadow_width, shadow_colors, shadow_colors_transparent, corner_detail); } //DRAW border - Color bg_color_array[4] = { bg_color, bg_color, bg_color, bg_color }; - const Color *inner_color = ((blend_border) ? bg_color_array : border_color.read().ptr()); - draw_ring(verts, indices, colors, style_rect, adapted_corner, - style_rect, adapted_border, inner_color, border_color.read().ptr(), corner_detail); + if (draw_border) { + draw_ring(verts, indices, colors, border_style_rect, adapted_corner, + border_style_rect, adapted_border, blend_on ? (draw_center ? bg_color : border_color_alpha) : border_color, border_color, corner_detail); + } //DRAW INFILL if (draw_center) { - int temp_vert_offset = verts.size(); int no_border[4] = { 0, 0, 0, 0 }; draw_ring(verts, indices, colors, style_rect, adapted_corner, - infill_rect, no_border, &bg_color, &bg_color, corner_detail); - int added_vert_count = verts.size() - temp_vert_offset; - //fill the indices and the colors for the center - for (int index = 0; index <= added_vert_count / 2; index += 2) { - int i = index; - //poly 1 - indices.push_back(temp_vert_offset + i); - indices.push_back(temp_vert_offset + added_vert_count - 4 - i); - indices.push_back(temp_vert_offset + i + 2); - //poly 1 - indices.push_back(temp_vert_offset + i); - indices.push_back(temp_vert_offset + added_vert_count - 2 - i); - indices.push_back(temp_vert_offset + added_vert_count - 4 - i); - } + infill_rect, no_border, bg_color, bg_color, corner_detail, true); } if (aa_on) { - - //HELPER ARRAYS - Color border_color_alpha[4]; - for (int i = 0; i < 4; i++) { - Color c = border_color.read().ptr()[i]; - border_color_alpha[i] = Color(c.r, c.g, c.b, 0); + Rect2 border_inner_rect = infill_rect; + int aa_border_width[4]; + int aa_fill_width[4]; + if (draw_border) { + border_inner_rect = border_style_rect.grow_individual(-adapted_border[MARGIN_LEFT], -adapted_border[MARGIN_TOP], -adapted_border[MARGIN_RIGHT], -adapted_border[MARGIN_BOTTOM]); + for (int i = 0; i < 4; i++) { + if (border_width[i] > 0) { + aa_border_width[i] = aa_size; + aa_fill_width[i] = 0; + } else { + aa_border_width[i] = 0; + aa_fill_width[i] = aa_size; + } + } + } else { + for (int i = 0; i < 4; i++) { + aa_fill_width[i] = aa_size; + } } - Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0); - Color bg_color_array_alpha[4] = { alpha_bg, alpha_bg, alpha_bg, alpha_bg }; - - int aa_border_width[4] = { aa_size, aa_size, aa_size, aa_size }; if (draw_center) { - if (!blend_border) { + if (!draw_border || !blend_on) { + Rect2 aa_rect = infill_rect.grow_individual(aa_fill_width[MARGIN_LEFT], aa_fill_width[MARGIN_TOP], + aa_fill_width[MARGIN_RIGHT], aa_fill_width[MARGIN_BOTTOM]); + + Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0); + //INFILL AA draw_ring(verts, indices, colors, style_rect, adapted_corner, - infill_rect.grow(aa_size), aa_border_width, bg_color_array, bg_color_array_alpha, corner_detail); + aa_rect, aa_fill_width, bg_color, alpha_bg, corner_detail); } - } else if (!(border_width[0] == 0 && border_width[1] == 0 && border_width[2] == 0 && border_width[3] == 0)) { - //DRAW INNER BORDER AA - draw_ring(verts, indices, colors, style_rect, adapted_corner, - infill_rect, aa_border_width, border_color_alpha, border_color.read().ptr(), corner_detail); } - //DRAW OUTER BORDER AA - if (!(border_width[0] == 0 && border_width[1] == 0 && border_width[2] == 0 && border_width[3] == 0)) { - draw_ring(verts, indices, colors, style_rect, adapted_corner, - style_rect.grow(aa_size), aa_border_width, border_color.read().ptr(), border_color_alpha, corner_detail); + + if (draw_border) { + if (!blend_on) { + //DRAW INNER BORDER AA + draw_ring(verts, indices, colors, border_style_rect, adapted_corner, + border_inner_rect, aa_border_width, border_color_alpha, border_color, corner_detail); + } + + Rect2 aa_rect = border_style_rect.grow_individual(aa_border_width[MARGIN_LEFT], aa_border_width[MARGIN_TOP], + aa_border_width[MARGIN_RIGHT], aa_border_width[MARGIN_BOTTOM]); + + //DRAW OUTER BORDER AA + draw_ring(verts, indices, colors, border_style_rect, adapted_corner, + aa_rect, aa_border_width, border_color, border_color_alpha, corner_detail); } } + //DRAWING + VisualServer *vs = VisualServer::get_singleton(); vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors); } @@ -770,8 +812,8 @@ void StyleBoxFlat::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bg_color", "color"), &StyleBoxFlat::set_bg_color); ClassDB::bind_method(D_METHOD("get_bg_color"), &StyleBoxFlat::get_bg_color); - ClassDB::bind_method(D_METHOD("set_border_color", "color"), &StyleBoxFlat::set_border_color_all); - ClassDB::bind_method(D_METHOD("get_border_color"), &StyleBoxFlat::get_border_color_all); + ClassDB::bind_method(D_METHOD("set_border_color", "color"), &StyleBoxFlat::set_border_color); + ClassDB::bind_method(D_METHOD("get_border_color"), &StyleBoxFlat::get_border_color); ClassDB::bind_method(D_METHOD("set_border_width_all", "width"), &StyleBoxFlat::set_border_width_all); ClassDB::bind_method(D_METHOD("get_border_width_min"), &StyleBoxFlat::get_border_width_min); @@ -802,6 +844,9 @@ void StyleBoxFlat::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shadow_size", "size"), &StyleBoxFlat::set_shadow_size); ClassDB::bind_method(D_METHOD("get_shadow_size"), &StyleBoxFlat::get_shadow_size); + ClassDB::bind_method(D_METHOD("set_shadow_offset", "offset"), &StyleBoxFlat::set_shadow_offset); + ClassDB::bind_method(D_METHOD("get_shadow_offset"), &StyleBoxFlat::get_shadow_offset); + ClassDB::bind_method(D_METHOD("set_anti_aliased", "anti_aliased"), &StyleBoxFlat::set_anti_aliased); ClassDB::bind_method(D_METHOD("is_anti_aliased"), &StyleBoxFlat::is_anti_aliased); @@ -843,6 +888,7 @@ void StyleBoxFlat::_bind_methods() { ADD_GROUP("Shadow", "shadow_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color"); ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size"), "set_shadow_size", "get_shadow_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset"), "set_shadow_offset", "get_shadow_offset"); ADD_GROUP("Anti Aliasing", "anti_aliasing_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "anti_aliasing"), "set_anti_aliased", "is_anti_aliased"); @@ -853,17 +899,14 @@ StyleBoxFlat::StyleBoxFlat() { bg_color = Color(0.6, 0.6, 0.6); shadow_color = Color(0, 0, 0, 0.6); - - border_color.append(Color(0.8, 0.8, 0.8)); - border_color.append(Color(0.8, 0.8, 0.8)); - border_color.append(Color(0.8, 0.8, 0.8)); - border_color.append(Color(0.8, 0.8, 0.8)); + border_color = Color(0.8, 0.8, 0.8); blend_border = false; draw_center = true; anti_aliased = true; shadow_size = 0; + shadow_offset = Point2(0, 0); corner_detail = 8; aa_size = 1; diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index 9062270765..c3965fe076 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -149,7 +149,7 @@ class StyleBoxFlat : public StyleBox { Color bg_color; Color shadow_color; - PoolVector<Color> border_color; + Color border_color; int border_width[4]; int expand_margin[4]; @@ -161,6 +161,7 @@ class StyleBoxFlat : public StyleBox { int corner_detail; int shadow_size; + Point2 shadow_offset; int aa_size; protected: @@ -173,10 +174,8 @@ public: Color get_bg_color() const; //Border Color - void set_border_color_all(const Color &p_color); - Color get_border_color_all() const; - void set_border_color(Margin p_border, const Color &p_color); - Color get_border_color(Margin p_border) const; + void set_border_color(const Color &p_color); + Color get_border_color() const; //BORDER //width @@ -218,6 +217,9 @@ public: void set_shadow_size(const int &p_size); int get_shadow_size() const; + void set_shadow_offset(const Point2 &p_offset); + Point2 get_shadow_offset() const; + //ANTI_ALIASING void set_anti_aliased(const bool &p_anti_aliased); bool is_anti_aliased() const; diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 83f29503fa..3ba43006a3 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -108,8 +108,55 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) { vtx.bones = last_bones; vtx.tangent = last_tangent.normal; vtx.binormal = last_normal.cross(last_tangent.normal).normalized() * last_tangent.d; + + const int expected_vertices = 4; + + if ((format & Mesh::ARRAY_FORMAT_WEIGHTS || format & Mesh::ARRAY_FORMAT_BONES) && (vtx.weights.size() != expected_vertices || vtx.bones.size() != expected_vertices)) { + //ensure vertices are the expected amount + ERR_FAIL_COND(vtx.weights.size() != vtx.bones.size()); + if (vtx.weights.size() < expected_vertices) { + //less than requred, fill + for (int i = vtx.weights.size(); i < expected_vertices; i++) { + vtx.weights.push_back(0); + vtx.bones.push_back(0); + } + } else if (vtx.weights.size() > expected_vertices) { + //more than required, sort, cap and normalize. + Vector<WeightSort> weights; + for (int i = 0; i < vtx.weights.size(); i++) { + WeightSort ws; + ws.index = vtx.bones[i]; + ws.weight = vtx.weights[i]; + weights.push_back(ws); + } + + //sort + weights.sort(); + //cap + weights.resize(expected_vertices); + //renormalize + float total = 0; + for (int i = 0; i < expected_vertices; i++) { + total += weights[i].weight; + } + + vtx.weights.resize(expected_vertices); + vtx.bones.resize(expected_vertices); + + for (int i = 0; i < expected_vertices; i++) { + if (total > 0) { + vtx.weights.write[i] = weights[i].weight / total; + } else { + vtx.weights.write[i] = 0; + } + vtx.bones.write[i] = weights[i].index; + } + } + } + vertex_array.push_back(vtx); first = false; + format |= Mesh::ARRAY_FORMAT_VERTEX; } void SurfaceTool::add_color(Color p_color) { @@ -161,7 +208,6 @@ void SurfaceTool::add_uv2(const Vector2 &p_uv2) { void SurfaceTool::add_bones(const Vector<int> &p_bones) { ERR_FAIL_COND(!begun); - ERR_FAIL_COND(p_bones.size() != 4); ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_BONES)); format |= Mesh::ARRAY_FORMAT_BONES; @@ -171,8 +217,6 @@ void SurfaceTool::add_bones(const Vector<int> &p_bones) { void SurfaceTool::add_weights(const Vector<float> &p_weights) { ERR_FAIL_COND(!begun); - - ERR_FAIL_COND(p_weights.size() != 4); ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_WEIGHTS)); format |= Mesh::ARRAY_FORMAT_WEIGHTS; @@ -397,7 +441,8 @@ Array SurfaceTool::commit_to_arrays() { a[i] = array; } break; - default: {} + default: { + } } } diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index a3b110f0d8..c55cade813 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -62,6 +62,14 @@ private: static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx); }; + struct WeightSort { + int index; + float weight; + bool operator<(const WeightSort &p_right) const { + return weight < p_right.weight; + } + }; + bool begun; bool first; Mesh::PrimitiveType primitive; diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 3ca2b56d08..a5eb950c36 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -34,6 +34,7 @@ #include "core/io/image_loader.h" #include "core/method_bind_ext.gen.inc" #include "core/os/os.h" +#include "mesh.h" #include "scene/resources/bit_map.h" Size2 Texture::get_size() const { @@ -1133,6 +1134,138 @@ AtlasTexture::AtlasTexture() { filter_clip = false; } +///////////////////////////////////////// + +int MeshTexture::get_width() const { + return size.width; +} +int MeshTexture::get_height() const { + return size.height; +} +RID MeshTexture::get_rid() const { + return RID(); +} + +bool MeshTexture::has_alpha() const { + return false; +} + +void MeshTexture::set_flags(uint32_t p_flags) { +} + +uint32_t MeshTexture::get_flags() const { + return 0; +} + +void MeshTexture::set_mesh(const Ref<Mesh> &p_mesh) { + mesh = p_mesh; +} +Ref<Mesh> MeshTexture::get_mesh() const { + return mesh; +} + +void MeshTexture::set_image_size(const Size2 &p_size) { + size = p_size; +} + +Size2 MeshTexture::get_image_size() const { + + return size; +} + +void MeshTexture::set_base_texture(const Ref<Texture> &p_texture) { + base_texture = p_texture; +} + +Ref<Texture> MeshTexture::get_base_texture() const { + return base_texture; +} + +void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map) const { + + if (mesh.is_null() || base_texture.is_null()) { + return; + } + Transform2D xform; + xform.set_origin(p_pos); + if (p_transpose) { + SWAP(xform.elements[0][1], xform.elements[1][0]); + SWAP(xform.elements[0][0], xform.elements[1][1]); + } + RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + VisualServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid); +} +void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map) const { + if (mesh.is_null() || base_texture.is_null()) { + return; + } + Transform2D xform; + Vector2 origin = p_rect.position; + if (p_rect.size.x < 0) { + origin.x += size.x; + } + if (p_rect.size.y < 0) { + origin.y += size.y; + } + xform.set_origin(origin); + xform.set_scale(p_rect.size / size); + + if (p_transpose) { + SWAP(xform.elements[0][1], xform.elements[1][0]); + SWAP(xform.elements[0][0], xform.elements[1][1]); + } + RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + VisualServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid); +} +void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map, bool p_clip_uv) const { + + if (mesh.is_null() || base_texture.is_null()) { + return; + } + Transform2D xform; + Vector2 origin = p_rect.position; + if (p_rect.size.x < 0) { + origin.x += size.x; + } + if (p_rect.size.y < 0) { + origin.y += size.y; + } + xform.set_origin(origin); + xform.set_scale(p_rect.size / size); + + if (p_transpose) { + SWAP(xform.elements[0][1], xform.elements[1][0]); + SWAP(xform.elements[0][0], xform.elements[1][1]); + } + RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); + VisualServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid); +} +bool MeshTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const { + r_rect = p_rect; + r_src_rect = p_src_rect; + return true; +} + +bool MeshTexture::is_pixel_opaque(int p_x, int p_y) const { + return true; +} + +void MeshTexture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &MeshTexture::set_mesh); + ClassDB::bind_method(D_METHOD("get_mesh"), &MeshTexture::get_mesh); + ClassDB::bind_method(D_METHOD("set_image_size", "size"), &MeshTexture::set_image_size); + ClassDB::bind_method(D_METHOD("get_image_size"), &MeshTexture::get_image_size); + ClassDB::bind_method(D_METHOD("set_base_texture", "texture"), &MeshTexture::set_base_texture); + ClassDB::bind_method(D_METHOD("get_base_texture"), &MeshTexture::get_base_texture); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_base_texture", "get_base_texture"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "image_size", PROPERTY_HINT_RANGE, "0,16384,1"), "set_image_size", "get_image_size"); +} + +MeshTexture::MeshTexture() { +} + ////////////////////////////////////////// int LargeTexture::get_width() const { diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 281a33929c..58287b7593 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -293,6 +293,49 @@ public: AtlasTexture(); }; +class Mesh; + +class MeshTexture : public Texture { + + GDCLASS(MeshTexture, Texture); + RES_BASE_EXTENSION("meshtex"); + + Ref<Texture> base_texture; + Ref<Mesh> mesh; + Size2i size; + +protected: + static void _bind_methods(); + +public: + virtual int get_width() const; + virtual int get_height() const; + virtual RID get_rid() const; + + virtual bool has_alpha() const; + + virtual void set_flags(uint32_t p_flags); + virtual uint32_t get_flags() const; + + void set_mesh(const Ref<Mesh> &p_mesh); + Ref<Mesh> get_mesh() const; + + void set_image_size(const Size2 &p_size); + Size2 get_image_size() const; + + void set_base_texture(const Ref<Texture> &p_texture); + Ref<Texture> get_base_texture() const; + + virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>()) const; + virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>()) const; + virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_clip_uv = true) const; + virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const; + + bool is_pixel_opaque(int p_x, int p_y) const; + + MeshTexture(); +}; + class LargeTexture : public Texture { GDCLASS(LargeTexture, Texture); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 4ddfc0331b..21b2893502 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -215,7 +215,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { r_ret = autotile_get_spacing(id); else if (what == "bitmask_flags") { Array p; - for (Map<Vector2, uint16_t>::Element *E = tile_map[id].autotile_data.flags.front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = tile_map[id].autotile_data.flags.front(); E; E = E->next()) { p.push_back(E->key()); p.push_back(E->value()); } @@ -544,7 +544,7 @@ const Map<Vector2, int> &TileSet::autotile_get_z_index_map(int p_id) const { return tile_map[p_id].autotile_data.z_index_map; } -void TileSet::autotile_set_bitmask(int p_id, Vector2 p_coord, uint16_t p_flag) { +void TileSet::autotile_set_bitmask(int p_id, Vector2 p_coord, uint32_t p_flag) { ERR_FAIL_COND(!tile_map.has(p_id)); if (p_flag == 0) { @@ -555,7 +555,7 @@ void TileSet::autotile_set_bitmask(int p_id, Vector2 p_coord, uint16_t p_flag) { } } -uint16_t TileSet::autotile_get_bitmask(int p_id, Vector2 p_coord) { +uint32_t TileSet::autotile_get_bitmask(int p_id, Vector2 p_coord) { ERR_FAIL_COND_V(!tile_map.has(p_id), 0); if (!tile_map[p_id].autotile_data.flags.has(p_coord)) { @@ -564,13 +564,13 @@ uint16_t TileSet::autotile_get_bitmask(int p_id, Vector2 p_coord) { return tile_map[p_id].autotile_data.flags[p_coord]; } -const Map<Vector2, uint16_t> &TileSet::autotile_get_bitmask_map(int p_id) { +const Map<Vector2, uint32_t> &TileSet::autotile_get_bitmask_map(int p_id) { - static Map<Vector2, uint16_t> dummy; - static Map<Vector2, uint16_t> dummy_atlas; + static Map<Vector2, uint32_t> dummy; + static Map<Vector2, uint32_t> dummy_atlas; ERR_FAIL_COND_V(!tile_map.has(p_id), dummy); if (tile_get_tile_mode(p_id) == ATLAS_TILE) { - dummy_atlas = Map<Vector2, uint16_t>(); + dummy_atlas = Map<Vector2, uint32_t>(); Rect2 region = tile_get_region(p_id); Size2 size = autotile_get_size(p_id); float spacing = autotile_get_spacing(p_id); @@ -600,18 +600,25 @@ Vector2 TileSet::autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, } List<Vector2> coords; - uint16_t mask; - for (Map<Vector2, uint16_t>::Element *E = tile_map[p_id].autotile_data.flags.front(); E; E = E->next()) { + uint32_t mask; + uint16_t mask_; + uint16_t mask_ignore; + for (Map<Vector2, uint32_t>::Element *E = tile_map[p_id].autotile_data.flags.front(); E; E = E->next()) { mask = E->get(); if (tile_map[p_id].autotile_data.bitmask_mode == BITMASK_2X2) { - mask &= (BIND_BOTTOMLEFT | BIND_BOTTOMRIGHT | BIND_TOPLEFT | BIND_TOPRIGHT); + mask |= (BIND_IGNORE_TOP | BIND_IGNORE_LEFT | BIND_IGNORE_CENTER | BIND_IGNORE_RIGHT | BIND_IGNORE_BOTTOM); } - if (mask == p_bitmask) { + + mask_ = mask & 0xFFFF; + mask_ignore = mask >> 16; + + if (((mask_ & (~mask_ignore)) == (p_bitmask & (~mask_ignore))) && (((~mask_) | mask_ignore) == ((~p_bitmask) | mask_ignore))) { for (int i = 0; i < autotile_get_subtile_priority(p_id, E->key()); i++) { coords.push_back(E->key()); } } } + if (coords.size() == 0) { return autotile_get_icon_coordinate(p_id); } else { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index fac48243d0..fb84cee218 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -72,7 +72,17 @@ public: BIND_RIGHT = 32, BIND_BOTTOMLEFT = 64, BIND_BOTTOM = 128, - BIND_BOTTOMRIGHT = 256 + BIND_BOTTOMRIGHT = 256, + + BIND_IGNORE_TOPLEFT = 1 << 16, + BIND_IGNORE_TOP = 1 << 17, + BIND_IGNORE_TOPRIGHT = 1 << 18, + BIND_IGNORE_LEFT = 1 << 19, + BIND_IGNORE_CENTER = 1 << 20, + BIND_IGNORE_RIGHT = 1 << 21, + BIND_IGNORE_BOTTOMLEFT = 1 << 22, + BIND_IGNORE_BOTTOM = 1 << 23, + BIND_IGNORE_BOTTOMRIGHT = 1 << 24 }; enum TileMode { @@ -86,7 +96,7 @@ public: Size2 size; int spacing; Vector2 icon_coord; - Map<Vector2, uint16_t> flags; + Map<Vector2, uint32_t> flags; Map<Vector2, Ref<OccluderPolygon2D> > occluder_map; Map<Vector2, Ref<NavigationPolygon> > navpoly_map; Map<Vector2, int> priority_map; @@ -181,9 +191,9 @@ public: int autotile_get_z_index(int p_id, const Vector2 &p_coord); const Map<Vector2, int> &autotile_get_z_index_map(int p_id) const; - void autotile_set_bitmask(int p_id, Vector2 p_coord, uint16_t p_flag); - uint16_t autotile_get_bitmask(int p_id, Vector2 p_coord); - const Map<Vector2, uint16_t> &autotile_get_bitmask_map(int p_id); + void autotile_set_bitmask(int p_id, Vector2 p_coord, uint32_t p_flag); + uint32_t autotile_get_bitmask(int p_id, Vector2 p_coord); + const Map<Vector2, uint32_t> &autotile_get_bitmask_map(int p_id); Vector2 autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, const Node *p_tilemap_node = NULL, const Vector2 &p_tile_location = Vector2()); void tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_shape); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 4e5909eb2e..4229147ba2 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -252,7 +252,7 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port); VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port); - if (MAX(0, from_port_type - 1) != (MAX(0, to_port_type - 1))) { + if (MAX(0, from_port_type - 2) != (MAX(0, to_port_type - 2))) { return false; } @@ -278,8 +278,8 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port); VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port); - if (MAX(0, from_port_type - 1) != (MAX(0, to_port_type - 1))) { - ERR_EXPLAIN("Incompatible port types (scalar/vec with transform"); + if (MAX(0, from_port_type - 2) != (MAX(0, to_port_type - 2))) { + ERR_EXPLAIN("Incompatible port types (scalar/vec/bool with transform"); ERR_FAIL_V(ERR_INVALID_PARAMETER) return ERR_INVALID_PARAMETER; } @@ -456,6 +456,8 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR) { code += "\tCOLOR.rgb = vec3( n_out" + itos(p_node) + "p" + itos(p_port) + " );\n"; + } else if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_BOOLEAN) { + code += "\tCOLOR.rgb = vec3( n_out" + itos(p_node) + "p" + itos(p_port) + " ? 1.0 : 0.0 );\n"; } else { code += "\tCOLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n"; } @@ -779,6 +781,14 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui inputs[i] = "dot(" + src_var + ",vec3(0.333333,0.333333,0.333333))"; } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_SCALAR) { inputs[i] = "vec3(" + src_var + ")"; + } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_VECTOR) { + inputs[i] = "all(bvec3(" + src_var + "))"; + } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_SCALAR) { + inputs[i] = src_var + ">0.0?true:false"; + } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) { + inputs[i] = src_var + "?1.0:0.0"; + } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) { + inputs[i] = "vec3(" + src_var + "?1.0:0.0)"; } } else { @@ -787,6 +797,10 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui float val = defval; inputs[i] = "n_in" + itos(node) + "p" + itos(i); code += "\tfloat " + inputs[i] + " = " + vformat("%.5f", val) + ";\n"; + } else if (defval.get_type() == Variant::BOOL) { + bool val = defval; + inputs[i] = "n_in" + itos(node) + "p" + itos(i); + code += "\nbool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n"; } else if (defval.get_type() == Variant::VECTOR3) { Vector3 val = defval; inputs[i] = "n_in" + itos(node) + "p" + itos(i); @@ -823,8 +837,10 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui switch (vsnode->get_output_port_type(i)) { case VisualShaderNode::PORT_TYPE_SCALAR: code += String() + "\tfloat " + outputs[i] + ";\n"; break; case VisualShaderNode::PORT_TYPE_VECTOR: code += String() + "\tvec3 " + outputs[i] + ";\n"; break; + case VisualShaderNode::PORT_TYPE_BOOLEAN: code += String() + "\tbool " + outputs[i] + ";\n"; break; case VisualShaderNode::PORT_TYPE_TRANSFORM: code += String() + "\tmat4 " + outputs[i] + ";\n"; break; - default: {} + default: { + } } } @@ -1262,6 +1278,11 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T case PORT_TYPE_TRANSFORM: { code = "\t" + p_output_vars[0] + " = mat4( vec4(1.0,0.0,0.0,0.0), vec4(0.0,1.0,0.0,0.0), vec4(0.0,0.0,1.0,0.0), vec4(0.0,0.0,0.0,1.0) );\n"; } break; //default (none found) is scalar + case PORT_TYPE_BOOLEAN: { + code = "\t" + p_output_vars[0] + " = false;\n"; + } break; + default: + break; } } diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 4792038351..cf10de9bf5 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -175,7 +175,9 @@ public: enum PortType { PORT_TYPE_SCALAR, PORT_TYPE_VECTOR, + PORT_TYPE_BOOLEAN, PORT_TYPE_TRANSFORM, + PORT_TYPE_COLOR // just a hint for node tree icons, do not use it as actual port type ! }; virtual String get_caption() const = 0; diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index e47069d723..d02902572c 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -88,6 +88,67 @@ VisualShaderNodeScalarConstant::VisualShaderNodeScalarConstant() { constant = 0; } +////////////// Boolean + +String VisualShaderNodeBooleanConstant::get_caption() const { + return "Boolean"; +} + +int VisualShaderNodeBooleanConstant::get_input_port_count() const { + return 0; +} + +VisualShaderNodeBooleanConstant::PortType VisualShaderNodeBooleanConstant::get_input_port_type(int p_port) const { + return PORT_TYPE_BOOLEAN; +} + +String VisualShaderNodeBooleanConstant::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeBooleanConstant::get_output_port_count() const { + return 1; +} + +VisualShaderNodeBooleanConstant::PortType VisualShaderNodeBooleanConstant::get_output_port_type(int p_port) const { + return PORT_TYPE_BOOLEAN; +} + +String VisualShaderNodeBooleanConstant::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeBooleanConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = " + (constant ? "true" : "false") + ";\n"; +} + +void VisualShaderNodeBooleanConstant::set_constant(bool p_value) { + constant = p_value; + emit_changed(); +} + +bool VisualShaderNodeBooleanConstant::get_constant() const { + return constant; +} + +Vector<StringName> VisualShaderNodeBooleanConstant::get_editable_properties() const { + Vector<StringName> props; + props.push_back("constant"); + return props; +} + +void VisualShaderNodeBooleanConstant::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeBooleanConstant::set_constant); + ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeBooleanConstant::get_constant); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constant"), "set_constant", "get_constant"); +} + +VisualShaderNodeBooleanConstant::VisualShaderNodeBooleanConstant() { + constant = false; +} + ////////////// Color String VisualShaderNodeColorConstant::get_caption() const { @@ -666,6 +727,7 @@ String VisualShaderNodeScalarOp::generate_code(Shader::Mode p_mode, VisualShader case OP_MAX: code += "max( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; case OP_MIN: code += "min( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; case OP_ATAN2: code += "atan( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_STEP: code += "step( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; } return code; @@ -693,7 +755,7 @@ void VisualShaderNodeScalarOp::_bind_methods() { ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeScalarOp::set_operator); ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeScalarOp::get_operator); - ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Atan2"), "set_operator", "get_operator"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Atan2,Step"), "set_operator", "get_operator"); BIND_ENUM_CONSTANT(OP_ADD); BIND_ENUM_CONSTANT(OP_SUB); @@ -704,6 +766,7 @@ void VisualShaderNodeScalarOp::_bind_methods() { BIND_ENUM_CONSTANT(OP_MAX); BIND_ENUM_CONSTANT(OP_MIN); BIND_ENUM_CONSTANT(OP_ATAN2); + BIND_ENUM_CONSTANT(OP_STEP); } VisualShaderNodeScalarOp::VisualShaderNodeScalarOp() { @@ -752,6 +815,9 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader case OP_MAX: code += "max( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; case OP_MIN: code += "min( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; case OP_CROSS: code += "cross( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_ATAN2: code += "atan( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_REFLECT: code += "reflect( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_STEP: code += "step( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; } return code; @@ -779,7 +845,7 @@ void VisualShaderNodeVectorOp::_bind_methods() { ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeVectorOp::set_operator); ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeVectorOp::get_operator); - ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Cross"), "set_operator", "get_operator"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Cross,Atan2,Reflect,Step"), "set_operator", "get_operator"); BIND_ENUM_CONSTANT(OP_ADD); BIND_ENUM_CONSTANT(OP_SUB); @@ -790,6 +856,9 @@ void VisualShaderNodeVectorOp::_bind_methods() { BIND_ENUM_CONSTANT(OP_MAX); BIND_ENUM_CONSTANT(OP_MIN); BIND_ENUM_CONSTANT(OP_CROSS); + BIND_ENUM_CONSTANT(OP_ATAN2); + BIND_ENUM_CONSTANT(OP_REFLECT); + BIND_ENUM_CONSTANT(OP_STEP); } VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() { @@ -976,8 +1045,12 @@ String VisualShaderNodeTransformMult::generate_code(Shader::Mode p_mode, VisualS if (op == OP_AxB) { return "\t" + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; - } else { + } else if (op == OP_BxA) { return "\t" + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n"; + } else if (op == OP_AxB_COMP) { + return "\t" + p_output_vars[0] + " = matrixCompMult( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; + } else { + return "\t" + p_output_vars[0] + " = matrixCompMult( " + p_input_vars[1] + " , " + p_input_vars[0] + " );\n"; } } @@ -1003,10 +1076,12 @@ void VisualShaderNodeTransformMult::_bind_methods() { ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformMult::set_operator); ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformMult::get_operator); - ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A"), "set_operator", "get_operator"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A,A x B(per component),B x A(per component)"), "set_operator", "get_operator"); BIND_ENUM_CONSTANT(OP_AxB); BIND_ENUM_CONSTANT(OP_BxA); + BIND_ENUM_CONSTANT(OP_AxB_COMP); + BIND_ENUM_CONSTANT(OP_BxA_COMP); } VisualShaderNodeTransformMult::VisualShaderNodeTransformMult() { @@ -1117,7 +1192,7 @@ String VisualShaderNodeScalarFunc::get_output_port_name(int p_port) const { String VisualShaderNodeScalarFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { - static const char *scalar_func_id[FUNC_NEGATE + 1] = { + static const char *scalar_func_id[FUNC_TRUNC + 1] = { "sin($)", "cos($)", "tan($)", @@ -1138,6 +1213,17 @@ String VisualShaderNodeScalarFunc::generate_code(Shader::Mode p_mode, VisualShad "fract($)", "min(max($,0.0),1.0)", "-($)", + "acosh($)", + "asinh($)", + "atanh($)", + "degrees($)", + "exp2($)", + "inversesqrt($)", + "log2($)", + "radians($)", + "1.0/($)", + "roundEven($)", + "trunc($)" }; return "\t" + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; @@ -1165,7 +1251,7 @@ void VisualShaderNodeScalarFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate,ACosH,ASinH,ATanH,Degrees,Exp2,InverseSqrt,Log2,Radians,Reciprocal,RoundEven,Trunc"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_SIN); BIND_ENUM_CONSTANT(FUNC_COS); @@ -1187,6 +1273,17 @@ void VisualShaderNodeScalarFunc::_bind_methods() { BIND_ENUM_CONSTANT(FUNC_FRAC); BIND_ENUM_CONSTANT(FUNC_SATURATE); BIND_ENUM_CONSTANT(FUNC_NEGATE); + BIND_ENUM_CONSTANT(FUNC_ACOSH); + BIND_ENUM_CONSTANT(FUNC_ASINH); + BIND_ENUM_CONSTANT(FUNC_ATANH); + BIND_ENUM_CONSTANT(FUNC_DEGREES); + BIND_ENUM_CONSTANT(FUNC_EXP2); + BIND_ENUM_CONSTANT(FUNC_INVERSE_SQRT); + BIND_ENUM_CONSTANT(FUNC_LOG2); + BIND_ENUM_CONSTANT(FUNC_RADIANS); + BIND_ENUM_CONSTANT(FUNC_RECIPROCAL); + BIND_ENUM_CONSTANT(FUNC_ROUNDEVEN); + BIND_ENUM_CONSTANT(FUNC_TRUNC); } VisualShaderNodeScalarFunc::VisualShaderNodeScalarFunc() { @@ -1222,13 +1319,41 @@ String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const { String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { - static const char *vec_func_id[FUNC_HSV2RGB + 1] = { + static const char *vec_func_id[FUNC_TRUNC + 1] = { "normalize($)", "max(min($,vec3(1.0)),vec3(0.0))", "-($)", "1.0/($)", "", "", + "abs($)", + "acos($)", + "acosh($)", + "asin($)", + "asinh($)", + "atan($)", + "atanh($)", + "ceil($)", + "cos($)", + "cosh($)", + "degrees($)", + "exp($)", + "exp2($)", + "floor($)", + "fract($)", + "inversesqrt($)", + "log($)", + "log2($)", + "radians($)", + "round($)", + "roundEven($)", + "sign($)", + "sin($)", + "sinh($)", + "sqrt($)", + "tan($)", + "tanh($)", + "trunc($)" }; String code; @@ -1280,7 +1405,7 @@ void VisualShaderNodeVectorFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_NORMALIZE); BIND_ENUM_CONSTANT(FUNC_SATURATE); @@ -1288,6 +1413,34 @@ void VisualShaderNodeVectorFunc::_bind_methods() { BIND_ENUM_CONSTANT(FUNC_RECIPROCAL); BIND_ENUM_CONSTANT(FUNC_RGB2HSV); BIND_ENUM_CONSTANT(FUNC_HSV2RGB); + BIND_ENUM_CONSTANT(FUNC_ABS); + BIND_ENUM_CONSTANT(FUNC_ACOS); + BIND_ENUM_CONSTANT(FUNC_ACOSH); + BIND_ENUM_CONSTANT(FUNC_ASIN); + BIND_ENUM_CONSTANT(FUNC_ASINH); + BIND_ENUM_CONSTANT(FUNC_ATAN); + BIND_ENUM_CONSTANT(FUNC_ATANH); + BIND_ENUM_CONSTANT(FUNC_CEIL); + BIND_ENUM_CONSTANT(FUNC_COS); + BIND_ENUM_CONSTANT(FUNC_COSH); + BIND_ENUM_CONSTANT(FUNC_DEGREES); + BIND_ENUM_CONSTANT(FUNC_EXP); + BIND_ENUM_CONSTANT(FUNC_EXP2); + BIND_ENUM_CONSTANT(FUNC_FLOOR); + BIND_ENUM_CONSTANT(FUNC_FRAC); + BIND_ENUM_CONSTANT(FUNC_INVERSE_SQRT); + BIND_ENUM_CONSTANT(FUNC_LOG); + BIND_ENUM_CONSTANT(FUNC_LOG2); + BIND_ENUM_CONSTANT(FUNC_RADIANS); + BIND_ENUM_CONSTANT(FUNC_ROUND); + BIND_ENUM_CONSTANT(FUNC_ROUNDEVEN); + BIND_ENUM_CONSTANT(FUNC_SIGN); + BIND_ENUM_CONSTANT(FUNC_SIN); + BIND_ENUM_CONSTANT(FUNC_SINH); + BIND_ENUM_CONSTANT(FUNC_SQRT); + BIND_ENUM_CONSTANT(FUNC_TAN); + BIND_ENUM_CONSTANT(FUNC_TANH); + BIND_ENUM_CONSTANT(FUNC_TRUNC); } VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() { @@ -1295,6 +1448,172 @@ VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() { set_input_port_default_value(0, Vector3()); } +////////////// ColorFunc + +String VisualShaderNodeColorFunc::get_caption() const { + return "ColorFunc"; +} + +int VisualShaderNodeColorFunc::get_input_port_count() const { + return 1; +} + +VisualShaderNodeColorFunc::PortType VisualShaderNodeColorFunc::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeColorFunc::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeColorFunc::get_output_port_count() const { + return 1; +} + +VisualShaderNodeColorFunc::PortType VisualShaderNodeColorFunc::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeColorFunc::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + + String code; + + switch (func) { + case FUNC_GRAYSCALE: + code += "\t{\n"; + code += "\t\tvec3 c = " + p_input_vars[0] + ";\n"; + code += "\t\tfloat max1 = max(c.r, c.g);\n"; + code += "\t\tfloat max2 = max(max1, c.b);\n"; + code += "\t\tfloat max3 = max(max1, max2);\n"; + code += "\t\t" + p_output_vars[0] + " = vec3(max3, max3, max3);\n"; + code += "\t}\n"; + break; + case FUNC_SEPIA: + code += "\t{\n"; + code += "\t\tvec3 c = " + p_input_vars[0] + ";\n"; + code += "\t\tfloat r = (c.r * .393) + (c.g *.769) + (c.b * .189);\n"; + code += "\t\tfloat g = (c.r * .349) + (c.g *.686) + (c.b * .168);\n"; + code += "\t\tfloat b = (c.r * .272) + (c.g *.534) + (c.b * .131);\n"; + code += "\t\t" + p_output_vars[0] + " = vec3(r, g, b);\n"; + code += "\t}\n"; + break; + } + + return code; +} + +void VisualShaderNodeColorFunc::set_function(Function p_func) { + + func = p_func; + emit_changed(); +} + +VisualShaderNodeColorFunc::Function VisualShaderNodeColorFunc::get_function() const { + + return func; +} + +Vector<StringName> VisualShaderNodeColorFunc::get_editable_properties() const { + Vector<StringName> props; + props.push_back("function"); + return props; +} + +void VisualShaderNodeColorFunc::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeColorFunc::set_function); + ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeColorFunc::get_function); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,Sepia"), "set_function", "get_function"); + + BIND_ENUM_CONSTANT(FUNC_GRAYSCALE); + BIND_ENUM_CONSTANT(FUNC_SEPIA); +} + +VisualShaderNodeColorFunc::VisualShaderNodeColorFunc() { + func = FUNC_GRAYSCALE; + set_input_port_default_value(0, Vector3()); +} + +////////////// Transform Func + +String VisualShaderNodeTransformFunc::get_caption() const { + return "TransformFunc"; +} + +int VisualShaderNodeTransformFunc::get_input_port_count() const { + return 1; +} + +VisualShaderNodeTransformFunc::PortType VisualShaderNodeTransformFunc::get_input_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} + +String VisualShaderNodeTransformFunc::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeTransformFunc::get_output_port_count() const { + return 1; +} + +VisualShaderNodeTransformFunc::PortType VisualShaderNodeTransformFunc::get_output_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} + +String VisualShaderNodeTransformFunc::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeTransformFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + + static const char *funcs[FUNC_TRANSPOSE + 1] = { + "inverse($)", + "transpose($)" + }; + + String code; + code += "\t" + p_output_vars[0] + "=" + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; + return code; +} + +void VisualShaderNodeTransformFunc::set_function(Function p_func) { + + func = p_func; + emit_changed(); +} + +VisualShaderNodeTransformFunc::Function VisualShaderNodeTransformFunc::get_function() const { + + return func; +} + +Vector<StringName> VisualShaderNodeTransformFunc::get_editable_properties() const { + Vector<StringName> props; + props.push_back("function"); + return props; +} + +void VisualShaderNodeTransformFunc::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeTransformFunc::set_function); + ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeTransformFunc::get_function); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Inverse,Transpose"), "set_function", "get_function"); + + BIND_ENUM_CONSTANT(FUNC_INVERSE); + BIND_ENUM_CONSTANT(FUNC_TRANSPOSE); +} + +VisualShaderNodeTransformFunc::VisualShaderNodeTransformFunc() { + func = FUNC_INVERSE; + set_input_port_default_value(0, Transform()); +} + ////////////// Dot Product String VisualShaderNodeDotProduct::get_caption() const { @@ -1364,6 +1683,670 @@ VisualShaderNodeVectorLen::VisualShaderNodeVectorLen() { set_input_port_default_value(0, Vector3()); } +////////////// Determinant + +String VisualShaderNodeDeterminant::get_caption() const { + return "Determinant"; +} + +int VisualShaderNodeDeterminant::get_input_port_count() const { + return 1; +} + +VisualShaderNodeScalarClamp::PortType VisualShaderNodeDeterminant::get_input_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} + +String VisualShaderNodeDeterminant::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeDeterminant::get_output_port_count() const { + return 1; +} + +VisualShaderNodeDeterminant::PortType VisualShaderNodeDeterminant::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeDeterminant::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeDeterminant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = determinant( " + p_input_vars[0] + " );\n"; +} + +VisualShaderNodeDeterminant::VisualShaderNodeDeterminant() { + set_input_port_default_value(0, Transform()); +} + +////////////// Scalar Derivative Function + +String VisualShaderNodeScalarDerivativeFunc::get_caption() const { + return "ScalarDerivativeFunc"; +} + +int VisualShaderNodeScalarDerivativeFunc::get_input_port_count() const { + return 1; +} + +VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeScalarDerivativeFunc::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeScalarDerivativeFunc::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeScalarDerivativeFunc::get_output_port_count() const { + return 1; +} + +VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeScalarDerivativeFunc::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeScalarDerivativeFunc::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeScalarDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + + static const char *funcs[FUNC_Y + 1] = { + "fwidth($)", + "dFdx($)", + "dFdy($)" + }; + + String code; + code += "\t" + p_output_vars[0] + "=" + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; + return code; +} + +void VisualShaderNodeScalarDerivativeFunc::set_function(Function p_func) { + + func = p_func; + emit_changed(); +} + +VisualShaderNodeScalarDerivativeFunc::Function VisualShaderNodeScalarDerivativeFunc::get_function() const { + + return func; +} + +Vector<StringName> VisualShaderNodeScalarDerivativeFunc::get_editable_properties() const { + Vector<StringName> props; + props.push_back("function"); + return props; +} + +void VisualShaderNodeScalarDerivativeFunc::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarDerivativeFunc::set_function); + ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarDerivativeFunc::get_function); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function"); + + BIND_ENUM_CONSTANT(FUNC_SUM); + BIND_ENUM_CONSTANT(FUNC_X); + BIND_ENUM_CONSTANT(FUNC_Y); +} + +VisualShaderNodeScalarDerivativeFunc::VisualShaderNodeScalarDerivativeFunc() { + func = FUNC_SUM; + set_input_port_default_value(0, 0.0); +} + +////////////// Vector Derivative Function + +String VisualShaderNodeVectorDerivativeFunc::get_caption() const { + return "VectorDerivativeFunc"; +} + +int VisualShaderNodeVectorDerivativeFunc::get_input_port_count() const { + return 1; +} + +VisualShaderNodeVectorDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorDerivativeFunc::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeVectorDerivativeFunc::get_output_port_count() const { + return 1; +} + +VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorDerivativeFunc::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeVectorDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + + static const char *funcs[FUNC_Y + 1] = { + "fwidth($)", + "dFdx($)", + "dFdy($)" + }; + + String code; + code += "\t" + p_output_vars[0] + "=" + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; + return code; +} + +void VisualShaderNodeVectorDerivativeFunc::set_function(Function p_func) { + + func = p_func; + emit_changed(); +} + +VisualShaderNodeVectorDerivativeFunc::Function VisualShaderNodeVectorDerivativeFunc::get_function() const { + + return func; +} + +Vector<StringName> VisualShaderNodeVectorDerivativeFunc::get_editable_properties() const { + Vector<StringName> props; + props.push_back("function"); + return props; +} + +void VisualShaderNodeVectorDerivativeFunc::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorDerivativeFunc::set_function); + ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorDerivativeFunc::get_function); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function"); + + BIND_ENUM_CONSTANT(FUNC_SUM); + BIND_ENUM_CONSTANT(FUNC_X); + BIND_ENUM_CONSTANT(FUNC_Y); +} + +VisualShaderNodeVectorDerivativeFunc::VisualShaderNodeVectorDerivativeFunc() { + func = FUNC_SUM; + set_input_port_default_value(0, Vector3()); +} + +////////////// Scalar Clamp + +String VisualShaderNodeScalarClamp::get_caption() const { + return "ScalarClamp"; +} + +int VisualShaderNodeScalarClamp::get_input_port_count() const { + return 3; +} + +VisualShaderNodeScalarClamp::PortType VisualShaderNodeScalarClamp::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeScalarClamp::get_input_port_name(int p_port) const { + if (p_port == 0) + return ""; + else if (p_port == 1) + return "min"; + else if (p_port == 2) + return "max"; + return ""; +} + +int VisualShaderNodeScalarClamp::get_output_port_count() const { + return 1; +} + +VisualShaderNodeScalarClamp::PortType VisualShaderNodeScalarClamp::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeScalarClamp::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeScalarClamp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = clamp( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeScalarClamp::VisualShaderNodeScalarClamp() { + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, 1.0); +} + +////////////// Vector Clamp + +String VisualShaderNodeVectorClamp::get_caption() const { + return "VectorClamp"; +} + +int VisualShaderNodeVectorClamp::get_input_port_count() const { + return 3; +} + +VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorClamp::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorClamp::get_input_port_name(int p_port) const { + if (p_port == 0) + return ""; + else if (p_port == 1) + return "min"; + else if (p_port == 2) + return "max"; + return ""; +} + +int VisualShaderNodeVectorClamp::get_output_port_count() const { + return 1; +} + +VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorClamp::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorClamp::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeVectorClamp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = clamp( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeVectorClamp::VisualShaderNodeVectorClamp() { + set_input_port_default_value(0, Vector3(0, 0, 0)); + set_input_port_default_value(1, Vector3(0, 0, 0)); + set_input_port_default_value(2, Vector3(1, 1, 1)); +} + +////////////// FaceForward + +String VisualShaderNodeFaceForward::get_caption() const { + return "FaceForward"; +} + +int VisualShaderNodeFaceForward::get_input_port_count() const { + return 3; +} + +VisualShaderNodeFaceForward::PortType VisualShaderNodeFaceForward::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeFaceForward::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "N"; + case 1: + return "I"; + case 2: + return "Nref"; + default: + return ""; + } +} + +int VisualShaderNodeFaceForward::get_output_port_count() const { + return 1; +} + +VisualShaderNodeFaceForward::PortType VisualShaderNodeFaceForward::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeFaceForward::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeFaceForward::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = faceforward( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeFaceForward::VisualShaderNodeFaceForward() { + set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); +} + +////////////// Outer Product + +String VisualShaderNodeOuterProduct::get_caption() const { + return "OuterProduct"; +} + +int VisualShaderNodeOuterProduct::get_input_port_count() const { + return 2; +} + +VisualShaderNodeFaceForward::PortType VisualShaderNodeOuterProduct::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeOuterProduct::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "c"; + case 1: + return "r"; + default: + return ""; + } +} + +int VisualShaderNodeOuterProduct::get_output_port_count() const { + return 1; +} + +VisualShaderNodeOuterProduct::PortType VisualShaderNodeOuterProduct::get_output_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} + +String VisualShaderNodeOuterProduct::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeOuterProduct::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = outerProduct( " + p_input_vars[0] + ", " + p_input_vars[1] + " );\n"; +} + +VisualShaderNodeOuterProduct::VisualShaderNodeOuterProduct() { + set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); +} + +////////////// Vector-Scalar Step + +String VisualShaderNodeVectorScalarStep::get_caption() const { + return "VectorScalarStep"; +} + +int VisualShaderNodeVectorScalarStep::get_input_port_count() const { + return 2; +} + +VisualShaderNodeVectorScalarStep::PortType VisualShaderNodeVectorScalarStep::get_input_port_type(int p_port) const { + if (p_port == 0) { + return PORT_TYPE_SCALAR; + } + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorScalarStep::get_input_port_name(int p_port) const { + if (p_port == 0) + return "edge"; + else if (p_port == 1) + return "x"; + return ""; +} + +int VisualShaderNodeVectorScalarStep::get_output_port_count() const { + return 1; +} + +VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorScalarStep::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorScalarStep::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeVectorScalarStep::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = step( " + p_input_vars[0] + ", " + p_input_vars[1] + " );\n"; +} + +VisualShaderNodeVectorScalarStep::VisualShaderNodeVectorScalarStep() { + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); +} + +////////////// Scalar SmoothStep + +String VisualShaderNodeScalarSmoothStep::get_caption() const { + return "ScalarSmoothStep"; +} + +int VisualShaderNodeScalarSmoothStep::get_input_port_count() const { + return 3; +} + +VisualShaderNodeScalarSmoothStep::PortType VisualShaderNodeScalarSmoothStep::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeScalarSmoothStep::get_input_port_name(int p_port) const { + if (p_port == 0) + return "edge0"; + else if (p_port == 1) + return "edge1"; + else if (p_port == 2) + return "x"; + return ""; +} + +int VisualShaderNodeScalarSmoothStep::get_output_port_count() const { + return 1; +} + +VisualShaderNodeScalarSmoothStep::PortType VisualShaderNodeScalarSmoothStep::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeScalarSmoothStep::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeScalarSmoothStep::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = smoothstep( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeScalarSmoothStep::VisualShaderNodeScalarSmoothStep() { + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, 0.0); +} + +////////////// Vector SmoothStep + +String VisualShaderNodeVectorSmoothStep::get_caption() const { + return "VectorSmoothStep"; +} + +int VisualShaderNodeVectorSmoothStep::get_input_port_count() const { + return 3; +} + +VisualShaderNodeVectorSmoothStep::PortType VisualShaderNodeVectorSmoothStep::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorSmoothStep::get_input_port_name(int p_port) const { + if (p_port == 0) + return "edge0"; + else if (p_port == 1) + return "edge1"; + else if (p_port == 2) + return "x"; + return ""; +} + +int VisualShaderNodeVectorSmoothStep::get_output_port_count() const { + return 1; +} + +VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorSmoothStep::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorSmoothStep::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeVectorSmoothStep::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = smoothstep( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeVectorSmoothStep::VisualShaderNodeVectorSmoothStep() { + set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); +} + +////////////// Vector-Scalar SmoothStep + +String VisualShaderNodeVectorScalarSmoothStep::get_caption() const { + return "VectorScalarSmoothStep"; +} + +int VisualShaderNodeVectorScalarSmoothStep::get_input_port_count() const { + return 3; +} + +VisualShaderNodeVectorScalarSmoothStep::PortType VisualShaderNodeVectorScalarSmoothStep::get_input_port_type(int p_port) const { + if (p_port == 0) { + return PORT_TYPE_SCALAR; + } else if (p_port == 1) { + return PORT_TYPE_SCALAR; + } + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorScalarSmoothStep::get_input_port_name(int p_port) const { + if (p_port == 0) + return "edge0"; + else if (p_port == 1) + return "edge1"; + else if (p_port == 2) + return "x"; + return ""; +} + +int VisualShaderNodeVectorScalarSmoothStep::get_output_port_count() const { + return 1; +} + +VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorScalarSmoothStep::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorScalarSmoothStep::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeVectorScalarSmoothStep::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = smoothstep( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeVectorScalarSmoothStep::VisualShaderNodeVectorScalarSmoothStep() { + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); +} + +////////////// Distance + +String VisualShaderNodeVectorDistance::get_caption() const { + return "Distance"; +} + +int VisualShaderNodeVectorDistance::get_input_port_count() const { + return 2; +} + +VisualShaderNodeVectorDistance::PortType VisualShaderNodeVectorDistance::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorDistance::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "p0"; + } else if (p_port == 1) { + return "p1"; + } + return ""; +} + +int VisualShaderNodeVectorDistance::get_output_port_count() const { + return 1; +} + +VisualShaderNodeVectorDistance::PortType VisualShaderNodeVectorDistance::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeVectorDistance::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeVectorDistance::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = distance( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; +} + +VisualShaderNodeVectorDistance::VisualShaderNodeVectorDistance() { + set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); +} + +////////////// Refract Vector + +String VisualShaderNodeVectorRefract::get_caption() const { + return "Refract"; +} + +int VisualShaderNodeVectorRefract::get_input_port_count() const { + return 3; +} + +VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_input_port_type(int p_port) const { + + if (p_port == 2) { + return PORT_TYPE_SCALAR; + } + + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorRefract::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "I"; + } else if (p_port == 1) { + return "N"; + } else if (p_port == 2) { + return "eta"; + } + return ""; +} + +int VisualShaderNodeVectorRefract::get_output_port_count() const { + return 1; +} + +VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeVectorRefract::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeVectorRefract::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = refract( " + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeVectorRefract::VisualShaderNodeVectorRefract() { + set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(2, 0.0); +} + ////////////// Scalar Interp String VisualShaderNodeScalarInterp::get_caption() const { @@ -1662,6 +2645,47 @@ String VisualShaderNodeScalarUniform::generate_code(Shader::Mode p_mode, VisualS VisualShaderNodeScalarUniform::VisualShaderNodeScalarUniform() { } +////////////// Boolean Uniform + +String VisualShaderNodeBooleanUniform::get_caption() const { + return "BooleanUniform"; +} + +int VisualShaderNodeBooleanUniform::get_input_port_count() const { + return 0; +} + +VisualShaderNodeBooleanUniform::PortType VisualShaderNodeBooleanUniform::get_input_port_type(int p_port) const { + return PORT_TYPE_BOOLEAN; +} + +String VisualShaderNodeBooleanUniform::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeBooleanUniform::get_output_port_count() const { + return 1; +} + +VisualShaderNodeBooleanUniform::PortType VisualShaderNodeBooleanUniform::get_output_port_type(int p_port) const { + return PORT_TYPE_BOOLEAN; +} + +String VisualShaderNodeBooleanUniform::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeBooleanUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return "uniform bool " + get_uniform_name() + ";\n"; +} + +String VisualShaderNodeBooleanUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +} + +VisualShaderNodeBooleanUniform::VisualShaderNodeBooleanUniform() { +} + ////////////// Color Uniform String VisualShaderNodeColorUniform::get_caption() const { @@ -1924,3 +2948,140 @@ String VisualShaderNodeCubeMapUniform::generate_code(Shader::Mode p_mode, Visual VisualShaderNodeCubeMapUniform::VisualShaderNodeCubeMapUniform() { } + +////////////// If + +String VisualShaderNodeIf::get_caption() const { + return "If"; +} + +int VisualShaderNodeIf::get_input_port_count() const { + return 6; +} + +VisualShaderNodeIf::PortType VisualShaderNodeIf::get_input_port_type(int p_port) const { + if (p_port == 0 || p_port == 1 || p_port == 2) { + return PORT_TYPE_SCALAR; + } + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeIf::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "a"; + case 1: + return "b"; + case 2: + return "tolerance"; + case 3: + return "a == b"; + case 4: + return "a > b"; + case 5: + return "a < b"; + default: + return ""; + } +} + +int VisualShaderNodeIf::get_output_port_count() const { + return 1; +} + +VisualShaderNodeIf::PortType VisualShaderNodeIf::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeIf::get_output_port_name(int p_port) const { + return "result"; +} + +String VisualShaderNodeIf::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + + String code; + code += "\tif(abs(" + p_input_vars[0] + "-" + p_input_vars[1] + ")<" + p_input_vars[2] + ")\n"; // abs(a - b) < tolerance eg. a == b + code += "\t{\n"; + code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[3] + ";\n"; + code += "\t}\n"; + code += "\telse if(" + p_input_vars[0] + "<" + p_input_vars[1] + ")\n"; // a < b + code += "\t{\n"; + code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[5] + ";\n"; + code += "\t}\n"; + code += "\telse\n"; // a > b (or a >= b if abs(a - b) < tolerance is false) + code += "\t{\n"; + code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[4] + ";\n"; + code += "\t}\n"; + return code; +} + +VisualShaderNodeIf::VisualShaderNodeIf() { + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, CMP_EPSILON); + set_input_port_default_value(3, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(4, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(5, Vector3(0.0, 0.0, 0.0)); +} + +////////////// Switch + +String VisualShaderNodeSwitch::get_caption() const { + return "Switch"; +} + +int VisualShaderNodeSwitch::get_input_port_count() const { + return 3; +} + +VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_input_port_type(int p_port) const { + if (p_port == 0) { + return PORT_TYPE_BOOLEAN; + } + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeSwitch::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "value"; + case 1: + return "true"; + case 2: + return "false"; + default: + return ""; + } +} + +int VisualShaderNodeSwitch::get_output_port_count() const { + return 1; +} + +VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeSwitch::get_output_port_name(int p_port) const { + return "result"; +} + +String VisualShaderNodeSwitch::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + + String code; + code += "\tif(" + p_input_vars[0] + ")\n"; + code += "\t{\n"; + code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[1] + ";\n"; + code += "\t}\n"; + code += "\telse\n"; + code += "\t{\n"; + code += "\t\t" + p_output_vars[0] + "=" + p_input_vars[2] + ";\n"; + code += "\t}\n"; + return code; +} + +VisualShaderNodeSwitch::VisualShaderNodeSwitch() { + set_input_port_default_value(0, false); + set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); +} diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 27b557494a..90c479bd48 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -33,7 +33,9 @@ #include "scene/resources/visual_shader.h" -/// CONSTANTS /// +/////////////////////////////////////// +/// CONSTANTS +/////////////////////////////////////// class VisualShaderNodeScalarConstant : public VisualShaderNode { GDCLASS(VisualShaderNodeScalarConstant, VisualShaderNode) @@ -63,6 +65,38 @@ public: VisualShaderNodeScalarConstant(); }; +/////////////////////////////////////// + +class VisualShaderNodeBooleanConstant : public VisualShaderNode { + GDCLASS(VisualShaderNodeBooleanConstant, VisualShaderNode) + bool constant; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_constant(bool p_value); + bool get_constant() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeBooleanConstant(); +}; + +/////////////////////////////////////// + class VisualShaderNodeColorConstant : public VisualShaderNode { GDCLASS(VisualShaderNodeColorConstant, VisualShaderNode) Color constant; @@ -91,6 +125,8 @@ public: VisualShaderNodeColorConstant(); }; +/////////////////////////////////////// + class VisualShaderNodeVec3Constant : public VisualShaderNode { GDCLASS(VisualShaderNodeVec3Constant, VisualShaderNode) Vector3 constant; @@ -119,6 +155,8 @@ public: VisualShaderNodeVec3Constant(); }; +/////////////////////////////////////// + class VisualShaderNodeTransformConstant : public VisualShaderNode { GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNode) Transform constant; @@ -147,7 +185,9 @@ public: VisualShaderNodeTransformConstant(); }; -////////////////////////////////// +/////////////////////////////////////// +/// TEXTURES +/////////////////////////////////////// class VisualShaderNodeTexture : public VisualShaderNode { GDCLASS(VisualShaderNodeTexture, VisualShaderNode) @@ -208,7 +248,7 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeTexture::TextureType) VARIANT_ENUM_CAST(VisualShaderNodeTexture::Source) -////////////////////////////////// +/////////////////////////////////////// class VisualShaderNodeCubeMap : public VisualShaderNode { GDCLASS(VisualShaderNodeCubeMap, VisualShaderNode) @@ -254,6 +294,9 @@ public: }; VARIANT_ENUM_CAST(VisualShaderNodeCubeMap::TextureType) + +/////////////////////////////////////// +/// OPS /////////////////////////////////////// class VisualShaderNodeScalarOp : public VisualShaderNode { @@ -269,7 +312,8 @@ public: OP_POW, OP_MAX, OP_MIN, - OP_ATAN2 + OP_ATAN2, + OP_STEP }; protected: @@ -313,8 +357,10 @@ public: OP_POW, OP_MAX, OP_MIN, - OP_CROSS - + OP_CROSS, + OP_ATAN2, + OP_REFLECT, + OP_STEP }; protected: @@ -345,6 +391,8 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeVectorOp::Operator) +/////////////////////////////////////// + class VisualShaderNodeColorOp : public VisualShaderNode { GDCLASS(VisualShaderNodeColorOp, VisualShaderNode) @@ -358,7 +406,7 @@ public: OP_DODGE, OP_BURN, OP_SOFT_LIGHT, - OP_HARD_LIGHT, + OP_HARD_LIGHT }; protected: @@ -389,6 +437,10 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeColorOp::Operator) +/////////////////////////////////////// +/// TRANSFORM-TRANSFORM MULTIPLICATION +/////////////////////////////////////// + class VisualShaderNodeTransformMult : public VisualShaderNode { GDCLASS(VisualShaderNodeTransformMult, VisualShaderNode) @@ -396,6 +448,8 @@ public: enum Operator { OP_AxB, OP_BxA, + OP_AxB_COMP, + OP_BxA_COMP }; protected: @@ -426,6 +480,10 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeTransformMult::Operator) +/////////////////////////////////////// +/// TRANSFORM-VECTOR MULTIPLICATION +/////////////////////////////////////// + class VisualShaderNodeTransformVecMult : public VisualShaderNode { GDCLASS(VisualShaderNodeTransformVecMult, VisualShaderNode) @@ -466,6 +524,8 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeTransformVecMult::Operator) /////////////////////////////////////// +/// SCALAR FUNC +/////////////////////////////////////// class VisualShaderNodeScalarFunc : public VisualShaderNode { GDCLASS(VisualShaderNodeScalarFunc, VisualShaderNode) @@ -492,6 +552,17 @@ public: FUNC_FRAC, FUNC_SATURATE, FUNC_NEGATE, + FUNC_ACOSH, + FUNC_ASINH, + FUNC_ATANH, + FUNC_DEGREES, + FUNC_EXP2, + FUNC_INVERSE_SQRT, + FUNC_LOG2, + FUNC_RADIANS, + FUNC_RECIPROCAL, + FUNC_ROUNDEVEN, + FUNC_TRUNC }; protected: @@ -523,6 +594,8 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeScalarFunc::Function) /////////////////////////////////////// +/// VECTOR FUNC +/////////////////////////////////////// class VisualShaderNodeVectorFunc : public VisualShaderNode { GDCLASS(VisualShaderNodeVectorFunc, VisualShaderNode) @@ -535,6 +608,34 @@ public: FUNC_RECIPROCAL, FUNC_RGB2HSV, FUNC_HSV2RGB, + FUNC_ABS, + FUNC_ACOS, + FUNC_ACOSH, + FUNC_ASIN, + FUNC_ASINH, + FUNC_ATAN, + FUNC_ATANH, + FUNC_CEIL, + FUNC_COS, + FUNC_COSH, + FUNC_DEGREES, + FUNC_EXP, + FUNC_EXP2, + FUNC_FLOOR, + FUNC_FRAC, + FUNC_INVERSE_SQRT, + FUNC_LOG, + FUNC_LOG2, + FUNC_RADIANS, + FUNC_ROUND, + FUNC_ROUNDEVEN, + FUNC_SIGN, + FUNC_SIN, + FUNC_SINH, + FUNC_SQRT, + FUNC_TAN, + FUNC_TANH, + FUNC_TRUNC }; protected: @@ -566,6 +667,90 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeVectorFunc::Function) /////////////////////////////////////// +/// COLOR FUNC +/////////////////////////////////////// + +class VisualShaderNodeColorFunc : public VisualShaderNode { + GDCLASS(VisualShaderNodeColorFunc, VisualShaderNode) + +public: + enum Function { + FUNC_GRAYSCALE, + FUNC_SEPIA + }; + +protected: + Function func; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_function(Function p_op); + Function get_function() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeColorFunc(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeColorFunc::Function) + +/////////////////////////////////////// +/// TRANSFORM FUNC +/////////////////////////////////////// + +class VisualShaderNodeTransformFunc : public VisualShaderNode { + GDCLASS(VisualShaderNodeTransformFunc, VisualShaderNode) + +public: + enum Function { + FUNC_INVERSE, + FUNC_TRANSPOSE + }; + +protected: + Function func; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_function(Function p_op); + Function get_function() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeTransformFunc(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeTransformFunc::Function) + +/////////////////////////////////////// +/// DOT +/////////////////////////////////////// class VisualShaderNodeDotProduct : public VisualShaderNode { GDCLASS(VisualShaderNodeDotProduct, VisualShaderNode) @@ -587,6 +772,8 @@ public: }; /////////////////////////////////////// +/// LENGTH +/////////////////////////////////////// class VisualShaderNodeVectorLen : public VisualShaderNode { GDCLASS(VisualShaderNodeVectorLen, VisualShaderNode) @@ -608,6 +795,337 @@ public: }; /////////////////////////////////////// +/// DETERMINANT +/////////////////////////////////////// + +class VisualShaderNodeDeterminant : public VisualShaderNode { + GDCLASS(VisualShaderNodeDeterminant, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeDeterminant(); +}; + +/////////////////////////////////////// +/// CLAMP +/////////////////////////////////////// + +class VisualShaderNodeScalarClamp : public VisualShaderNode { + GDCLASS(VisualShaderNodeScalarClamp, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeScalarClamp(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeVectorClamp : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorClamp, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorClamp(); +}; + +/////////////////////////////////////// +/// DERIVATIVE FUNCTIONS +/////////////////////////////////////// + +class VisualShaderNodeScalarDerivativeFunc : public VisualShaderNode { + GDCLASS(VisualShaderNodeScalarDerivativeFunc, VisualShaderNode) + +public: + enum Function { + FUNC_SUM, + FUNC_X, + FUNC_Y + }; + +protected: + Function func; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_function(Function p_op); + Function get_function() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeScalarDerivativeFunc(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeScalarDerivativeFunc::Function) + +/////////////////////////////////////// + +class VisualShaderNodeVectorDerivativeFunc : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorDerivativeFunc, VisualShaderNode) + +public: + enum Function { + FUNC_SUM, + FUNC_X, + FUNC_Y + }; + +protected: + Function func; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_function(Function p_op); + Function get_function() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeVectorDerivativeFunc(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeVectorDerivativeFunc::Function) + +/////////////////////////////////////// +/// FACEFORWARD +/////////////////////////////////////// + +class VisualShaderNodeFaceForward : public VisualShaderNode { + GDCLASS(VisualShaderNodeFaceForward, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeFaceForward(); +}; + +/////////////////////////////////////// +/// OUTER PRODUCT +/////////////////////////////////////// + +class VisualShaderNodeOuterProduct : public VisualShaderNode { + GDCLASS(VisualShaderNodeOuterProduct, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeOuterProduct(); +}; + +/////////////////////////////////////// +/// STEP +/////////////////////////////////////// + +class VisualShaderNodeVectorScalarStep : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorScalarStep, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorScalarStep(); +}; + +/////////////////////////////////////// +/// SMOOTHSTEP +/////////////////////////////////////// + +class VisualShaderNodeScalarSmoothStep : public VisualShaderNode { + GDCLASS(VisualShaderNodeScalarSmoothStep, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeScalarSmoothStep(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeVectorSmoothStep : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorSmoothStep, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorSmoothStep(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeVectorScalarSmoothStep : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorScalarSmoothStep, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorScalarSmoothStep(); +}; + +/////////////////////////////////////// +/// DISTANCE +/////////////////////////////////////// + +class VisualShaderNodeVectorDistance : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorDistance, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorDistance(); +}; + +/////////////////////////////////////// +/// REFRACT +/////////////////////////////////////// + +class VisualShaderNodeVectorRefract : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorRefract, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorRefract(); +}; + +/////////////////////////////////////// +/// MIX +/////////////////////////////////////// class VisualShaderNodeScalarInterp : public VisualShaderNode { GDCLASS(VisualShaderNodeScalarInterp, VisualShaderNode) @@ -650,6 +1168,8 @@ public: }; /////////////////////////////////////// +/// COMPOSE +/////////////////////////////////////// class VisualShaderNodeVectorCompose : public VisualShaderNode { GDCLASS(VisualShaderNodeVectorCompose, VisualShaderNode) @@ -692,6 +1212,8 @@ public: }; /////////////////////////////////////// +/// DECOMPOSE +/////////////////////////////////////// class VisualShaderNodeVectorDecompose : public VisualShaderNode { GDCLASS(VisualShaderNodeVectorDecompose, VisualShaderNode) @@ -734,6 +1256,8 @@ public: }; /////////////////////////////////////// +/// UNIFORMS +/////////////////////////////////////// class VisualShaderNodeScalarUniform : public VisualShaderNodeUniform { GDCLASS(VisualShaderNodeScalarUniform, VisualShaderNodeUniform) @@ -755,6 +1279,30 @@ public: VisualShaderNodeScalarUniform(); }; +/////////////////////////////////////// + +class VisualShaderNodeBooleanUniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeBooleanUniform, VisualShaderNodeUniform) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeBooleanUniform(); +}; + +/////////////////////////////////////// + class VisualShaderNodeColorUniform : public VisualShaderNodeUniform { GDCLASS(VisualShaderNodeColorUniform, VisualShaderNodeUniform) @@ -775,6 +1323,8 @@ public: VisualShaderNodeColorUniform(); }; +/////////////////////////////////////// + class VisualShaderNodeVec3Uniform : public VisualShaderNodeUniform { GDCLASS(VisualShaderNodeVec3Uniform, VisualShaderNodeUniform) @@ -795,6 +1345,8 @@ public: VisualShaderNodeVec3Uniform(); }; +/////////////////////////////////////// + class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform { GDCLASS(VisualShaderNodeTransformUniform, VisualShaderNodeUniform) @@ -815,7 +1367,7 @@ public: VisualShaderNodeTransformUniform(); }; -////////////////////////////////// +/////////////////////////////////////// class VisualShaderNodeTextureUniform : public VisualShaderNodeUniform { GDCLASS(VisualShaderNodeTextureUniform, VisualShaderNodeUniform) @@ -867,7 +1419,7 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureType) VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::ColorDefault) -////////////////////////////////// +/////////////////////////////////////// class VisualShaderNodeCubeMapUniform : public VisualShaderNode { GDCLASS(VisualShaderNodeCubeMapUniform, VisualShaderNode) @@ -888,4 +1440,48 @@ public: VisualShaderNodeCubeMapUniform(); }; +/////////////////////////////////////// +/// IF +/////////////////////////////////////// + +class VisualShaderNodeIf : public VisualShaderNode { + GDCLASS(VisualShaderNodeIf, VisualShaderNode) +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; + + VisualShaderNodeIf(); +}; + +/////////////////////////////////////// +/// SWITCH +/////////////////////////////////////// + +class VisualShaderNodeSwitch : public VisualShaderNode { + GDCLASS(VisualShaderNodeSwitch, VisualShaderNode) +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; + + VisualShaderNodeSwitch(); +}; + #endif // VISUAL_SHADER_NODES_H diff --git a/servers/arvr/arvr_interface.cpp b/servers/arvr/arvr_interface.cpp index 3e59daff6c..686ad0ba9b 100644 --- a/servers/arvr/arvr_interface.cpp +++ b/servers/arvr/arvr_interface.cpp @@ -123,6 +123,11 @@ ARVRInterface::ARVRInterface() { ARVRInterface::~ARVRInterface(){}; +// optional render to external texture which enhances performance on those platforms that require us to submit our end result into special textures. +unsigned int ARVRInterface::get_external_texture_for_eye(ARVRInterface::Eyes p_eye) { + return 0; +}; + /** these will only be implemented on AR interfaces, so we want dummies for VR **/ bool ARVRInterface::get_anchor_detection_is_enabled() const { return false; diff --git a/servers/arvr/arvr_interface.h b/servers/arvr/arvr_interface.h index 6908f3006a..8459a82388 100644 --- a/servers/arvr/arvr_interface.h +++ b/servers/arvr/arvr_interface.h @@ -108,6 +108,7 @@ public: virtual bool is_stereo() = 0; /* returns true if this interface requires stereo rendering (for VR HMDs) or mono rendering (for mobile AR) */ virtual Transform get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform) = 0; /* get each eyes camera transform, also implement EYE_MONO */ virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) = 0; /* get each eyes projection matrix */ + virtual unsigned int get_external_texture_for_eye(ARVRInterface::Eyes p_eye); /* if applicable return external texture to render to */ virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) = 0; /* output the left or right eye */ virtual void process() = 0; diff --git a/servers/arvr/arvr_positional_tracker.cpp b/servers/arvr/arvr_positional_tracker.cpp index b96e9596f3..aabe617a8a 100644 --- a/servers/arvr/arvr_positional_tracker.cpp +++ b/servers/arvr/arvr_positional_tracker.cpp @@ -46,6 +46,7 @@ void ARVRPositionalTracker::_bind_methods() { ClassDB::bind_method(D_METHOD("get_position"), &ARVRPositionalTracker::get_position); ClassDB::bind_method(D_METHOD("get_hand"), &ARVRPositionalTracker::get_hand); ClassDB::bind_method(D_METHOD("get_transform", "adjust_by_reference_frame"), &ARVRPositionalTracker::get_transform); + ClassDB::bind_method(D_METHOD("get_mesh"), &ARVRPositionalTracker::get_mesh); // these functions we don't want to expose to normal users but do need to be callable from GDNative ClassDB::bind_method(D_METHOD("_set_type", "type"), &ARVRPositionalTracker::set_type); @@ -53,7 +54,7 @@ void ARVRPositionalTracker::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_joy_id", "joy_id"), &ARVRPositionalTracker::set_joy_id); ClassDB::bind_method(D_METHOD("_set_orientation", "orientation"), &ARVRPositionalTracker::set_orientation); ClassDB::bind_method(D_METHOD("_set_rw_position", "rw_position"), &ARVRPositionalTracker::set_rw_position); - + ClassDB::bind_method(D_METHOD("_set_mesh", "mesh"), &ARVRPositionalTracker::set_mesh); ClassDB::bind_method(D_METHOD("get_rumble"), &ARVRPositionalTracker::get_rumble); ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &ARVRPositionalTracker::set_rumble); @@ -154,6 +155,18 @@ Vector3 ARVRPositionalTracker::get_rw_position() const { return rw_position; }; +void ARVRPositionalTracker::set_mesh(const Ref<Mesh> &p_mesh) { + _THREAD_SAFE_METHOD_ + + mesh = p_mesh; +}; + +Ref<Mesh> ARVRPositionalTracker::get_mesh() const { + _THREAD_SAFE_METHOD_ + + return mesh; +}; + ARVRPositionalTracker::TrackerHand ARVRPositionalTracker::get_hand() const { return hand; }; diff --git a/servers/arvr/arvr_positional_tracker.h b/servers/arvr/arvr_positional_tracker.h index 7cb9486f59..0d6a69540f 100644 --- a/servers/arvr/arvr_positional_tracker.h +++ b/servers/arvr/arvr_positional_tracker.h @@ -32,6 +32,7 @@ #define ARVR_POSITIONAL_TRACKER_H #include "core/os/thread_safe.h" +#include "scene/resources/mesh.h" #include "servers/arvr_server.h" /** @@ -40,9 +41,6 @@ The positional tracker object as an object that represents the position and orientation of a tracked object like a controller or headset. An AR/VR Interface will registered the trackers it manages with our AR/VR server and update its position and orientation. This is where potentially additional AR/VR interfaces may be active as there are AR/VR SDKs that solely deal with positional tracking. - - @TODO: - - create subclass of spatial node that uses one of our positional trackers to automatically determine its position */ class ARVRPositionalTracker : public Object { @@ -65,6 +63,7 @@ private: Basis orientation; // our orientation bool tracks_position; // do we track position? Vector3 rw_position; // our position "in the real world, so without world_scale applied" + Ref<Mesh> mesh; // when available, a mesh that can be used to render this tracker TrackerHand hand; // if known, the hand this tracker is held in real_t rumble; // rumble strength, 0.0 is off, 1.0 is maximum, note that we only record here, arvr_interface is responsible for execution @@ -91,6 +90,8 @@ public: void set_hand(const ARVRPositionalTracker::TrackerHand p_hand); real_t get_rumble() const; void set_rumble(real_t p_rumble); + void set_mesh(const Ref<Mesh> &p_mesh); + Ref<Mesh> get_mesh() const; Transform get_transform(bool p_adjust_by_reference_frame) const; diff --git a/servers/audio/effects/audio_effect_spectrum_analyzer.cpp b/servers/audio/effects/audio_effect_spectrum_analyzer.cpp new file mode 100644 index 0000000000..01a52aa01f --- /dev/null +++ b/servers/audio/effects/audio_effect_spectrum_analyzer.cpp @@ -0,0 +1,256 @@ +#include "audio_effect_spectrum_analyzer.h" +#include "servers/audio_server.h" + +static void smbFft(float *fftBuffer, long fftFrameSize, long sign) +/* + FFT routine, (C)1996 S.M.Bernsee. Sign = -1 is FFT, 1 is iFFT (inverse) + Fills fftBuffer[0...2*fftFrameSize-1] with the Fourier transform of the + time domain data in fftBuffer[0...2*fftFrameSize-1]. The FFT array takes + and returns the cosine and sine parts in an interleaved manner, ie. + fftBuffer[0] = cosPart[0], fftBuffer[1] = sinPart[0], asf. fftFrameSize + must be a power of 2. It expects a complex input signal (see footnote 2), + ie. when working with 'common' audio signals our input signal has to be + passed as {in[0],0.,in[1],0.,in[2],0.,...} asf. In that case, the transform + of the frequencies of interest is in fftBuffer[0...fftFrameSize]. +*/ +{ + float wr, wi, arg, *p1, *p2, temp; + float tr, ti, ur, ui, *p1r, *p1i, *p2r, *p2i; + long i, bitm, j, le, le2, k; + + for (i = 2; i < 2 * fftFrameSize - 2; i += 2) { + for (bitm = 2, j = 0; bitm < 2 * fftFrameSize; bitm <<= 1) { + if (i & bitm) j++; + j <<= 1; + } + if (i < j) { + p1 = fftBuffer + i; + p2 = fftBuffer + j; + temp = *p1; + *(p1++) = *p2; + *(p2++) = temp; + temp = *p1; + *p1 = *p2; + *p2 = temp; + } + } + for (k = 0, le = 2; k < (long)(log((double)fftFrameSize) / log(2.) + .5); k++) { + le <<= 1; + le2 = le >> 1; + ur = 1.0; + ui = 0.0; + arg = Math_PI / (le2 >> 1); + wr = cos(arg); + wi = sign * sin(arg); + for (j = 0; j < le2; j += 2) { + p1r = fftBuffer + j; + p1i = p1r + 1; + p2r = p1r + le2; + p2i = p2r + 1; + for (i = j; i < 2 * fftFrameSize; i += le) { + tr = *p2r * ur - *p2i * ui; + ti = *p2r * ui + *p2i * ur; + *p2r = *p1r - tr; + *p2i = *p1i - ti; + *p1r += tr; + *p1i += ti; + p1r += le; + p1i += le; + p2r += le; + p2i += le; + } + tr = ur * wr - ui * wi; + ui = ur * wi + ui * wr; + ur = tr; + } + } +} +void AudioEffectSpectrumAnalyzerInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) { + + uint64_t time = OS::get_singleton()->get_ticks_usec(); + + //copy everything over first, since this only really does capture + for (int i = 0; i < p_frame_count; i++) { + p_dst_frames[i] = p_src_frames[i]; + } + + //capture spectrum + while (p_frame_count) { + int to_fill = fft_size * 2 - temporal_fft_pos; + to_fill = MIN(to_fill, p_frame_count); + + float *fftw = temporal_fft.ptrw(); + for (int i = 0; i < to_fill; i++) { //left and right buffers + fftw[(i + temporal_fft_pos) * 2] = p_src_frames[i].l; + fftw[(i + temporal_fft_pos) * 2 + 1] = 0; + fftw[(i + temporal_fft_pos + fft_size * 2) * 2] = p_src_frames[i].r; + fftw[(i + temporal_fft_pos + fft_size * 2) * 2 + 1] = 0; + } + + p_src_frames += to_fill; + temporal_fft_pos += to_fill; + p_frame_count -= to_fill; + + if (temporal_fft_pos == fft_size * 2) { + //time to do a FFT + smbFft(fftw, fft_size * 2, -1); + smbFft(fftw + fft_size * 4, fft_size * 2, -1); + int next = (fft_pos + 1) % fft_count; + + AudioFrame *hw = (AudioFrame *)fft_history[next].ptr(); //do not use write, avoid cow + + for (int i = 0; i < fft_size; i++) { + //abs(vec)/fft_size normalizes each frequency + float window = 1.0; //-.5 * Math::cos(2. * Math_PI * (double)i / (double)fft_size) + .5; + hw[i].l = window * Vector2(fftw[i * 2], fftw[i * 2 + 1]).length() / float(fft_size); + hw[i].r = window * Vector2(fftw[fft_size * 4 + i * 2], fftw[fft_size * 4 + i * 2 + 1]).length() / float(fft_size); + } + + fft_pos = next; //swap + temporal_fft_pos = 0; + } + } + + //determine time of capture + double remainer_sec = (temporal_fft_pos / mix_rate); //substract remainder from mix time + last_fft_time = time - uint64_t(remainer_sec * 1000000.0); +} + +void AudioEffectSpectrumAnalyzerInstance::_bind_methods() { + + ClassDB::bind_method(D_METHOD("get_magnitude_for_frequency_range", "from_hz", "to_hz", "mode"), &AudioEffectSpectrumAnalyzerInstance::get_magnitude_for_frequency_range, DEFVAL(MAGNITUDE_MAX)); + BIND_ENUM_CONSTANT(MAGNITUDE_AVERAGE); + BIND_ENUM_CONSTANT(MAGNITUDE_MAX); +} + +Vector2 AudioEffectSpectrumAnalyzerInstance::get_magnitude_for_frequency_range(float p_begin, float p_end, MagnitudeMode p_mode) const { + + if (last_fft_time == 0) { + return Vector2(); + } + uint64_t time = OS::get_singleton()->get_ticks_usec(); + float diff = double(time - last_fft_time) / 1000000.0 + base->get_tap_back_pos(); + diff -= AudioServer::get_singleton()->get_output_delay(); + float fft_time_size = float(fft_size) / mix_rate; + + int fft_index = fft_pos; + + while (diff > fft_time_size) { + diff -= fft_time_size; + fft_index -= 1; + if (fft_index < 0) { + fft_index = fft_count - 1; + } + } + + int begin_pos = p_begin * fft_size / (mix_rate * 0.5); + int end_pos = p_end * fft_size / (mix_rate * 0.5); + + begin_pos = CLAMP(begin_pos, 0, fft_size - 1); + end_pos = CLAMP(end_pos, 0, fft_size - 1); + + if (begin_pos > end_pos) { + SWAP(begin_pos, end_pos); + } + const AudioFrame *r = fft_history[fft_index].ptr(); + + if (p_mode == MAGNITUDE_AVERAGE) { + Vector2 avg; + + for (int i = begin_pos; i <= end_pos; i++) { + avg += Vector2(r[i]); + } + + avg /= float(end_pos - begin_pos + 1); + + return avg; + } else { + + Vector2 max; + + for (int i = begin_pos; i <= end_pos; i++) { + max.x = MAX(max.x, r[i].l); + max.y = MAX(max.x, r[i].r); + } + + return max; + } +} + +Ref<AudioEffectInstance> AudioEffectSpectrumAnalyzer::instance() { + + Ref<AudioEffectSpectrumAnalyzerInstance> ins; + ins.instance(); + ins->base = Ref<AudioEffectSpectrumAnalyzer>(this); + static const int fft_sizes[FFT_SIZE_MAX] = { 256, 512, 1024, 2048, 4096 }; + ins->fft_size = fft_sizes[fft_size]; + ins->mix_rate = AudioServer::get_singleton()->get_mix_rate(); + ins->fft_count = (buffer_length / (float(ins->fft_size) / ins->mix_rate)) + 1; + ins->fft_pos = 0; + ins->last_fft_time = 0; + ins->fft_history.resize(ins->fft_count); + ins->temporal_fft.resize(ins->fft_size * 8); //x2 stereo, x2 amount of samples for freqs, x2 for input + ins->temporal_fft_pos = 0; + for (int i = 0; i < ins->fft_count; i++) { + ins->fft_history.write[i].resize(ins->fft_size); //only magnitude matters + for (int j = 0; j < ins->fft_size; j++) { + ins->fft_history.write[i].write[j] = AudioFrame(0, 0); + } + } + return ins; +} + +void AudioEffectSpectrumAnalyzer::set_buffer_length(float p_volume) { + buffer_length = p_volume; +} + +float AudioEffectSpectrumAnalyzer::get_buffer_length() const { + + return buffer_length; +} + +void AudioEffectSpectrumAnalyzer::set_tap_back_pos(float p_seconds) { + tapback_pos = p_seconds; +} + +float AudioEffectSpectrumAnalyzer::get_tap_back_pos() const { + return tapback_pos; +} + +void AudioEffectSpectrumAnalyzer::set_fft_size(FFT_Size p_fft_size) { + ERR_FAIL_INDEX(p_fft_size, FFT_SIZE_MAX); + fft_size = p_fft_size; +} + +AudioEffectSpectrumAnalyzer::FFT_Size AudioEffectSpectrumAnalyzer::get_fft_size() const { + return fft_size; +} + +void AudioEffectSpectrumAnalyzer::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_buffer_length", "seconds"), &AudioEffectSpectrumAnalyzer::set_buffer_length); + ClassDB::bind_method(D_METHOD("get_buffer_length"), &AudioEffectSpectrumAnalyzer::get_buffer_length); + + ClassDB::bind_method(D_METHOD("set_tap_back_pos", "seconds"), &AudioEffectSpectrumAnalyzer::set_tap_back_pos); + ClassDB::bind_method(D_METHOD("get_tap_back_pos"), &AudioEffectSpectrumAnalyzer::get_tap_back_pos); + + ClassDB::bind_method(D_METHOD("set_fft_size", "size"), &AudioEffectSpectrumAnalyzer::set_fft_size); + ClassDB::bind_method(D_METHOD("get_fft_size"), &AudioEffectSpectrumAnalyzer::get_fft_size); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "buffer_length", PROPERTY_HINT_RANGE, "0.1,4,0.1"), "set_buffer_length", "get_buffer_length"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "tap_back_pos", PROPERTY_HINT_RANGE, "0.1,4,0.1"), "set_tap_back_pos", "get_tap_back_pos"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fft_size", PROPERTY_HINT_ENUM, "256,512,1024,2048,4096"), "set_fft_size", "get_fft_size"); + + BIND_ENUM_CONSTANT(FFT_SIZE_256); + BIND_ENUM_CONSTANT(FFT_SIZE_512); + BIND_ENUM_CONSTANT(FFT_SIZE_1024); + BIND_ENUM_CONSTANT(FFT_SIZE_2048); + BIND_ENUM_CONSTANT(FFT_SIZE_4096); + BIND_ENUM_CONSTANT(FFT_SIZE_MAX); +} + +AudioEffectSpectrumAnalyzer::AudioEffectSpectrumAnalyzer() { + buffer_length = 2; + tapback_pos = 0.01; + fft_size = FFT_SIZE_1024; +} diff --git a/servers/audio/effects/audio_effect_spectrum_analyzer.h b/servers/audio/effects/audio_effect_spectrum_analyzer.h new file mode 100644 index 0000000000..0534426da3 --- /dev/null +++ b/servers/audio/effects/audio_effect_spectrum_analyzer.h @@ -0,0 +1,76 @@ +#ifndef AUDIO_EFFECT_SPECTRUM_ANALYZER_H +#define AUDIO_EFFECT_SPECTRUM_ANALYZER_H + +#include "servers/audio/audio_effect.h" + +class AudioEffectSpectrumAnalyzer; + +class AudioEffectSpectrumAnalyzerInstance : public AudioEffectInstance { + GDCLASS(AudioEffectSpectrumAnalyzerInstance, AudioEffectInstance) + +public: + enum MagnitudeMode { + MAGNITUDE_AVERAGE, + MAGNITUDE_MAX, + }; + +private: + friend class AudioEffectSpectrumAnalyzer; + Ref<AudioEffectSpectrumAnalyzer> base; + + Vector<Vector<AudioFrame> > fft_history; + Vector<float> temporal_fft; + int temporal_fft_pos; + int fft_size; + int fft_count; + int fft_pos; + float mix_rate; + uint64_t last_fft_time; + +protected: + static void _bind_methods(); + +public: + virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count); + Vector2 get_magnitude_for_frequency_range(float p_begin, float p_end, MagnitudeMode p_mode = MAGNITUDE_MAX) const; +}; + +VARIANT_ENUM_CAST(AudioEffectSpectrumAnalyzerInstance::MagnitudeMode) + +class AudioEffectSpectrumAnalyzer : public AudioEffect { + GDCLASS(AudioEffectSpectrumAnalyzer, AudioEffect) +public: + enum FFT_Size { + FFT_SIZE_256, + FFT_SIZE_512, + FFT_SIZE_1024, + FFT_SIZE_2048, + FFT_SIZE_4096, + FFT_SIZE_MAX + }; + +public: + friend class AudioEffectSpectrumAnalyzerInstance; + float buffer_length; + float tapback_pos; + FFT_Size fft_size; + +protected: + static void _bind_methods(); + +public: + Ref<AudioEffectInstance> instance(); + void set_buffer_length(float p_seconds); + float get_buffer_length() const; + void set_tap_back_pos(float p_seconds); + float get_tap_back_pos() const; + + void set_fft_size(FFT_Size); + FFT_Size get_fft_size() const; + + AudioEffectSpectrumAnalyzer(); +}; + +VARIANT_ENUM_CAST(AudioEffectSpectrumAnalyzer::FFT_Size); + +#endif // AUDIO_EFFECT_SPECTRUM_ANALYZER_H diff --git a/servers/audio/effects/audio_stream_generator.cpp b/servers/audio/effects/audio_stream_generator.cpp new file mode 100644 index 0000000000..f4a66b5643 --- /dev/null +++ b/servers/audio/effects/audio_stream_generator.cpp @@ -0,0 +1,182 @@ +#include "audio_stream_generator.h" + +void AudioStreamGenerator::set_mix_rate(float p_mix_rate) { + mix_rate = p_mix_rate; +} + +float AudioStreamGenerator::get_mix_rate() const { + + return mix_rate; +} + +void AudioStreamGenerator::set_buffer_length(float p_seconds) { + + buffer_len = p_seconds; +} +float AudioStreamGenerator::get_buffer_length() const { + + return buffer_len; +} + +Ref<AudioStreamPlayback> AudioStreamGenerator::instance_playback() { + + Ref<AudioStreamGeneratorPlayback> playback; + playback.instance(); + playback->generator = this; + int target_buffer_size = mix_rate * buffer_len; + playback->buffer.resize(nearest_shift(target_buffer_size)); + playback->buffer.clear(); + return playback; +} +String AudioStreamGenerator::get_stream_name() const { + + return "UserFeed"; +} + +float AudioStreamGenerator::get_length() const { + return 0; +} + +void AudioStreamGenerator::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_mix_rate", "hz"), &AudioStreamGenerator::set_mix_rate); + ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamGenerator::get_mix_rate); + + ClassDB::bind_method(D_METHOD("set_buffer_length", "seconds"), &AudioStreamGenerator::set_buffer_length); + ClassDB::bind_method(D_METHOD("get_buffer_length"), &AudioStreamGenerator::get_buffer_length); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "mix_rate", PROPERTY_HINT_RANGE, "20,192000,1"), "set_mix_rate", "get_mix_rate"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "buffer_length", PROPERTY_HINT_RANGE, "0.01,10,0.01"), "set_buffer_length", "get_buffer_length"); +} + +AudioStreamGenerator::AudioStreamGenerator() { + mix_rate = 44100; + buffer_len = 0.5; +} + +//////////////// + +bool AudioStreamGeneratorPlayback::push_frame(const Vector2 &p_frame) { + if (buffer.space_left() < 1) { + return false; + } + + AudioFrame f = p_frame; + + buffer.write(&f, 1); + return true; +} + +bool AudioStreamGeneratorPlayback::can_push_buffer(int p_frames) const { + return buffer.space_left() >= p_frames; +} +bool AudioStreamGeneratorPlayback::push_buffer(const PoolVector2Array &p_frames) { + + int to_write = p_frames.size(); + if (buffer.space_left() < to_write) { + return false; + } + + PoolVector2Array::Read r = p_frames.read(); + if (sizeof(real_t) == 4) { + //write directly + buffer.write((const AudioFrame *)r.ptr(), to_write); + } else { + //convert from double + AudioFrame buf[2048]; + int ofs = 0; + while (to_write) { + + int w = MIN(to_write, 2048); + for (int i = 0; i < w; i++) { + buf[i] = r[i + ofs]; + } + buffer.write(buf, w); + ofs += w; + to_write -= w; + } + } + return true; +} + +int AudioStreamGeneratorPlayback::get_frames_available() const { + return buffer.space_left(); +} + +int AudioStreamGeneratorPlayback::get_skips() const { + return skips; +} + +void AudioStreamGeneratorPlayback::clear_buffer() { + ERR_FAIL_COND(active); + buffer.clear(); + mixed = 0; +} + +void AudioStreamGeneratorPlayback::_mix_internal(AudioFrame *p_buffer, int p_frames) { + + int read_amount = buffer.data_left(); + if (p_frames < read_amount) { + read_amount = p_frames; + } + + buffer.read(p_buffer, read_amount); + + if (read_amount < p_frames) { + //skipped, not ideal + for (int i = read_amount; i < p_frames; i++) { + p_buffer[i] = AudioFrame(0, 0); + } + + skips++; + } + + mixed += p_frames / generator->get_mix_rate(); +} +float AudioStreamGeneratorPlayback::get_stream_sampling_rate() { + return generator->get_mix_rate(); +} + +void AudioStreamGeneratorPlayback::start(float p_from_pos) { + + if (mixed == 0.0) { + _begin_resample(); + } + skips = 0; + active = true; + mixed = 0.0; +} + +void AudioStreamGeneratorPlayback::stop() { + active = false; +} +bool AudioStreamGeneratorPlayback::is_playing() const { + + return active; //always playing, can't be stopped +} + +int AudioStreamGeneratorPlayback::get_loop_count() const { + return 0; +} + +float AudioStreamGeneratorPlayback::get_playback_position() const { + return mixed; +} +void AudioStreamGeneratorPlayback::seek(float p_time) { + //no seek possible +} + +void AudioStreamGeneratorPlayback::_bind_methods() { + ClassDB::bind_method(D_METHOD("push_frame", "frame"), &AudioStreamGeneratorPlayback::push_frame); + ClassDB::bind_method(D_METHOD("can_push_buffer", "amount"), &AudioStreamGeneratorPlayback::can_push_buffer); + ClassDB::bind_method(D_METHOD("push_buffer", "frames"), &AudioStreamGeneratorPlayback::push_buffer); + ClassDB::bind_method(D_METHOD("get_frames_available"), &AudioStreamGeneratorPlayback::get_frames_available); + ClassDB::bind_method(D_METHOD("get_skips"), &AudioStreamGeneratorPlayback::get_skips); + ClassDB::bind_method(D_METHOD("clear_buffer"), &AudioStreamGeneratorPlayback::clear_buffer); +} + +AudioStreamGeneratorPlayback::AudioStreamGeneratorPlayback() { + generator = NULL; + skips = 0; + active = false; + mixed = 0; +} diff --git a/servers/audio/effects/audio_stream_generator.h b/servers/audio/effects/audio_stream_generator.h new file mode 100644 index 0000000000..2082682907 --- /dev/null +++ b/servers/audio/effects/audio_stream_generator.h @@ -0,0 +1,66 @@ +#ifndef AUDIO_STREAM_USER_FED_H +#define AUDIO_STREAM_USER_FED_H + +#include "core/ring_buffer.h" +#include "servers/audio/audio_stream.h" + +class AudioStreamGenerator : public AudioStream { + GDCLASS(AudioStreamGenerator, AudioStream) + + float mix_rate; + float buffer_len; + +protected: + static void _bind_methods(); + +public: + void set_mix_rate(float p_mix_rate); + float get_mix_rate() const; + + void set_buffer_length(float p_seconds); + float get_buffer_length() const; + + virtual Ref<AudioStreamPlayback> instance_playback(); + virtual String get_stream_name() const; + + virtual float get_length() const; + AudioStreamGenerator(); +}; + +class AudioStreamGeneratorPlayback : public AudioStreamPlaybackResampled { + + GDCLASS(AudioStreamGeneratorPlayback, AudioStreamPlaybackResampled) + friend class AudioStreamGenerator; + RingBuffer<AudioFrame> buffer; + int skips; + bool active; + float mixed; + AudioStreamGenerator *generator; + +protected: + virtual void _mix_internal(AudioFrame *p_buffer, int p_frames); + virtual float get_stream_sampling_rate(); + + static void _bind_methods(); + +public: + virtual void start(float p_from_pos = 0.0); + virtual void stop(); + virtual bool is_playing() const; + + virtual int get_loop_count() const; //times it looped + + virtual float get_playback_position() const; + virtual void seek(float p_time); + + bool push_frame(const Vector2 &p_frame); + bool can_push_buffer(int p_frames) const; + bool push_buffer(const PoolVector2Array &p_frames); + int get_frames_available() const; + int get_skips() const; + + void clear_buffer(); + + AudioStreamGeneratorPlayback(); +}; +#endif // AUDIO_STREAM_USER_FED_H diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 8c092a02a2..c83c3029f3 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -876,6 +876,15 @@ int AudioServer::get_bus_effect_count(int p_bus) { return buses[p_bus]->effects.size(); } +Ref<AudioEffectInstance> AudioServer::get_bus_effect_instance(int p_bus, int p_effect, int p_channel) { + + ERR_FAIL_INDEX_V(p_bus, buses.size(), Ref<AudioEffectInstance>()); + ERR_FAIL_INDEX_V(p_effect, buses[p_bus]->effects.size(), Ref<AudioEffectInstance>()); + ERR_FAIL_INDEX_V(p_channel, buses[p_bus]->channels.size(), Ref<AudioEffectInstance>()); + + return buses[p_bus]->channels[p_channel].effect_instances[p_effect]; +} + Ref<AudioEffect> AudioServer::get_bus_effect(int p_bus, int p_effect) { ERR_FAIL_INDEX_V(p_bus, buses.size(), Ref<AudioEffect>()); @@ -1045,8 +1054,10 @@ void AudioServer::update() { void AudioServer::load_default_bus_layout() { - if (ResourceLoader::exists("res://default_bus_layout.tres")) { - Ref<AudioBusLayout> default_layout = ResourceLoader::load("res://default_bus_layout.tres"); + String layout_path = ProjectSettings::get_singleton()->get("audio/default_bus_layout"); + + if (ResourceLoader::exists(layout_path)) { + Ref<AudioBusLayout> default_layout = ResourceLoader::load(layout_path); if (default_layout.is_valid()) { set_bus_layout(default_layout); } @@ -1328,6 +1339,7 @@ void AudioServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bus_effect_count", "bus_idx"), &AudioServer::get_bus_effect_count); ClassDB::bind_method(D_METHOD("get_bus_effect", "bus_idx", "effect_idx"), &AudioServer::get_bus_effect); + ClassDB::bind_method(D_METHOD("get_bus_effect_instance", "bus_idx", "effect_idx", "channel"), &AudioServer::get_bus_effect_instance, DEFVAL(0)); ClassDB::bind_method(D_METHOD("swap_bus_effects", "bus_idx", "effect_idx", "by_effect_idx"), &AudioServer::swap_bus_effects); ClassDB::bind_method(D_METHOD("set_bus_effect_enabled", "bus_idx", "effect_idx", "enabled"), &AudioServer::set_bus_effect_enabled); diff --git a/servers/audio_server.h b/servers/audio_server.h index 6bd8093c76..3b69cb1b88 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -322,6 +322,7 @@ public: int get_bus_effect_count(int p_bus); Ref<AudioEffect> get_bus_effect(int p_bus, int p_effect); + Ref<AudioEffectInstance> get_bus_effect_instance(int p_bus, int p_effect, int p_channel = 0); void swap_bus_effects(int p_bus, int p_effect, int p_by_effect); diff --git a/servers/physics/body_sw.cpp b/servers/physics/body_sw.cpp index f0fbbafe1c..b4c3670a7b 100644 --- a/servers/physics/body_sw.cpp +++ b/servers/physics/body_sw.cpp @@ -196,7 +196,8 @@ void BodySW::set_param(PhysicsServer::BodyParameter p_param, real_t p_value) { angular_damp = p_value; } break; - default: {} + default: { + } } } @@ -226,7 +227,8 @@ real_t BodySW::get_param(PhysicsServer::BodyParameter p_param) const { return angular_damp; } break; - default: {} + default: { + } } return 0; @@ -474,7 +476,8 @@ void BodySW::integrate_forces(real_t p_step) { _compute_area_gravity_and_dampenings(aa[i].area); stopped = mode == PhysicsServer::AREA_SPACE_OVERRIDE_REPLACE; } break; - default: {} + default: { + } } } } diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp index 60bcb2e28e..60bbcef4b6 100644 --- a/servers/physics_2d/body_2d_sw.cpp +++ b/servers/physics_2d/body_2d_sw.cpp @@ -174,7 +174,8 @@ void Body2DSW::set_param(Physics2DServer::BodyParameter p_param, real_t p_value) angular_damp = p_value; } break; - default: {} + default: { + } } } @@ -206,7 +207,8 @@ real_t Body2DSW::get_param(Physics2DServer::BodyParameter p_param) const { return angular_damp; } break; - default: {} + default: { + } } return 0; @@ -443,7 +445,8 @@ void Body2DSW::integrate_forces(real_t p_step) { _compute_area_gravity_and_dampenings(aa[i].area); stopped = mode == Physics2DServer::AREA_SPACE_OVERRIDE_REPLACE; } break; - default: {} + default: { + } } } } diff --git a/servers/physics_2d/joints_2d_sw.cpp b/servers/physics_2d/joints_2d_sw.cpp index 5c2242c4c5..954b0fa3ea 100644 --- a/servers/physics_2d/joints_2d_sw.cpp +++ b/servers/physics_2d/joints_2d_sw.cpp @@ -299,9 +299,7 @@ bool GrooveJoint2DSW::setup(real_t p_step) { Vector2 delta = (B->get_transform().get_origin() + rB) - (A->get_transform().get_origin() + rA); - // FIXME: We used to do this assignment and then override it with 0.001 right after. Investigate why. - //real_t _b = get_bias(); - real_t _b = 0.001; + real_t _b = get_bias(); gbias = (delta * -(_b == 0 ? space->get_constraint_bias() : _b) * (1.0 / p_step)).clamped(get_max_bias()); // apply accumulated impulse diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h index 1de9c7df93..0ba8a6605d 100644 --- a/servers/physics_2d_server.h +++ b/servers/physics_2d_server.h @@ -653,6 +653,12 @@ class Physics2DServerManager { ClassInfo(const ClassInfo &p_ci) : name(p_ci.name), create_callback(p_ci.create_callback) {} + + ClassInfo operator=(const ClassInfo &p_ci) { + name = p_ci.name; + create_callback = p_ci.create_callback; + return *this; + } }; static Vector<ClassInfo> physics_2d_servers; diff --git a/servers/physics_server.h b/servers/physics_server.h index c71bb01943..9895ef2455 100644 --- a/servers/physics_server.h +++ b/servers/physics_server.h @@ -794,6 +794,12 @@ class PhysicsServerManager { ClassInfo(const ClassInfo &p_ci) : name(p_ci.name), create_callback(p_ci.create_callback) {} + + ClassInfo operator=(const ClassInfo &p_ci) { + name = p_ci.name; + create_callback = p_ci.create_callback; + return *this; + } }; static Vector<ClassInfo> physics_servers; diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 0cc1cc119c..f3394019f5 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -50,7 +50,9 @@ #include "audio/effects/audio_effect_pitch_shift.h" #include "audio/effects/audio_effect_record.h" #include "audio/effects/audio_effect_reverb.h" +#include "audio/effects/audio_effect_spectrum_analyzer.h" #include "audio/effects/audio_effect_stereo_enhance.h" +#include "audio/effects/audio_stream_generator.h" #include "audio_server.h" #include "core/script_debugger_remote.h" #include "physics/physics_server_sw.h" @@ -120,13 +122,18 @@ void register_server_types() { ClassDB::register_virtual_class<AudioStream>(); ClassDB::register_virtual_class<AudioStreamPlayback>(); + ClassDB::register_virtual_class<AudioStreamPlaybackResampled>(); ClassDB::register_class<AudioStreamMicrophone>(); ClassDB::register_class<AudioStreamRandomPitch>(); ClassDB::register_virtual_class<AudioEffect>(); + ClassDB::register_virtual_class<AudioEffectInstance>(); ClassDB::register_class<AudioEffectEQ>(); ClassDB::register_class<AudioEffectFilter>(); ClassDB::register_class<AudioBusLayout>(); + ClassDB::register_class<AudioStreamGenerator>(); + ClassDB::register_virtual_class<AudioStreamGeneratorPlayback>(); + { //audio effects ClassDB::register_class<AudioEffectAmplify>(); @@ -156,7 +163,10 @@ void register_server_types() { ClassDB::register_class<AudioEffectLimiter>(); ClassDB::register_class<AudioEffectPitchShift>(); ClassDB::register_class<AudioEffectPhaser>(); + ClassDB::register_class<AudioEffectRecord>(); + ClassDB::register_class<AudioEffectSpectrumAnalyzer>(); + ClassDB::register_virtual_class<AudioEffectSpectrumAnalyzerInstance>(); } ClassDB::register_virtual_class<Physics2DDirectBodyState>(); diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index dd54698471..b22e26f903 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -554,6 +554,7 @@ public: virtual RID render_target_create() = 0; virtual void render_target_set_size(RID p_render_target, int p_width, int p_height) = 0; virtual RID render_target_get_texture(RID p_render_target) const = 0; + virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) = 0; virtual void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) = 0; virtual bool render_target_was_used(RID p_render_target) = 0; virtual void render_target_clear_used(RID p_render_target) = 0; @@ -788,6 +789,8 @@ public: RID mesh; RID texture; RID normal_map; + Transform2D transform; + Color modulate; CommandMesh() { type = TYPE_MESH; } }; @@ -1101,7 +1104,7 @@ public: virtual void initialize() = 0; virtual void begin_frame(double frame_step) = 0; virtual void set_current_render_target(RID p_render_target) = 0; - virtual void restore_render_target() = 0; + virtual void restore_render_target(bool p_3d) = 0; virtual void clear_render_target(const Color &p_color) = 0; virtual void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0) = 0; virtual void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) = 0; diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 1f9d263354..33714a79b2 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -3620,7 +3620,8 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha case TYPE_FLOAT: { nv.real = -cn->values[i].real; } break; - default: {} + default: { + } } values.push_back(nv); @@ -4822,7 +4823,8 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct case TYPE_MAT2: limit = 2; break; case TYPE_MAT3: limit = 3; break; case TYPE_MAT4: limit = 4; break; - default: {} + default: { + } } for (int i = 0; i < limit; i++) { diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index 85dcaa6b03..791d59038a 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -51,20 +51,23 @@ void VisualServerCanvas::_render_canvas_item_tree(Item *p_canvas_item, const Tra } } -void _collect_ysort_children(VisualServerCanvas::Item *p_canvas_item, Transform2D p_transform, VisualServerCanvas::Item **r_items, int &r_index) { +void _collect_ysort_children(VisualServerCanvas::Item *p_canvas_item, Transform2D p_transform, VisualServerCanvas::Item *p_material_owner, VisualServerCanvas::Item **r_items, int &r_index) { int child_item_count = p_canvas_item->child_items.size(); VisualServerCanvas::Item **child_items = p_canvas_item->child_items.ptrw(); for (int i = 0; i < child_item_count; i++) { - if (r_items) { - r_items[r_index] = child_items[i]; - child_items[i]->ysort_xform = p_transform; - child_items[i]->ysort_pos = p_transform.xform(child_items[i]->xform.elements[2]); - } + if (child_items[i]->visible) { + if (r_items) { + r_items[r_index] = child_items[i]; + child_items[i]->ysort_xform = p_transform; + child_items[i]->ysort_pos = p_transform.xform(child_items[i]->xform.elements[2]); + child_items[i]->material_owner = child_items[i]->use_parent_material ? p_material_owner : NULL; + } - r_index++; + r_index++; - if (child_items[i]->sort_y) - _collect_ysort_children(child_items[i], p_transform * child_items[i]->xform, r_items, r_index); + if (child_items[i]->sort_y) + _collect_ysort_children(child_items[i], p_transform * child_items[i]->xform, child_items[i]->use_parent_material ? p_material_owner : child_items[i], r_items, r_index); + } } } @@ -124,14 +127,14 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor if (ci->ysort_children_count == -1) { ci->ysort_children_count = 0; - _collect_ysort_children(ci, Transform2D(), NULL, ci->ysort_children_count); + _collect_ysort_children(ci, Transform2D(), p_material_owner, NULL, ci->ysort_children_count); } child_item_count = ci->ysort_children_count; child_items = (Item **)alloca(child_item_count * sizeof(Item *)); int i = 0; - _collect_ysort_children(ci, Transform2D(), child_items, i); + _collect_ysort_children(ci, Transform2D(), p_material_owner, child_items, i); SortArray<Item *, ItemPtrSort> sorter; sorter.sort(child_items, child_item_count); @@ -147,7 +150,7 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor if (!child_items[i]->behind || (ci->sort_y && child_items[i]->sort_y)) continue; if (ci->sort_y) { - _render_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner); + _render_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner); } else { _render_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner); } @@ -189,7 +192,7 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor if (child_items[i]->behind || (ci->sort_y && child_items[i]->sort_y)) continue; if (ci->sort_y) { - _render_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner); + _render_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner); } else { _render_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner); } @@ -319,6 +322,19 @@ void VisualServerCanvas::canvas_set_modulate(RID p_canvas, const Color &p_color) canvas->modulate = p_color; } +void VisualServerCanvas::canvas_set_disable_scale(bool p_disable) { + disable_scale = p_disable; +} + +void VisualServerCanvas::canvas_set_parent(RID p_canvas, RID p_parent, float p_scale) { + + Canvas *canvas = canvas_owner.get(p_canvas); + ERR_FAIL_COND(!canvas); + + canvas->parent = p_parent; + canvas->parent_scale = p_scale; +} + RID VisualServerCanvas::canvas_item_create() { Item *canvas_item = memnew(Item); @@ -380,6 +396,10 @@ void VisualServerCanvas::canvas_item_set_visible(RID p_item, bool p_visible) { ERR_FAIL_COND(!canvas_item); canvas_item->visible = p_visible; + + if (canvas_item->parent.is_valid() && canvas_item_owner.owns(canvas_item->parent)) { + _mark_ysort_dirty(canvas_item_owner.get(canvas_item->parent), canvas_item_owner); + } } void VisualServerCanvas::canvas_item_set_light_mask(RID p_item, int p_mask) { @@ -616,7 +636,7 @@ void VisualServerCanvas::canvas_item_add_texture_rect(RID p_item, const Rect2 &p if (p_tile) { rect->flags |= RasterizerCanvas::CANVAS_RECT_TILE; rect->flags |= RasterizerCanvas::CANVAS_RECT_REGION; - rect->source = Rect2(0, 0, p_rect.size.width, p_rect.size.height); + rect->source = Rect2(0, 0, fabsf(p_rect.size.width), fabsf(p_rect.size.height)); } if (p_rect.size.x < 0) { @@ -810,7 +830,7 @@ void VisualServerCanvas::canvas_item_add_set_transform(RID p_item, const Transfo canvas_item->commands.push_back(tr); } -void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture, RID p_normal_map) { +void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, const Transform2D &p_transform, const Color &p_modulate, RID p_texture, RID p_normal_map) { Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); @@ -820,6 +840,8 @@ void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID m->mesh = p_mesh; m->texture = p_texture; m->normal_map = p_normal_map; + m->transform = p_transform; + m->modulate = p_modulate; canvas_item->commands.push_back(m); } @@ -1434,4 +1456,5 @@ bool VisualServerCanvas::free(RID p_rid) { } VisualServerCanvas::VisualServerCanvas() { + disable_scale = false; } diff --git a/servers/visual/visual_server_canvas.h b/servers/visual/visual_server_canvas.h index 7d788cbe14..7691d68639 100644 --- a/servers/visual/visual_server_canvas.h +++ b/servers/visual/visual_server_canvas.h @@ -126,6 +126,8 @@ public: bool children_order_dirty; Vector<ChildItem> child_items; Color modulate; + RID parent; + float parent_scale; int find_item(Item *p_item) { for (int i = 0; i < child_items.size(); i++) { @@ -143,13 +145,16 @@ public: Canvas() { modulate = Color(1, 1, 1, 1); children_order_dirty = true; + parent_scale = 1.0; } }; - RID_Owner<Canvas> canvas_owner; + mutable RID_Owner<Canvas> canvas_owner; RID_Owner<Item> canvas_item_owner; RID_Owner<RasterizerCanvas::Light> canvas_light_owner; + bool disable_scale; + private: void _render_canvas_item_tree(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights); void _render_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RasterizerCanvas::Item **z_list, RasterizerCanvas::Item **z_last_list, Item *p_canvas_clip, Item *p_material_owner); @@ -161,6 +166,8 @@ public: RID canvas_create(); void canvas_set_item_mirroring(RID p_canvas, RID p_item, const Point2 &p_mirroring); void canvas_set_modulate(RID p_canvas, const Color &p_color); + void canvas_set_parent(RID p_canvas, RID p_parent, float p_scale); + void canvas_set_disable_scale(bool p_disable); RID canvas_item_create(); void canvas_item_set_parent(RID p_item, RID p_parent); @@ -190,7 +197,7 @@ public: void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0, RID p_normal_map = RID()); void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), RID p_normal_map = RID(), bool p_antialiased = false); void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID()); - void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID()); + void canvas_item_add_mesh(RID p_item, const RID &p_mesh, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1), RID p_texture = RID(), RID p_normal_map = RID()); void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID()); void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal); void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform); diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index a1204c7573..9c7f474f44 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -569,6 +569,8 @@ public: BIND0R(RID, canvas_create) BIND3(canvas_set_item_mirroring, RID, RID, const Point2 &) BIND2(canvas_set_modulate, RID, const Color &) + BIND3(canvas_set_parent, RID, RID, float) + BIND1(canvas_set_disable_scale, bool) BIND0R(RID, canvas_item_create) BIND2(canvas_item_set_parent, RID, RID) @@ -598,7 +600,7 @@ public: BIND7(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float, RID) BIND7(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, RID, bool) BIND10(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, const Vector<int> &, const Vector<float> &, RID, int, RID) - BIND4(canvas_item_add_mesh, RID, const RID &, RID, RID) + BIND6(canvas_item_add_mesh, RID, const RID &, const Transform2D &, const Color &, RID, RID) BIND4(canvas_item_add_multimesh, RID, RID, RID, RID) BIND4(canvas_item_add_particles, RID, RID, RID, RID) BIND2(canvas_item_add_set_transform, RID, const Transform2D &) diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index 2590e29aef..a5858ab661 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -399,7 +399,8 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) { VSG::scene_render->free(gi_probe->probe_instance); } break; - default: {} + default: { + } } if (instance->base_data) { @@ -476,7 +477,8 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) { gi_probe->probe_instance = VSG::scene_render->gi_probe_instance_create(); } break; - default: {} + default: { + } } VSG::storage->instance_add_dependency(p_base, instance); @@ -524,7 +526,8 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) { gi_probe_update_list.remove(&gi_probe->update_element); } } break; - default: {} + default: { + } } instance->scenario = NULL; @@ -556,7 +559,8 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) { gi_probe_update_list.add(&gi_probe->update_element); } } break; - default: {} + default: { + } } _instance_queue_update(instance, true, true); @@ -671,7 +675,8 @@ void VisualServerScene::instance_set_visible(RID p_instance, bool p_visible) { } } break; - default: {} + default: { + } } } inline bool is_geometry_instance(VisualServer::InstanceType p_type) { @@ -848,7 +853,8 @@ void VisualServerScene::instance_geometry_set_flag(RID p_instance, VS::InstanceF instance->redraw_if_visible = p_enabled; } break; - default: {} + default: { + } } } void VisualServerScene::instance_geometry_set_cast_shadows_setting(RID p_instance, VS::ShadowCastingSetting p_shadow_casting_setting) { @@ -1040,7 +1046,8 @@ void VisualServerScene::_update_instance_aabb(Instance *p_instance) { new_aabb = VSG::storage->lightmap_capture_get_bounds(p_instance->base); } break; - default: {} + default: { + } } // <Zylann> This is why I didn't re-use Instance::aabb to implement custom AABBs diff --git a/servers/visual/visual_server_viewport.cpp b/servers/visual/visual_server_viewport.cpp index 2c6709662f..e7f60c2c1f 100644 --- a/servers/visual/visual_server_viewport.cpp +++ b/servers/visual/visual_server_viewport.cpp @@ -35,6 +35,43 @@ #include "visual_server_globals.h" #include "visual_server_scene.h" +static Transform2D _canvas_get_transform(VisualServerViewport::Viewport *p_viewport, VisualServerCanvas::Canvas *p_canvas, VisualServerViewport::Viewport::CanvasData *p_canvas_data, const Vector2 &p_vp_size) { + + Transform2D xf = p_viewport->global_transform; + + float scale = 1.0; + if (p_viewport->canvas_map.has(p_canvas->parent)) { + xf = xf * p_viewport->canvas_map[p_canvas->parent].transform; + scale = p_canvas->parent_scale; + } + + xf = xf * p_canvas_data->transform; + + if (scale != 1.0 && !VSG::canvas->disable_scale) { + Vector2 pivot = p_vp_size * 0.5; + Transform2D xfpivot; + xfpivot.set_origin(pivot); + Transform2D xfscale; + xfscale.scale(Vector2(scale, scale)); + + xf = xfpivot.affine_inverse() * xf; + xf = xfscale * xf; + xf = xfpivot * xf; + } + + return xf; +} + +void VisualServerViewport::_draw_3d(Viewport *p_viewport, ARVRInterface::Eyes p_eye) { + Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); + + if (p_viewport->use_arvr && arvr_interface.is_valid()) { + VSG::scene->render_camera(arvr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); + } else { + VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); + } +} + void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::Eyes p_eye) { /* Camera should always be BEFORE any other 3D */ @@ -62,13 +99,7 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E } if (!scenario_draw_canvas_bg && can_draw_3d) { - Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); - - if (p_viewport->use_arvr && arvr_interface.is_valid()) { - VSG::scene->render_camera(arvr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); - } else { - VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); - } + _draw_3d(p_viewport, p_eye); } if (!p_viewport->hide_canvas) { @@ -86,10 +117,10 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E for (Map<RID, Viewport::CanvasData>::Element *E = p_viewport->canvas_map.front(); E; E = E->next()) { - Transform2D xf = p_viewport->global_transform * E->get().transform; - VisualServerCanvas::Canvas *canvas = static_cast<VisualServerCanvas::Canvas *>(E->get().canvas); + Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E->get(), clip_rect.size); + //find lights in canvas for (Set<RasterizerCanvas::Light *>::Element *F = canvas->lights.front(); F; F = F->next()) { @@ -174,17 +205,13 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E //VSG::canvas_render->reset_canvas(); } - VSG::rasterizer->restore_render_target(); + VSG::rasterizer->restore_render_target(!scenario_draw_canvas_bg && can_draw_3d); if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().get_layer() > scenario_canvas_max_layer) { - Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); - if (!can_draw_3d) { VSG::scene->render_empty_scene(p_viewport->scenario, p_viewport->shadow_atlas); - } else if (p_viewport->use_arvr && arvr_interface.is_valid()) { - VSG::scene->render_camera(arvr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); } else { - VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); + _draw_3d(p_viewport, p_eye); } scenario_draw_canvas_bg = false; } @@ -193,7 +220,7 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E VisualServerCanvas::Canvas *canvas = static_cast<VisualServerCanvas::Canvas *>(E->get()->canvas); - Transform2D xform = p_viewport->global_transform * E->get()->transform; + Transform2D xform = _canvas_get_transform(p_viewport, canvas, E->get(), clip_rect.size); RasterizerCanvas::Light *canvas_lights = NULL; @@ -210,14 +237,10 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E i++; if (scenario_draw_canvas_bg && E->key().get_layer() >= scenario_canvas_max_layer) { - Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); - if (!can_draw_3d) { VSG::scene->render_empty_scene(p_viewport->scenario, p_viewport->shadow_atlas); - } else if (p_viewport->use_arvr && arvr_interface.is_valid()) { - VSG::scene->render_camera(arvr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); } else { - VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); + _draw_3d(p_viewport, p_eye); } scenario_draw_canvas_bg = false; @@ -225,14 +248,10 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E } if (scenario_draw_canvas_bg) { - Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); - if (!can_draw_3d) { VSG::scene->render_empty_scene(p_viewport->scenario, p_viewport->shadow_atlas); - } else if (p_viewport->use_arvr && arvr_interface.is_valid()) { - VSG::scene->render_camera(arvr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); } else { - VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); + _draw_3d(p_viewport, p_eye); } scenario_draw_canvas_bg = false; @@ -276,17 +295,27 @@ void VisualServerViewport::draw_viewports() { if (vp->use_arvr && arvr_interface.is_valid()) { // override our size, make sure it matches our required size - Size2 size = arvr_interface->get_render_targetsize(); - VSG::storage->render_target_set_size(vp->render_target, size.x, size.y); + vp->size = arvr_interface->get_render_targetsize(); + VSG::storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y); // render mono or left eye first ARVRInterface::Eyes leftOrMono = arvr_interface->is_stereo() ? ARVRInterface::EYE_LEFT : ARVRInterface::EYE_MONO; + + // check for an external texture destination for our left eye/mono + VSG::storage->render_target_set_external_texture(vp->render_target, arvr_interface->get_external_texture_for_eye(leftOrMono)); + + // set our render target as current VSG::rasterizer->set_current_render_target(vp->render_target); + + // and draw left eye/mono _draw_viewport(vp, leftOrMono); arvr_interface->commit_for_eye(leftOrMono, vp->render_target, vp->viewport_to_screen_rect); // render right eye if (leftOrMono == ARVRInterface::EYE_LEFT) { + // check for an external texture destination for our right eye + VSG::storage->render_target_set_external_texture(vp->render_target, arvr_interface->get_external_texture_for_eye(ARVRInterface::EYE_RIGHT)); + // commit for eye may have changed the render target VSG::rasterizer->set_current_render_target(vp->render_target); @@ -297,6 +326,7 @@ void VisualServerViewport::draw_viewports() { // and for our frame timing, mark when we've finished committing our eyes ARVRServer::get_singleton()->_mark_commit(); } else { + VSG::storage->render_target_set_external_texture(vp->render_target, 0); VSG::rasterizer->set_current_render_target(vp->render_target); VSG::scene_render->set_debug_draw_mode(vp->debug_draw); diff --git a/servers/visual/visual_server_viewport.h b/servers/visual/visual_server_viewport.h index 5176551540..555b40a103 100644 --- a/servers/visual/visual_server_viewport.h +++ b/servers/visual/visual_server_viewport.h @@ -147,6 +147,7 @@ public: private: Color clear_color; + void _draw_3d(Viewport *p_viewport, ARVRInterface::Eyes p_eye); void _draw_viewport(Viewport *p_viewport, ARVRInterface::Eyes p_eye = ARVRInterface::EYE_MONO); public: diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index c6da6799a5..9f23ad644e 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -487,6 +487,8 @@ public: FUNCRID(canvas) FUNC3(canvas_set_item_mirroring, RID, RID, const Point2 &) FUNC2(canvas_set_modulate, RID, const Color &) + FUNC3(canvas_set_parent, RID, RID, float) + FUNC1(canvas_set_disable_scale, bool) FUNCRID(canvas_item) FUNC2(canvas_item_set_parent, RID, RID) @@ -516,7 +518,7 @@ public: FUNC7(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float, RID) FUNC7(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, RID, bool) FUNC10(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, const Vector<int> &, const Vector<float> &, RID, int, RID) - FUNC4(canvas_item_add_mesh, RID, const RID &, RID, RID) + FUNC6(canvas_item_add_mesh, RID, const RID &, const Transform2D &, const Color &, RID, RID) FUNC4(canvas_item_add_multimesh, RID, RID, RID, RID) FUNC4(canvas_item_add_particles, RID, RID, RID, RID) FUNC2(canvas_item_add_set_transform, RID, const Transform2D &) diff --git a/servers/visual_server.h b/servers/visual_server.h index 63ddc3328a..c98c5b39a9 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -860,6 +860,9 @@ public: virtual RID canvas_create() = 0; virtual void canvas_set_item_mirroring(RID p_canvas, RID p_item, const Point2 &p_mirroring) = 0; virtual void canvas_set_modulate(RID p_canvas, const Color &p_color) = 0; + virtual void canvas_set_parent(RID p_canvas, RID p_parent, float p_scale) = 0; + + virtual void canvas_set_disable_scale(bool p_disable) = 0; virtual RID canvas_item_create() = 0; virtual void canvas_item_set_parent(RID p_item, RID p_parent) = 0; @@ -895,7 +898,7 @@ public: virtual void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0, RID p_normal_map = RID()) = 0; virtual void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), RID p_normal_map = RID(), bool p_antialiased = false) = 0; virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID()) = 0; - virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0; + virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1), RID p_texture = RID(), RID p_normal_map = RID()) = 0; virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0; virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map) = 0; virtual void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform) = 0; diff --git a/thirdparty/README.md b/thirdparty/README.md index 4fb4786d67..25f731df08 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -1,6 +1,13 @@ # Third party libraries +## assimp + +- Upstream: http://github.com/assimp/assimp +- Version: git (d2b45377e4b09a1f43be95e45553afcc06b03f4b) +- License: BSD-3-Clause + + ## b2d_convexdecomp - Upstream: https://github.com/erincatto/Box2D/tree/master/Contributions/Utilities/ConvexDecomposition @@ -288,8 +295,12 @@ Godot build configurations, check them out when updating. File extracted from upstream release tarball `mbedtls-2.16.0-apache.tgz`: - All `*.h` from `include/mbedtls/` to `thirdparty/mbedtls/include/mbedtls/` - All `*.c` from `library/` to `thirdparty/mbedtls/library/` -- Applied the patch in `thirdparty/mbedtls/1453.diff` (PR 1453). Soon to be merged upstream. Check it out at next update. -- Applied the patch in `thirdparty/mbedtls/padlock.diff`. This disables VIA padlock support which defines a symbol `unsupported` which clashses with a symbol in libwebsockets. +- Applied the patch in `thirdparty/mbedtls/1453.diff` (PR 1453). + Soon to be merged upstream. Check it out at next update. +- Applied the patch in `thirdparty/mbedtls/padlock.diff`. This disables VIA + padlock support which defines a symbol `unsupported` which clashes with + a symbol in libwebsockets. + ## miniupnpc @@ -491,30 +502,46 @@ They are marked with `// -- GODOT start --` and `// -- GODOT end --` comments and a patch is provided in the squish/ folder. -## thekla_atlas - -- Upstream: https://github.com/Thekla/thekla_atlas -- Version: git (80a1430, 2017) -- License: MIT +## tinyexr -Files extracted from the upstream source: +- Upstream: https://github.com/syoyo/tinyexr +- Version: git (65f9859, 2018) +- License: BSD-3-Clause -- Relevant sources from src/ -- License.txt +Files extracted from upstream source: -Important: Some files have Godot-made changes, those -changes are marked with `// -- GODOT --` comments. +- `tinyexr.{cc,h}` -## tinyexr +## vhacd -- Upstream: https://github.com/syoyo/tinyexr -- Version: git (65f9859, 2018) +- Upstream: https://github.com/kmammou/v-hacd +- Version: git (2297aa1, 2018) - License: BSD-3-Clause Files extracted from upstream source: -- `tinyexr.{cc,h}` +- From `src/VHACD_Lib/`: `inc`, `public` and `src` +- `LICENSE` + +Some downstream changes have been made and are identified by +`// -- GODOT start --` and `// -- GODOT end --` comments. +They can be reapplied using the patches included in the `vhacd` +folder. + + +## xatlas + +- Upstream: https://github.com/jpcy/xatlas +- Version: git (b8ec29b, 2018) +- License: MIT + +Files extracted from upstream source: + +- `xatlas.{cpp,h}` + +Note: License is marked as Public Domain in the files, but it was +later clarified upstream to MIT license. ## zlib @@ -531,7 +558,7 @@ Files extracted from upstream source: ## zstd - Upstream: https://github.com/facebook/zstd -- Version: 1.3.8 +- Version: 1.4.0 - License: BSD-3-Clause Files extracted from upstream source: diff --git a/thirdparty/assimp/CREDITS b/thirdparty/assimp/CREDITS new file mode 100644 index 0000000000..26e21d2f41 --- /dev/null +++ b/thirdparty/assimp/CREDITS @@ -0,0 +1,183 @@ +=============================================================== +Open Asset Import Library (Assimp) +Developers and Contributors +=============================================================== + +The following is a non-exhaustive list of all constributors over the years. +If you think your name should be listed here, drop us a line and we'll add you. + +- Alexander Gessler, +3DS-, BLEND-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL-, RAW-, OFF-, MS3D-, Q3D- and LWO-Loader, Assimp-Viewer, assimp-cmd, -noboost, Website (Design). + +- Thomas Schulze, +X-, Collada-, BVH-Loader, Postprocessing framework. Data structure & Interface design, documentation. + +- Kim Kulling, +Obj-, Q3BSD-, OpenGEX-Loader, Logging system, CMake-build-environment, Linux-build, Website ( Admin ), Coverity ( Admin ), Glitter ( Admin ). + +- R.Schmidt, +Linux build, eclipse support. + +- Matthias Gubisch, +Assimp.net +Visual Studio 9 support, bugfixes. + +- Mark Sibly +B3D-Loader, Assimp testing + +- Jonathan Klein +Ogre Loader, VC2010 fixes and CMake fixes. + +- Sebastian Hempel, +PyAssimp (first version) +Compile-Bugfixes for mingw, add environment for static library support in make. + +- Jonathan Pokrass +Supplied a bugfix concerning the scaling in the md3 loader. + +- Andrew Galante, +Submitted patches to make Assimp compile with GCC-4, a makefile and the xcode3 workspace. + +- Andreas Nagel +First Assimp testing & verification under Windows Vista 64 Bit. + +- Marius Schr�der +Allowed us to use many of his models for screenshots and testing. + +- Christian Schubert +Supplied various XFiles for testing purposes. + +- Tizian Wieland +Searched the web for hundreds of test models for internal use + +- John Connors +Supplied patches for linux and SCons. + +- T. R. +The GUY who performed some of the CSM mocaps. + +- Andy Maloney +Contributed fixes for the documentation and the doxygen markup + +- Zhao Lei +Contributed several bugfixes fixing memory leaks and improving float parsing + +- sueastside +Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date. + +- Tobias Rittig +Collada testing with Cinema 4D + +- Brad Grantham +Improvements in OpenGL-Sample. + +- Robert Ramirez +Add group loading feature to Obj-Loader. + +- Chris Maiwald +Many bugreports, improving Assimp's portability, regular testing & feedback. + +- Stepan Hrbek +Bugreport and fix for a obj-materialloader crash. + +- David Nadlinger +D bindings, CMake install support. + +- Dario Accornero +Contributed several patches regarding Mac OS/XCode targets, bug reports. + +- Martin Walser (Samhayne) +Contributed the 'SimpleTexturedOpenGl' sample. + +- Matthias Fauconneau +Contributed a fix for the Q3-BSP loader. + +- Jørgen P. Tjernø +Contributed updated and improved xcode workspaces + +- drparallax +Contributed the /samples/SimpleAssimpViewX sample + +- Carsten Fuchs +Contributed a fix for the Normalize method in aiQuaternion. + +- dbburgess +Contributes a Android-specific build issue: log the hardware architecture for ARM. + +- alfiereinre7 +Contributes a obj-fileparser fix: missing tokens in the obj-token list. + +- Roman Kharitonov +Contributes a fix for the configure script environment. + +- Ed Diana +Contributed AssimpDelphi (/port/AssimpDelphi). + +- rdb +Contributes a bundle of fixes and improvements for the bsp-importer. + +- Mick P +For contributing the De-bone postprocessing step and filing various bug reports. + +- Rosen Diankov +Contributed patches to build assimp debian packages using cmake. + +- Mark Page +Contributed a patch to fix the VertexTriangleAdjacency postprocessing step. + +- IOhannes +Contributed the Debian build fixes ( architecture macro ). + +- gellule +Several LWO and LWS fixes (pivoting). + +- Marcel Metz +GCC/Linux fixes for the SimpleOpenGL sample. + +- Brian Miller +Bugfix for a compiler fix for iOS on arm. + +- Séverin Lemaignan +Rewrite of PyAssimp, distutils and Python3 support + +- albert-wang +Bugfixes for the collada parser + +- Ya ping Jin +Bugfixes for uv-tanget calculation. + +- Jonne Nauha +Ogre Binary format support + +- Filip Wasil, Tieto Poland Sp. z o.o. +Android JNI asset extraction support + +- Richard Steffen +Contributed ExportProperties interface +Contributed X File exporter +Contributed Step (stp) exporter + +- Thomas Iorns (mesilliac) +Initial FBX Export support + +For a more detailed list just check: https://github.com/assimp/assimp/network/members + + +======== +Patreons +======== + +Huge thanks to our Patreons! + +- migenius +- Marcus +- Cort +- elect +- Steffen + + +=================== +Commercial Sponsors +=================== + +- MyDidimo (mydidimo.com): Sponsored development of FBX Export support diff --git a/thirdparty/assimp/LICENSE b/thirdparty/assimp/LICENSE new file mode 100644 index 0000000000..262606aff3 --- /dev/null +++ b/thirdparty/assimp/LICENSE @@ -0,0 +1,78 @@ +Open Asset Import Library (assimp) + +Copyright (c) 2006-2016, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +****************************************************************************** + +AN EXCEPTION applies to all files in the ./test/models-nonbsd folder. +These are 3d models for testing purposes, from various free sources +on the internet. They are - unless otherwise stated - copyright of +their respective creators, which may impose additional requirements +on the use of their work. For any of these models, see +<model-name>.source.txt for more legal information. Contact us if you +are a copyright holder and believe that we credited you inproperly or +if you don't want your files to appear in the repository. + + +****************************************************************************** + +Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors +http://code.google.com/p/poly2tri/ + +All rights reserved. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of Poly2Tri nor the names of its contributors may be + used to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/thirdparty/assimp/assimp/config.h b/thirdparty/assimp/assimp/config.h new file mode 100644 index 0000000000..8b0634d28b --- /dev/null +++ b/thirdparty/assimp/assimp/config.h @@ -0,0 +1,980 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file config.h + * @brief Defines constants for configurable properties for the library + * + * Typically these properties are set via + * #Assimp::Importer::SetPropertyFloat, + * #Assimp::Importer::SetPropertyInteger or + * #Assimp::Importer::SetPropertyString, + * depending on the data type of a property. All properties have a + * default value. See the doc for the mentioned methods for more details. + * + * <br><br> + * The corresponding functions for use with the plain-c API are: + * #aiSetImportPropertyInteger, + * #aiSetImportPropertyFloat, + * #aiSetImportPropertyString + */ +#pragma once +#ifndef AI_CONFIG_H_INC +#define AI_CONFIG_H_INC + +// ########################################################################### +// LIBRARY SETTINGS +// General, global settings +// ########################################################################### + +// --------------------------------------------------------------------------- +/** @brief Enables time measurements. + * + * If enabled, measures the time needed for each part of the loading + * process (i.e. IO time, importing, postprocessing, ..) and dumps + * these timings to the DefaultLogger. See the @link perf Performance + * Page@endlink for more information on this topic. + * + * Property type: bool. Default value: false. + */ +#define AI_CONFIG_GLOB_MEASURE_TIME \ + "GLOB_MEASURE_TIME" + +// --------------------------------------------------------------------------- +/** @brief Global setting to disable generation of skeleton dummy meshes + * + * Skeleton dummy meshes are generated as a visualization aid in cases which + * the input data contains no geometry, but only animation data. + * Property data type: bool. Default value: false + */ +// --------------------------------------------------------------------------- +#define AI_CONFIG_IMPORT_NO_SKELETON_MESHES \ + "IMPORT_NO_SKELETON_MESHES" + +#if 0 // not implemented yet +// --------------------------------------------------------------------------- +/** @brief Set Assimp's multithreading policy. + * + * This setting is ignored if Assimp was built without boost.thread + * support (ASSIMP_BUILD_NO_THREADING, which is implied by ASSIMP_BUILD_BOOST_WORKAROUND). + * Possible values are: -1 to let Assimp decide what to do, 0 to disable + * multithreading entirely and any number larger than 0 to force a specific + * number of threads. Assimp is always free to ignore this settings, which is + * merely a hint. Usually, the default value (-1) will be fine. However, if + * Assimp is used concurrently from multiple user threads, it might be useful + * to limit each Importer instance to a specific number of cores. + * + * For more information, see the @link threading Threading page@endlink. + * Property type: int, default value: -1. + */ +#define AI_CONFIG_GLOB_MULTITHREADING \ + "GLOB_MULTITHREADING" +#endif + +// ########################################################################### +// POST PROCESSING SETTINGS +// Various stuff to fine-tune the behavior of a specific post processing step. +// ########################################################################### + +// --------------------------------------------------------------------------- +/** @brief Maximum bone count per mesh for the SplitbyBoneCount step. + * + * Meshes are split until the maximum number of bones is reached. The default + * value is AI_SBBC_DEFAULT_MAX_BONES, which may be altered at + * compile-time. + * Property data type: integer. + */ +// --------------------------------------------------------------------------- +#define AI_CONFIG_PP_SBBC_MAX_BONES \ + "PP_SBBC_MAX_BONES" + +// default limit for bone count +#if (!defined AI_SBBC_DEFAULT_MAX_BONES) +#define AI_SBBC_DEFAULT_MAX_BONES 60 +#endif + +// --------------------------------------------------------------------------- +/** @brief Specifies the maximum angle that may be between two vertex tangents + * that their tangents and bi-tangents are smoothed. + * + * This applies to the CalcTangentSpace-Step. The angle is specified + * in degrees. The maximum value is 175. + * Property type: float. Default value: 45 degrees + */ +#define AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE \ + "PP_CT_MAX_SMOOTHING_ANGLE" + +// --------------------------------------------------------------------------- +/** @brief Source UV channel for tangent space computation. + * + * The specified channel must exist or an error will be raised. + * Property type: integer. Default value: 0 + */ +// --------------------------------------------------------------------------- +#define AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX \ + "PP_CT_TEXTURE_CHANNEL_INDEX" + +// --------------------------------------------------------------------------- +/** @brief Specifies the maximum angle that may be between two face normals + * at the same vertex position that their are smoothed together. + * + * Sometimes referred to as 'crease angle'. + * This applies to the GenSmoothNormals-Step. The angle is specified + * in degrees, so 180 is PI. The default value is 175 degrees (all vertex + * normals are smoothed). The maximum value is 175, too. Property type: float. + * Warning: setting this option may cause a severe loss of performance. The + * performance is unaffected if the #AI_CONFIG_FAVOUR_SPEED flag is set but + * the output quality may be reduced. + */ +#define AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE \ + "PP_GSN_MAX_SMOOTHING_ANGLE" + +// --------------------------------------------------------------------------- +/** @brief Sets the colormap (= palette) to be used to decode embedded + * textures in MDL (Quake or 3DGS) files. + * + * This must be a valid path to a file. The file is 768 (256*3) bytes + * large and contains RGB triplets for each of the 256 palette entries. + * The default value is colormap.lmp. If the file is not found, + * a default palette (from Quake 1) is used. + * Property type: string. + */ +#define AI_CONFIG_IMPORT_MDL_COLORMAP \ + "IMPORT_MDL_COLORMAP" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_RemoveRedundantMaterials step to + * keep materials matching a name in a given list. + * + * This is a list of 1 to n strings, ' ' serves as delimiter character. + * Identifiers containing whitespaces must be enclosed in *single* + * quotation marks. For example:<tt> + * "keep-me and_me_to anotherMaterialToBeKept \'name with whitespace\'"</tt>. + * If a material matches on of these names, it will not be modified or + * removed by the postprocessing step nor will other materials be replaced + * by a reference to it. <br> + * This option might be useful if you are using some magic material names + * to pass additional semantics through the content pipeline. This ensures + * they won't be optimized away, but a general optimization is still + * performed for materials not contained in the list. + * Property type: String. Default value: n/a + * @note Linefeeds, tabs or carriage returns are treated as whitespace. + * Material names are case sensitive. + */ +#define AI_CONFIG_PP_RRM_EXCLUDE_LIST \ + "PP_RRM_EXCLUDE_LIST" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_PreTransformVertices step to + * keep the scene hierarchy. Meshes are moved to worldspace, but + * no optimization is performed (read: meshes with equal materials are not + * joined. The total number of meshes won't change). + * + * This option could be of use for you if the scene hierarchy contains + * important additional information which you intend to parse. + * For rendering, you can still render all meshes in the scene without + * any transformations. + * Property type: bool. Default value: false. + */ +#define AI_CONFIG_PP_PTV_KEEP_HIERARCHY \ + "PP_PTV_KEEP_HIERARCHY" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_PreTransformVertices step to normalize + * all vertex components into the [-1,1] range. That is, a bounding box + * for the whole scene is computed, the maximum component is taken and all + * meshes are scaled appropriately (uniformly of course!). + * This might be useful if you don't know the spatial dimension of the input + * data*/ +#define AI_CONFIG_PP_PTV_NORMALIZE \ + "PP_PTV_NORMALIZE" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_PreTransformVertices step to use + * a users defined matrix as the scene root node transformation before + * transforming vertices. + * Property type: bool. Default value: false. + */ +#define AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION \ + "PP_PTV_ADD_ROOT_TRANSFORMATION" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_PreTransformVertices step to use + * a users defined matrix as the scene root node transformation before + * transforming vertices. This property correspond to the 'a1' component + * of the transformation matrix. + * Property type: aiMatrix4x4. + */ +#define AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION \ + "PP_PTV_ROOT_TRANSFORMATION" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_FindDegenerates step to + * remove degenerated primitives from the import - immediately. + * + * The default behaviour converts degenerated triangles to lines and + * degenerated lines to points. See the documentation to the + * #aiProcess_FindDegenerates step for a detailed example of the various ways + * to get rid of these lines and points if you don't want them. + * Property type: bool. Default value: false. + */ +#define AI_CONFIG_PP_FD_REMOVE \ + "PP_FD_REMOVE" + +// --------------------------------------------------------------------------- +/** + * @brief Configures the #aiProcess_FindDegenerates to check the area of a + * trinagle to be greates than e-6. If this is not the case the triangle will + * be removed if #AI_CONFIG_PP_FD_REMOVE is set to true. + */ +#define AI_CONFIG_PP_FD_CHECKAREA \ + "PP_FD_CHECKAREA" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_OptimizeGraph step to preserve nodes + * matching a name in a given list. + * + * This is a list of 1 to n strings, ' ' serves as delimiter character. + * Identifiers containing whitespaces must be enclosed in *single* + * quotation marks. For example:<tt> + * "keep-me and_me_to anotherNodeToBeKept \'name with whitespace\'"</tt>. + * If a node matches on of these names, it will not be modified or + * removed by the postprocessing step.<br> + * This option might be useful if you are using some magic node names + * to pass additional semantics through the content pipeline. This ensures + * they won't be optimized away, but a general optimization is still + * performed for nodes not contained in the list. + * Property type: String. Default value: n/a + * @note Linefeeds, tabs or carriage returns are treated as whitespace. + * Node names are case sensitive. + */ +#define AI_CONFIG_PP_OG_EXCLUDE_LIST \ + "PP_OG_EXCLUDE_LIST" + +// --------------------------------------------------------------------------- +/** @brief Set the maximum number of triangles in a mesh. + * + * This is used by the "SplitLargeMeshes" PostProcess-Step to determine + * whether a mesh must be split or not. + * @note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES + * Property type: integer. + */ +#define AI_CONFIG_PP_SLM_TRIANGLE_LIMIT \ + "PP_SLM_TRIANGLE_LIMIT" + +// default value for AI_CONFIG_PP_SLM_TRIANGLE_LIMIT +#if (!defined AI_SLM_DEFAULT_MAX_TRIANGLES) +#define AI_SLM_DEFAULT_MAX_TRIANGLES 1000000 +#endif + +// --------------------------------------------------------------------------- +/** @brief Set the maximum number of vertices in a mesh. + * + * This is used by the "SplitLargeMeshes" PostProcess-Step to determine + * whether a mesh must be split or not. + * @note The default value is AI_SLM_DEFAULT_MAX_VERTICES + * Property type: integer. + */ +#define AI_CONFIG_PP_SLM_VERTEX_LIMIT \ + "PP_SLM_VERTEX_LIMIT" + +// default value for AI_CONFIG_PP_SLM_VERTEX_LIMIT +#if (!defined AI_SLM_DEFAULT_MAX_VERTICES) +#define AI_SLM_DEFAULT_MAX_VERTICES 1000000 +#endif + +// --------------------------------------------------------------------------- +/** @brief Set the maximum number of bones affecting a single vertex + * + * This is used by the #aiProcess_LimitBoneWeights PostProcess-Step. + * @note The default value is AI_LMW_MAX_WEIGHTS + * Property type: integer.*/ +#define AI_CONFIG_PP_LBW_MAX_WEIGHTS \ + "PP_LBW_MAX_WEIGHTS" + +// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS +#if (!defined AI_LMW_MAX_WEIGHTS) +#define AI_LMW_MAX_WEIGHTS 0x4 +#endif // !! AI_LMW_MAX_WEIGHTS + +// --------------------------------------------------------------------------- +/** @brief Lower the deboning threshold in order to remove more bones. + * + * This is used by the #aiProcess_Debone PostProcess-Step. + * @note The default value is AI_DEBONE_THRESHOLD + * Property type: float.*/ +#define AI_CONFIG_PP_DB_THRESHOLD \ + "PP_DB_THRESHOLD" + +// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS +#if (!defined AI_DEBONE_THRESHOLD) +#define AI_DEBONE_THRESHOLD 1.0f +#endif // !! AI_DEBONE_THRESHOLD + +// --------------------------------------------------------------------------- +/** @brief Require all bones qualify for deboning before removing any + * + * This is used by the #aiProcess_Debone PostProcess-Step. + * @note The default value is 0 + * Property type: bool.*/ +#define AI_CONFIG_PP_DB_ALL_OR_NONE \ + "PP_DB_ALL_OR_NONE" + +/** @brief Default value for the #AI_CONFIG_PP_ICL_PTCACHE_SIZE property + */ +#ifndef PP_ICL_PTCACHE_SIZE +#define PP_ICL_PTCACHE_SIZE 12 +#endif + +// --------------------------------------------------------------------------- +/** @brief Set the size of the post-transform vertex cache to optimize the + * vertices for. This configures the #aiProcess_ImproveCacheLocality step. + * + * The size is given in vertices. Of course you can't know how the vertex + * format will exactly look like after the import returns, but you can still + * guess what your meshes will probably have. + * @note The default value is #PP_ICL_PTCACHE_SIZE. That results in slight + * performance improvements for most nVidia/AMD cards since 2002. + * Property type: integer. + */ +#define AI_CONFIG_PP_ICL_PTCACHE_SIZE "PP_ICL_PTCACHE_SIZE" + +// --------------------------------------------------------------------------- +/** @brief Enumerates components of the aiScene and aiMesh data structures + * that can be excluded from the import using the #aiProcess_RemoveComponent step. + * + * See the documentation to #aiProcess_RemoveComponent for more details. + */ +enum aiComponent { +/** Normal vectors */ +#ifdef SWIG + aiComponent_NORMALS = 0x2, +#else + aiComponent_NORMALS = 0x2u, +#endif + +/** Tangents and bitangents go always together ... */ +#ifdef SWIG + aiComponent_TANGENTS_AND_BITANGENTS = 0x4, +#else + aiComponent_TANGENTS_AND_BITANGENTS = 0x4u, +#endif + + /** ALL color sets + * Use aiComponent_COLORn(N) to specify the N'th set */ + aiComponent_COLORS = 0x8, + + /** ALL texture UV sets + * aiComponent_TEXCOORDn(N) to specify the N'th set */ + aiComponent_TEXCOORDS = 0x10, + + /** Removes all bone weights from all meshes. + * The scenegraph nodes corresponding to the bones are NOT removed. + * use the #aiProcess_OptimizeGraph step to do this */ + aiComponent_BONEWEIGHTS = 0x20, + + /** Removes all node animations (aiScene::mAnimations). + * The corresponding scenegraph nodes are NOT removed. + * use the #aiProcess_OptimizeGraph step to do this */ + aiComponent_ANIMATIONS = 0x40, + + /** Removes all embedded textures (aiScene::mTextures) */ + aiComponent_TEXTURES = 0x80, + + /** Removes all light sources (aiScene::mLights). + * The corresponding scenegraph nodes are NOT removed. + * use the #aiProcess_OptimizeGraph step to do this */ + aiComponent_LIGHTS = 0x100, + + /** Removes all cameras (aiScene::mCameras). + * The corresponding scenegraph nodes are NOT removed. + * use the #aiProcess_OptimizeGraph step to do this */ + aiComponent_CAMERAS = 0x200, + + /** Removes all meshes (aiScene::mMeshes). */ + aiComponent_MESHES = 0x400, + + /** Removes all materials. One default material will + * be generated, so aiScene::mNumMaterials will be 1. */ + aiComponent_MATERIALS = 0x800, + +/** This value is not used. It is just there to force the + * compiler to map this enum to a 32 Bit integer. */ +#ifndef SWIG + _aiComponent_Force32Bit = 0x9fffffff +#endif +}; + +// Remove a specific color channel 'n' +#define aiComponent_COLORSn(n) (1u << (n + 20u)) + +// Remove a specific UV channel 'n' +#define aiComponent_TEXCOORDSn(n) (1u << (n + 25u)) + +// --------------------------------------------------------------------------- +/** @brief Input parameter to the #aiProcess_RemoveComponent step: + * Specifies the parts of the data structure to be removed. + * + * See the documentation to this step for further details. The property + * is expected to be an integer, a bitwise combination of the + * #aiComponent flags defined above in this header. The default + * value is 0. Important: if no valid mesh is remaining after the + * step has been executed (e.g you thought it was funny to specify ALL + * of the flags defined above) the import FAILS. Mainly because there is + * no data to work on anymore ... + */ +#define AI_CONFIG_PP_RVC_FLAGS \ + "PP_RVC_FLAGS" + +// --------------------------------------------------------------------------- +/** @brief Input parameter to the #aiProcess_SortByPType step: + * Specifies which primitive types are removed by the step. + * + * This is a bitwise combination of the aiPrimitiveType flags. + * Specifying all of them is illegal, of course. A typical use would + * be to exclude all line and point meshes from the import. This + * is an integer property, its default value is 0. + */ +#define AI_CONFIG_PP_SBP_REMOVE \ + "PP_SBP_REMOVE" + +// --------------------------------------------------------------------------- +/** @brief Input parameter to the #aiProcess_FindInvalidData step: + * Specifies the floating-point accuracy for animation values. The step + * checks for animation tracks where all frame values are absolutely equal + * and removes them. This tweakable controls the epsilon for floating-point + * comparisons - two keys are considered equal if the invariant + * abs(n0-n1)>epsilon holds true for all vector respectively quaternion + * components. The default value is 0.f - comparisons are exact then. + */ +#define AI_CONFIG_PP_FID_ANIM_ACCURACY \ + "PP_FID_ANIM_ACCURACY" + +// TransformUVCoords evaluates UV scalings +#define AI_UVTRAFO_SCALING 0x1 + +// TransformUVCoords evaluates UV rotations +#define AI_UVTRAFO_ROTATION 0x2 + +// TransformUVCoords evaluates UV translation +#define AI_UVTRAFO_TRANSLATION 0x4 + +// Everything baked together -> default value +#define AI_UVTRAFO_ALL (AI_UVTRAFO_SCALING | AI_UVTRAFO_ROTATION | AI_UVTRAFO_TRANSLATION) + +// --------------------------------------------------------------------------- +/** @brief Input parameter to the #aiProcess_FindInvalidData step: + * Set to true to ignore texture coordinates. This may be useful if you have + * to assign different kind of textures like one for the summer or one for the winter. + */ +#define AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS \ + "PP_FID_IGNORE_TEXTURECOORDS" + +// --------------------------------------------------------------------------- +/** @brief Input parameter to the #aiProcess_TransformUVCoords step: + * Specifies which UV transformations are evaluated. + * + * This is a bitwise combination of the AI_UVTRAFO_XXX flags (integer + * property, of course). By default all transformations are enabled + * (AI_UVTRAFO_ALL). + */ +#define AI_CONFIG_PP_TUV_EVALUATE \ + "PP_TUV_EVALUATE" + +// --------------------------------------------------------------------------- +/** @brief A hint to assimp to favour speed against import quality. + * + * Enabling this option may result in faster loading, but it needn't. + * It represents just a hint to loaders and post-processing steps to use + * faster code paths, if possible. + * This property is expected to be an integer, != 0 stands for true. + * The default value is 0. + */ +#define AI_CONFIG_FAVOUR_SPEED \ + "FAVOUR_SPEED" + +// ########################################################################### +// IMPORTER SETTINGS +// Various stuff to fine-tune the behaviour of specific importer plugins. +// ########################################################################### + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will merge all geometry layers present + * in the source file or take only the first. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS \ + "IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read all materials present in the + * source file or take only the referenced materials. + * + * This is void unless IMPORT_FBX_READ_MATERIALS=1. + * + * The default value is false (0) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS \ + "IMPORT_FBX_READ_ALL_MATERIALS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read materials. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_MATERIALS \ + "IMPORT_FBX_READ_MATERIALS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read embedded textures. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_TEXTURES \ + "IMPORT_FBX_READ_TEXTURES" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read cameras. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_CAMERAS \ + "IMPORT_FBX_READ_CAMERAS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read light sources. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_LIGHTS \ + "IMPORT_FBX_READ_LIGHTS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read animations. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS \ + "IMPORT_FBX_READ_ANIMATIONS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will act in strict mode in which only + * FBX 2013 is supported and any other sub formats are rejected. FBX 2013 + * is the primary target for the importer, so this format is best + * supported and well-tested. + * + * The default value is false (0) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_STRICT_MODE \ + "IMPORT_FBX_STRICT_MODE" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will preserve pivot points for + * transformations (as extra nodes). If set to false, pivots and offsets + * will be evaluated whenever possible. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS \ + "IMPORT_FBX_PRESERVE_PIVOTS" + +// --------------------------------------------------------------------------- +/** @brief Specifies whether the importer will drop empty animation curves or + * animation curves which match the bind pose transformation over their + * entire defined range. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES \ + "IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will use the legacy embedded texture naming. +* +* The default value is false (0) +* Property type: bool +*/ +#define AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING \ + "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING" + +// --------------------------------------------------------------------------- +/** @brief Set the vertex animation keyframe to be imported + * + * ASSIMP does not support vertex keyframes (only bone animation is supported). + * The library reads only one frame of models with vertex animations. + * By default this is the first frame. + * \note The default value is 0. This option applies to all importers. + * However, it is also possible to override the global setting + * for a specific loader. You can use the AI_CONFIG_IMPORT_XXX_KEYFRAME + * options (where XXX is a placeholder for the file format for which you + * want to override the global setting). + * Property type: integer. + */ +#define AI_CONFIG_IMPORT_GLOBAL_KEYFRAME "IMPORT_GLOBAL_KEYFRAME" + +#define AI_CONFIG_IMPORT_MD3_KEYFRAME "IMPORT_MD3_KEYFRAME" +#define AI_CONFIG_IMPORT_MD2_KEYFRAME "IMPORT_MD2_KEYFRAME" +#define AI_CONFIG_IMPORT_MDL_KEYFRAME "IMPORT_MDL_KEYFRAME" +#define AI_CONFIG_IMPORT_MDC_KEYFRAME "IMPORT_MDC_KEYFRAME" +#define AI_CONFIG_IMPORT_SMD_KEYFRAME "IMPORT_SMD_KEYFRAME" +#define AI_CONFIG_IMPORT_UNREAL_KEYFRAME "IMPORT_UNREAL_KEYFRAME" + +// --------------------------------------------------------------------------- +/** Smd load multiple animations + * + * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_SMD_LOAD_ANIMATION_LIST "IMPORT_SMD_LOAD_ANIMATION_LIST" + +// --------------------------------------------------------------------------- +/** @brief Configures the AC loader to collect all surfaces which have the + * "Backface cull" flag set in separate meshes. + * + * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL \ + "IMPORT_AC_SEPARATE_BFCULL" + +// --------------------------------------------------------------------------- +/** @brief Configures whether the AC loader evaluates subdivision surfaces ( + * indicated by the presence of the 'subdiv' attribute in the file). By + * default, Assimp performs the subdivision using the standard + * Catmull-Clark algorithm + * + * * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION \ + "IMPORT_AC_EVAL_SUBDIVISION" + +// --------------------------------------------------------------------------- +/** @brief Configures the UNREAL 3D loader to separate faces with different + * surface flags (e.g. two-sided vs. single-sided). + * + * * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS \ + "UNREAL_HANDLE_FLAGS" + +// --------------------------------------------------------------------------- +/** @brief Configures the terragen import plugin to compute uv's for + * terrains, if not given. Furthermore a default texture is assigned. + * + * UV coordinates for terrains are so simple to compute that you'll usually + * want to compute them on your own, if you need them. This option is intended + * for model viewers which want to offer an easy way to apply textures to + * terrains. + * * Property type: bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_TER_MAKE_UVS \ + "IMPORT_TER_MAKE_UVS" + +// --------------------------------------------------------------------------- +/** @brief Configures the ASE loader to always reconstruct normal vectors + * basing on the smoothing groups loaded from the file. + * + * Some ASE files have carry invalid normals, other don't. + * * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS \ + "IMPORT_ASE_RECONSTRUCT_NORMALS" + +// --------------------------------------------------------------------------- +/** @brief Configures the M3D loader to detect and process multi-part + * Quake player models. + * + * These models usually consist of 3 files, lower.md3, upper.md3 and + * head.md3. If this property is set to true, Assimp will try to load and + * combine all three files if one of them is loaded. + * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART \ + "IMPORT_MD3_HANDLE_MULTIPART" + +// --------------------------------------------------------------------------- +/** @brief Tells the MD3 loader which skin files to load. + * + * When loading MD3 files, Assimp checks whether a file + * [md3_file_name]_[skin_name].skin is existing. These files are used by + * Quake III to be able to assign different skins (e.g. red and blue team) + * to models. 'default', 'red', 'blue' are typical skin names. + * Property type: String. Default value: "default". + */ +#define AI_CONFIG_IMPORT_MD3_SKIN_NAME \ + "IMPORT_MD3_SKIN_NAME" + +// --------------------------------------------------------------------------- +/** @brief Specify the Quake 3 shader file to be used for a particular + * MD3 file. This can also be a search path. + * + * By default Assimp's behaviour is as follows: If a MD3 file + * <tt>any_path/models/any_q3_subdir/model_name/file_name.md3</tt> is + * loaded, the library tries to locate the corresponding shader file in + * <tt>any_path/scripts/model_name.shader</tt>. This property overrides this + * behaviour. It can either specify a full path to the shader to be loaded + * or alternatively the path (relative or absolute) to the directory where + * the shaders for all MD3s to be loaded reside. Assimp attempts to open + * <tt>IMPORT_MD3_SHADER_SRC/model_name.shader</tt> first, <tt>IMPORT_MD3_SHADER_SRC/file_name.shader</tt> + * is the fallback file. Note that IMPORT_MD3_SHADER_SRC should have a terminal (back)slash. + * Property type: String. Default value: n/a. + */ +#define AI_CONFIG_IMPORT_MD3_SHADER_SRC \ + "IMPORT_MD3_SHADER_SRC" + +// --------------------------------------------------------------------------- +/** @brief Configures the LWO loader to load just one layer from the model. + * + * LWO files consist of layers and in some cases it could be useful to load + * only one of them. This property can be either a string - which specifies + * the name of the layer - or an integer - the index of the layer. If the + * property is not set the whole LWO model is loaded. Loading fails if the + * requested layer is not available. The layer index is zero-based and the + * layer name may not be empty.<br> + * Property type: Integer. Default value: all layers are loaded. + */ +#define AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY \ + "IMPORT_LWO_ONE_LAYER_ONLY" + +// --------------------------------------------------------------------------- +/** @brief Configures the MD5 loader to not load the MD5ANIM file for + * a MD5MESH file automatically. + * + * The default strategy is to look for a file with the same name but the + * MD5ANIM extension in the same directory. If it is found, it is loaded + * and combined with the MD5MESH file. This configuration option can be + * used to disable this behaviour. + * + * * Property type: bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD \ + "IMPORT_MD5_NO_ANIM_AUTOLOAD" + +// --------------------------------------------------------------------------- +/** @brief Defines the begin of the time range for which the LWS loader + * evaluates animations and computes aiNodeAnim's. + * + * Assimp provides full conversion of LightWave's envelope system, including + * pre and post conditions. The loader computes linearly subsampled animation + * chanels with the frame rate given in the LWS file. This property defines + * the start time. Note: animation channels are only generated if a node + * has at least one envelope with more tan one key assigned. This property. + * is given in frames, '0' is the first frame. By default, if this property + * is not set, the importer takes the animation start from the input LWS + * file ('FirstFrame' line)<br> + * Property type: Integer. Default value: taken from file. + * + * @see AI_CONFIG_IMPORT_LWS_ANIM_END - end of the imported time range + */ +#define AI_CONFIG_IMPORT_LWS_ANIM_START \ + "IMPORT_LWS_ANIM_START" +#define AI_CONFIG_IMPORT_LWS_ANIM_END \ + "IMPORT_LWS_ANIM_END" + +// --------------------------------------------------------------------------- +/** @brief Defines the output frame rate of the IRR loader. + * + * IRR animations are difficult to convert for Assimp and there will + * always be a loss of quality. This setting defines how many keys per second + * are returned by the converter.<br> + * Property type: integer. Default value: 100 + */ +#define AI_CONFIG_IMPORT_IRR_ANIM_FPS \ + "IMPORT_IRR_ANIM_FPS" + +// --------------------------------------------------------------------------- +/** @brief Ogre Importer will try to find referenced materials from this file. + * + * Ogre meshes reference with material names, this does not tell Assimp the file + * where it is located in. Assimp will try to find the source file in the following + * order: <material-name>.material, <mesh-filename-base>.material and + * lastly the material name defined by this config property. + * <br> + * Property type: String. Default value: Scene.material. + */ +#define AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE \ + "IMPORT_OGRE_MATERIAL_FILE" + +// --------------------------------------------------------------------------- +/** @brief Ogre Importer detect the texture usage from its filename. + * + * Ogre material texture units do not define texture type, the textures usage + * depends on the used shader or Ogre's fixed pipeline. If this config property + * is true Assimp will try to detect the type from the textures filename postfix: + * _n, _nrm, _nrml, _normal, _normals and _normalmap for normal map, _s, _spec, + * _specular and _specularmap for specular map, _l, _light, _lightmap, _occ + * and _occlusion for light map, _disp and _displacement for displacement map. + * The matching is case insensitive. Post fix is taken between the last + * underscore and the last period. + * Default behavior is to detect type from lower cased texture unit name by + * matching against: normalmap, specularmap, lightmap and displacementmap. + * For both cases if no match is found aiTextureType_DIFFUSE is used. + * <br> + * Property type: Bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME \ + "IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME" + +/** @brief Specifies whether the Android JNI asset extraction is supported. + * + * Turn on this option if you want to manage assets in native + * Android application without having to keep the internal directory and asset + * manager pointer. + */ +#define AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT "AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT" + +// --------------------------------------------------------------------------- +/** @brief Specifies whether the IFC loader skips over IfcSpace elements. + * + * IfcSpace elements (and their geometric representations) are used to + * represent, well, free space in a building storey.<br> + * Property type: Bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS "IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS" + +// --------------------------------------------------------------------------- +/** @brief Specifies whether the IFC loader will use its own, custom triangulation + * algorithm to triangulate wall and floor meshes. + * + * If this property is set to false, walls will be either triangulated by + * #aiProcess_Triangulate or will be passed through as huge polygons with + * faked holes (i.e. holes that are connected with the outer boundary using + * a dummy edge). It is highly recommended to set this property to true + * if you want triangulated data because #aiProcess_Triangulate is known to + * have problems with the kind of polygons that the IFC loader spits out for + * complicated meshes. + * Property type: Bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_IFC_CUSTOM_TRIANGULATION "IMPORT_IFC_CUSTOM_TRIANGULATION" + +// --------------------------------------------------------------------------- +/** @brief Set the tessellation conic angle for IFC smoothing curves. + * + * This is used by the IFC importer to determine the tessellation parameter + * for smoothing curves. + * @note The default value is AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE and the + * accepted values are in range [5.0, 120.0]. + * Property type: Float. + */ +#define AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE "IMPORT_IFC_SMOOTHING_ANGLE" + +// default value for AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE +#if (!defined AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE) +#define AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE 10.0f +#endif + +// --------------------------------------------------------------------------- +/** @brief Set the tessellation for IFC cylindrical shapes. + * + * This is used by the IFC importer to determine the tessellation parameter + * for cylindrical shapes, i.e. the number of segments used to approximate a circle. + * @note The default value is AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION and the + * accepted values are in range [3, 180]. + * Property type: Integer. + */ +#define AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION "IMPORT_IFC_CYLINDRICAL_TESSELLATION" + +// default value for AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION +#if (!defined AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION) +#define AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION 32 +#endif + +// --------------------------------------------------------------------------- +/** @brief Specifies whether the Collada loader will ignore the provided up direction. + * + * If this property is set to true, the up direction provided in the file header will + * be ignored and the file will be loaded as is. + * Property type: Bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION "IMPORT_COLLADA_IGNORE_UP_DIRECTION" + +// --------------------------------------------------------------------------- +/** @brief Specifies whether the Collada loader should use Collada names as node names. + * + * If this property is set to true, the Collada names will be used as the + * node name. The default is to use the id tag (resp. sid tag, if no id tag is present) + * instead. + * Property type: Bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES "IMPORT_COLLADA_USE_COLLADA_NAMES" + +// ---------- All the Export defines ------------ + +/** @brief Specifies the xfile use double for real values of float + * + * Property type: Bool. Default value: false. + */ + +#define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT" + +/** + * + */ +#define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS" + +/** + * @brief Specifies a gobal key factor for scale, float value + */ +#define AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY "GLOBAL_SCALE_FACTOR" + +#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT) +#define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f +#endif // !! AI_DEBONE_THRESHOLD + +// ---------- All the Build/Compile-time defines ------------ + +/** @brief Specifies if double precision is supported inside assimp + * + * Property type: Bool. Default value: undefined. + */ + +/* #cmakedefine ASSIMP_DOUBLE_PRECISION 1 */ + +#endif // !! AI_CONFIG_H_INC diff --git a/thirdparty/assimp/code/BaseImporter.cpp b/thirdparty/assimp/code/BaseImporter.cpp new file mode 100644 index 0000000000..4803c6d6f2 --- /dev/null +++ b/thirdparty/assimp/code/BaseImporter.cpp @@ -0,0 +1,616 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file BaseImporter.cpp + * @brief Implementation of BaseImporter + */ + +#include <assimp/BaseImporter.h> +#include <assimp/ParsingUtils.h> +#include "FileSystemFilter.h" +#include "Importer.h" +#include <assimp/ByteSwapper.h> +#include <assimp/scene.h> +#include <assimp/Importer.hpp> +#include <assimp/postprocess.h> +#include <assimp/importerdesc.h> + +#include <ios> +#include <list> +#include <memory> +#include <sstream> +#include <cctype> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BaseImporter::BaseImporter() AI_NO_EXCEPT +: m_progress() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BaseImporter::~BaseImporter() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file and returns the imported data. +aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) { + m_progress = pImp->GetProgressHandler(); + if (nullptr == m_progress) { + return nullptr; + } + + ai_assert(m_progress); + + // Gather configuration properties for this run + SetupProperties( pImp ); + + // Construct a file system filter to improve our success ratio at reading external files + FileSystemFilter filter(pFile,pIOHandler); + + // create a scene object to hold the data + std::unique_ptr<aiScene> sc(new aiScene()); + + // dispatch importing + try + { + InternReadFile( pFile, sc.get(), &filter); + + } catch( const std::exception& err ) { + // extract error description + m_ErrorText = err.what(); + ASSIMP_LOG_ERROR(m_ErrorText); + return nullptr; + } + + // return what we gathered from the import. + return sc.release(); +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::SetupProperties(const Importer* /*pImp*/) +{ + // the default implementation does nothing +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::GetExtensionList(std::set<std::string>& extensions) { + const aiImporterDesc* desc = GetInfo(); + ai_assert(desc != nullptr); + + const char* ext = desc->mFileExtensions; + ai_assert(ext != nullptr ); + + const char* last = ext; + do { + if (!*ext || *ext == ' ') { + extensions.insert(std::string(last,ext-last)); + ai_assert(ext-last > 0); + last = ext; + while(*last == ' ') { + ++last; + } + } + } + while(*ext++); +} + +// ------------------------------------------------------------------------------------------------ +/*static*/ bool BaseImporter::SearchFileHeaderForToken( IOSystem* pIOHandler, + const std::string& pFile, + const char** tokens, + unsigned int numTokens, + unsigned int searchBytes /* = 200 */, + bool tokensSol /* false */, + bool noAlphaBeforeTokens /* false */) +{ + ai_assert( nullptr != tokens ); + ai_assert( 0 != numTokens ); + ai_assert( 0 != searchBytes); + + if ( nullptr == pIOHandler ) { + return false; + } + + std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile)); + if (pStream.get() ) { + // read 200 characters from the file + std::unique_ptr<char[]> _buffer (new char[searchBytes+1 /* for the '\0' */]); + char *buffer( _buffer.get() ); + const size_t read( pStream->Read(buffer,1,searchBytes) ); + if( 0 == read ) { + return false; + } + + for( size_t i = 0; i < read; ++i ) { + buffer[ i ] = static_cast<char>( ::tolower( buffer[ i ] ) ); + } + + // It is not a proper handling of unicode files here ... + // ehm ... but it works in most cases. + char* cur = buffer,*cur2 = buffer,*end = &buffer[read]; + while (cur != end) { + if( *cur ) { + *cur2++ = *cur; + } + ++cur; + } + *cur2 = '\0'; + + std::string token; + for (unsigned int i = 0; i < numTokens; ++i ) { + ai_assert( nullptr != tokens[i] ); + const size_t len( strlen( tokens[ i ] ) ); + token.clear(); + const char *ptr( tokens[ i ] ); + for ( size_t tokIdx = 0; tokIdx < len; ++tokIdx ) { + token.push_back( static_cast<char>( tolower( *ptr ) ) ); + ++ptr; + } + const char* r = strstr( buffer, token.c_str() ); + if( !r ) { + continue; + } + // We need to make sure that we didn't accidentially identify the end of another token as our token, + // e.g. in a previous version the "gltf " present in some gltf files was detected as "f " + if (noAlphaBeforeTokens && (r != buffer && isalpha(r[-1]))) { + continue; + } + // We got a match, either we don't care where it is, or it happens to + // be in the beginning of the file / line + if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') { + ASSIMP_LOG_DEBUG_F( "Found positive match for header keyword: ", tokens[i] ); + return true; + } + } + } + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Simple check for file extension +/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile, + const char* ext0, + const char* ext1, + const char* ext2) +{ + std::string::size_type pos = pFile.find_last_of('.'); + + // no file extension - can't read + if( pos == std::string::npos) + return false; + + const char* ext_real = & pFile[ pos+1 ]; + if( !ASSIMP_stricmp(ext_real,ext0) ) + return true; + + // check for other, optional, file extensions + if (ext1 && !ASSIMP_stricmp(ext_real,ext1)) + return true; + + if (ext2 && !ASSIMP_stricmp(ext_real,ext2)) + return true; + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Get file extension from path +std::string BaseImporter::GetExtension( const std::string& file ) { + std::string::size_type pos = file.find_last_of('.'); + + // no file extension at all + if (pos == std::string::npos) { + return ""; + } + + + // thanks to Andy Maloney for the hint + std::string ret = file.substr( pos + 1 ); + std::transform( ret.begin(), ret.end(), ret.begin(), ToLower<char>); + + return ret; +} + +// ------------------------------------------------------------------------------------------------ +// Check for magic bytes at the beginning of the file. +/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile, + const void* _magic, unsigned int num, unsigned int offset, unsigned int size) +{ + ai_assert( size <= 16 ); + ai_assert( _magic ); + + if (!pIOHandler) { + return false; + } + union { + const char* magic; + const uint16_t* magic_u16; + const uint32_t* magic_u32; + }; + magic = reinterpret_cast<const char*>(_magic); + std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile)); + if (pStream.get() ) { + + // skip to offset + pStream->Seek(offset,aiOrigin_SET); + + // read 'size' characters from the file + union { + char data[16]; + uint16_t data_u16[8]; + uint32_t data_u32[4]; + }; + if(size != pStream->Read(data,1,size)) { + return false; + } + + for (unsigned int i = 0; i < num; ++i) { + // also check against big endian versions of tokens with size 2,4 + // that's just for convenience, the chance that we cause conflicts + // is quite low and it can save some lines and prevent nasty bugs + if (2 == size) { + uint16_t rev = *magic_u16; + ByteSwap::Swap(&rev); + if (data_u16[0] == *magic_u16 || data_u16[0] == rev) { + return true; + } + } + else if (4 == size) { + uint32_t rev = *magic_u32; + ByteSwap::Swap(&rev); + if (data_u32[0] == *magic_u32 || data_u32[0] == rev) { + return true; + } + } + else { + // any length ... just compare + if(!memcmp(magic,data,size)) { + return true; + } + } + magic += size; + } + } + return false; +} + +#include "../contrib/utf8cpp/source/utf8.h" + +// ------------------------------------------------------------------------------------------------ +// Convert to UTF8 data +void BaseImporter::ConvertToUTF8(std::vector<char>& data) +{ + //ConversionResult result; + if(data.size() < 8) { + throw DeadlyImportError("File is too small"); + } + + // UTF 8 with BOM + if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) { + ASSIMP_LOG_DEBUG("Found UTF-8 BOM ..."); + + std::copy(data.begin()+3,data.end(),data.begin()); + data.resize(data.size()-3); + return; + } + + + // UTF 32 BE with BOM + if(*((uint32_t*)&data.front()) == 0xFFFE0000) { + + // swap the endianness .. + for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) { + AI_SWAP4P(p); + } + } + + // UTF 32 LE with BOM + if(*((uint32_t*)&data.front()) == 0x0000FFFE) { + ASSIMP_LOG_DEBUG("Found UTF-32 BOM ..."); + + std::vector<char> output; + int *ptr = (int*)&data[ 0 ]; + int *end = ptr + ( data.size() / sizeof(int) ) +1; + utf8::utf32to8( ptr, end, back_inserter(output)); + return; + } + + // UTF 16 BE with BOM + if(*((uint16_t*)&data.front()) == 0xFFFE) { + + // swap the endianness .. + for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) { + ByteSwap::Swap2(p); + } + } + + // UTF 16 LE with BOM + if(*((uint16_t*)&data.front()) == 0xFEFF) { + ASSIMP_LOG_DEBUG("Found UTF-16 BOM ..."); + + std::vector<unsigned char> output; + utf8::utf16to8(data.begin(), data.end(), back_inserter(output)); + return; + } +} + +// ------------------------------------------------------------------------------------------------ +// Convert to UTF8 data to ISO-8859-1 +void BaseImporter::ConvertUTF8toISO8859_1(std::string& data) +{ + size_t size = data.size(); + size_t i = 0, j = 0; + + while(i < size) { + if ((unsigned char) data[i] < (size_t) 0x80) { + data[j] = data[i]; + } else if(i < size - 1) { + if((unsigned char) data[i] == 0xC2) { + data[j] = data[++i]; + } else if((unsigned char) data[i] == 0xC3) { + data[j] = ((unsigned char) data[++i] + 0x40); + } else { + std::stringstream stream; + stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1."; + ASSIMP_LOG_ERROR( stream.str() ); + + data[j++] = data[i++]; + data[j] = data[i]; + } + } else { + ASSIMP_LOG_ERROR("UTF8 code but only one character remaining"); + + data[j] = data[i]; + } + + i++; j++; + } + + data.resize(j); +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::TextFileToBuffer(IOStream* stream, + std::vector<char>& data, + TextFileMode mode) +{ + ai_assert(nullptr != stream); + + const size_t fileSize = stream->FileSize(); + if (mode == FORBID_EMPTY) { + if(!fileSize) { + throw DeadlyImportError("File is empty"); + } + } + + data.reserve(fileSize+1); + data.resize(fileSize); + if(fileSize > 0) { + if(fileSize != stream->Read( &data[0], 1, fileSize)) { + throw DeadlyImportError("File read error"); + } + + ConvertToUTF8(data); + } + + // append a binary zero to simplify string parsing + data.push_back(0); +} + +// ------------------------------------------------------------------------------------------------ +namespace Assimp { + // Represents an import request + struct LoadRequest { + LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id) + : file(_file) + , flags(_flags) + , refCnt(1) + , scene(NULL) + , loaded(false) + , id(_id) { + if ( _map ) { + map = *_map; + } + } + + bool operator== ( const std::string& f ) const { + return file == f; + } + + const std::string file; + unsigned int flags; + unsigned int refCnt; + aiScene *scene; + bool loaded; + BatchLoader::PropertyMap map; + unsigned int id; + }; +} + +// ------------------------------------------------------------------------------------------------ +// BatchLoader::pimpl data structure +struct Assimp::BatchData { + BatchData( IOSystem* pIO, bool validate ) + : pIOSystem( pIO ) + , pImporter( nullptr ) + , next_id(0xffff) + , validate( validate ) { + ai_assert( nullptr != pIO ); + + pImporter = new Importer(); + pImporter->SetIOHandler( pIO ); + } + + ~BatchData() { + pImporter->SetIOHandler( nullptr ); /* get pointer back into our possession */ + delete pImporter; + } + + // IO system to be used for all imports + IOSystem* pIOSystem; + + // Importer used to load all meshes + Importer* pImporter; + + // List of all imports + std::list<LoadRequest> requests; + + // Base path + std::string pathBase; + + // Id for next item + unsigned int next_id; + + // Validation enabled state + bool validate; +}; + +typedef std::list<LoadRequest>::iterator LoadReqIt; + +// ------------------------------------------------------------------------------------------------ +BatchLoader::BatchLoader(IOSystem* pIO, bool validate ) { + ai_assert(nullptr != pIO); + + m_data = new BatchData( pIO, validate ); +} + +// ------------------------------------------------------------------------------------------------ +BatchLoader::~BatchLoader() +{ + // delete all scenes what have not been polled by the user + for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { + delete (*it).scene; + } + delete m_data; +} + +// ------------------------------------------------------------------------------------------------ +void BatchLoader::setValidation( bool enabled ) { + m_data->validate = enabled; +} + +// ------------------------------------------------------------------------------------------------ +bool BatchLoader::getValidation() const { + return m_data->validate; +} + +// ------------------------------------------------------------------------------------------------ +unsigned int BatchLoader::AddLoadRequest(const std::string& file, + unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/) +{ + ai_assert(!file.empty()); + + // check whether we have this loading request already + for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { + // Call IOSystem's path comparison function here + if ( m_data->pIOSystem->ComparePaths((*it).file,file)) { + if (map) { + if ( !( ( *it ).map == *map ) ) { + continue; + } + } + else if ( !( *it ).map.empty() ) { + continue; + } + + (*it).refCnt++; + return (*it).id; + } + } + + // no, we don't have it. So add it to the queue ... + m_data->requests.push_back(LoadRequest(file,steps,map, m_data->next_id)); + return m_data->next_id++; +} + +// ------------------------------------------------------------------------------------------------ +aiScene* BatchLoader::GetImport( unsigned int which ) +{ + for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { + if ((*it).id == which && (*it).loaded) { + aiScene* sc = (*it).scene; + if (!(--(*it).refCnt)) { + m_data->requests.erase(it); + } + return sc; + } + } + return nullptr; +} + +// ------------------------------------------------------------------------------------------------ +void BatchLoader::LoadAll() +{ + // no threaded implementation for the moment + for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) { + // force validation in debug builds + unsigned int pp = (*it).flags; + if ( m_data->validate ) { + pp |= aiProcess_ValidateDataStructure; + } + + // setup config properties if necessary + ImporterPimpl* pimpl = m_data->pImporter->Pimpl(); + pimpl->mFloatProperties = (*it).map.floats; + pimpl->mIntProperties = (*it).map.ints; + pimpl->mStringProperties = (*it).map.strings; + pimpl->mMatrixProperties = (*it).map.matrices; + + if (!DefaultLogger::isNullLogger()) + { + ASSIMP_LOG_INFO("%%% BEGIN EXTERNAL FILE %%%"); + ASSIMP_LOG_INFO_F("File: ", (*it).file); + } + m_data->pImporter->ReadFile((*it).file,pp); + (*it).scene = m_data->pImporter->GetOrphanedScene(); + (*it).loaded = true; + + ASSIMP_LOG_INFO("%%% END EXTERNAL FILE %%%"); + } +} diff --git a/thirdparty/assimp/code/BaseProcess.cpp b/thirdparty/assimp/code/BaseProcess.cpp new file mode 100644 index 0000000000..18872c3693 --- /dev/null +++ b/thirdparty/assimp/code/BaseProcess.cpp @@ -0,0 +1,107 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of BaseProcess */ + +#include <assimp/BaseImporter.h> +#include "BaseProcess.h" +#include <assimp/DefaultLogger.hpp> +#include <assimp/scene.h> +#include "Importer.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BaseProcess::BaseProcess() AI_NO_EXCEPT +: shared() +, progress() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BaseProcess::~BaseProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +void BaseProcess::ExecuteOnScene( Importer* pImp) +{ + ai_assert(NULL != pImp && NULL != pImp->Pimpl()->mScene); + + progress = pImp->GetProgressHandler(); + ai_assert(progress); + + SetupProperties( pImp ); + + // catch exceptions thrown inside the PostProcess-Step + try + { + Execute(pImp->Pimpl()->mScene); + + } catch( const std::exception& err ) { + + // extract error description + pImp->Pimpl()->mErrorString = err.what(); + ASSIMP_LOG_ERROR(pImp->Pimpl()->mErrorString); + + // and kill the partially imported data + delete pImp->Pimpl()->mScene; + pImp->Pimpl()->mScene = NULL; + } +} + +// ------------------------------------------------------------------------------------------------ +void BaseProcess::SetupProperties(const Importer* /*pImp*/) +{ + // the default implementation does nothing +} + +// ------------------------------------------------------------------------------------------------ +bool BaseProcess::RequireVerboseFormat() const +{ + return true; +} + diff --git a/thirdparty/assimp/code/BaseProcess.h b/thirdparty/assimp/code/BaseProcess.h new file mode 100644 index 0000000000..4d5c7a76be --- /dev/null +++ b/thirdparty/assimp/code/BaseProcess.h @@ -0,0 +1,290 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Base class of all import post processing steps */ +#ifndef INCLUDED_AI_BASEPROCESS_H +#define INCLUDED_AI_BASEPROCESS_H + +#include <map> +#include <assimp/GenericProperty.h> + +struct aiScene; + +namespace Assimp { + +class Importer; + +// --------------------------------------------------------------------------- +/** Helper class to allow post-processing steps to interact with each other. + * + * The class maintains a simple property list that can be used by pp-steps + * to provide additional information to other steps. This is primarily + * intended for cross-step optimizations. + */ +class SharedPostProcessInfo +{ +public: + + struct Base + { + virtual ~Base() + {} + }; + + //! Represents data that is allocated on the heap, thus needs to be deleted + template <typename T> + struct THeapData : public Base + { + explicit THeapData(T* in) + : data (in) + {} + + ~THeapData() + { + delete data; + } + T* data; + }; + + //! Represents static, by-value data not allocated on the heap + template <typename T> + struct TStaticData : public Base + { + explicit TStaticData(T in) + : data (in) + {} + + ~TStaticData() + {} + + T data; + }; + + // some typedefs for cleaner code + typedef unsigned int KeyType; + typedef std::map<KeyType, Base*> PropertyMap; + +public: + + //! Destructor + ~SharedPostProcessInfo() + { + Clean(); + } + + //! Remove all stored properties from the table + void Clean() + { + // invoke the virtual destructor for all stored properties + for (PropertyMap::iterator it = pmap.begin(), end = pmap.end(); + it != end; ++it) + { + delete (*it).second; + } + pmap.clear(); + } + + //! Add a heap property to the list + template <typename T> + void AddProperty( const char* name, T* in ){ + AddProperty(name,(Base*)new THeapData<T>(in)); + } + + //! Add a static by-value property to the list + template <typename T> + void AddProperty( const char* name, T in ){ + AddProperty(name,(Base*)new TStaticData<T>(in)); + } + + + //! Get a heap property + template <typename T> + bool GetProperty( const char* name, T*& out ) const + { + THeapData<T>* t = (THeapData<T>*)GetPropertyInternal(name); + if(!t) + { + out = NULL; + return false; + } + out = t->data; + return true; + } + + //! Get a static, by-value property + template <typename T> + bool GetProperty( const char* name, T& out ) const + { + TStaticData<T>* t = (TStaticData<T>*)GetPropertyInternal(name); + if(!t)return false; + out = t->data; + return true; + } + + //! Remove a property of a specific type + void RemoveProperty( const char* name) { + SetGenericPropertyPtr<Base>(pmap,name,NULL); + } + +private: + + void AddProperty( const char* name, Base* data) { + SetGenericPropertyPtr<Base>(pmap,name,data); + } + + Base* GetPropertyInternal( const char* name) const { + return GetGenericProperty<Base*>(pmap,name,NULL); + } + +private: + + //! Map of all stored properties + PropertyMap pmap; +}; + +#if 0 + +// --------------------------------------------------------------------------- +/** @brief Represents a dependency table for a postprocessing steps. + * + * For future use. + */ + struct PPDependencyTable + { + unsigned int execute_me_before_these; + unsigned int execute_me_after_these; + unsigned int only_if_these_are_not_specified; + unsigned int mutually_exclusive_with; + }; + +#endif + + +#define AI_SPP_SPATIAL_SORT "$Spat" + +// --------------------------------------------------------------------------- +/** The BaseProcess defines a common interface for all post processing steps. + * A post processing step is run after a successful import if the caller + * specified the corresponding flag when calling ReadFile(). + * Enum #aiPostProcessSteps defines which flags are available. + * After a successful import the Importer iterates over its internal array + * of processes and calls IsActive() on each process to evaluate if the step + * should be executed. If the function returns true, the class' Execute() + * function is called subsequently. + */ +class ASSIMP_API_WINONLY BaseProcess { + friend class Importer; + +public: + /** Constructor to be privately used by Importer */ + BaseProcess() AI_NO_EXCEPT; + + /** Destructor, private as well */ + virtual ~BaseProcess(); + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. A + * bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + virtual bool IsActive( unsigned int pFlags) const = 0; + + // ------------------------------------------------------------------- + /** Check whether this step expects its input vertex data to be + * in verbose format. */ + virtual bool RequireVerboseFormat() const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * The function deletes the scene if the postprocess step fails ( + * the object pointer will be set to NULL). + * @param pImp Importer instance (pImp->mScene must be valid) + */ + void ExecuteOnScene( Importer* pImp); + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * A process should throw an ImportErrorException* if it fails. + * This method must be implemented by deriving classes. + * @param pScene The imported data to work at. + */ + virtual void Execute( aiScene* pScene) = 0; + + + // ------------------------------------------------------------------- + /** Assign a new SharedPostProcessInfo to the step. This object + * allows multiple postprocess steps to share data. + * @param sh May be NULL + */ + inline void SetSharedData(SharedPostProcessInfo* sh) { + shared = sh; + } + + // ------------------------------------------------------------------- + /** Get the shared data that is assigned to the step. + */ + inline SharedPostProcessInfo* GetSharedData() { + return shared; + } + +protected: + + /** See the doc of #SharedPostProcessInfo for more details */ + SharedPostProcessInfo* shared; + + /** Currently active progress handler */ + ProgressHandler* progress; +}; + + +} // end of namespace Assimp + +#endif // AI_BASEPROCESS_H_INC diff --git a/thirdparty/assimp/code/Bitmap.cpp b/thirdparty/assimp/code/Bitmap.cpp new file mode 100644 index 0000000000..b22b71ea9e --- /dev/null +++ b/thirdparty/assimp/code/Bitmap.cpp @@ -0,0 +1,155 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Bitmap.cpp + * @brief Defines bitmap format helper for textures + * + * Used for file formats which embed their textures into the model file. + */ + + +#include <assimp/Bitmap.h> +#include <assimp/texture.h> +#include <assimp/IOStream.hpp> +#include <assimp/ByteSwapper.h> + +namespace Assimp { + + void Bitmap::Save(aiTexture* texture, IOStream* file) { + if(file != NULL) { + Header header; + DIB dib; + + dib.size = DIB::dib_size; + dib.width = texture->mWidth; + dib.height = texture->mHeight; + dib.planes = 1; + dib.bits_per_pixel = 8 * mBytesPerPixel; + dib.compression = 0; + dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height; + dib.x_resolution = 0; + dib.y_resolution = 0; + dib.nb_colors = 0; + dib.nb_important_colors = 0; + + header.type = 0x4D42; // 'BM' + header.offset = Header::header_size + DIB::dib_size; + header.size = header.offset + dib.image_size; + header.reserved1 = 0; + header.reserved2 = 0; + + WriteHeader(header, file); + WriteDIB(dib, file); + WriteData(texture, file); + } + } + + template<typename T> + inline + std::size_t Copy(uint8_t* data, const T &field) { +#ifdef AI_BUILD_BIG_ENDIAN + T field_swapped=AI_BE(field); + std::memcpy(data, &field_swapped, sizeof(field)); return sizeof(field); +#else + std::memcpy(data, &AI_BE(field), sizeof(field)); return sizeof(field); +#endif + } + + void Bitmap::WriteHeader(Header& header, IOStream* file) { + uint8_t data[Header::header_size]; + + std::size_t offset = 0; + + offset += Copy(&data[offset], header.type); + offset += Copy(&data[offset], header.size); + offset += Copy(&data[offset], header.reserved1); + offset += Copy(&data[offset], header.reserved2); + Copy(&data[offset], header.offset); + + file->Write(data, Header::header_size, 1); + } + + void Bitmap::WriteDIB(DIB& dib, IOStream* file) { + uint8_t data[DIB::dib_size]; + + std::size_t offset = 0; + + offset += Copy(&data[offset], dib.size); + offset += Copy(&data[offset], dib.width); + offset += Copy(&data[offset], dib.height); + offset += Copy(&data[offset], dib.planes); + offset += Copy(&data[offset], dib.bits_per_pixel); + offset += Copy(&data[offset], dib.compression); + offset += Copy(&data[offset], dib.image_size); + offset += Copy(&data[offset], dib.x_resolution); + offset += Copy(&data[offset], dib.y_resolution); + offset += Copy(&data[offset], dib.nb_colors); + Copy(&data[offset], dib.nb_important_colors); + + file->Write(data, DIB::dib_size, 1); + } + + void Bitmap::WriteData(aiTexture* texture, IOStream* file) { + static const std::size_t padding_offset = 4; + static const uint8_t padding_data[padding_offset] = {0x0, 0x0, 0x0, 0x0}; + + unsigned int padding = (padding_offset - ((mBytesPerPixel * texture->mWidth) % padding_offset)) % padding_offset; + uint8_t pixel[mBytesPerPixel]; + + for(std::size_t i = 0; i < texture->mHeight; ++i) { + for(std::size_t j = 0; j < texture->mWidth; ++j) { + const aiTexel& texel = texture->pcData[(texture->mHeight - i - 1) * texture->mWidth + j]; // Bitmap files are stored in bottom-up format + + pixel[0] = texel.r; + pixel[1] = texel.g; + pixel[2] = texel.b; + pixel[3] = texel.a; + + file->Write(pixel, mBytesPerPixel, 1); + } + + file->Write(padding_data, padding, 1); + } + } + +} diff --git a/thirdparty/assimp/code/CInterfaceIOWrapper.cpp b/thirdparty/assimp/code/CInterfaceIOWrapper.cpp new file mode 100644 index 0000000000..5a3a49565a --- /dev/null +++ b/thirdparty/assimp/code/CInterfaceIOWrapper.cpp @@ -0,0 +1,136 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file aiFileIO -> IOSystem wrapper*/ + +#include "CInterfaceIOWrapper.h" + +namespace Assimp { + +CIOStreamWrapper::~CIOStreamWrapper(void) +{ + /* Various places depend on this destructor to close the file */ + if (mFile) { + mIO->mFileSystem->CloseProc(mIO->mFileSystem, mFile); + mFile = nullptr; + } +} + +// ................................................................... +size_t CIOStreamWrapper::Read(void* pvBuffer, + size_t pSize, + size_t pCount +){ + // need to typecast here as C has no void* + return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount); +} + +// ................................................................... +size_t CIOStreamWrapper::Write(const void* pvBuffer, + size_t pSize, + size_t pCount +){ + // need to typecast here as C has no void* + return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount); +} + +// ................................................................... +aiReturn CIOStreamWrapper::Seek(size_t pOffset, + aiOrigin pOrigin +){ + return mFile->SeekProc(mFile,pOffset,pOrigin); +} + +// ................................................................... +size_t CIOStreamWrapper::Tell(void) const { + return mFile->TellProc(mFile); +} + +// ................................................................... +size_t CIOStreamWrapper::FileSize() const { + return mFile->FileSizeProc(mFile); +} + +// ................................................................... +void CIOStreamWrapper::Flush () { + return mFile->FlushProc(mFile); +} + +// ------------------------------------------------------------------------------------------------ +// Custom IOStream implementation for the C-API +bool CIOSystemWrapper::Exists( const char* pFile) const { + aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,"rb"); + if (p){ + mFileSystem->CloseProc(mFileSystem,p); + return true; + } + return false; +} + +// ................................................................... +char CIOSystemWrapper::getOsSeparator() const { +#ifndef _WIN32 + return '/'; +#else + return '\\'; +#endif +} + +// ................................................................... +IOStream* CIOSystemWrapper::Open(const char* pFile,const char* pMode) { + aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,pMode); + if (!p) { + return NULL; + } + return new CIOStreamWrapper(p, this); +} + +// ................................................................... +void CIOSystemWrapper::Close( IOStream* pFile) { + if (!pFile) { + return; + } + delete pFile; +} + +} diff --git a/thirdparty/assimp/code/CInterfaceIOWrapper.h b/thirdparty/assimp/code/CInterfaceIOWrapper.h new file mode 100644 index 0000000000..2162320302 --- /dev/null +++ b/thirdparty/assimp/code/CInterfaceIOWrapper.h @@ -0,0 +1,99 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file aiFileIO -> IOSystem wrapper*/ + +#ifndef AI_CIOSYSTEM_H_INCLUDED +#define AI_CIOSYSTEM_H_INCLUDED + +#include <assimp/cfileio.h> +#include <assimp/IOStream.hpp> +#include <assimp/IOSystem.hpp> + +namespace Assimp { + +class CIOSystemWrapper; + +// ------------------------------------------------------------------------------------------------ +// Custom IOStream implementation for the C-API +class CIOStreamWrapper : public IOStream +{ +public: + explicit CIOStreamWrapper(aiFile* pFile, CIOSystemWrapper* io) + : mFile(pFile), + mIO(io) + {} + ~CIOStreamWrapper(void); + + size_t Read(void* pvBuffer, size_t pSize, size_t pCount); + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount); + aiReturn Seek(size_t pOffset, aiOrigin pOrigin); + size_t Tell(void) const; + size_t FileSize() const; + void Flush(); + +private: + aiFile* mFile; + CIOSystemWrapper* mIO; +}; + +class CIOSystemWrapper : public IOSystem +{ + friend class CIOStreamWrapper; +public: + explicit CIOSystemWrapper(aiFileIO* pFile) + : mFileSystem(pFile) + {} + + bool Exists( const char* pFile) const; + char getOsSeparator() const; + IOStream* Open(const char* pFile,const char* pMode = "rb"); + void Close( IOStream* pFile); +private: + aiFileIO* mFileSystem; +}; + +} + +#endif + diff --git a/thirdparty/assimp/code/CalcTangentsProcess.cpp b/thirdparty/assimp/code/CalcTangentsProcess.cpp new file mode 100644 index 0000000000..b30f39c274 --- /dev/null +++ b/thirdparty/assimp/code/CalcTangentsProcess.cpp @@ -0,0 +1,319 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to calculate + * tangents and bitangents for all imported meshes + */ + +// internal headers +#include "CalcTangentsProcess.h" +#include "ProcessHelper.h" +#include <assimp/TinyFormatter.h> +#include <assimp/qnan.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +CalcTangentsProcess::CalcTangentsProcess() +: configMaxAngle( AI_DEG_TO_RAD(45.f) ) +, configSourceUV( 0 ) { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +CalcTangentsProcess::~CalcTangentsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool CalcTangentsProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_CalcTangentSpace) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void CalcTangentsProcess::SetupProperties(const Importer* pImp) +{ + ai_assert( NULL != pImp ); + + // get the current value of the property + configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f); + configMaxAngle = std::max(std::min(configMaxAngle,45.0f),0.0f); + configMaxAngle = AI_DEG_TO_RAD(configMaxAngle); + + configSourceUV = pImp->GetPropertyInteger(AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX,0); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void CalcTangentsProcess::Execute( aiScene* pScene) +{ + ai_assert( NULL != pScene ); + + ASSIMP_LOG_DEBUG("CalcTangentsProcess begin"); + + bool bHas = false; + for ( unsigned int a = 0; a < pScene->mNumMeshes; a++ ) { + if(ProcessMesh( pScene->mMeshes[a],a))bHas = true; + } + + if ( bHas ) { + ASSIMP_LOG_INFO("CalcTangentsProcess finished. Tangents have been calculated"); + } else { + ASSIMP_LOG_DEBUG("CalcTangentsProcess finished"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Calculates tangents and bi-tangents for the given mesh +bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) +{ + // we assume that the mesh is still in the verbose vertex format where each face has its own set + // of vertices and no vertices are shared between faces. Sadly I don't know any quick test to + // assert() it here. + // assert( must be verbose, dammit); + + if (pMesh->mTangents) // this implies that mBitangents is also there + return false; + + // If the mesh consists of lines and/or points but not of + // triangles or higher-order polygons the normal vectors + // are undefined. + if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) + { + ASSIMP_LOG_INFO("Tangents are undefined for line and point meshes"); + return false; + } + + // what we can check, though, is if the mesh has normals and texture coordinates. That's a requirement + if( pMesh->mNormals == NULL) + { + ASSIMP_LOG_ERROR("Failed to compute tangents; need normals"); + return false; + } + if( configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV] ) + { + ASSIMP_LOG_ERROR((Formatter::format("Failed to compute tangents; need UV data in channel"),configSourceUV)); + return false; + } + + const float angleEpsilon = 0.9999f; + + std::vector<bool> vertexDone( pMesh->mNumVertices, false); + const float qnan = get_qnan(); + + // create space for the tangents and bitangents + pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; + pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; + + const aiVector3D* meshPos = pMesh->mVertices; + const aiVector3D* meshNorm = pMesh->mNormals; + const aiVector3D* meshTex = pMesh->mTextureCoords[configSourceUV]; + aiVector3D* meshTang = pMesh->mTangents; + aiVector3D* meshBitang = pMesh->mBitangents; + + // calculate the tangent and bitangent for every face + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + const aiFace& face = pMesh->mFaces[a]; + if (face.mNumIndices < 3) + { + // There are less than three indices, thus the tangent vector + // is not defined. We are finished with these vertices now, + // their tangent vectors are set to qnan. + for (unsigned int i = 0; i < face.mNumIndices;++i) + { + unsigned int idx = face.mIndices[i]; + vertexDone [idx] = true; + meshTang [idx] = aiVector3D(qnan); + meshBitang [idx] = aiVector3D(qnan); + } + + continue; + } + + // triangle or polygon... we always use only the first three indices. A polygon + // is supposed to be planar anyways.... + // FIXME: (thom) create correct calculation for multi-vertex polygons maybe? + const unsigned int p0 = face.mIndices[0], p1 = face.mIndices[1], p2 = face.mIndices[2]; + + // position differences p1->p2 and p1->p3 + aiVector3D v = meshPos[p1] - meshPos[p0], w = meshPos[p2] - meshPos[p0]; + + // texture offset p1->p2 and p1->p3 + float sx = meshTex[p1].x - meshTex[p0].x, sy = meshTex[p1].y - meshTex[p0].y; + float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y; + float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f; + // when t1, t2, t3 in same position in UV space, just use default UV direction. + if ( sx * ty == sy * tx ) { + sx = 0.0; sy = 1.0; + tx = 1.0; ty = 0.0; + } + + // tangent points in the direction where to positive X axis of the texture coord's would point in model space + // bitangent's points along the positive Y axis of the texture coord's, respectively + aiVector3D tangent, bitangent; + tangent.x = (w.x * sy - v.x * ty) * dirCorrection; + tangent.y = (w.y * sy - v.y * ty) * dirCorrection; + tangent.z = (w.z * sy - v.z * ty) * dirCorrection; + bitangent.x = (w.x * sx - v.x * tx) * dirCorrection; + bitangent.y = (w.y * sx - v.y * tx) * dirCorrection; + bitangent.z = (w.z * sx - v.z * tx) * dirCorrection; + + // store for every vertex of that face + for( unsigned int b = 0; b < face.mNumIndices; ++b ) { + unsigned int p = face.mIndices[b]; + + // project tangent and bitangent into the plane formed by the vertex' normal + aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]); + aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]); + localTangent.Normalize(); localBitangent.Normalize(); + + // reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN. + bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z); + bool invalid_bitangent = is_special_float(localBitangent.x) || is_special_float(localBitangent.y) || is_special_float(localBitangent.z); + if (invalid_tangent != invalid_bitangent) { + if (invalid_tangent) { + localTangent = meshNorm[p] ^ localBitangent; + localTangent.Normalize(); + } else { + localBitangent = localTangent ^ meshNorm[p]; + localBitangent.Normalize(); + } + } + + // and write it into the mesh. + meshTang[ p ] = localTangent; + meshBitang[ p ] = localBitangent; + } + } + + + // create a helper to quickly find locally close vertices among the vertex array + // FIX: check whether we can reuse the SpatialSort of a previous step + SpatialSort* vertexFinder = NULL; + SpatialSort _vertexFinder; + float posEpsilon; + if (shared) + { + std::vector<std::pair<SpatialSort,float> >* avf; + shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); + if (avf) + { + std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex); + vertexFinder = &blubb.first; + posEpsilon = blubb.second;; + } + } + if (!vertexFinder) + { + _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); + vertexFinder = &_vertexFinder; + posEpsilon = ComputePositionEpsilon(pMesh); + } + std::vector<unsigned int> verticesFound; + + const float fLimit = std::cos(configMaxAngle); + std::vector<unsigned int> closeVertices; + + // in the second pass we now smooth out all tangents and bitangents at the same local position + // if they are not too far off. + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + { + if( vertexDone[a]) + continue; + + const aiVector3D& origPos = pMesh->mVertices[a]; + const aiVector3D& origNorm = pMesh->mNormals[a]; + const aiVector3D& origTang = pMesh->mTangents[a]; + const aiVector3D& origBitang = pMesh->mBitangents[a]; + closeVertices.resize( 0 ); + + // find all vertices close to that position + vertexFinder->FindPositions( origPos, posEpsilon, verticesFound); + + closeVertices.reserve (verticesFound.size()+5); + closeVertices.push_back( a); + + // look among them for other vertices sharing the same normal and a close-enough tangent/bitangent + for( unsigned int b = 0; b < verticesFound.size(); b++) + { + unsigned int idx = verticesFound[b]; + if( vertexDone[idx]) + continue; + if( meshNorm[idx] * origNorm < angleEpsilon) + continue; + if( meshTang[idx] * origTang < fLimit) + continue; + if( meshBitang[idx] * origBitang < fLimit) + continue; + + // it's similar enough -> add it to the smoothing group + closeVertices.push_back( idx); + vertexDone[idx] = true; + } + + // smooth the tangents and bitangents of all vertices that were found to be close enough + aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0); + for( unsigned int b = 0; b < closeVertices.size(); ++b) + { + smoothTangent += meshTang[ closeVertices[b] ]; + smoothBitangent += meshBitang[ closeVertices[b] ]; + } + smoothTangent.Normalize(); + smoothBitangent.Normalize(); + + // and write it back into all affected tangents + for( unsigned int b = 0; b < closeVertices.size(); ++b) + { + meshTang[ closeVertices[b] ] = smoothTangent; + meshBitang[ closeVertices[b] ] = smoothBitangent; + } + } + return true; +} diff --git a/thirdparty/assimp/code/CalcTangentsProcess.h b/thirdparty/assimp/code/CalcTangentsProcess.h new file mode 100644 index 0000000000..18775abcc7 --- /dev/null +++ b/thirdparty/assimp/code/CalcTangentsProcess.h @@ -0,0 +1,117 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + + +/** @file Defines a post processing step to calculate tangents and + bitangents on all imported meshes.*/ +#ifndef AI_CALCTANGENTSPROCESS_H_INC +#define AI_CALCTANGENTSPROCESS_H_INC + +#include "BaseProcess.h" + +struct aiMesh; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The CalcTangentsProcess calculates the tangent and bitangent for any vertex + * of all meshes. It is expected to be run before the JoinVerticesProcess runs + * because the joining of vertices also considers tangents and bitangents for + * uniqueness. + */ +class ASSIMP_API_WINONLY CalcTangentsProcess : public BaseProcess +{ +public: + + CalcTangentsProcess(); + ~CalcTangentsProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. + * A bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + + + // setter for configMaxAngle + inline void SetMaxSmoothAngle(float f) + { + configMaxAngle =f; + } + +protected: + + // ------------------------------------------------------------------- + /** Calculates tangents and bitangents for a specific mesh. + * @param pMesh The mesh to process. + * @param meshIndex Index of the mesh + */ + bool ProcessMesh( aiMesh* pMesh, unsigned int meshIndex); + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + +private: + + /** Configuration option: maximum smoothing angle, in radians*/ + float configMaxAngle; + unsigned int configSourceUV; +}; + +} // end of namespace Assimp + +#endif // AI_CALCTANGENTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/ComputeUVMappingProcess.cpp b/thirdparty/assimp/code/ComputeUVMappingProcess.cpp new file mode 100644 index 0000000000..bb571a551b --- /dev/null +++ b/thirdparty/assimp/code/ComputeUVMappingProcess.cpp @@ -0,0 +1,506 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file GenUVCoords step */ + + +#include "ComputeUVMappingProcess.h" +#include "ProcessHelper.h" +#include <assimp/Exceptional.h> + +using namespace Assimp; + +namespace { + + const static aiVector3D base_axis_y(0.0,1.0,0.0); + const static aiVector3D base_axis_x(1.0,0.0,0.0); + const static aiVector3D base_axis_z(0.0,0.0,1.0); + const static ai_real angle_epsilon = ai_real( 0.95 ); +} + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +ComputeUVMappingProcess::ComputeUVMappingProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +ComputeUVMappingProcess::~ComputeUVMappingProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool ComputeUVMappingProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_GenUVCoords) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Check whether a ray intersects a plane and find the intersection point +inline bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos, + const aiVector3D& planeNormal, aiVector3D& pos) +{ + const ai_real b = planeNormal * (planePos - ray.pos); + ai_real h = ray.dir * planeNormal; + if ((h < 10e-5 && h > -10e-5) || (h = b/h) < 0) + return false; + + pos = ray.pos + (ray.dir * h); + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Find the first empty UV channel in a mesh +inline unsigned int FindEmptyUVChannel (aiMesh* mesh) +{ + for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS;++m) + if (!mesh->mTextureCoords[m])return m; + + ASSIMP_LOG_ERROR("Unable to compute UV coordinates, no free UV slot found"); + return UINT_MAX; +} + +// ------------------------------------------------------------------------------------------------ +// Try to remove UV seams +void RemoveUVSeams (aiMesh* mesh, aiVector3D* out) +{ + // TODO: just a very rough algorithm. I think it could be done + // much easier, but I don't know how and am currently too tired to + // to think about a better solution. + + const static ai_real LOWER_LIMIT = ai_real( 0.1 ); + const static ai_real UPPER_LIMIT = ai_real( 0.9 ); + + const static ai_real LOWER_EPSILON = ai_real( 10e-3 ); + const static ai_real UPPER_EPSILON = ai_real( 1.0-10e-3 ); + + for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx) + { + const aiFace& face = mesh->mFaces[fidx]; + if (face.mNumIndices < 3) continue; // triangles and polygons only, please + + unsigned int small = face.mNumIndices, large = small; + bool zero = false, one = false, round_to_zero = false; + + // Check whether this face lies on a UV seam. We can just guess, + // but the assumption that a face with at least one very small + // on the one side and one very large U coord on the other side + // lies on a UV seam should work for most cases. + for (unsigned int n = 0; n < face.mNumIndices;++n) + { + if (out[face.mIndices[n]].x < LOWER_LIMIT) + { + small = n; + + // If we have a U value very close to 0 we can't + // round the others to 0, too. + if (out[face.mIndices[n]].x <= LOWER_EPSILON) + zero = true; + else round_to_zero = true; + } + if (out[face.mIndices[n]].x > UPPER_LIMIT) + { + large = n; + + // If we have a U value very close to 1 we can't + // round the others to 1, too. + if (out[face.mIndices[n]].x >= UPPER_EPSILON) + one = true; + } + } + if (small != face.mNumIndices && large != face.mNumIndices) + { + for (unsigned int n = 0; n < face.mNumIndices;++n) + { + // If the u value is over the upper limit and no other u + // value of that face is 0, round it to 0 + if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero) + out[face.mIndices[n]].x = 0.0; + + // If the u value is below the lower limit and no other u + // value of that face is 1, round it to 1 + else if (out[face.mIndices[n]].x < LOWER_LIMIT && !one) + out[face.mIndices[n]].x = 1.0; + + // The face contains both 0 and 1 as UV coords. This can occur + // for faces which have an edge that lies directly on the seam. + // Due to numerical inaccuracies one U coord becomes 0, the + // other 1. But we do still have a third UV coord to determine + // to which side we must round to. + else if (one && zero) + { + if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON) + out[face.mIndices[n]].x = 0.0; + else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON) + out[face.mIndices[n]].x = 1.0; + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) +{ + aiVector3D center, min, max; + FindMeshCenter(mesh, center, min, max); + + // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ... + // currently the mapping axis will always be one of x,y,z, except if the + // PretransformVertices step is used (it transforms the meshes into worldspace, + // thus changing the mapping axis) + if (axis * base_axis_x >= angle_epsilon) { + + // For each point get a normalized projection vector in the sphere, + // get its longitude and latitude and map them to their respective + // UV axes. Problems occur around the poles ... unsolvable. + // + // The spherical coordinate system looks like this: + // x = cos(lon)*cos(lat) + // y = sin(lon)*cos(lat) + // z = sin(lat) + // + // Thus we can derive: + // lat = arcsin (z) + // lon = arctan (y/x) + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize(); + out[pnt] = aiVector3D((std::atan2(diff.z, diff.y) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, + (std::asin (diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); + } + } + else if (axis * base_axis_y >= angle_epsilon) { + // ... just the same again + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize(); + out[pnt] = aiVector3D((std::atan2(diff.x, diff.z) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, + (std::asin (diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); + } + } + else if (axis * base_axis_z >= angle_epsilon) { + // ... just the same again + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize(); + out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, + (std::asin (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); + } + } + // slower code path in case the mapping axis is not one of the coordinate system axes + else { + aiMatrix4x4 mTrafo; + aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo); + + // again the same, except we're applying a transformation now + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D diff = ((mTrafo*mesh->mVertices[pnt])-center).Normalize(); + out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, + (std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); + } + } + + + // Now find and remove UV seams. A seam occurs if a face has a tcoord + // close to zero on the one side, and a tcoord close to one on the + // other side. + RemoveUVSeams(mesh,out); +} + +// ------------------------------------------------------------------------------------------------ +void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) +{ + aiVector3D center, min, max; + + // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ... + // currently the mapping axis will always be one of x,y,z, except if the + // PretransformVertices step is used (it transforms the meshes into worldspace, + // thus changing the mapping axis) + if (axis * base_axis_x >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + const ai_real diff = max.x - min.x; + + // If the main axis is 'z', the z coordinate of a point 'p' is mapped + // directly to the texture V axis. The other axis is derived from + // the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where + // 'c' is the center point of the mesh. + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + aiVector3D& uv = out[pnt]; + + uv.y = (pos.x - min.x) / diff; + uv.x = (std::atan2( pos.z - center.z, pos.y - center.y) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; + } + } + else if (axis * base_axis_y >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + const ai_real diff = max.y - min.y; + + // just the same ... + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + aiVector3D& uv = out[pnt]; + + uv.y = (pos.y - min.y) / diff; + uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; + } + } + else if (axis * base_axis_z >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + const ai_real diff = max.z - min.z; + + // just the same ... + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + aiVector3D& uv = out[pnt]; + + uv.y = (pos.z - min.z) / diff; + uv.x = (std::atan2( pos.y - center.y, pos.x - center.x) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; + } + } + // slower code path in case the mapping axis is not one of the coordinate system axes + else { + aiMatrix4x4 mTrafo; + aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo); + FindMeshCenterTransformed(mesh, center, min, max,mTrafo); + const ai_real diff = max.y - min.y; + + // again the same, except we're applying a transformation now + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt){ + const aiVector3D pos = mTrafo* mesh->mVertices[pnt]; + aiVector3D& uv = out[pnt]; + + uv.y = (pos.y - min.y) / diff; + uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; + } + } + + // Now find and remove UV seams. A seam occurs if a face has a tcoord + // close to zero on the one side, and a tcoord close to one on the + // other side. + RemoveUVSeams(mesh,out); +} + +// ------------------------------------------------------------------------------------------------ +void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) +{ + ai_real diffu,diffv; + aiVector3D center, min, max; + + // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ... + // currently the mapping axis will always be one of x,y,z, except if the + // PretransformVertices step is used (it transforms the meshes into worldspace, + // thus changing the mapping axis) + if (axis * base_axis_x >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + diffu = max.z - min.z; + diffv = max.y - min.y; + + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + out[pnt].Set((pos.z - min.z) / diffu,(pos.y - min.y) / diffv,0.0); + } + } + else if (axis * base_axis_y >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + diffu = max.x - min.x; + diffv = max.z - min.z; + + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0); + } + } + else if (axis * base_axis_z >= angle_epsilon) { + FindMeshCenter(mesh, center, min, max); + diffu = max.y - min.y; + diffv = max.z - min.z; + + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D& pos = mesh->mVertices[pnt]; + out[pnt].Set((pos.y - min.y) / diffu,(pos.x - min.x) / diffv,0.0); + } + } + // slower code path in case the mapping axis is not one of the coordinate system axes + else + { + aiMatrix4x4 mTrafo; + aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo); + FindMeshCenterTransformed(mesh, center, min, max,mTrafo); + diffu = max.x - min.x; + diffv = max.z - min.z; + + // again the same, except we're applying a transformation now + for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { + const aiVector3D pos = mTrafo * mesh->mVertices[pnt]; + out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0); + } + } + + // shouldn't be necessary to remove UV seams ... +} + +// ------------------------------------------------------------------------------------------------ +void ComputeUVMappingProcess::ComputeBoxMapping( aiMesh*, aiVector3D* ) +{ + ASSIMP_LOG_ERROR("Mapping type currently not implemented"); +} + +// ------------------------------------------------------------------------------------------------ +void ComputeUVMappingProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("GenUVCoordsProcess begin"); + char buffer[1024]; + + if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) + throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); + + std::list<MappingInfo> mappingStack; + + /* Iterate through all materials and search for non-UV mapped textures + */ + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) + { + mappingStack.clear(); + aiMaterial* mat = pScene->mMaterials[i]; + for (unsigned int a = 0; a < mat->mNumProperties;++a) + { + aiMaterialProperty* prop = mat->mProperties[a]; + if (!::strcmp( prop->mKey.data, "$tex.mapping")) + { + aiTextureMapping& mapping = *((aiTextureMapping*)prop->mData); + if (aiTextureMapping_UV != mapping) + { + if (!DefaultLogger::isNullLogger()) + { + ai_snprintf(buffer, 1024, "Found non-UV mapped texture (%s,%u). Mapping type: %s", + TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex, + MappingTypeToString(mapping)); + + ASSIMP_LOG_INFO(buffer); + } + + if (aiTextureMapping_OTHER == mapping) + continue; + + MappingInfo info (mapping); + + // Get further properties - currently only the major axis + for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2) + { + aiMaterialProperty* prop2 = mat->mProperties[a2]; + if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex) + continue; + + if ( !::strcmp( prop2->mKey.data, "$tex.mapaxis")) { + info.axis = *((aiVector3D*)prop2->mData); + break; + } + } + + unsigned int idx( 99999999 ); + + // Check whether we have this mapping mode already + std::list<MappingInfo>::iterator it = std::find (mappingStack.begin(),mappingStack.end(), info); + if (mappingStack.end() != it) + { + idx = (*it).uv; + } + else + { + /* We have found a non-UV mapped texture. Now + * we need to find all meshes using this material + * that we can compute UV channels for them. + */ + for (unsigned int m = 0; m < pScene->mNumMeshes;++m) + { + aiMesh* mesh = pScene->mMeshes[m]; + unsigned int outIdx = 0; + if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == UINT_MAX || + !mesh->mNumVertices) + { + continue; + } + + // Allocate output storage + aiVector3D* p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices]; + + switch (mapping) + { + case aiTextureMapping_SPHERE: + ComputeSphereMapping(mesh,info.axis,p); + break; + case aiTextureMapping_CYLINDER: + ComputeCylinderMapping(mesh,info.axis,p); + break; + case aiTextureMapping_PLANE: + ComputePlaneMapping(mesh,info.axis,p); + break; + case aiTextureMapping_BOX: + ComputeBoxMapping(mesh,p); + break; + default: + ai_assert(false); + } + if (m && idx != outIdx) + { + ASSIMP_LOG_WARN("UV index mismatch. Not all meshes assigned to " + "this material have equal numbers of UV channels. The UV index stored in " + "the material structure does therefore not apply for all meshes. "); + } + idx = outIdx; + } + info.uv = idx; + mappingStack.push_back(info); + } + + // Update the material property list + mapping = aiTextureMapping_UV; + ((aiMaterial*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex)); + } + } + } + } + ASSIMP_LOG_DEBUG("GenUVCoordsProcess finished"); +} diff --git a/thirdparty/assimp/code/ComputeUVMappingProcess.h b/thirdparty/assimp/code/ComputeUVMappingProcess.h new file mode 100644 index 0000000000..24f6bb7218 --- /dev/null +++ b/thirdparty/assimp/code/ComputeUVMappingProcess.h @@ -0,0 +1,148 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to compute UV coordinates + from abstract mappings, such as box or spherical*/ +#ifndef AI_COMPUTEUVMAPPING_H_INC +#define AI_COMPUTEUVMAPPING_H_INC + +#include "BaseProcess.h" +#include <assimp/mesh.h> +#include <assimp/material.h> +#include <assimp/types.h> + +class ComputeUVMappingTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** ComputeUVMappingProcess - converts special mappings, such as spherical, + * cylindrical or boxed to proper UV coordinates for rendering. +*/ +class ComputeUVMappingProcess : public BaseProcess +{ +public: + ComputeUVMappingProcess(); + ~ComputeUVMappingProcess(); + +public: + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + +protected: + + // ------------------------------------------------------------------- + /** Computes spherical UV coordinates for a mesh + * + * @param mesh Mesh to be processed + * @param axis Main axis + * @param out Receives output UV coordinates + */ + void ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, + aiVector3D* out); + + // ------------------------------------------------------------------- + /** Computes cylindrical UV coordinates for a mesh + * + * @param mesh Mesh to be processed + * @param axis Main axis + * @param out Receives output UV coordinates + */ + void ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, + aiVector3D* out); + + // ------------------------------------------------------------------- + /** Computes planar UV coordinates for a mesh + * + * @param mesh Mesh to be processed + * @param axis Main axis + * @param out Receives output UV coordinates + */ + void ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, + aiVector3D* out); + + // ------------------------------------------------------------------- + /** Computes cubic UV coordinates for a mesh + * + * @param mesh Mesh to be processed + * @param out Receives output UV coordinates + */ + void ComputeBoxMapping(aiMesh* mesh, aiVector3D* out); + +private: + + // temporary structure to describe a mapping + struct MappingInfo + { + explicit MappingInfo(aiTextureMapping _type) + : type (_type) + , axis (0.f,1.f,0.f) + , uv (0u) + {} + + aiTextureMapping type; + aiVector3D axis; + unsigned int uv; + + bool operator== (const MappingInfo& other) + { + return type == other.type && axis == other.axis; + } + }; +}; + +} // end of namespace Assimp + +#endif // AI_COMPUTEUVMAPPING_H_INC diff --git a/thirdparty/assimp/code/ConvertToLHProcess.cpp b/thirdparty/assimp/code/ConvertToLHProcess.cpp new file mode 100644 index 0000000000..b7cd4f0bc6 --- /dev/null +++ b/thirdparty/assimp/code/ConvertToLHProcess.cpp @@ -0,0 +1,414 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file MakeLeftHandedProcess.cpp + * @brief Implementation of the post processing step to convert all + * imported data to a left-handed coordinate system. + * + * Face order & UV flip are also implemented here, for the sake of a + * better location. + */ + + +#include "ConvertToLHProcess.h" +#include <assimp/scene.h> +#include <assimp/postprocess.h> +#include <assimp/DefaultLogger.hpp> + +using namespace Assimp; + +#ifndef ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS + +namespace { + +template <typename aiMeshType> +void flipUVs(aiMeshType* pMesh) { + if (pMesh == nullptr) { return; } + // mirror texture y coordinate + for (unsigned int tcIdx = 0; tcIdx < AI_MAX_NUMBER_OF_TEXTURECOORDS; tcIdx++) { + if (!pMesh->HasTextureCoords(tcIdx)) { + break; + } + + for (unsigned int vIdx = 0; vIdx < pMesh->mNumVertices; vIdx++) { + pMesh->mTextureCoords[tcIdx][vIdx].y = 1.0f - pMesh->mTextureCoords[tcIdx][vIdx].y; + } + } +} + +} // namespace + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +MakeLeftHandedProcess::MakeLeftHandedProcess() +: BaseProcess() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +MakeLeftHandedProcess::~MakeLeftHandedProcess() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool MakeLeftHandedProcess::IsActive( unsigned int pFlags) const +{ + return 0 != (pFlags & aiProcess_MakeLeftHanded); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void MakeLeftHandedProcess::Execute( aiScene* pScene) +{ + // Check for an existent root node to proceed + ai_assert(pScene->mRootNode != NULL); + ASSIMP_LOG_DEBUG("MakeLeftHandedProcess begin"); + + // recursively convert all the nodes + ProcessNode( pScene->mRootNode, aiMatrix4x4()); + + // process the meshes accordingly + for ( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { + ProcessMesh( pScene->mMeshes[ a ] ); + } + + // process the materials accordingly + for ( unsigned int a = 0; a < pScene->mNumMaterials; ++a ) { + ProcessMaterial( pScene->mMaterials[ a ] ); + } + + // transform all animation channels as well + for( unsigned int a = 0; a < pScene->mNumAnimations; a++) + { + aiAnimation* anim = pScene->mAnimations[a]; + for( unsigned int b = 0; b < anim->mNumChannels; b++) + { + aiNodeAnim* nodeAnim = anim->mChannels[b]; + ProcessAnimation( nodeAnim); + } + } + ASSIMP_LOG_DEBUG("MakeLeftHandedProcess finished"); +} + +// ------------------------------------------------------------------------------------------------ +// Recursively converts a node, all of its children and all of its meshes +void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation) +{ + // mirror all base vectors at the local Z axis + pNode->mTransformation.c1 = -pNode->mTransformation.c1; + pNode->mTransformation.c2 = -pNode->mTransformation.c2; + pNode->mTransformation.c3 = -pNode->mTransformation.c3; + pNode->mTransformation.c4 = -pNode->mTransformation.c4; + + // now invert the Z axis again to keep the matrix determinant positive. + // The local meshes will be inverted accordingly so that the result should look just fine again. + pNode->mTransformation.a3 = -pNode->mTransformation.a3; + pNode->mTransformation.b3 = -pNode->mTransformation.b3; + pNode->mTransformation.c3 = -pNode->mTransformation.c3; + pNode->mTransformation.d3 = -pNode->mTransformation.d3; // useless, but anyways... + + // continue for all children + for( size_t a = 0; a < pNode->mNumChildren; ++a ) { + ProcessNode( pNode->mChildren[ a ], pParentGlobalRotation * pNode->mTransformation ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts a single mesh to left handed coordinates. +void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) { + if ( nullptr == pMesh ) { + ASSIMP_LOG_ERROR( "Nullptr to mesh found." ); + return; + } + // mirror positions, normals and stuff along the Z axis + for( size_t a = 0; a < pMesh->mNumVertices; ++a) + { + pMesh->mVertices[a].z *= -1.0f; + if (pMesh->HasNormals()) { + pMesh->mNormals[a].z *= -1.0f; + } + if( pMesh->HasTangentsAndBitangents()) + { + pMesh->mTangents[a].z *= -1.0f; + pMesh->mBitangents[a].z *= -1.0f; + } + } + + // mirror anim meshes positions, normals and stuff along the Z axis + for (size_t m = 0; m < pMesh->mNumAnimMeshes; ++m) + { + for (size_t a = 0; a < pMesh->mAnimMeshes[m]->mNumVertices; ++a) + { + pMesh->mAnimMeshes[m]->mVertices[a].z *= -1.0f; + if (pMesh->mAnimMeshes[m]->HasNormals()) { + pMesh->mAnimMeshes[m]->mNormals[a].z *= -1.0f; + } + if (pMesh->mAnimMeshes[m]->HasTangentsAndBitangents()) + { + pMesh->mAnimMeshes[m]->mTangents[a].z *= -1.0f; + pMesh->mAnimMeshes[m]->mBitangents[a].z *= -1.0f; + } + } + } + + // mirror offset matrices of all bones + for( size_t a = 0; a < pMesh->mNumBones; ++a) + { + aiBone* bone = pMesh->mBones[a]; + bone->mOffsetMatrix.a3 = -bone->mOffsetMatrix.a3; + bone->mOffsetMatrix.b3 = -bone->mOffsetMatrix.b3; + bone->mOffsetMatrix.d3 = -bone->mOffsetMatrix.d3; + bone->mOffsetMatrix.c1 = -bone->mOffsetMatrix.c1; + bone->mOffsetMatrix.c2 = -bone->mOffsetMatrix.c2; + bone->mOffsetMatrix.c4 = -bone->mOffsetMatrix.c4; + } + + // mirror bitangents as well as they're derived from the texture coords + if( pMesh->HasTangentsAndBitangents()) + { + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + pMesh->mBitangents[a] *= -1.0f; + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts a single material to left handed coordinates. +void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) { + if ( nullptr == _mat ) { + ASSIMP_LOG_ERROR( "Nullptr to aiMaterial found." ); + return; + } + + aiMaterial* mat = (aiMaterial*)_mat; + for (unsigned int a = 0; a < mat->mNumProperties;++a) { + aiMaterialProperty* prop = mat->mProperties[a]; + + // Mapping axis for UV mappings? + if (!::strcmp( prop->mKey.data, "$tex.mapaxis")) { + ai_assert( prop->mDataLength >= sizeof(aiVector3D)); /* something is wrong with the validation if we end up here */ + aiVector3D* pff = (aiVector3D*)prop->mData; + pff->z *= -1.f; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts the given animation to LH coordinates. +void MakeLeftHandedProcess::ProcessAnimation( aiNodeAnim* pAnim) +{ + // position keys + for( unsigned int a = 0; a < pAnim->mNumPositionKeys; a++) + pAnim->mPositionKeys[a].mValue.z *= -1.0f; + + // rotation keys + for( unsigned int a = 0; a < pAnim->mNumRotationKeys; a++) + { + /* That's the safe version, but the float errors add up. So we try the short version instead + aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix(); + rotmat.a3 = -rotmat.a3; rotmat.b3 = -rotmat.b3; + rotmat.c1 = -rotmat.c1; rotmat.c2 = -rotmat.c2; + aiQuaternion rotquat( rotmat); + pAnim->mRotationKeys[a].mValue = rotquat; + */ + pAnim->mRotationKeys[a].mValue.x *= -1.0f; + pAnim->mRotationKeys[a].mValue.y *= -1.0f; + } +} + +#endif // !! ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS +#ifndef ASSIMP_BUILD_NO_FLIPUVS_PROCESS +// # FlipUVsProcess + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FlipUVsProcess::FlipUVsProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FlipUVsProcess::~FlipUVsProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FlipUVsProcess::IsActive( unsigned int pFlags) const +{ + return 0 != (pFlags & aiProcess_FlipUVs); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FlipUVsProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("FlipUVsProcess begin"); + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + ProcessMesh(pScene->mMeshes[i]); + + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) + ProcessMaterial(pScene->mMaterials[i]); + ASSIMP_LOG_DEBUG("FlipUVsProcess finished"); +} + +// ------------------------------------------------------------------------------------------------ +// Converts a single material +void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat) +{ + aiMaterial* mat = (aiMaterial*)_mat; + for (unsigned int a = 0; a < mat->mNumProperties;++a) { + aiMaterialProperty* prop = mat->mProperties[a]; + if( !prop ) { + ASSIMP_LOG_DEBUG( "Property is null" ); + continue; + } + + // UV transformation key? + if (!::strcmp( prop->mKey.data, "$tex.uvtrafo")) { + ai_assert( prop->mDataLength >= sizeof(aiUVTransform)); /* something is wrong with the validation if we end up here */ + aiUVTransform* uv = (aiUVTransform*)prop->mData; + + // just flip it, that's everything + uv->mTranslation.y *= -1.f; + uv->mRotation *= -1.f; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts a single mesh +void FlipUVsProcess::ProcessMesh( aiMesh* pMesh) +{ + flipUVs(pMesh); + for (unsigned int idx = 0; idx < pMesh->mNumAnimMeshes; idx++) { + flipUVs(pMesh->mAnimMeshes[idx]); + } +} + +#endif // !ASSIMP_BUILD_NO_FLIPUVS_PROCESS +#ifndef ASSIMP_BUILD_NO_FLIPWINDING_PROCESS +// # FlipWindingOrderProcess + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FlipWindingOrderProcess::FlipWindingOrderProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FlipWindingOrderProcess::~FlipWindingOrderProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FlipWindingOrderProcess::IsActive( unsigned int pFlags) const +{ + return 0 != (pFlags & aiProcess_FlipWindingOrder); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FlipWindingOrderProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("FlipWindingOrderProcess begin"); + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + ProcessMesh(pScene->mMeshes[i]); + ASSIMP_LOG_DEBUG("FlipWindingOrderProcess finished"); +} + +// ------------------------------------------------------------------------------------------------ +// Converts a single mesh +void FlipWindingOrderProcess::ProcessMesh( aiMesh* pMesh) +{ + // invert the order of all faces in this mesh + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + aiFace& face = pMesh->mFaces[a]; + for (unsigned int b = 0; b < face.mNumIndices / 2; b++) { + std::swap(face.mIndices[b], face.mIndices[face.mNumIndices - 1 - b]); + } + } + + // invert the order of all components in this mesh anim meshes + for (unsigned int m = 0; m < pMesh->mNumAnimMeshes; m++) { + aiAnimMesh* animMesh = pMesh->mAnimMeshes[m]; + unsigned int numVertices = animMesh->mNumVertices; + if (animMesh->HasPositions()) { + for (unsigned int a = 0; a < numVertices; a++) + { + std::swap(animMesh->mVertices[a], animMesh->mVertices[numVertices - 1 - a]); + } + } + if (animMesh->HasNormals()) { + for (unsigned int a = 0; a < numVertices; a++) + { + std::swap(animMesh->mNormals[a], animMesh->mNormals[numVertices - 1 - a]); + } + } + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++) { + if (animMesh->HasTextureCoords(i)) { + for (unsigned int a = 0; a < numVertices; a++) + { + std::swap(animMesh->mTextureCoords[i][a], animMesh->mTextureCoords[i][numVertices - 1 - a]); + } + } + } + if (animMesh->HasTangentsAndBitangents()) { + for (unsigned int a = 0; a < numVertices; a++) + { + std::swap(animMesh->mTangents[a], animMesh->mTangents[numVertices - 1 - a]); + std::swap(animMesh->mBitangents[a], animMesh->mBitangents[numVertices - 1 - a]); + } + } + for (unsigned int v = 0; v < AI_MAX_NUMBER_OF_COLOR_SETS; v++) { + if (animMesh->HasVertexColors(v)) { + for (unsigned int a = 0; a < numVertices; a++) + { + std::swap(animMesh->mColors[v][a], animMesh->mColors[v][numVertices - 1 - a]); + } + } + } + } +} + +#endif // !! ASSIMP_BUILD_NO_FLIPWINDING_PROCESS diff --git a/thirdparty/assimp/code/ConvertToLHProcess.h b/thirdparty/assimp/code/ConvertToLHProcess.h new file mode 100644 index 0000000000..63351568d0 --- /dev/null +++ b/thirdparty/assimp/code/ConvertToLHProcess.h @@ -0,0 +1,170 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file MakeLeftHandedProcess.h + * @brief Defines a bunch of post-processing steps to handle + * coordinate system conversions. + * + * - LH to RH + * - UV origin upper-left to lower-left + * - face order cw to ccw + */ +#ifndef AI_CONVERTTOLHPROCESS_H_INC +#define AI_CONVERTTOLHPROCESS_H_INC + +#include <assimp/types.h> +#include "BaseProcess.h" + +struct aiMesh; +struct aiNodeAnim; +struct aiNode; +struct aiMaterial; + +namespace Assimp { + +// ----------------------------------------------------------------------------------- +/** @brief The MakeLeftHandedProcess converts all imported data to a left-handed + * coordinate system. + * + * This implies a mirroring of the Z axis of the coordinate system. But to keep + * transformation matrices free from reflections we shift the reflection to other + * places. We mirror the meshes and adapt the rotations. + * + * @note RH-LH and LH-RH is the same, so this class can be used for both + */ +class MakeLeftHandedProcess : public BaseProcess +{ + + +public: + MakeLeftHandedProcess(); + ~MakeLeftHandedProcess(); + + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + +protected: + + // ------------------------------------------------------------------- + /** Recursively converts a node and all of its children + */ + void ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation); + + // ------------------------------------------------------------------- + /** Converts a single mesh to left handed coordinates. + * This means that positions, normals and tangents are mirrored at + * the local Z axis and the order of all faces are inverted. + * @param pMesh The mesh to convert. + */ + void ProcessMesh( aiMesh* pMesh); + + // ------------------------------------------------------------------- + /** Converts a single material to left-handed coordinates + * @param pMat Material to convert + */ + void ProcessMaterial( aiMaterial* pMat); + + // ------------------------------------------------------------------- + /** Converts the given animation to LH coordinates. + * The rotation and translation keys are transformed, the scale keys + * work in local space and can therefore be left untouched. + * @param pAnim The bone animation to transform + */ + void ProcessAnimation( aiNodeAnim* pAnim); +}; + + +// --------------------------------------------------------------------------- +/** Postprocessing step to flip the face order of the imported data + */ +class FlipWindingOrderProcess : public BaseProcess +{ + friend class Importer; + +public: + /** Constructor to be privately used by Importer */ + FlipWindingOrderProcess(); + + /** Destructor, private as well */ + ~FlipWindingOrderProcess(); + + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + +protected: + void ProcessMesh( aiMesh* pMesh); +}; + +// --------------------------------------------------------------------------- +/** Postprocessing step to flip the UV coordinate system of the import data + */ +class FlipUVsProcess : public BaseProcess +{ + friend class Importer; + +public: + /** Constructor to be privately used by Importer */ + FlipUVsProcess(); + + /** Destructor, private as well */ + ~FlipUVsProcess(); + + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + +protected: + void ProcessMesh( aiMesh* pMesh); + void ProcessMaterial( aiMaterial* mat); +}; + +} // end of namespace Assimp + +#endif // AI_CONVERTTOLHPROCESS_H_INC diff --git a/thirdparty/assimp/code/CreateAnimMesh.cpp b/thirdparty/assimp/code/CreateAnimMesh.cpp new file mode 100644 index 0000000000..1a052849bb --- /dev/null +++ b/thirdparty/assimp/code/CreateAnimMesh.cpp @@ -0,0 +1,92 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (C) 2016 The Qt Company Ltd. +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#include <assimp/CreateAnimMesh.h> + +namespace Assimp { + +aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh) +{ + aiAnimMesh *animesh = new aiAnimMesh; + animesh->mVertices = NULL; + animesh->mNormals = NULL; + animesh->mTangents = NULL; + animesh->mBitangents = NULL; + animesh->mNumVertices = mesh->mNumVertices; + if (mesh->mVertices) { + animesh->mVertices = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mVertices, mesh->mVertices, mesh->mNumVertices * sizeof(aiVector3D)); + } + if (mesh->mNormals) { + animesh->mNormals = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mNormals, mesh->mNormals, mesh->mNumVertices * sizeof(aiVector3D)); + } + if (mesh->mTangents) { + animesh->mTangents = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mTangents, mesh->mTangents, mesh->mNumVertices * sizeof(aiVector3D)); + } + if (mesh->mBitangents) { + animesh->mBitangents = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mBitangents, mesh->mBitangents, mesh->mNumVertices * sizeof(aiVector3D)); + } + + for (int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { + if (mesh->mColors[i]) { + animesh->mColors[i] = new aiColor4D[animesh->mNumVertices]; + std::memcpy(animesh->mColors[i], mesh->mColors[i], mesh->mNumVertices * sizeof(aiColor4D)); + } else { + animesh->mColors[i] = NULL; + } + } + + for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (mesh->mTextureCoords[i]) { + animesh->mTextureCoords[i] = new aiVector3D[animesh->mNumVertices]; + std::memcpy(animesh->mTextureCoords[i], mesh->mTextureCoords[i], mesh->mNumVertices * sizeof(aiVector3D)); + } else { + animesh->mTextureCoords[i] = NULL; + } + } + return animesh; +} + +} // end of namespace Assimp diff --git a/thirdparty/assimp/code/DeboneProcess.cpp b/thirdparty/assimp/code/DeboneProcess.cpp new file mode 100644 index 0000000000..83b8336bc9 --- /dev/null +++ b/thirdparty/assimp/code/DeboneProcess.cpp @@ -0,0 +1,465 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/// @file DeboneProcess.cpp +/** Implementation of the DeboneProcess post processing step */ + + + +// internal headers of the post-processing framework +#include "ProcessHelper.h" +#include "DeboneProcess.h" +#include <stdio.h> + + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +DeboneProcess::DeboneProcess() +{ + mNumBones = 0; + mNumBonesCanDoWithout = 0; + + mThreshold = AI_DEBONE_THRESHOLD; + mAllOrNone = false; +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +DeboneProcess::~DeboneProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool DeboneProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_Debone) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void DeboneProcess::SetupProperties(const Importer* pImp) +{ + // get the current value of the property + mAllOrNone = pImp->GetPropertyInteger(AI_CONFIG_PP_DB_ALL_OR_NONE,0)?true:false; + mThreshold = pImp->GetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD,AI_DEBONE_THRESHOLD); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void DeboneProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("DeboneProcess begin"); + + if(!pScene->mNumMeshes) { + return; + } + + std::vector<bool> splitList(pScene->mNumMeshes); + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { + splitList[a] = ConsiderMesh( pScene->mMeshes[a] ); + } + + int numSplits = 0; + + if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones)) { + for(unsigned int a = 0; a < pScene->mNumMeshes; a++) { + if(splitList[a]) { + numSplits++; + } + } + } + + if(numSplits) { + // we need to do something. Let's go. + //mSubMeshIndices.clear(); // really needed? + mSubMeshIndices.resize(pScene->mNumMeshes); // because we're doing it here anyway + + // build a new array of meshes for the scene + std::vector<aiMesh*> meshes; + + for(unsigned int a=0;a<pScene->mNumMeshes;a++) + { + aiMesh* srcMesh = pScene->mMeshes[a]; + + std::vector<std::pair<aiMesh*,const aiBone*> > newMeshes; + + if(splitList[a]) { + SplitMesh(srcMesh,newMeshes); + } + + // mesh was split + if(!newMeshes.empty()) { + unsigned int out = 0, in = srcMesh->mNumBones; + + // store new meshes and indices of the new meshes + for(unsigned int b=0;b<newMeshes.size();b++) { + const aiString *find = newMeshes[b].second?&newMeshes[b].second->mName:0; + + aiNode *theNode = find?pScene->mRootNode->FindNode(*find):0; + std::pair<unsigned int,aiNode*> push_pair(static_cast<unsigned int>(meshes.size()),theNode); + + mSubMeshIndices[a].push_back(push_pair); + meshes.push_back(newMeshes[b].first); + + out+=newMeshes[b].first->mNumBones; + } + + if(!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_INFO_F("Removed %u bones. Input bones:", in - out, ". Output bones: ", out); + } + + // and destroy the source mesh. It should be completely contained inside the new submeshes + delete srcMesh; + } + else { + // Mesh is kept unchanged - store it's new place in the mesh array + mSubMeshIndices[a].push_back(std::pair<unsigned int,aiNode*>(static_cast<unsigned int>(meshes.size()),(aiNode*)0)); + meshes.push_back(srcMesh); + } + } + + // rebuild the scene's mesh array + pScene->mNumMeshes = static_cast<unsigned int>(meshes.size()); + delete [] pScene->mMeshes; + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + std::copy( meshes.begin(), meshes.end(), pScene->mMeshes); + + // recurse through all nodes and translate the node's mesh indices to fit the new mesh array + UpdateNode( pScene->mRootNode); + } + + ASSIMP_LOG_DEBUG("DeboneProcess end"); +} + +// ------------------------------------------------------------------------------------------------ +// Counts bones total/removable in a given mesh. +bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh) +{ + if(!pMesh->HasBones()) { + return false; + } + + bool split = false; + + //interstitial faces not permitted + bool isInterstitialRequired = false; + + std::vector<bool> isBoneNecessary(pMesh->mNumBones,false); + std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX); + + const unsigned int cUnowned = UINT_MAX; + const unsigned int cCoowned = UINT_MAX-1; + + for(unsigned int i=0;i<pMesh->mNumBones;i++) { + for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++) { + float w = pMesh->mBones[i]->mWeights[j].mWeight; + + if(w==0.0f) { + continue; + } + + unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId; + if(w>=mThreshold) { + + if(vertexBones[vid]!=cUnowned) { + if(vertexBones[vid]==i) //double entry + { + ASSIMP_LOG_WARN("Encountered double entry in bone weights"); + } + else //TODO: track attraction in order to break tie + { + vertexBones[vid] = cCoowned; + } + } + else vertexBones[vid] = i; + } + + if(!isBoneNecessary[i]) { + isBoneNecessary[i] = w<mThreshold; + } + } + + if(!isBoneNecessary[i]) { + isInterstitialRequired = true; + } + } + + if(isInterstitialRequired) { + for(unsigned int i=0;i<pMesh->mNumFaces;i++) { + unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]]; + + for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) { + unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]]; + + if(v!=w) { + if(v<pMesh->mNumBones) isBoneNecessary[v] = true; + if(w<pMesh->mNumBones) isBoneNecessary[w] = true; + } + } + } + } + + for(unsigned int i=0;i<pMesh->mNumBones;i++) { + if(!isBoneNecessary[i]) { + mNumBonesCanDoWithout++; + split = true; + } + + mNumBones++; + } + return split; +} + +// ------------------------------------------------------------------------------------------------ +// Splits the given mesh by bone count. +void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const +{ + // same deal here as ConsiderMesh basically + + std::vector<bool> isBoneNecessary(pMesh->mNumBones,false); + std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX); + + const unsigned int cUnowned = UINT_MAX; + const unsigned int cCoowned = UINT_MAX-1; + + for(unsigned int i=0;i<pMesh->mNumBones;i++) { + for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++) { + float w = pMesh->mBones[i]->mWeights[j].mWeight; + + if(w==0.0f) { + continue; + } + + unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId; + + if(w>=mThreshold) { + if(vertexBones[vid]!=cUnowned) { + if(vertexBones[vid]==i) //double entry + { + ASSIMP_LOG_WARN("Encountered double entry in bone weights"); + } + else //TODO: track attraction in order to break tie + { + vertexBones[vid] = cCoowned; + } + } + else vertexBones[vid] = i; + } + + if(!isBoneNecessary[i]) { + isBoneNecessary[i] = w<mThreshold; + } + } + } + + unsigned int nFacesUnowned = 0; + + std::vector<unsigned int> faceBones(pMesh->mNumFaces,UINT_MAX); + std::vector<unsigned int> facesPerBone(pMesh->mNumBones,0); + + for(unsigned int i=0;i<pMesh->mNumFaces;i++) { + unsigned int nInterstitial = 1; + + unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]]; + + for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) { + unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]]; + + if(v!=w) { + if(v<pMesh->mNumBones) isBoneNecessary[v] = true; + if(w<pMesh->mNumBones) isBoneNecessary[w] = true; + } + else nInterstitial++; + } + + if(v<pMesh->mNumBones &&nInterstitial==pMesh->mFaces[i].mNumIndices) { + faceBones[i] = v; //primitive belongs to bone #v + facesPerBone[v]++; + } + else nFacesUnowned++; + } + + // invalidate any "cojoined" faces + for(unsigned int i=0;i<pMesh->mNumFaces;i++) { + if(faceBones[i]<pMesh->mNumBones&&isBoneNecessary[faceBones[i]]) + { + ai_assert(facesPerBone[faceBones[i]]>0); + facesPerBone[faceBones[i]]--; + + nFacesUnowned++; + faceBones[i] = cUnowned; + } + } + + if(nFacesUnowned) { + std::vector<unsigned int> subFaces; + + for(unsigned int i=0;i<pMesh->mNumFaces;i++) { + if(faceBones[i]==cUnowned) { + subFaces.push_back(i); + } + } + + aiMesh *baseMesh = MakeSubmesh(pMesh,subFaces,0); + std::pair<aiMesh*,const aiBone*> push_pair(baseMesh,(const aiBone*)0); + + poNewMeshes.push_back(push_pair); + } + + for(unsigned int i=0;i<pMesh->mNumBones;i++) { + + if(!isBoneNecessary[i]&&facesPerBone[i]>0) { + std::vector<unsigned int> subFaces; + + for(unsigned int j=0;j<pMesh->mNumFaces;j++) { + if(faceBones[j]==i) { + subFaces.push_back(j); + } + } + + unsigned int f = AI_SUBMESH_FLAGS_SANS_BONES; + aiMesh *subMesh =MakeSubmesh(pMesh,subFaces,f); + + //Lifted from PretransformVertices.cpp + ApplyTransform(subMesh,pMesh->mBones[i]->mOffsetMatrix); + std::pair<aiMesh*,const aiBone*> push_pair(subMesh,pMesh->mBones[i]); + + poNewMeshes.push_back(push_pair); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Recursively updates the node's mesh list to account for the changed mesh list +void DeboneProcess::UpdateNode(aiNode* pNode) const +{ + // rebuild the node's mesh index list + + std::vector<unsigned int> newMeshList; + + // this will require two passes + + unsigned int m = static_cast<unsigned int>(pNode->mNumMeshes), n = static_cast<unsigned int>(mSubMeshIndices.size()); + + // first pass, look for meshes which have not moved + + for(unsigned int a=0;a<m;a++) { + + unsigned int srcIndex = pNode->mMeshes[a]; + const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[srcIndex]; + unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size()); + + for(unsigned int b=0;b<nSubmeshes;b++) { + if(!subMeshes[b].second) { + newMeshList.push_back(subMeshes[b].first); + } + } + } + + // second pass, collect deboned meshes + + for(unsigned int a=0;a<n;a++) + { + const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[a]; + unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size()); + + for(unsigned int b=0;b<nSubmeshes;b++) { + if(subMeshes[b].second == pNode) { + newMeshList.push_back(subMeshes[b].first); + } + } + } + + if( pNode->mNumMeshes > 0 ) { + delete [] pNode->mMeshes; pNode->mMeshes = NULL; + } + + pNode->mNumMeshes = static_cast<unsigned int>(newMeshList.size()); + + if(pNode->mNumMeshes) { + pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; + std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes); + } + + // do that also recursively for all children + for( unsigned int a = 0; a < pNode->mNumChildren; ++a ) { + UpdateNode( pNode->mChildren[a]); + } +} + +// ------------------------------------------------------------------------------------------------ +// Apply the node transformation to a mesh +void DeboneProcess::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const +{ + // Check whether we need to transform the coordinates at all + if (!mat.IsIdentity()) { + + if (mesh->HasPositions()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mVertices[i] = mat * mesh->mVertices[i]; + } + } + if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) { + aiMatrix4x4 mWorldIT = mat; + mWorldIT.Inverse().Transpose(); + + // TODO: implement Inverse() for aiMatrix3x3 + aiMatrix3x3 m = aiMatrix3x3(mWorldIT); + + if (mesh->HasNormals()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize(); + } + } + if (mesh->HasTangentsAndBitangents()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize(); + mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize(); + } + } + } + } +} diff --git a/thirdparty/assimp/code/DeboneProcess.h b/thirdparty/assimp/code/DeboneProcess.h new file mode 100644 index 0000000000..ba77aba70e --- /dev/null +++ b/thirdparty/assimp/code/DeboneProcess.h @@ -0,0 +1,134 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** Defines a post processing step to limit the number of bones affecting a single vertex. */ +#ifndef AI_DEBONEPROCESS_H_INC +#define AI_DEBONEPROCESS_H_INC + +#include <vector> +#include <utility> +#include "BaseProcess.h" + +#include <assimp/mesh.h> +#include <assimp/scene.h> + +class DeboneTest; + +namespace Assimp +{ + +#if (!defined AI_DEBONE_THRESHOLD) +# define AI_DEBONE_THRESHOLD 1.0f +#endif // !! AI_DEBONE_THRESHOLD + +// --------------------------------------------------------------------------- +/** This post processing step removes bones nearly losslessly or according to +* a configured threshold. In order to remove the bone, the primitives affected by +* the bone are split from the mesh. The split off (new) mesh is boneless. At any +* point in time, bones without affect upon a given mesh are to be removed. +*/ +class DeboneProcess : public BaseProcess +{ +public: + + DeboneProcess(); + ~DeboneProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. + * A bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + +protected: + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + /** Counts bones total/removable in a given mesh. + * @param pMesh The mesh to process. + */ + bool ConsiderMesh( const aiMesh* pMesh); + + /// Splits the given mesh by bone count. + /// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split. + /// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary. + void SplitMesh(const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const; + + /// Recursively updates the node's mesh list to account for the changed mesh list + void UpdateNode(aiNode* pNode) const; + + // ------------------------------------------------------------------- + // Apply transformation to a mesh + void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const; + +public: + /** Number of bones present in the scene. */ + unsigned int mNumBones; + unsigned int mNumBonesCanDoWithout; + + float mThreshold; + bool mAllOrNone; + + /// Per mesh index: Array of indices of the new submeshes. + std::vector< std::vector< std::pair< unsigned int,aiNode* > > > mSubMeshIndices; +}; + +} // end of namespace Assimp + +#endif // AI_DEBONEPROCESS_H_INC diff --git a/thirdparty/assimp/code/DefaultIOStream.cpp b/thirdparty/assimp/code/DefaultIOStream.cpp new file mode 100644 index 0000000000..1c100b6189 --- /dev/null +++ b/thirdparty/assimp/code/DefaultIOStream.cpp @@ -0,0 +1,154 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file DefaultIOStream.cpp + * @brief Default File I/O implementation for #Importer + */ + + +#include <assimp/ai_assert.h> +#include <assimp/DefaultIOStream.h> +#include <sys/types.h> +#include <sys/stat.h> + +using namespace Assimp; + +// ---------------------------------------------------------------------------------- +DefaultIOStream::~DefaultIOStream() +{ + if (mFile) { + ::fclose(mFile); + mFile = nullptr; + } +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::Read(void* pvBuffer, + size_t pSize, + size_t pCount) +{ + ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount); + return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0); +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::Write(const void* pvBuffer, + size_t pSize, + size_t pCount) +{ + ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount); + return (mFile ? ::fwrite(pvBuffer, pSize, pCount, mFile) : 0); +} + +// ---------------------------------------------------------------------------------- +aiReturn DefaultIOStream::Seek(size_t pOffset, + aiOrigin pOrigin) +{ + if (!mFile) { + return AI_FAILURE; + } + + // Just to check whether our enum maps one to one with the CRT constants + static_assert(aiOrigin_CUR == SEEK_CUR && + aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET, "aiOrigin_CUR == SEEK_CUR && \ + aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET"); + + // do the seek + return (0 == ::fseek(mFile, (long)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE); +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::Tell() const +{ + if (!mFile) { + return 0; + } + return ::ftell(mFile); +} + +// ---------------------------------------------------------------------------------- +size_t DefaultIOStream::FileSize() const +{ + if (! mFile || mFilename.empty()) { + return 0; + } + + if (SIZE_MAX == mCachedSize ) { + + // Although fseek/ftell would allow us to reuse the existing file handle here, + // it is generally unsafe because: + // - For binary streams, it is not technically well-defined + // - For text files the results are meaningless + // That's why we use the safer variant fstat here. + // + // See here for details: + // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file +#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) + struct __stat64 fileStat; + //using fileno + fstat avoids having to handle the filename + int err = _fstat64( _fileno(mFile), &fileStat ); + if (0 != err) + return 0; + mCachedSize = (size_t) (fileStat.st_size); +#elif defined __GNUC__ || defined __APPLE__ || defined __MACH__ || defined __FreeBSD__ + struct stat fileStat; + int err = stat(mFilename.c_str(), &fileStat ); + if (0 != err) + return 0; + const unsigned long long cachedSize = fileStat.st_size; + mCachedSize = static_cast< size_t >( cachedSize ); +#else +# error "Unknown platform" +#endif + } + return mCachedSize; +} + +// ---------------------------------------------------------------------------------- +void DefaultIOStream::Flush() +{ + if (mFile) { + ::fflush(mFile); + } +} + +// ---------------------------------------------------------------------------------- diff --git a/thirdparty/assimp/code/DefaultIOSystem.cpp b/thirdparty/assimp/code/DefaultIOSystem.cpp new file mode 100644 index 0000000000..d40b67de32 --- /dev/null +++ b/thirdparty/assimp/code/DefaultIOSystem.cpp @@ -0,0 +1,257 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file Default implementation of IOSystem using the standard C file functions */ + +#include <assimp/StringComparison.h> + +#include <assimp/DefaultIOSystem.h> +#include <assimp/DefaultIOStream.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/ai_assert.h> +#include <stdlib.h> + +#ifdef __unix__ +#include <sys/param.h> +#include <stdlib.h> +#endif + +#ifdef _WIN32 +#include <windows.h> +#endif + +using namespace Assimp; + +// maximum path length +// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html +#ifdef PATH_MAX +# define PATHLIMIT PATH_MAX +#else +# define PATHLIMIT 4096 +#endif + +// ------------------------------------------------------------------------------------------------ +// Tests for the existence of a file at the given path. +bool DefaultIOSystem::Exists( const char* pFile) const +{ +#ifdef _WIN32 + wchar_t fileName16[PATHLIMIT]; + +#ifndef WindowsStore + bool isUnicode = IsTextUnicode(pFile, static_cast<int>(strlen(pFile)), NULL) != 0; + if (isUnicode) { + + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT); + struct __stat64 filestat; + if (0 != _wstat64(fileName16, &filestat)) { + return false; + } + } else { +#endif + FILE* file = ::fopen(pFile, "rb"); + if (!file) + return false; + + ::fclose(file); +#ifndef WindowsStore + } +#endif +#else + FILE* file = ::fopen( pFile, "rb"); + if( !file) + return false; + + ::fclose( file); +#endif + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Open a new file with a given path. +IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode) +{ + ai_assert(NULL != strFile); + ai_assert(NULL != strMode); + FILE* file; +#ifdef _WIN32 + wchar_t fileName16[PATHLIMIT]; +#ifndef WindowsStore + bool isUnicode = IsTextUnicode(strFile, static_cast<int>(strlen(strFile)), NULL) != 0; + if (isUnicode) { + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT); + std::string mode8(strMode); + file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str()); + } else { +#endif + file = ::fopen(strFile, strMode); +#ifndef WindowsStore + } +#endif +#else + file = ::fopen(strFile, strMode); +#endif + if (nullptr == file) + return nullptr; + + return new DefaultIOStream(file, (std::string) strFile); +} + +// ------------------------------------------------------------------------------------------------ +// Closes the given file and releases all resources associated with it. +void DefaultIOSystem::Close( IOStream* pFile) +{ + delete pFile; +} + +// ------------------------------------------------------------------------------------------------ +// Returns the operation specific directory separator +char DefaultIOSystem::getOsSeparator() const +{ +#ifndef _WIN32 + return '/'; +#else + return '\\'; +#endif +} + +// ------------------------------------------------------------------------------------------------ +// IOSystem default implementation (ComparePaths isn't a pure virtual function) +bool IOSystem::ComparePaths (const char* one, const char* second) const +{ + return !ASSIMP_stricmp(one,second); +} + +// ------------------------------------------------------------------------------------------------ +// Convert a relative path into an absolute path +inline static void MakeAbsolutePath (const char* in, char* _out) +{ + ai_assert(in && _out); +#if defined( _MSC_VER ) || defined( __MINGW32__ ) +#ifndef WindowsStore + bool isUnicode = IsTextUnicode(in, static_cast<int>(strlen(in)), NULL) != 0; + if (isUnicode) { + wchar_t out16[PATHLIMIT]; + wchar_t in16[PATHLIMIT]; + MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT); + wchar_t* ret = ::_wfullpath(out16, in16, PATHLIMIT); + if (ret) { + WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr); + } + if (!ret) { + // preserve the input path, maybe someone else is able to fix + // the path before it is accessed (e.g. our file system filter) + ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in)); + strcpy(_out, in); + } + + } else { +#endif + char* ret = :: _fullpath(_out, in, PATHLIMIT); + if (!ret) { + // preserve the input path, maybe someone else is able to fix + // the path before it is accessed (e.g. our file system filter) + ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in)); + strcpy(_out, in); + } +#ifndef WindowsStore + } +#endif +#else + // use realpath + char* ret = realpath(in, _out); + if(!ret) { + // preserve the input path, maybe someone else is able to fix + // the path before it is accessed (e.g. our file system filter) + ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in)); + strcpy(_out,in); + } +#endif +} + +// ------------------------------------------------------------------------------------------------ +// DefaultIOSystem's more specialized implementation +bool DefaultIOSystem::ComparePaths (const char* one, const char* second) const +{ + // chances are quite good both paths are formatted identically, + // so we can hopefully return here already + if( !ASSIMP_stricmp(one,second) ) + return true; + + char temp1[PATHLIMIT]; + char temp2[PATHLIMIT]; + + MakeAbsolutePath (one, temp1); + MakeAbsolutePath (second, temp2); + + return !ASSIMP_stricmp(temp1,temp2); +} + +// ------------------------------------------------------------------------------------------------ +std::string DefaultIOSystem::fileName( const std::string &path ) +{ + std::string ret = path; + std::size_t last = ret.find_last_of("\\/"); + if (last != std::string::npos) ret = ret.substr(last + 1); + return ret; +} + +// ------------------------------------------------------------------------------------------------ +std::string DefaultIOSystem::completeBaseName( const std::string &path ) +{ + std::string ret = fileName(path); + std::size_t pos = ret.find_last_of('.'); + if(pos != ret.npos) ret = ret.substr(0, pos); + return ret; +} + +// ------------------------------------------------------------------------------------------------ +std::string DefaultIOSystem::absolutePath( const std::string &path ) +{ + std::string ret = path; + std::size_t last = ret.find_last_of("\\/"); + if (last != std::string::npos) ret = ret.substr(0, last); + return ret; +} + +// ------------------------------------------------------------------------------------------------ + +#undef PATHLIMIT diff --git a/thirdparty/assimp/code/DefaultLogger.cpp b/thirdparty/assimp/code/DefaultLogger.cpp new file mode 100644 index 0000000000..de3528d2b4 --- /dev/null +++ b/thirdparty/assimp/code/DefaultLogger.cpp @@ -0,0 +1,418 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file DefaultLogger.cpp + * @brief Implementation of DefaultLogger (and Logger) + */ + +// Default log streams +#include "Win32DebugLogStream.h" +#include "StdOStreamLogStream.h" +#include "FileLogStream.h" +#include <assimp/StringUtils.h> + +#include <assimp/DefaultIOSystem.h> +#include <assimp/NullLogger.hpp> +#include <assimp/DefaultLogger.hpp> +#include <assimp/ai_assert.h> +#include <iostream> +#include <stdio.h> + +#ifndef ASSIMP_BUILD_SINGLETHREADED +# include <thread> +# include <mutex> + std::mutex loggerMutex; +#endif + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +NullLogger DefaultLogger::s_pNullLogger; +Logger *DefaultLogger::m_pLogger = &DefaultLogger::s_pNullLogger; + +static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; + +// ---------------------------------------------------------------------------------- +// Represents a log-stream + its error severity +struct LogStreamInfo { + unsigned int m_uiErrorSeverity; + LogStream *m_pStream; + + // Constructor + LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) : + m_uiErrorSeverity( uiErrorSev ), + m_pStream( pStream ) { + // empty + } + + // Destructor + ~LogStreamInfo() { + delete m_pStream; + } +}; + +// ---------------------------------------------------------------------------------- +// Construct a default log stream +LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams, + const char* name /*= "AssimpLog.txt"*/, + IOSystem* io /*= NULL*/) +{ + switch (streams) + { + // This is a platform-specific feature + case aiDefaultLogStream_DEBUGGER: +#ifdef WIN32 + return new Win32DebugLogStream(); +#else + return nullptr; +#endif + + // Platform-independent default streams + case aiDefaultLogStream_STDERR: + return new StdOStreamLogStream(std::cerr); + case aiDefaultLogStream_STDOUT: + return new StdOStreamLogStream(std::cout); + case aiDefaultLogStream_FILE: + return (name && *name ? new FileLogStream(name,io) : nullptr ); + default: + // We don't know this default log stream, so raise an assertion + ai_assert(false); + + }; + + // For compilers without dead code path detection + return NULL; +} + +// ---------------------------------------------------------------------------------- +// Creates the only singleton instance +Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/, + LogSeverity severity /*= NORMAL*/, + unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/, + IOSystem* io /*= NULL*/) { + // enter the mutex here to avoid concurrency problems +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard<std::mutex> lock(loggerMutex); +#endif + + if ( m_pLogger && !isNullLogger() ) { + delete m_pLogger; + } + + m_pLogger = new DefaultLogger( severity ); + + // Attach default log streams + // Stream the log to the MSVC debugger? + if ( defStreams & aiDefaultLogStream_DEBUGGER ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_DEBUGGER ) ); + } + + // Stream the log to COUT? + if ( defStreams & aiDefaultLogStream_STDOUT ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDOUT ) ); + } + + // Stream the log to CERR? + if ( defStreams & aiDefaultLogStream_STDERR ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDERR ) ); + } + + // Stream the log to a file + if ( defStreams & aiDefaultLogStream_FILE && name && *name ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_FILE, name, io ) ); + } + + return m_pLogger; +} + +// ---------------------------------------------------------------------------------- +void Logger::debug(const char* message) { + + // SECURITY FIX: otherwise it's easy to produce overruns since + // sometimes importers will include data from the input file + // (i.e. node names) in their messages. + if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { + return; + } + return OnDebug(message); +} + +// ---------------------------------------------------------------------------------- +void Logger::info(const char* message) { + + // SECURITY FIX: see above + if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { + return; + } + return OnInfo(message); +} + +// ---------------------------------------------------------------------------------- +void Logger::warn(const char* message) { + + // SECURITY FIX: see above + if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { + return; + } + return OnWarn(message); +} + +// ---------------------------------------------------------------------------------- +void Logger::error(const char* message) { + // SECURITY FIX: see above + if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { + return; + } + return OnError(message); +} + +// ---------------------------------------------------------------------------------- +void DefaultLogger::set( Logger *logger ) { + // enter the mutex here to avoid concurrency problems +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard<std::mutex> lock(loggerMutex); +#endif + + if ( nullptr == logger ) { + logger = &s_pNullLogger; + } + if ( nullptr != m_pLogger && !isNullLogger() ) { + delete m_pLogger; + } + + DefaultLogger::m_pLogger = logger; +} + +// ---------------------------------------------------------------------------------- +bool DefaultLogger::isNullLogger() { + return m_pLogger == &s_pNullLogger; +} + +// ---------------------------------------------------------------------------------- +Logger *DefaultLogger::get() { + return m_pLogger; +} + +// ---------------------------------------------------------------------------------- +// Kills the only instance +void DefaultLogger::kill() { + // enter the mutex here to avoid concurrency problems +#ifndef ASSIMP_BUILD_SINGLETHREADED + std::lock_guard<std::mutex> lock(loggerMutex); +#endif + + if ( m_pLogger == &s_pNullLogger ) { + return; + } + delete m_pLogger; + m_pLogger = &s_pNullLogger; +} + +// ---------------------------------------------------------------------------------- +// Debug message +void DefaultLogger::OnDebug( const char* message ) { + if ( m_Severity == Logger::NORMAL ) { + return; + } + + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[Size]; + ai_snprintf(msg, Size, "Debug, T%u: %s", GetThreadID(), message); + + WriteToStreams( msg, Logger::Debugging ); +} + +// ---------------------------------------------------------------------------------- +// Logs an info +void DefaultLogger::OnInfo( const char* message ){ + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[Size]; + ai_snprintf(msg, Size, "Info, T%u: %s", GetThreadID(), message ); + + WriteToStreams( msg , Logger::Info ); +} + +// ---------------------------------------------------------------------------------- +// Logs a warning +void DefaultLogger::OnWarn( const char* message ) { + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[Size]; + ai_snprintf(msg, Size, "Warn, T%u: %s", GetThreadID(), message ); + + WriteToStreams( msg, Logger::Warn ); +} + +// ---------------------------------------------------------------------------------- +// Logs an error +void DefaultLogger::OnError( const char* message ) { + static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; + char msg[ Size ]; + ai_snprintf(msg, Size, "Error, T%u: %s", GetThreadID(), message ); + + WriteToStreams( msg, Logger::Err ); +} + +// ---------------------------------------------------------------------------------- +// Will attach a new stream +bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) { + if ( nullptr == pStream ) { + return false; + } + + if (0 == severity) { + severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; + } + + for ( StreamIt it = m_StreamArray.begin(); + it != m_StreamArray.end(); + ++it ) + { + if ( (*it)->m_pStream == pStream ) { + (*it)->m_uiErrorSeverity |= severity; + return true; + } + } + + LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream ); + m_StreamArray.push_back( pInfo ); + return true; +} + +// ---------------------------------------------------------------------------------- +// Detach a stream +bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) { + if ( nullptr == pStream ) { + return false; + } + + if (0 == severity) { + severity = SeverityAll; + } + + bool res( false ); + for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) { + if ( (*it)->m_pStream == pStream ) { + (*it)->m_uiErrorSeverity &= ~severity; + if ( (*it)->m_uiErrorSeverity == 0 ) { + // don't delete the underlying stream 'cause the caller gains ownership again + (**it).m_pStream = nullptr; + delete *it; + m_StreamArray.erase( it ); + res = true; + break; + } + return true; + } + } + return res; +} + +// ---------------------------------------------------------------------------------- +// Constructor +DefaultLogger::DefaultLogger(LogSeverity severity) + : Logger ( severity ) + , noRepeatMsg (false) + , lastLen( 0 ) { + lastMsg[0] = '\0'; +} + +// ---------------------------------------------------------------------------------- +// Destructor +DefaultLogger::~DefaultLogger() { + for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) { + // also frees the underlying stream, we are its owner. + delete *it; + } +} + +// ---------------------------------------------------------------------------------- +// Writes message to stream +void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) { + ai_assert(nullptr != message); + + // Check whether this is a repeated message + if (! ::strncmp( message,lastMsg, lastLen-1)) + { + if (!noRepeatMsg) + { + noRepeatMsg = true; + message = "Skipping one or more lines with the same contents\n"; + } + else return; + } + else + { + // append a new-line character to the message to be printed + lastLen = ::strlen(message); + ::memcpy(lastMsg,message,lastLen+1); + ::strcat(lastMsg+lastLen,"\n"); + + message = lastMsg; + noRepeatMsg = false; + ++lastLen; + } + for ( ConstStreamIt it = m_StreamArray.begin(); + it != m_StreamArray.end(); + ++it) + { + if ( ErrorSev & (*it)->m_uiErrorSeverity ) + (*it)->m_pStream->write( message); + } +} + +// ---------------------------------------------------------------------------------- +// Returns thread id, if not supported only a zero will be returned. +unsigned int DefaultLogger::GetThreadID() +{ + // fixme: we can get this value via std::threads + // std::this_thread::get_id().hash() returns a (big) size_t, not sure if this is useful in this case. +#ifdef WIN32 + return (unsigned int)::GetCurrentThreadId(); +#else + return 0; // not supported +#endif +} + +// ---------------------------------------------------------------------------------- + +} // !namespace Assimp diff --git a/thirdparty/assimp/code/DefaultProgressHandler.h b/thirdparty/assimp/code/DefaultProgressHandler.h new file mode 100644 index 0000000000..bd2cce00be --- /dev/null +++ b/thirdparty/assimp/code/DefaultProgressHandler.h @@ -0,0 +1,65 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file ProgressHandler.hpp + * @brief Abstract base class 'ProgressHandler'. + */ +#ifndef INCLUDED_AI_DEFAULTPROGRESSHANDLER_H +#define INCLUDED_AI_DEFAULTPROGRESSHANDLER_H + +#include <assimp/ProgressHandler.hpp> + +namespace Assimp { + +// ------------------------------------------------------------------------------------ +/** @brief Internal default implementation of the #ProgressHandler interface. */ +class DefaultProgressHandler : public ProgressHandler { + + virtual bool Update(float /*percentage*/) { + return false; + } + + +}; // !class DefaultProgressHandler +} // Namespace Assimp + +#endif diff --git a/thirdparty/assimp/code/DropFaceNormalsProcess.cpp b/thirdparty/assimp/code/DropFaceNormalsProcess.cpp new file mode 100644 index 0000000000..b11615bb82 --- /dev/null +++ b/thirdparty/assimp/code/DropFaceNormalsProcess.cpp @@ -0,0 +1,109 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to drop face +* normals for all imported faces. +*/ + + +#include "DropFaceNormalsProcess.h" +#include <assimp/postprocess.h> +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/Exceptional.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +DropFaceNormalsProcess::DropFaceNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +DropFaceNormalsProcess::~DropFaceNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool DropFaceNormalsProcess::IsActive( unsigned int pFlags) const { + return (pFlags & aiProcess_DropNormals) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void DropFaceNormalsProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("DropFaceNormalsProcess begin"); + + if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { + throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); + } + + bool bHas = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { + bHas |= this->DropMeshFaceNormals( pScene->mMeshes[a]); + } + if (bHas) { + ASSIMP_LOG_INFO("DropFaceNormalsProcess finished. " + "Face normals have been removed"); + } else { + ASSIMP_LOG_DEBUG("DropFaceNormalsProcess finished. " + "No normals were present"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* pMesh) { + if (NULL == pMesh->mNormals) { + return false; + } + + delete[] pMesh->mNormals; + pMesh->mNormals = nullptr; + return true; +} diff --git a/thirdparty/assimp/code/DropFaceNormalsProcess.h b/thirdparty/assimp/code/DropFaceNormalsProcess.h new file mode 100644 index 0000000000..0d116663b7 --- /dev/null +++ b/thirdparty/assimp/code/DropFaceNormalsProcess.h @@ -0,0 +1,86 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to compute face normals for all loaded faces*/ +#ifndef AI_DROPFACENORMALPROCESS_H_INC +#define AI_DROPFACENORMALPROCESS_H_INC + +#include "BaseProcess.h" +#include <assimp/mesh.h> + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The DropFaceNormalsProcess computes face normals for all faces of all meshes +*/ +class ASSIMP_API_WINONLY DropFaceNormalsProcess : public BaseProcess +{ +public: + + DropFaceNormalsProcess(); + ~DropFaceNormalsProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + +private: + bool DropMeshFaceNormals(aiMesh* pcMesh); +}; + +} // end of namespace Assimp + +#endif // !!AI_DROPFACENORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/EmbedTexturesProcess.cpp b/thirdparty/assimp/code/EmbedTexturesProcess.cpp new file mode 100644 index 0000000000..739382a057 --- /dev/null +++ b/thirdparty/assimp/code/EmbedTexturesProcess.cpp @@ -0,0 +1,152 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include "EmbedTexturesProcess.h" +#include <assimp/ParsingUtils.h> +#include "ProcessHelper.h" + +#include <fstream> + +using namespace Assimp; + +EmbedTexturesProcess::EmbedTexturesProcess() +: BaseProcess() { +} + +EmbedTexturesProcess::~EmbedTexturesProcess() { +} + +bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const { + return (pFlags & aiProcess_EmbedTextures) != 0; +} + +void EmbedTexturesProcess::SetupProperties(const Importer* pImp) { + mRootPath = pImp->GetPropertyString("sourceFilePath"); + mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u); +} + +void EmbedTexturesProcess::Execute(aiScene* pScene) { + if (pScene == nullptr || pScene->mRootNode == nullptr) return; + + aiString path; + + uint32_t embeddedTexturesCount = 0u; + + for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) { + auto material = pScene->mMaterials[matId]; + + for (auto ttId = 1u; ttId < AI_TEXTURE_TYPE_MAX; ++ttId) { + auto tt = static_cast<aiTextureType>(ttId); + auto texturesCount = material->GetTextureCount(tt); + + for (auto texId = 0u; texId < texturesCount; ++texId) { + material->GetTexture(tt, texId, &path); + if (path.data[0] == '*') continue; // Already embedded + + // Indeed embed + if (addTexture(pScene, path.data)) { + auto embeddedTextureId = pScene->mNumTextures - 1u; + ::ai_snprintf(path.data, 1024, "*%u", embeddedTextureId); + material->AddProperty(&path, AI_MATKEY_TEXTURE(tt, texId)); + embeddedTexturesCount++; + } + } + } + } + + ASSIMP_LOG_INFO_F("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." ); +} + +bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { + std::streampos imageSize = 0; + std::string imagePath = path; + + // Test path directly + std::ifstream file(imagePath, std::ios::binary | std::ios::ate); + if ((imageSize = file.tellg()) == std::streampos(-1)) { + ASSIMP_LOG_WARN_F("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder."); + + // Test path in root path + imagePath = mRootPath + path; + file.open(imagePath, std::ios::binary | std::ios::ate); + if ((imageSize = file.tellg()) == std::streampos(-1)) { + // Test path basename in root path + imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u); + file.open(imagePath, std::ios::binary | std::ios::ate); + if ((imageSize = file.tellg()) == std::streampos(-1)) { + ASSIMP_LOG_ERROR_F("EmbedTexturesProcess: Unable to embed texture: ", path, "."); + return false; + } + } + } + + aiTexel* imageContent = new aiTexel[ 1ul + static_cast<unsigned long>( imageSize ) / sizeof(aiTexel)]; + file.seekg(0, std::ios::beg); + file.read(reinterpret_cast<char*>(imageContent), imageSize); + + // Enlarging the textures table + unsigned int textureId = pScene->mNumTextures++; + auto oldTextures = pScene->mTextures; + pScene->mTextures = new aiTexture*[pScene->mNumTextures]; + ::memmove(pScene->mTextures, oldTextures, sizeof(aiTexture*) * (pScene->mNumTextures - 1u)); + + // Add the new texture + auto pTexture = new aiTexture; + pTexture->mHeight = 0; // Means that this is still compressed + pTexture->mWidth = static_cast<uint32_t>(imageSize); + pTexture->pcData = imageContent; + + auto extension = path.substr(path.find_last_of('.') + 1u); + std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); + if (extension == "jpeg") { + extension = "jpg"; + } + + size_t len = extension.size(); + if (len > HINTMAXTEXTURELEN -1 ) { + len = HINTMAXTEXTURELEN - 1; + } + ::strncpy(pTexture->achFormatHint, extension.c_str(), len); + pScene->mTextures[textureId] = pTexture; + + return true; +} diff --git a/thirdparty/assimp/code/EmbedTexturesProcess.h b/thirdparty/assimp/code/EmbedTexturesProcess.h new file mode 100644 index 0000000000..cdf40bef74 --- /dev/null +++ b/thirdparty/assimp/code/EmbedTexturesProcess.h @@ -0,0 +1,85 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#pragma once + +#include "BaseProcess.h" + +#include <string> + +struct aiNode; + +namespace Assimp { + +/** + * Force embedding of textures (using the path = "*1" convention). + * If a texture's file does not exist at the specified path + * (due, for instance, to an absolute path generated on another system), + * it will check if a file with the same name exists at the root folder + * of the imported model. And if so, it uses that. + */ +class ASSIMP_API EmbedTexturesProcess : public BaseProcess { +public: + /// The default class constructor. + EmbedTexturesProcess(); + + /// The class destructor. + virtual ~EmbedTexturesProcess(); + + /// Overwritten, @see BaseProcess + virtual bool IsActive(unsigned int pFlags) const; + + /// Overwritten, @see BaseProcess + virtual void SetupProperties(const Importer* pImp); + + /// Overwritten, @see BaseProcess + virtual void Execute(aiScene* pScene); + +private: + // Resolve the path and add the file content to the scene as a texture. + bool addTexture(aiScene* pScene, std::string path) const; + +private: + std::string mRootPath; +}; + +} // namespace Assimp diff --git a/thirdparty/assimp/code/Exporter.cpp b/thirdparty/assimp/code/Exporter.cpp new file mode 100644 index 0000000000..8848e87f5b --- /dev/null +++ b/thirdparty/assimp/code/Exporter.cpp @@ -0,0 +1,648 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Exporter.cpp + +Assimp export interface. While it's public interface bears many similarities +to the import interface (in fact, it is largely symmetric), the internal +implementations differs a lot. Exporters are considered stateless and are +simple callbacks which we maintain in a global list along with their +description strings. + +Here we implement only the C++ interface (Assimp::Exporter). +*/ + +#ifndef ASSIMP_BUILD_NO_EXPORT + +#include <assimp/BlobIOSystem.h> +#include <assimp/SceneCombiner.h> +#include <assimp/DefaultIOSystem.h> +#include <assimp/Exporter.hpp> +#include <assimp/mesh.h> +#include <assimp/postprocess.h> +#include <assimp/scene.h> + +#include "DefaultProgressHandler.h" +#include "BaseProcess.h" +#include "JoinVerticesProcess.h" +#include "MakeVerboseFormat.h" +#include "ConvertToLHProcess.h" +#include "PretransformVertices.h" +#include <assimp/Exceptional.h> +#include "ScenePrivate.h" + +#include <memory> + +namespace Assimp { + +// PostStepRegistry.cpp +void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); + +// ------------------------------------------------------------------------------------------------ +// Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype +// do not use const, because some exporter need to convert the scene temporary +void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneXFile(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneStep(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneObj(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneObjNoMtl(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneSTL(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportScenePly(const char*,IOSystem*, const aiScene*, const ExportProperties*); +void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* ); + +// ------------------------------------------------------------------------------------------------ +// global array of all export formats which Assimp supports in its current build +Exporter::ExportFormatEntry gExporters[] = +{ +#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER + Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada ), +#endif + +#ifndef ASSIMP_BUILD_NO_X_EXPORTER + Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile, + aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs ), +#endif + +#ifndef ASSIMP_BUILD_NO_STEP_EXPORTER + Exporter::ExportFormatEntry( "stp", "Step Files", "stp", &ExportSceneStep, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER + Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj, + aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ), + Exporter::ExportFormatEntry( "objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl, + aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ), +#endif + +#ifndef ASSIMP_BUILD_NO_STL_EXPORTER + Exporter::ExportFormatEntry( "stl", "Stereolithography", "stl" , &ExportSceneSTL, + aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices + ), + Exporter::ExportFormatEntry( "stlb", "Stereolithography (binary)", "stl" , &ExportSceneSTLBinary, + aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices + ), +#endif + +#ifndef ASSIMP_BUILD_NO_PLY_EXPORTER + Exporter::ExportFormatEntry( "ply", "Stanford Polygon Library", "ply" , &ExportScenePly, + aiProcess_PreTransformVertices + ), + Exporter::ExportFormatEntry( "plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary, + aiProcess_PreTransformVertices + ), +#endif + +#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER + Exporter::ExportFormatEntry( "3ds", "Autodesk 3DS (legacy)", "3ds" , &ExportScene3DS, + aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices ), +#endif + +#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER + Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), + Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), + Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), + Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), +#endif + +#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER + Exporter::ExportFormatEntry( "assbin", "Assimp Binary", "assbin" , &ExportSceneAssbin, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER + Exporter::ExportFormatEntry( "assxml", "Assxml Document", "assxml" , &ExportSceneAssxml, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER + Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ), + Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ), +#endif + +#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER + Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ) +#endif +}; + +#define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0])) + + +class ExporterPimpl { +public: + ExporterPimpl() + : blob() + , mIOSystem(new Assimp::DefaultIOSystem()) + , mIsDefaultIOHandler(true) + , mProgressHandler( nullptr ) + , mIsDefaultProgressHandler( true ) + , mPostProcessingSteps() + , mError() + , mExporters() { + GetPostProcessingStepInstanceList(mPostProcessingSteps); + + // grab all built-in exporters + if ( 0 != ( ASSIMP_NUM_EXPORTERS ) ) { + mExporters.resize( ASSIMP_NUM_EXPORTERS ); + std::copy( gExporters, gExporters + ASSIMP_NUM_EXPORTERS, mExporters.begin() ); + } + } + + ~ExporterPimpl() { + delete blob; + + // Delete all post-processing plug-ins + for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) { + delete mPostProcessingSteps[a]; + } + delete mProgressHandler; + } + +public: + aiExportDataBlob* blob; + std::shared_ptr< Assimp::IOSystem > mIOSystem; + bool mIsDefaultIOHandler; + + /** The progress handler */ + ProgressHandler *mProgressHandler; + bool mIsDefaultProgressHandler; + + /** Post processing steps we can apply at the imported data. */ + std::vector< BaseProcess* > mPostProcessingSteps; + + /** Last fatal export error */ + std::string mError; + + /** Exporters, this includes those registered using #Assimp::Exporter::RegisterExporter */ + std::vector<Exporter::ExportFormatEntry> mExporters; +}; + +} // end of namespace Assimp + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +Exporter :: Exporter() +: pimpl(new ExporterPimpl()) { + pimpl->mProgressHandler = new DefaultProgressHandler(); +} + +// ------------------------------------------------------------------------------------------------ +Exporter::~Exporter() { + FreeBlob(); + delete pimpl; +} + +// ------------------------------------------------------------------------------------------------ +void Exporter::SetIOHandler( IOSystem* pIOHandler) { + pimpl->mIsDefaultIOHandler = !pIOHandler; + pimpl->mIOSystem.reset(pIOHandler); +} + +// ------------------------------------------------------------------------------------------------ +IOSystem* Exporter::GetIOHandler() const { + return pimpl->mIOSystem.get(); +} + +// ------------------------------------------------------------------------------------------------ +bool Exporter::IsDefaultIOHandler() const { + return pimpl->mIsDefaultIOHandler; +} + +// ------------------------------------------------------------------------------------------------ +void Exporter::SetProgressHandler(ProgressHandler* pHandler) { + ai_assert(nullptr != pimpl); + + if ( nullptr == pHandler) { + // Release pointer in the possession of the caller + pimpl->mProgressHandler = new DefaultProgressHandler(); + pimpl->mIsDefaultProgressHandler = true; + return; + } + + if (pimpl->mProgressHandler == pHandler) { + return; + } + + delete pimpl->mProgressHandler; + pimpl->mProgressHandler = pHandler; + pimpl->mIsDefaultProgressHandler = false; +} + +// ------------------------------------------------------------------------------------------------ +const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId, + unsigned int, const ExportProperties* /*pProperties*/ ) { + if (pimpl->blob) { + delete pimpl->blob; + pimpl->blob = nullptr; + } + + std::shared_ptr<IOSystem> old = pimpl->mIOSystem; + BlobIOSystem* blobio = new BlobIOSystem(); + pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio ); + + if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName())) { + pimpl->mIOSystem = old; + return nullptr; + } + + pimpl->blob = blobio->GetBlobChain(); + pimpl->mIOSystem = old; + + return pimpl->blob; +} + +// ------------------------------------------------------------------------------------------------ +bool IsVerboseFormat(const aiMesh* mesh) { + // avoid slow vector<bool> specialization + std::vector<unsigned int> seen(mesh->mNumVertices,0); + for(unsigned int i = 0; i < mesh->mNumFaces; ++i) { + const aiFace& f = mesh->mFaces[i]; + for(unsigned int j = 0; j < f.mNumIndices; ++j) { + if(++seen[f.mIndices[j]] == 2) { + // found a duplicate index + return false; + } + } + } + + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool IsVerboseFormat(const aiScene* pScene) { + for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + if(!IsVerboseFormat(pScene->mMeshes[i])) { + return false; + } + } + + return true; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, + unsigned int pPreprocessing, const ExportProperties* pProperties) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // when they create scenes from scratch, users will likely create them not in verbose + // format. They will likely not be aware that there is a flag in the scene to indicate + // this, however. To avoid surprises and bug reports, we check for duplicates in + // meshes upfront. + const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || IsVerboseFormat(pScene); + + pimpl->mProgressHandler->UpdateFileWrite(0, 4); + + pimpl->mError = ""; + for (size_t i = 0; i < pimpl->mExporters.size(); ++i) { + const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i]; + if (!strcmp(exp.mDescription.id,pFormatId)) { + try { + // Always create a full copy of the scene. We might optimize this one day, + // but for now it is the most pragmatic way. + aiScene* scenecopy_tmp = nullptr; + SceneCombiner::CopyScene(&scenecopy_tmp,pScene); + + pimpl->mProgressHandler->UpdateFileWrite(1, 4); + + std::unique_ptr<aiScene> scenecopy(scenecopy_tmp); + const ScenePrivateData* const priv = ScenePriv(pScene); + + // steps that are not idempotent, i.e. we might need to run them again, usually to get back to the + // original state before the step was applied first. When checking which steps we don't need + // to run, those are excluded. + const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded; + + // Erase all pp steps that were already applied to this scene + const unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv && !priv->mIsCopy + ? (priv->mPPStepsApplied & ~nonIdempotentSteps) + : 0u); + + // If no extra post-processing was specified, and we obtained this scene from an + // Assimp importer, apply the reverse steps automatically. + // TODO: either drop this, or document it. Otherwise it is just a bad surprise. + //if (!pPreprocessing && priv) { + // pp |= (nonIdempotentSteps & priv->mPPStepsApplied); + //} + + // If the input scene is not in verbose format, but there is at least post-processing step that relies on it, + // we need to run the MakeVerboseFormat step first. + bool must_join_again = false; + if (!is_verbose_format) { + bool verbosify = false; + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + BaseProcess* const p = pimpl->mPostProcessingSteps[a]; + + if (p->IsActive(pp) && p->RequireVerboseFormat()) { + verbosify = true; + break; + } + } + + if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { + ASSIMP_LOG_DEBUG("export: Scene data not in verbose format, applying MakeVerboseFormat step first"); + + MakeVerboseFormatProcess proc; + proc.Execute(scenecopy.get()); + + if(!(exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { + must_join_again = true; + } + } + } + + pimpl->mProgressHandler->UpdateFileWrite(2, 4); + + if (pp) { + // the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout + { + FlipWindingOrderProcess step; + if (step.IsActive(pp)) { + step.Execute(scenecopy.get()); + } + } + + { + FlipUVsProcess step; + if (step.IsActive(pp)) { + step.Execute(scenecopy.get()); + } + } + + { + MakeLeftHandedProcess step; + if (step.IsActive(pp)) { + step.Execute(scenecopy.get()); + } + } + + bool exportPointCloud(false); + if (nullptr != pProperties) { + exportPointCloud = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); + } + + // dispatch other processes + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + BaseProcess* const p = pimpl->mPostProcessingSteps[a]; + + if (p->IsActive(pp) + && !dynamic_cast<FlipUVsProcess*>(p) + && !dynamic_cast<FlipWindingOrderProcess*>(p) + && !dynamic_cast<MakeLeftHandedProcess*>(p)) { + if (dynamic_cast<PretransformVertices*>(p) && exportPointCloud) { + continue; + } + p->Execute(scenecopy.get()); + } + } + ScenePrivateData* const privOut = ScenePriv(scenecopy.get()); + ai_assert(nullptr != privOut); + + privOut->mPPStepsApplied |= pp; + } + + pimpl->mProgressHandler->UpdateFileWrite(3, 4); + + if(must_join_again) { + JoinVerticesProcess proc; + proc.Execute(scenecopy.get()); + } + + ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry. + exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties); + + pimpl->mProgressHandler->UpdateFileWrite(4, 4); + } catch (DeadlyExportError& err) { + pimpl->mError = err.what(); + return AI_FAILURE; + } + return AI_SUCCESS; + } + } + + pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId; + ASSIMP_END_EXCEPTION_REGION(aiReturn); + + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +const char* Exporter::GetErrorString() const { + return pimpl->mError.c_str(); +} + +// ------------------------------------------------------------------------------------------------ +void Exporter::FreeBlob() { + delete pimpl->blob; + pimpl->blob = nullptr; + + pimpl->mError = ""; +} + +// ------------------------------------------------------------------------------------------------ +const aiExportDataBlob* Exporter::GetBlob() const { + return pimpl->blob; +} + +// ------------------------------------------------------------------------------------------------ +const aiExportDataBlob* Exporter::GetOrphanedBlob() const { + const aiExportDataBlob* tmp = pimpl->blob; + pimpl->blob = nullptr; + return tmp; +} + +// ------------------------------------------------------------------------------------------------ +size_t Exporter::GetExportFormatCount() const { + return pimpl->mExporters.size(); +} + +// ------------------------------------------------------------------------------------------------ +const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const { + if (index >= GetExportFormatCount()) { + return nullptr; + } + + // Return from static storage if the requested index is built-in. + if (index < sizeof(gExporters) / sizeof(gExporters[0])) { + return &gExporters[index].mDescription; + } + + return &pimpl->mExporters[index].mDescription; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) { + for(const ExportFormatEntry& e : pimpl->mExporters) { + if (!strcmp(e.mDescription.id,desc.mDescription.id)) { + return aiReturn_FAILURE; + } + } + + pimpl->mExporters.push_back(desc); + return aiReturn_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +void Exporter::UnregisterExporter(const char* id) { + for(std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin(); + it != pimpl->mExporters.end(); ++it) { + if (!strcmp((*it).mDescription.id,id)) { + pimpl->mExporters.erase(it); + break; + } + } +} + +// ------------------------------------------------------------------------------------------------ +ExportProperties::ExportProperties() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +ExportProperties::ExportProperties(const ExportProperties &other) +: mIntProperties(other.mIntProperties) +, mFloatProperties(other.mFloatProperties) +, mStringProperties(other.mStringProperties) +, mMatrixProperties(other.mMatrixProperties) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyInteger(const char* szName, int iValue) { + return SetGenericProperty<int>(mIntProperties, szName,iValue); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyFloat(const char* szName, ai_real iValue) { + return SetGenericProperty<ai_real>(mFloatProperties, szName,iValue); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyString(const char* szName, const std::string& value) { + return SetGenericProperty<std::string>(mStringProperties, szName,value); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool ExportProperties::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { + return SetGenericProperty<aiMatrix4x4>(mMatrixProperties, szName,value); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +int ExportProperties::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { + return GetGenericProperty<int>(mIntProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +ai_real ExportProperties::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const { + return GetGenericProperty<ai_real>(mFloatProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +const std::string ExportProperties::GetPropertyString(const char* szName, + const std::string& iErrorReturn /*= ""*/) const { + return GetGenericProperty<std::string>(mStringProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +const aiMatrix4x4 ExportProperties::GetPropertyMatrix(const char* szName, + const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const { + return GetGenericProperty<aiMatrix4x4>(mMatrixProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyInteger(const char* szName) const { + return HasGenericProperty<int>(mIntProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyBool(const char* szName) const { + return HasGenericProperty<int>(mIntProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyFloat(const char* szName) const { + return HasGenericProperty<ai_real>(mFloatProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyString(const char* szName) const { + return HasGenericProperty<std::string>(mStringProperties, szName); +} + +// ------------------------------------------------------------------------------------------------ +// Has a configuration property +bool ExportProperties::HasPropertyMatrix(const char* szName) const { + return HasGenericProperty<aiMatrix4x4>(mMatrixProperties, szName); +} + + +#endif // !ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/FBXAnimation.cpp b/thirdparty/assimp/code/FBXAnimation.cpp new file mode 100644 index 0000000000..874914431b --- /dev/null +++ b/thirdparty/assimp/code/FBXAnimation.cpp @@ -0,0 +1,305 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXAnimation.cpp + * @brief Assimp::FBX::AnimationCurve, Assimp::FBX::AnimationCurveNode, + * Assimp::FBX::AnimationLayer, Assimp::FBX::AnimationStack + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXImporter.h" +#include "FBXDocumentUtil.h" + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& /*doc*/) +: Object(id, element, name) +{ + const Scope& sc = GetRequiredScope(element); + const Element& KeyTime = GetRequiredElement(sc,"KeyTime"); + const Element& KeyValueFloat = GetRequiredElement(sc,"KeyValueFloat"); + + ParseVectorDataArray(keys, KeyTime); + ParseVectorDataArray(values, KeyValueFloat); + + if(keys.size() != values.size()) { + DOMError("the number of key times does not match the number of keyframe values",&KeyTime); + } + + // check if the key times are well-ordered + if(!std::equal(keys.begin(), keys.end() - 1, keys.begin() + 1, std::less<KeyTimeList::value_type>())) { + DOMError("the keyframes are not in ascending order",&KeyTime); + } + + const Element* KeyAttrDataFloat = sc["KeyAttrDataFloat"]; + if(KeyAttrDataFloat) { + ParseVectorDataArray(attributes, *KeyAttrDataFloat); + } + + const Element* KeyAttrFlags = sc["KeyAttrFlags"]; + if(KeyAttrFlags) { + ParseVectorDataArray(flags, *KeyAttrFlags); + } +} + +// ------------------------------------------------------------------------------------------------ +AnimationCurve::~AnimationCurve() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, + const Document& doc, const char* const * target_prop_whitelist /*= NULL*/, + size_t whitelist_size /*= 0*/) +: Object(id, element, name) +, target() +, doc(doc) +{ + const Scope& sc = GetRequiredScope(element); + + // find target node + const char* whitelist[] = {"Model","NodeAttribute","Deformer"}; + const std::vector<const Connection*>& conns = doc.GetConnectionsBySourceSequenced(ID(),whitelist,3); + + for(const Connection* con : conns) { + + // link should go for a property + if (!con->PropertyName().length()) { + continue; + } + + if(target_prop_whitelist) { + const char* const s = con->PropertyName().c_str(); + bool ok = false; + for (size_t i = 0; i < whitelist_size; ++i) { + if (!strcmp(s, target_prop_whitelist[i])) { + ok = true; + break; + } + } + + if (!ok) { + throw std::range_error("AnimationCurveNode target property is not in whitelist"); + } + } + + const Object* const ob = con->DestinationObject(); + if(!ob) { + DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring",&element); + continue; + } + + // XXX support constraints as DOM class + //ai_assert(dynamic_cast<const Model*>(ob) || dynamic_cast<const NodeAttribute*>(ob)); + target = ob; + if(!target) { + continue; + } + + prop = con->PropertyName(); + break; + } + + if(!target) { + DOMWarning("failed to resolve target Model/NodeAttribute/Constraint for AnimationCurveNode",&element); + } + + props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc,false); +} + +// ------------------------------------------------------------------------------------------------ +AnimationCurveNode::~AnimationCurveNode() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +const AnimationCurveMap& AnimationCurveNode::Curves() const +{ + if ( curves.empty() ) { + // resolve attached animation curves + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve"); + + for(const Connection* con : conns) { + + // link should go for a property + if (!con->PropertyName().length()) { + continue; + } + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring",&element); + continue; + } + + const AnimationCurve* const anim = dynamic_cast<const AnimationCurve*>(ob); + if(!anim) { + DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve",&element); + continue; + } + + curves[con->PropertyName()] = anim; + } + } + + return curves; +} + +// ------------------------------------------------------------------------------------------------ +AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc) +: Object(id, element, name) +, doc(doc) +{ + const Scope& sc = GetRequiredScope(element); + + // note: the props table here bears little importance and is usually absent + props = GetPropertyTable(doc,"AnimationLayer.FbxAnimLayer",element,sc, true); +} + +// ------------------------------------------------------------------------------------------------ +AnimationLayer::~AnimationLayer() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whitelist /*= NULL*/, + size_t whitelist_size /*= 0*/) const +{ + AnimationCurveNodeList nodes; + + // resolve attached animation nodes + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurveNode"); + nodes.reserve(conns.size()); + + for(const Connection* con : conns) { + + // link should not go to a property + if (con->PropertyName().length()) { + continue; + } + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring",&element); + continue; + } + + const AnimationCurveNode* const anim = dynamic_cast<const AnimationCurveNode*>(ob); + if(!anim) { + DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode",&element); + continue; + } + + if(target_prop_whitelist) { + const char* s = anim->TargetProperty().c_str(); + bool ok = false; + for (size_t i = 0; i < whitelist_size; ++i) { + if (!strcmp(s, target_prop_whitelist[i])) { + ok = true; + break; + } + } + if(!ok) { + continue; + } + } + nodes.push_back(anim); + } + + return nodes; // pray for NRVO +} + +// ------------------------------------------------------------------------------------------------ +AnimationStack::AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc) +: Object(id, element, name) +{ + const Scope& sc = GetRequiredScope(element); + + // note: we don't currently use any of these properties so we shouldn't bother if it is missing + props = GetPropertyTable(doc,"AnimationStack.FbxAnimStack",element,sc, true); + + // resolve attached animation layers + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationLayer"); + layers.reserve(conns.size()); + + for(const Connection* con : conns) { + + // link should not go to a property + if (con->PropertyName().length()) { + continue; + } + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring",&element); + continue; + } + + const AnimationLayer* const anim = dynamic_cast<const AnimationLayer*>(ob); + if(!anim) { + DOMWarning("source object for ->AnimationStack link is not an AnimationLayer",&element); + continue; + } + layers.push_back(anim); + } +} + +// ------------------------------------------------------------------------------------------------ +AnimationStack::~AnimationStack() +{ + // empty +} + +} //!FBX +} //!Assimp + +#endif // ASSIMP_BUILD_NO_FBX_IMPORTER diff --git a/thirdparty/assimp/code/FBXBinaryTokenizer.cpp b/thirdparty/assimp/code/FBXBinaryTokenizer.cpp new file mode 100644 index 0000000000..7138df4315 --- /dev/null +++ b/thirdparty/assimp/code/FBXBinaryTokenizer.cpp @@ -0,0 +1,466 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/** @file FBXBinaryTokenizer.cpp + * @brief Implementation of a fake lexer for binary fbx files - + * we emit tokens so the parser needs almost no special handling + * for binary files. + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXTokenizer.h" +#include "FBXUtil.h" +#include <assimp/defs.h> +#include <stdint.h> +#include <assimp/Exceptional.h> +#include <assimp/ByteSwapper.h> + +namespace Assimp { +namespace FBX { + +//enum Flag +//{ +// e_unknown_0 = 1 << 0, +// e_unknown_1 = 1 << 1, +// e_unknown_2 = 1 << 2, +// e_unknown_3 = 1 << 3, +// e_unknown_4 = 1 << 4, +// e_unknown_5 = 1 << 5, +// e_unknown_6 = 1 << 6, +// e_unknown_7 = 1 << 7, +// e_unknown_8 = 1 << 8, +// e_unknown_9 = 1 << 9, +// e_unknown_10 = 1 << 10, +// e_unknown_11 = 1 << 11, +// e_unknown_12 = 1 << 12, +// e_unknown_13 = 1 << 13, +// e_unknown_14 = 1 << 14, +// e_unknown_15 = 1 << 15, +// e_unknown_16 = 1 << 16, +// e_unknown_17 = 1 << 17, +// e_unknown_18 = 1 << 18, +// e_unknown_19 = 1 << 19, +// e_unknown_20 = 1 << 20, +// e_unknown_21 = 1 << 21, +// e_unknown_22 = 1 << 22, +// e_unknown_23 = 1 << 23, +// e_flag_field_size_64_bit = 1 << 24, // Not sure what is +// e_unknown_25 = 1 << 25, +// e_unknown_26 = 1 << 26, +// e_unknown_27 = 1 << 27, +// e_unknown_28 = 1 << 28, +// e_unknown_29 = 1 << 29, +// e_unknown_30 = 1 << 30, +// e_unknown_31 = 1 << 31 +//}; +// +//bool check_flag(uint32_t flags, Flag to_check) +//{ +// return (flags & to_check) != 0; +//} +// ------------------------------------------------------------------------------------------------ +Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int offset) + : + #ifdef DEBUG + contents(sbegin, static_cast<size_t>(send-sbegin)), + #endif + sbegin(sbegin) + , send(send) + , type(type) + , line(offset) + , column(BINARY_MARKER) +{ + ai_assert(sbegin); + ai_assert(send); + + // binary tokens may have zero length because they are sometimes dummies + // inserted by TokenizeBinary() + ai_assert(send >= sbegin); +} + + +namespace { + +// ------------------------------------------------------------------------------------------------ +// signal tokenization error, this is always unrecoverable. Throws DeadlyImportError. +AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset) AI_WONT_RETURN_SUFFIX; +AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset) +{ + throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset)); +} + + +// ------------------------------------------------------------------------------------------------ +uint32_t Offset(const char* begin, const char* cursor) { + ai_assert(begin <= cursor); + + return static_cast<unsigned int>(cursor - begin); +} + +// ------------------------------------------------------------------------------------------------ +void TokenizeError(const std::string& message, const char* begin, const char* cursor) { + TokenizeError(message, Offset(begin, cursor)); +} + +// ------------------------------------------------------------------------------------------------ +uint32_t ReadWord(const char* input, const char*& cursor, const char* end) { + const size_t k_to_read = sizeof( uint32_t ); + if(Offset(cursor, end) < k_to_read ) { + TokenizeError("cannot ReadWord, out of bounds",input, cursor); + } + + uint32_t word; + ::memcpy(&word, cursor, 4); + AI_SWAP4(word); + + cursor += k_to_read; + + return word; +} + +// ------------------------------------------------------------------------------------------------ +uint64_t ReadDoubleWord(const char* input, const char*& cursor, const char* end) { + const size_t k_to_read = sizeof(uint64_t); + if(Offset(cursor, end) < k_to_read) { + TokenizeError("cannot ReadDoubleWord, out of bounds",input, cursor); + } + + uint64_t dword /*= *reinterpret_cast<const uint64_t*>(cursor)*/; + ::memcpy( &dword, cursor, sizeof( uint64_t ) ); + AI_SWAP8(dword); + + cursor += k_to_read; + + return dword; +} + +// ------------------------------------------------------------------------------------------------ +uint8_t ReadByte(const char* input, const char*& cursor, const char* end) { + if(Offset(cursor, end) < sizeof( uint8_t ) ) { + TokenizeError("cannot ReadByte, out of bounds",input, cursor); + } + + uint8_t word;/* = *reinterpret_cast< const uint8_t* >( cursor )*/ + ::memcpy( &word, cursor, sizeof( uint8_t ) ); + ++cursor; + + return word; +} + +// ------------------------------------------------------------------------------------------------ +unsigned int ReadString(const char*& sbegin_out, const char*& send_out, const char* input, + const char*& cursor, const char* end, bool long_length = false, bool allow_null = false) { + const uint32_t len_len = long_length ? 4 : 1; + if(Offset(cursor, end) < len_len) { + TokenizeError("cannot ReadString, out of bounds reading length",input, cursor); + } + + const uint32_t length = long_length ? ReadWord(input, cursor, end) : ReadByte(input, cursor, end); + + if (Offset(cursor, end) < length) { + TokenizeError("cannot ReadString, length is out of bounds",input, cursor); + } + + sbegin_out = cursor; + cursor += length; + + send_out = cursor; + + if(!allow_null) { + for (unsigned int i = 0; i < length; ++i) { + if(sbegin_out[i] == '\0') { + TokenizeError("failed ReadString, unexpected NUL character in string",input, cursor); + } + } + } + + return length; +} + +// ------------------------------------------------------------------------------------------------ +void ReadData(const char*& sbegin_out, const char*& send_out, const char* input, const char*& cursor, const char* end) { + if(Offset(cursor, end) < 1) { + TokenizeError("cannot ReadData, out of bounds reading length",input, cursor); + } + + const char type = *cursor; + sbegin_out = cursor++; + + switch(type) + { + // 16 bit int + case 'Y': + cursor += 2; + break; + + // 1 bit bool flag (yes/no) + case 'C': + cursor += 1; + break; + + // 32 bit int + case 'I': + // <- fall through + + // float + case 'F': + cursor += 4; + break; + + // double + case 'D': + cursor += 8; + break; + + // 64 bit int + case 'L': + cursor += 8; + break; + + // note: do not write cursor += ReadWord(...cursor) as this would be UB + + // raw binary data + case 'R': + { + const uint32_t length = ReadWord(input, cursor, end); + cursor += length; + break; + } + + case 'b': + // TODO: what is the 'b' type code? Right now we just skip over it / + // take the full range we could get + cursor = end; + break; + + // array of * + case 'f': + case 'd': + case 'l': + case 'i': + case 'c': { + const uint32_t length = ReadWord(input, cursor, end); + const uint32_t encoding = ReadWord(input, cursor, end); + + const uint32_t comp_len = ReadWord(input, cursor, end); + + // compute length based on type and check against the stored value + if(encoding == 0) { + uint32_t stride = 0; + switch(type) + { + case 'f': + case 'i': + stride = 4; + break; + + case 'd': + case 'l': + stride = 8; + break; + + case 'c': + stride = 1; + break; + + default: + ai_assert(false); + }; + ai_assert(stride > 0); + if(length * stride != comp_len) { + TokenizeError("cannot ReadData, calculated data stride differs from what the file claims",input, cursor); + } + } + // zip/deflate algorithm (encoding==1)? take given length. anything else? die + else if (encoding != 1) { + TokenizeError("cannot ReadData, unknown encoding",input, cursor); + } + cursor += comp_len; + break; + } + + // string + case 'S': { + const char* sb, *se; + // 0 characters can legally happen in such strings + ReadString(sb, se, input, cursor, end, true, true); + break; + } + default: + TokenizeError("cannot ReadData, unexpected type code: " + std::string(&type, 1),input, cursor); + } + + if(cursor > end) { + TokenizeError("cannot ReadData, the remaining size is too small for the data type: " + std::string(&type, 1),input, cursor); + } + + // the type code is contained in the returned range + send_out = cursor; +} + + +// ------------------------------------------------------------------------------------------------ +bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor, const char* end, bool const is64bits) +{ + // the first word contains the offset at which this block ends + const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); + + // we may get 0 if reading reached the end of the file - + // fbx files have a mysterious extra footer which I don't know + // how to extract any information from, but at least it always + // starts with a 0. + if(!end_offset) { + return false; + } + + if(end_offset > Offset(input, end)) { + TokenizeError("block offset is out of range",input, cursor); + } + else if(end_offset < Offset(input, cursor)) { + TokenizeError("block offset is negative out of range",input, cursor); + } + + // the second data word contains the number of properties in the scope + const uint64_t prop_count = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); + + // the third data word contains the length of the property list + const uint64_t prop_length = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); + + // now comes the name of the scope/key + const char* sbeg, *send; + ReadString(sbeg, send, input, cursor, end); + + output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, cursor) )); + + // now come the individual properties + const char* begin_cursor = cursor; + for (unsigned int i = 0; i < prop_count; ++i) { + ReadData(sbeg, send, input, cursor, begin_cursor + prop_length); + + output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor) )); + + if(i != prop_count-1) { + output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_COMMA, Offset(input, cursor) )); + } + } + + if (Offset(begin_cursor, cursor) != prop_length) { + TokenizeError("property length not reached, something is wrong",input, cursor); + } + + // at the end of each nested block, there is a NUL record to indicate + // that the sub-scope exists (i.e. to distinguish between P: and P : {}) + // this NUL record is 13 bytes long on 32 bit version and 25 bytes long on 64 bit. + const size_t sentinel_block_length = is64bits ? (sizeof(uint64_t)* 3 + 1) : (sizeof(uint32_t)* 3 + 1); + + if (Offset(input, cursor) < end_offset) { + if (end_offset - Offset(input, cursor) < sentinel_block_length) { + TokenizeError("insufficient padding bytes at block end",input, cursor); + } + + output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_OPEN_BRACKET, Offset(input, cursor) )); + + // XXX this is vulnerable to stack overflowing .. + while(Offset(input, cursor) < end_offset - sentinel_block_length) { + ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits); + } + output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor) )); + + for (unsigned int i = 0; i < sentinel_block_length; ++i) { + if(cursor[i] != '\0') { + TokenizeError("failed to read nested block sentinel, expected all bytes to be 0",input, cursor); + } + } + cursor += sentinel_block_length; + } + + if (Offset(input, cursor) != end_offset) { + TokenizeError("scope length not reached, something is wrong",input, cursor); + } + + return true; +} + +} // anonymous namespace + +// ------------------------------------------------------------------------------------------------ +// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent +void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int length) +{ + ai_assert(input); + + if(length < 0x1b) { + TokenizeError("file is too short",0); + } + + //uint32_t offset = 0x15; +/* const char* cursor = input + 0x15; + + const uint32_t flags = ReadWord(input, cursor, input + length); + + const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused + const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused*/ + + if (strncmp(input,"Kaydara FBX Binary",18)) { + TokenizeError("magic bytes not found",0); + } + + const char* cursor = input + 18; + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + /*Result ignored*/ ReadByte(input, cursor, input + length); + const uint32_t version = ReadWord(input, cursor, input + length); + const bool is64bits = version >= 7500; + const char *end = input + length; + while (cursor < end ) { + if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) { + break; + } + } +} + +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBXCommon.h b/thirdparty/assimp/code/FBXCommon.h new file mode 100644 index 0000000000..fcb20a5cad --- /dev/null +++ b/thirdparty/assimp/code/FBXCommon.h @@ -0,0 +1,86 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXCommon.h +* Some useful constants and enums for dealing with FBX files. +*/ +#ifndef AI_FBXCOMMON_H_INC +#define AI_FBXCOMMON_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + + +namespace FBX +{ + const std::string NULL_RECORD = { // 13 null bytes + '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0' + }; // who knows why + const std::string SEPARATOR = {'\x00', '\x01'}; // for use inside strings + const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import + const int64_t SECOND = 46186158000; // FBX's kTime unit + + // rotation order. We'll probably use EulerXYZ for everything + enum RotOrder { + RotOrder_EulerXYZ = 0, + RotOrder_EulerXZY, + RotOrder_EulerYZX, + RotOrder_EulerYXZ, + RotOrder_EulerZXY, + RotOrder_EulerZYX, + + RotOrder_SphericXYZ, + + RotOrder_MAX // end-of-enum sentinel + }; + + // transformation inheritance method. Most of the time RSrs + enum TransformInheritance { + TransformInheritance_RrSs = 0, + TransformInheritance_RSrs, + TransformInheritance_Rrs, + + TransformInheritance_MAX // end-of-enum sentinel + }; +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXCOMMON_H_INC diff --git a/thirdparty/assimp/code/FBXCompileConfig.h b/thirdparty/assimp/code/FBXCompileConfig.h new file mode 100644 index 0000000000..3a3841fa5b --- /dev/null +++ b/thirdparty/assimp/code/FBXCompileConfig.h @@ -0,0 +1,70 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXCompileConfig.h + * @brief FBX importer compile-time switches + */ +#ifndef INCLUDED_AI_FBX_COMPILECONFIG_H +#define INCLUDED_AI_FBX_COMPILECONFIG_H + +#include <map> + +// +#if _MSC_VER > 1500 || (defined __GNUC___) +# define ASSIMP_FBX_USE_UNORDERED_MULTIMAP +# else +# define fbx_unordered_map map +# define fbx_unordered_multimap multimap +#endif + +#ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP +# include <unordered_map> +# if _MSC_VER > 1600 +# define fbx_unordered_map unordered_map +# define fbx_unordered_multimap unordered_multimap +# else +# define fbx_unordered_map tr1::unordered_map +# define fbx_unordered_multimap tr1::unordered_multimap +# endif +#endif + +#endif // INCLUDED_AI_FBX_COMPILECONFIG_H diff --git a/thirdparty/assimp/code/FBXConverter.cpp b/thirdparty/assimp/code/FBXConverter.cpp new file mode 100644 index 0000000000..000c4ed53b --- /dev/null +++ b/thirdparty/assimp/code/FBXConverter.cpp @@ -0,0 +1,3515 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXConverter.cpp + * @brief Implementation of the FBX DOM -> aiScene converter + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXConverter.h" +#include "FBXParser.h" +#include "FBXMeshGeometry.h" +#include "FBXDocument.h" +#include "FBXUtil.h" +#include "FBXProperties.h" +#include "FBXImporter.h" + +#include <assimp/StringComparison.h> + +#include <assimp/scene.h> + +#include <assimp/CreateAnimMesh.h> + +#include <tuple> +#include <memory> +#include <iterator> +#include <vector> +#include <sstream> +#include <iomanip> + +namespace Assimp { + namespace FBX { + + using namespace Util; + +#define MAGIC_NODE_TAG "_$AssimpFbx$" + +#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L + + FBXConverter::FBXConverter(aiScene* out, const Document& doc) + : defaultMaterialIndex() + , out(out) + , doc(doc) { + // animations need to be converted first since this will + // populate the node_anim_chain_bits map, which is needed + // to determine which nodes need to be generated. + ConvertAnimations(); + ConvertRootNode(); + + if (doc.Settings().readAllMaterials) { + // unfortunately this means we have to evaluate all objects + for (const ObjectMap::value_type& v : doc.Objects()) { + + const Object* ob = v.second->Get(); + if (!ob) { + continue; + } + + const Material* mat = dynamic_cast<const Material*>(ob); + if (mat) { + + if (materials_converted.find(mat) == materials_converted.end()) { + ConvertMaterial(*mat, 0); + } + } + } + } + + ConvertGlobalSettings(); + TransferDataToScene(); + + // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE + // to make sure the scene passes assimp's validation. FBX files + // need not contain geometry (i.e. camera animations, raw armatures). + if (out->mNumMeshes == 0) { + out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + } + } + + + FBXConverter::~FBXConverter() { + std::for_each(meshes.begin(), meshes.end(), Util::delete_fun<aiMesh>()); + std::for_each(materials.begin(), materials.end(), Util::delete_fun<aiMaterial>()); + std::for_each(animations.begin(), animations.end(), Util::delete_fun<aiAnimation>()); + std::for_each(lights.begin(), lights.end(), Util::delete_fun<aiLight>()); + std::for_each(cameras.begin(), cameras.end(), Util::delete_fun<aiCamera>()); + std::for_each(textures.begin(), textures.end(), Util::delete_fun<aiTexture>()); + } + + void FBXConverter::ConvertRootNode() { + out->mRootNode = new aiNode(); + out->mRootNode->mName.Set("RootNode"); + + // root has ID 0 + ConvertNodes(0L, *out->mRootNode); + } + + void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) { + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model"); + + std::vector<aiNode*> nodes; + nodes.reserve(conns.size()); + + std::vector<aiNode*> nodes_chain; + std::vector<aiNode*> post_nodes_chain; + + try { + for (const Connection* con : conns) { + + // ignore object-property links + if (con->PropertyName().length()) { + continue; + } + + const Object* const object = con->SourceObject(); + if (nullptr == object) { + FBXImporter::LogWarn("failed to convert source object for Model link"); + continue; + } + + const Model* const model = dynamic_cast<const Model*>(object); + + if (nullptr != model) { + nodes_chain.clear(); + post_nodes_chain.clear(); + + aiMatrix4x4 new_abs_transform = parent_transform; + + // even though there is only a single input node, the design of + // assimp (or rather: the complicated transformation chain that + // is employed by fbx) means that we may need multiple aiNode's + // to represent a fbx node's transformation. + GenerateTransformationNodeChain(*model, nodes_chain, post_nodes_chain); + + ai_assert(nodes_chain.size()); + + std::string original_name = FixNodeName(model->Name()); + + // check if any of the nodes in the chain has the name the fbx node + // is supposed to have. If there is none, add another node to + // preserve the name - people might have scripts etc. that rely + // on specific node names. + aiNode* name_carrier = NULL; + for (aiNode* prenode : nodes_chain) { + if (!strcmp(prenode->mName.C_Str(), original_name.c_str())) { + name_carrier = prenode; + break; + } + } + + if (!name_carrier) { + std::string old_original_name = original_name; + GetUniqueName(old_original_name, original_name); + nodes_chain.push_back(new aiNode(original_name)); + } + else { + original_name = nodes_chain.back()->mName.C_Str(); + } + + //setup metadata on newest node + SetupNodeMetadata(*model, *nodes_chain.back()); + + // link all nodes in a row + aiNode* last_parent = &parent; + for (aiNode* prenode : nodes_chain) { + ai_assert(prenode); + + if (last_parent != &parent) { + last_parent->mNumChildren = 1; + last_parent->mChildren = new aiNode*[1]; + last_parent->mChildren[0] = prenode; + } + + prenode->mParent = last_parent; + last_parent = prenode; + + new_abs_transform *= prenode->mTransformation; + } + + // attach geometry + ConvertModel(*model, *nodes_chain.back(), new_abs_transform); + + // check if there will be any child nodes + const std::vector<const Connection*>& child_conns + = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model"); + + // if so, link the geometric transform inverse nodes + // before we attach any child nodes + if (child_conns.size()) { + for (aiNode* postnode : post_nodes_chain) { + ai_assert(postnode); + + if (last_parent != &parent) { + last_parent->mNumChildren = 1; + last_parent->mChildren = new aiNode*[1]; + last_parent->mChildren[0] = postnode; + } + + postnode->mParent = last_parent; + last_parent = postnode; + + new_abs_transform *= postnode->mTransformation; + } + } + else { + // free the nodes we allocated as we don't need them + Util::delete_fun<aiNode> deleter; + std::for_each( + post_nodes_chain.begin(), + post_nodes_chain.end(), + deleter + ); + } + + // attach sub-nodes (if any) + ConvertNodes(model->ID(), *last_parent, new_abs_transform); + + if (doc.Settings().readLights) { + ConvertLights(*model, original_name); + } + + if (doc.Settings().readCameras) { + ConvertCameras(*model, original_name); + } + + nodes.push_back(nodes_chain.front()); + nodes_chain.clear(); + } + } + + if (nodes.size()) { + parent.mChildren = new aiNode*[nodes.size()](); + parent.mNumChildren = static_cast<unsigned int>(nodes.size()); + + std::swap_ranges(nodes.begin(), nodes.end(), parent.mChildren); + } + } + catch (std::exception&) { + Util::delete_fun<aiNode> deleter; + std::for_each(nodes.begin(), nodes.end(), deleter); + std::for_each(nodes_chain.begin(), nodes_chain.end(), deleter); + std::for_each(post_nodes_chain.begin(), post_nodes_chain.end(), deleter); + } + } + + + void FBXConverter::ConvertLights(const Model& model, const std::string &orig_name) { + const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes(); + for (const NodeAttribute* attr : node_attrs) { + const Light* const light = dynamic_cast<const Light*>(attr); + if (light) { + ConvertLight(*light, orig_name); + } + } + } + + void FBXConverter::ConvertCameras(const Model& model, const std::string &orig_name) { + const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes(); + for (const NodeAttribute* attr : node_attrs) { + const Camera* const cam = dynamic_cast<const Camera*>(attr); + if (cam) { + ConvertCamera(*cam, orig_name); + } + } + } + + void FBXConverter::ConvertLight(const Light& light, const std::string &orig_name) { + lights.push_back(new aiLight()); + aiLight* const out_light = lights.back(); + + out_light->mName.Set(orig_name); + + const float intensity = light.Intensity() / 100.0f; + const aiVector3D& col = light.Color(); + + out_light->mColorDiffuse = aiColor3D(col.x, col.y, col.z); + out_light->mColorDiffuse.r *= intensity; + out_light->mColorDiffuse.g *= intensity; + out_light->mColorDiffuse.b *= intensity; + + out_light->mColorSpecular = out_light->mColorDiffuse; + + //lights are defined along negative y direction + out_light->mPosition = aiVector3D(0.0f); + out_light->mDirection = aiVector3D(0.0f, -1.0f, 0.0f); + out_light->mUp = aiVector3D(0.0f, 0.0f, -1.0f); + + switch (light.LightType()) + { + case Light::Type_Point: + out_light->mType = aiLightSource_POINT; + break; + + case Light::Type_Directional: + out_light->mType = aiLightSource_DIRECTIONAL; + break; + + case Light::Type_Spot: + out_light->mType = aiLightSource_SPOT; + out_light->mAngleOuterCone = AI_DEG_TO_RAD(light.OuterAngle()); + out_light->mAngleInnerCone = AI_DEG_TO_RAD(light.InnerAngle()); + break; + + case Light::Type_Area: + FBXImporter::LogWarn("cannot represent area light, set to UNDEFINED"); + out_light->mType = aiLightSource_UNDEFINED; + break; + + case Light::Type_Volume: + FBXImporter::LogWarn("cannot represent volume light, set to UNDEFINED"); + out_light->mType = aiLightSource_UNDEFINED; + break; + default: + ai_assert(false); + } + + float decay = light.DecayStart(); + switch (light.DecayType()) + { + case Light::Decay_None: + out_light->mAttenuationConstant = decay; + out_light->mAttenuationLinear = 0.0f; + out_light->mAttenuationQuadratic = 0.0f; + break; + case Light::Decay_Linear: + out_light->mAttenuationConstant = 0.0f; + out_light->mAttenuationLinear = 2.0f / decay; + out_light->mAttenuationQuadratic = 0.0f; + break; + case Light::Decay_Quadratic: + out_light->mAttenuationConstant = 0.0f; + out_light->mAttenuationLinear = 0.0f; + out_light->mAttenuationQuadratic = 2.0f / (decay * decay); + break; + case Light::Decay_Cubic: + FBXImporter::LogWarn("cannot represent cubic attenuation, set to Quadratic"); + out_light->mAttenuationQuadratic = 1.0f; + break; + default: + ai_assert(false); + } + } + + void FBXConverter::ConvertCamera(const Camera& cam, const std::string &orig_name) + { + cameras.push_back(new aiCamera()); + aiCamera* const out_camera = cameras.back(); + + out_camera->mName.Set(orig_name); + + out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight(); + + //cameras are defined along positive x direction + /*out_camera->mPosition = cam.Position(); + out_camera->mLookAt = (cam.InterestPosition() - out_camera->mPosition).Normalize(); + out_camera->mUp = cam.UpVector();*/ + + out_camera->mPosition = aiVector3D(0.0f); + out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f); + out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f); + + out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView()); + + out_camera->mClipPlaneNear = cam.NearPlane(); + out_camera->mClipPlaneFar = cam.FarPlane(); + + out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView()); + out_camera->mClipPlaneNear = cam.NearPlane(); + out_camera->mClipPlaneFar = cam.FarPlane(); + } + + void FBXConverter::GetUniqueName(const std::string &name, std::string &uniqueName) + { + int i = 0; + uniqueName = name; + while (mNodeNames.find(uniqueName) != mNodeNames.end()) + { + ++i; + std::stringstream ext; + ext << name << std::setfill('0') << std::setw(3) << i; + uniqueName = ext.str(); + } + mNodeNames.insert(uniqueName); + } + + + const char* FBXConverter::NameTransformationComp(TransformationComp comp) { + switch (comp) { + case TransformationComp_Translation: + return "Translation"; + case TransformationComp_RotationOffset: + return "RotationOffset"; + case TransformationComp_RotationPivot: + return "RotationPivot"; + case TransformationComp_PreRotation: + return "PreRotation"; + case TransformationComp_Rotation: + return "Rotation"; + case TransformationComp_PostRotation: + return "PostRotation"; + case TransformationComp_RotationPivotInverse: + return "RotationPivotInverse"; + case TransformationComp_ScalingOffset: + return "ScalingOffset"; + case TransformationComp_ScalingPivot: + return "ScalingPivot"; + case TransformationComp_Scaling: + return "Scaling"; + case TransformationComp_ScalingPivotInverse: + return "ScalingPivotInverse"; + case TransformationComp_GeometricScaling: + return "GeometricScaling"; + case TransformationComp_GeometricRotation: + return "GeometricRotation"; + case TransformationComp_GeometricTranslation: + return "GeometricTranslation"; + case TransformationComp_GeometricScalingInverse: + return "GeometricScalingInverse"; + case TransformationComp_GeometricRotationInverse: + return "GeometricRotationInverse"; + case TransformationComp_GeometricTranslationInverse: + return "GeometricTranslationInverse"; + case TransformationComp_MAXIMUM: // this is to silence compiler warnings + default: + break; + } + + ai_assert(false); + + return nullptr; + } + + const char* FBXConverter::NameTransformationCompProperty(TransformationComp comp) { + switch (comp) { + case TransformationComp_Translation: + return "Lcl Translation"; + case TransformationComp_RotationOffset: + return "RotationOffset"; + case TransformationComp_RotationPivot: + return "RotationPivot"; + case TransformationComp_PreRotation: + return "PreRotation"; + case TransformationComp_Rotation: + return "Lcl Rotation"; + case TransformationComp_PostRotation: + return "PostRotation"; + case TransformationComp_RotationPivotInverse: + return "RotationPivotInverse"; + case TransformationComp_ScalingOffset: + return "ScalingOffset"; + case TransformationComp_ScalingPivot: + return "ScalingPivot"; + case TransformationComp_Scaling: + return "Lcl Scaling"; + case TransformationComp_ScalingPivotInverse: + return "ScalingPivotInverse"; + case TransformationComp_GeometricScaling: + return "GeometricScaling"; + case TransformationComp_GeometricRotation: + return "GeometricRotation"; + case TransformationComp_GeometricTranslation: + return "GeometricTranslation"; + case TransformationComp_GeometricScalingInverse: + return "GeometricScalingInverse"; + case TransformationComp_GeometricRotationInverse: + return "GeometricRotationInverse"; + case TransformationComp_GeometricTranslationInverse: + return "GeometricTranslationInverse"; + case TransformationComp_MAXIMUM: // this is to silence compiler warnings + break; + } + + ai_assert(false); + + return nullptr; + } + + aiVector3D FBXConverter::TransformationCompDefaultValue(TransformationComp comp) + { + // XXX a neat way to solve the never-ending special cases for scaling + // would be to do everything in log space! + return comp == TransformationComp_Scaling ? aiVector3D(1.f, 1.f, 1.f) : aiVector3D(); + } + + void FBXConverter::GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out) + { + if (mode == Model::RotOrder_SphericXYZ) { + FBXImporter::LogError("Unsupported RotationMode: SphericXYZ"); + out = aiMatrix4x4(); + return; + } + + const float angle_epsilon = 1e-6f; + + out = aiMatrix4x4(); + + bool is_id[3] = { true, true, true }; + + aiMatrix4x4 temp[3]; + if (std::fabs(rotation.z) > angle_epsilon) { + aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(rotation.z), temp[2]); + is_id[2] = false; + } + if (std::fabs(rotation.y) > angle_epsilon) { + aiMatrix4x4::RotationY(AI_DEG_TO_RAD(rotation.y), temp[1]); + is_id[1] = false; + } + if (std::fabs(rotation.x) > angle_epsilon) { + aiMatrix4x4::RotationX(AI_DEG_TO_RAD(rotation.x), temp[0]); + is_id[0] = false; + } + + int order[3] = { -1, -1, -1 }; + + // note: rotation order is inverted since we're left multiplying as is usual in assimp + switch (mode) + { + case Model::RotOrder_EulerXYZ: + order[0] = 2; + order[1] = 1; + order[2] = 0; + break; + + case Model::RotOrder_EulerXZY: + order[0] = 1; + order[1] = 2; + order[2] = 0; + break; + + case Model::RotOrder_EulerYZX: + order[0] = 0; + order[1] = 2; + order[2] = 1; + break; + + case Model::RotOrder_EulerYXZ: + order[0] = 2; + order[1] = 0; + order[2] = 1; + break; + + case Model::RotOrder_EulerZXY: + order[0] = 1; + order[1] = 0; + order[2] = 2; + break; + + case Model::RotOrder_EulerZYX: + order[0] = 0; + order[1] = 1; + order[2] = 2; + break; + + default: + ai_assert(false); + break; + } + + ai_assert(order[0] >= 0); + ai_assert(order[0] <= 2); + ai_assert(order[1] >= 0); + ai_assert(order[1] <= 2); + ai_assert(order[2] >= 0); + ai_assert(order[2] <= 2); + + if (!is_id[order[0]]) { + out = temp[order[0]]; + } + + if (!is_id[order[1]]) { + out = out * temp[order[1]]; + } + + if (!is_id[order[2]]) { + out = out * temp[order[2]]; + } + } + + bool FBXConverter::NeedsComplexTransformationChain(const Model& model) + { + const PropertyTable& props = model.Props(); + bool ok; + + const float zero_epsilon = 1e-6f; + const aiVector3D all_ones(1.0f, 1.0f, 1.0f); + for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) { + const TransformationComp comp = static_cast<TransformationComp>(i); + + if (comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation) { + continue; + } + + bool scale_compare = (comp == TransformationComp_GeometricScaling || comp == TransformationComp_Scaling); + + const aiVector3D& v = PropertyGet<aiVector3D>(props, NameTransformationCompProperty(comp), ok); + if (ok && scale_compare) { + if ((v - all_ones).SquareLength() > zero_epsilon) { + return true; + } + } + else if (ok) { + if (v.SquareLength() > zero_epsilon) { + return true; + } + } + } + + return false; + } + + std::string FBXConverter::NameTransformationChainNode(const std::string& name, TransformationComp comp) + { + return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp); + } + + void FBXConverter::GenerateTransformationNodeChain(const Model& model, std::vector<aiNode*>& output_nodes, + std::vector<aiNode*>& post_output_nodes) { + const PropertyTable& props = model.Props(); + const Model::RotOrder rot = model.RotationOrder(); + + bool ok; + + aiMatrix4x4 chain[TransformationComp_MAXIMUM]; + std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4()); + + // generate transformation matrices for all the different transformation components + const float zero_epsilon = 1e-6f; + const aiVector3D all_ones(1.0f, 1.0f, 1.0f); + bool is_complex = false; + + const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok); + if (ok && PreRotation.SquareLength() > zero_epsilon) { + is_complex = true; + + GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[TransformationComp_PreRotation]); + } + + const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props, "PostRotation", ok); + if (ok && PostRotation.SquareLength() > zero_epsilon) { + is_complex = true; + + GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[TransformationComp_PostRotation]); + } + + const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props, "RotationPivot", ok); + if (ok && RotationPivot.SquareLength() > zero_epsilon) { + is_complex = true; + + aiMatrix4x4::Translation(RotationPivot, chain[TransformationComp_RotationPivot]); + aiMatrix4x4::Translation(-RotationPivot, chain[TransformationComp_RotationPivotInverse]); + } + + const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props, "RotationOffset", ok); + if (ok && RotationOffset.SquareLength() > zero_epsilon) { + is_complex = true; + + aiMatrix4x4::Translation(RotationOffset, chain[TransformationComp_RotationOffset]); + } + + const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props, "ScalingOffset", ok); + if (ok && ScalingOffset.SquareLength() > zero_epsilon) { + is_complex = true; + + aiMatrix4x4::Translation(ScalingOffset, chain[TransformationComp_ScalingOffset]); + } + + const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props, "ScalingPivot", ok); + if (ok && ScalingPivot.SquareLength() > zero_epsilon) { + is_complex = true; + + aiMatrix4x4::Translation(ScalingPivot, chain[TransformationComp_ScalingPivot]); + aiMatrix4x4::Translation(-ScalingPivot, chain[TransformationComp_ScalingPivotInverse]); + } + + const aiVector3D& Translation = PropertyGet<aiVector3D>(props, "Lcl Translation", ok); + if (ok && Translation.SquareLength() > zero_epsilon) { + aiMatrix4x4::Translation(Translation, chain[TransformationComp_Translation]); + } + + const aiVector3D& Scaling = PropertyGet<aiVector3D>(props, "Lcl Scaling", ok); + if (ok && (Scaling - all_ones).SquareLength() > zero_epsilon) { + aiMatrix4x4::Scaling(Scaling, chain[TransformationComp_Scaling]); + } + + const aiVector3D& Rotation = PropertyGet<aiVector3D>(props, "Lcl Rotation", ok); + if (ok && Rotation.SquareLength() > zero_epsilon) { + GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]); + } + + const aiVector3D& GeometricScaling = PropertyGet<aiVector3D>(props, "GeometricScaling", ok); + if (ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon) { + is_complex = true; + aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]); + aiVector3D GeometricScalingInverse = GeometricScaling; + bool canscale = true; + for (unsigned int i = 0; i < 3; ++i) { + if (std::fabs(GeometricScalingInverse[i]) > zero_epsilon) { + GeometricScalingInverse[i] = 1.0f / GeometricScaling[i]; + } + else { + FBXImporter::LogError("cannot invert geometric scaling matrix with a 0.0 scale component"); + canscale = false; + break; + } + } + if (canscale) { + aiMatrix4x4::Scaling(GeometricScalingInverse, chain[TransformationComp_GeometricScalingInverse]); + } + } + + const aiVector3D& GeometricRotation = PropertyGet<aiVector3D>(props, "GeometricRotation", ok); + if (ok && GeometricRotation.SquareLength() > zero_epsilon) { + is_complex = true; + GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotation]); + GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotationInverse]); + chain[TransformationComp_GeometricRotationInverse].Inverse(); + } + + const aiVector3D& GeometricTranslation = PropertyGet<aiVector3D>(props, "GeometricTranslation", ok); + if (ok && GeometricTranslation.SquareLength() > zero_epsilon) { + is_complex = true; + aiMatrix4x4::Translation(GeometricTranslation, chain[TransformationComp_GeometricTranslation]); + aiMatrix4x4::Translation(-GeometricTranslation, chain[TransformationComp_GeometricTranslationInverse]); + } + + // is_complex needs to be consistent with NeedsComplexTransformationChain() + // or the interplay between this code and the animation converter would + // not be guaranteed. + ai_assert(NeedsComplexTransformationChain(model) == is_complex); + + std::string name = FixNodeName(model.Name()); + + // now, if we have more than just Translation, Scaling and Rotation, + // we need to generate a full node chain to accommodate for assimp's + // lack to express pivots and offsets. + if (is_complex && doc.Settings().preservePivots) { + FBXImporter::LogInfo("generating full transformation chain for node: " + name); + + // query the anim_chain_bits dictionary to find out which chain elements + // have associated node animation channels. These can not be dropped + // even if they have identity transform in bind pose. + NodeAnimBitMap::const_iterator it = node_anim_chain_bits.find(name); + const unsigned int anim_chain_bitmask = (it == node_anim_chain_bits.end() ? 0 : (*it).second); + + unsigned int bit = 0x1; + for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) { + const TransformationComp comp = static_cast<TransformationComp>(i); + + if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) { + continue; + } + + if (comp == TransformationComp_PostRotation) { + chain[i] = chain[i].Inverse(); + } + + aiNode* nd = new aiNode(); + nd->mName.Set(NameTransformationChainNode(name, comp)); + nd->mTransformation = chain[i]; + + // geometric inverses go in a post-node chain + if (comp == TransformationComp_GeometricScalingInverse || + comp == TransformationComp_GeometricRotationInverse || + comp == TransformationComp_GeometricTranslationInverse + ) { + post_output_nodes.push_back(nd); + } + else { + output_nodes.push_back(nd); + } + } + + ai_assert(output_nodes.size()); + return; + } + + // else, we can just multiply the matrices together + aiNode* nd = new aiNode(); + output_nodes.push_back(nd); + std::string uniqueName; + GetUniqueName(name, uniqueName); + + nd->mName.Set(uniqueName); + + for (const auto &transform : chain) { + nd->mTransformation = nd->mTransformation * transform; + } + } + + void FBXConverter::SetupNodeMetadata(const Model& model, aiNode& nd) + { + const PropertyTable& props = model.Props(); + DirectPropertyMap unparsedProperties = props.GetUnparsedProperties(); + + // create metadata on node + const std::size_t numStaticMetaData = 2; + aiMetadata* data = aiMetadata::Alloc(static_cast<unsigned int>(unparsedProperties.size() + numStaticMetaData)); + nd.mMetaData = data; + int index = 0; + + // find user defined properties (3ds Max) + data->Set(index++, "UserProperties", aiString(PropertyGet<std::string>(props, "UDP3DSMAX", ""))); + // preserve the info that a node was marked as Null node in the original file. + data->Set(index++, "IsNull", model.IsNull() ? true : false); + + // add unparsed properties to the node's metadata + for (const DirectPropertyMap::value_type& prop : unparsedProperties) { + // Interpret the property as a concrete type + if (const TypedProperty<bool>* interpreted = prop.second->As<TypedProperty<bool> >()) { + data->Set(index++, prop.first, interpreted->Value()); + } + else if (const TypedProperty<int>* interpreted = prop.second->As<TypedProperty<int> >()) { + data->Set(index++, prop.first, interpreted->Value()); + } + else if (const TypedProperty<uint64_t>* interpreted = prop.second->As<TypedProperty<uint64_t> >()) { + data->Set(index++, prop.first, interpreted->Value()); + } + else if (const TypedProperty<float>* interpreted = prop.second->As<TypedProperty<float> >()) { + data->Set(index++, prop.first, interpreted->Value()); + } + else if (const TypedProperty<std::string>* interpreted = prop.second->As<TypedProperty<std::string> >()) { + data->Set(index++, prop.first, aiString(interpreted->Value())); + } + else if (const TypedProperty<aiVector3D>* interpreted = prop.second->As<TypedProperty<aiVector3D> >()) { + data->Set(index++, prop.first, interpreted->Value()); + } + else { + ai_assert(false); + } + } + } + + void FBXConverter::ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform) + { + const std::vector<const Geometry*>& geos = model.GetGeometry(); + + std::vector<unsigned int> meshes; + meshes.reserve(geos.size()); + + for (const Geometry* geo : geos) { + + const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo); + const LineGeometry* const line = dynamic_cast<const LineGeometry*>(geo); + if (mesh) { + const std::vector<unsigned int>& indices = ConvertMesh(*mesh, model, node_global_transform, nd); + std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); + } + else if (line) { + const std::vector<unsigned int>& indices = ConvertLine(*line, model, node_global_transform, nd); + std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); + } + else { + FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name()); + } + } + + if (meshes.size()) { + nd.mMeshes = new unsigned int[meshes.size()](); + nd.mNumMeshes = static_cast<unsigned int>(meshes.size()); + + std::swap_ranges(meshes.begin(), meshes.end(), nd.mMeshes); + } + } + + std::vector<unsigned int> FBXConverter::ConvertMesh(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd) + { + std::vector<unsigned int> temp; + + MeshMap::const_iterator it = meshes_converted.find(&mesh); + if (it != meshes_converted.end()) { + std::copy((*it).second.begin(), (*it).second.end(), std::back_inserter(temp)); + return temp; + } + + const std::vector<aiVector3D>& vertices = mesh.GetVertices(); + const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts(); + if (vertices.empty() || faces.empty()) { + FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name()); + return temp; + } + + // one material per mesh maps easily to aiMesh. Multiple material + // meshes need to be split. + const MatIndexArray& mindices = mesh.GetMaterialIndices(); + if (doc.Settings().readMaterials && !mindices.empty()) { + const MatIndexArray::value_type base = mindices[0]; + for (MatIndexArray::value_type index : mindices) { + if (index != base) { + return ConvertMeshMultiMaterial(mesh, model, node_global_transform, nd); + } + } + } + + // faster code-path, just copy the data + temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform, nd)); + return temp; + } + + std::vector<unsigned int> FBXConverter::ConvertLine(const LineGeometry& line, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd) + { + std::vector<unsigned int> temp; + + const std::vector<aiVector3D>& vertices = line.GetVertices(); + const std::vector<int>& indices = line.GetIndices(); + if (vertices.empty() || indices.empty()) { + FBXImporter::LogWarn("ignoring empty line: " + line.Name()); + return temp; + } + + aiMesh* const out_mesh = SetupEmptyMesh(line, nd); + out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + + // copy vertices + out_mesh->mNumVertices = static_cast<unsigned int>(vertices.size()); + out_mesh->mVertices = new aiVector3D[out_mesh->mNumVertices]; + std::copy(vertices.begin(), vertices.end(), out_mesh->mVertices); + + //Number of line segments (faces) is "Number of Points - Number of Endpoints" + //N.B.: Endpoints in FbxLine are denoted by negative indices. + //If such an Index is encountered, add 1 and multiply by -1 to get the real index. + unsigned int epcount = 0; + for (unsigned i = 0; i < indices.size(); i++) + { + if (indices[i] < 0) epcount++; + } + unsigned int pcount = static_cast<unsigned int>( indices.size() ); + unsigned int scount = out_mesh->mNumFaces = pcount - epcount; + + aiFace* fac = out_mesh->mFaces = new aiFace[scount](); + for (unsigned int i = 0; i < pcount; ++i) { + if (indices[i] < 0) continue; + aiFace& f = *fac++; + f.mNumIndices = 2; //2 == aiPrimitiveType_LINE + f.mIndices = new unsigned int[2]; + f.mIndices[0] = indices[i]; + int segid = indices[(i + 1 == pcount ? 0 : i + 1)]; //If we have reached he last point, wrap around + f.mIndices[1] = (segid < 0 ? (segid + 1)*-1 : segid); //Convert EndPoint Index to normal Index + } + temp.push_back(static_cast<unsigned int>(meshes.size() - 1)); + return temp; + } + + aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode& nd) + { + aiMesh* const out_mesh = new aiMesh(); + meshes.push_back(out_mesh); + meshes_converted[&mesh].push_back(static_cast<unsigned int>(meshes.size() - 1)); + + // set name + std::string name = mesh.Name(); + if (name.substr(0, 10) == "Geometry::") { + name = name.substr(10); + } + + if (name.length()) { + out_mesh->mName.Set(name); + } + else + { + out_mesh->mName = nd.mName; + } + + return out_mesh; + } + + unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd) + { + const MatIndexArray& mindices = mesh.GetMaterialIndices(); + aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); + + const std::vector<aiVector3D>& vertices = mesh.GetVertices(); + const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts(); + + // copy vertices + out_mesh->mNumVertices = static_cast<unsigned int>(vertices.size()); + out_mesh->mVertices = new aiVector3D[vertices.size()]; + + std::copy(vertices.begin(), vertices.end(), out_mesh->mVertices); + + // generate dummy faces + out_mesh->mNumFaces = static_cast<unsigned int>(faces.size()); + aiFace* fac = out_mesh->mFaces = new aiFace[faces.size()](); + + unsigned int cursor = 0; + for (unsigned int pcount : faces) { + aiFace& f = *fac++; + f.mNumIndices = pcount; + f.mIndices = new unsigned int[pcount]; + switch (pcount) + { + case 1: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 2: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 3: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + } + for (unsigned int i = 0; i < pcount; ++i) { + f.mIndices[i] = cursor++; + } + } + + // copy normals + const std::vector<aiVector3D>& normals = mesh.GetNormals(); + if (normals.size()) { + ai_assert(normals.size() == vertices.size()); + + out_mesh->mNormals = new aiVector3D[vertices.size()]; + std::copy(normals.begin(), normals.end(), out_mesh->mNormals); + } + + // copy tangents - assimp requires both tangents and bitangents (binormals) + // to be present, or neither of them. Compute binormals from normals + // and tangents if needed. + const std::vector<aiVector3D>& tangents = mesh.GetTangents(); + const std::vector<aiVector3D>* binormals = &mesh.GetBinormals(); + + if (tangents.size()) { + std::vector<aiVector3D> tempBinormals; + if (!binormals->size()) { + if (normals.size()) { + tempBinormals.resize(normals.size()); + for (unsigned int i = 0; i < tangents.size(); ++i) { + tempBinormals[i] = normals[i] ^ tangents[i]; + } + + binormals = &tempBinormals; + } + else { + binormals = NULL; + } + } + + if (binormals) { + ai_assert(tangents.size() == vertices.size()); + ai_assert(binormals->size() == vertices.size()); + + out_mesh->mTangents = new aiVector3D[vertices.size()]; + std::copy(tangents.begin(), tangents.end(), out_mesh->mTangents); + + out_mesh->mBitangents = new aiVector3D[vertices.size()]; + std::copy(binormals->begin(), binormals->end(), out_mesh->mBitangents); + } + } + + // copy texture coords + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i); + if (uvs.empty()) { + break; + } + + aiVector3D* out_uv = out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()]; + for (const aiVector2D& v : uvs) { + *out_uv++ = aiVector3D(v.x, v.y, 0.0f); + } + + out_mesh->mNumUVComponents[i] = 2; + } + + // copy vertex colors + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { + const std::vector<aiColor4D>& colors = mesh.GetVertexColors(i); + if (colors.empty()) { + break; + } + + out_mesh->mColors[i] = new aiColor4D[vertices.size()]; + std::copy(colors.begin(), colors.end(), out_mesh->mColors[i]); + } + + if (!doc.Settings().readMaterials || mindices.empty()) { + FBXImporter::LogError("no material assigned to mesh, setting default material"); + out_mesh->mMaterialIndex = GetDefaultMaterial(); + } + else { + ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]); + } + + if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) { + ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION); + } + + std::vector<aiAnimMesh*> animMeshes; + for (const BlendShape* blendShape : mesh.GetBlendShapes()) { + for (const BlendShapeChannel* blendShapeChannel : blendShape->BlendShapeChannels()) { + const std::vector<const ShapeGeometry*>& shapeGeometries = blendShapeChannel->GetShapeGeometries(); + for (size_t i = 0; i < shapeGeometries.size(); i++) { + aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh); + const ShapeGeometry* shapeGeometry = shapeGeometries.at(i); + const std::vector<aiVector3D>& vertices = shapeGeometry->GetVertices(); + const std::vector<aiVector3D>& normals = shapeGeometry->GetNormals(); + const std::vector<unsigned int>& indices = shapeGeometry->GetIndices(); + animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name())); + for (size_t j = 0; j < indices.size(); j++) { + unsigned int index = indices.at(j); + aiVector3D vertex = vertices.at(j); + aiVector3D normal = normals.at(j); + unsigned int count = 0; + const unsigned int* outIndices = mesh.ToOutputVertexIndex(index, count); + for (unsigned int k = 0; k < count; k++) { + unsigned int index = outIndices[k]; + animMesh->mVertices[index] += vertex; + if (animMesh->mNormals != nullptr) { + animMesh->mNormals[index] += normal; + animMesh->mNormals[index].NormalizeSafe(); + } + } + } + animMesh->mWeight = shapeGeometries.size() > 1 ? blendShapeChannel->DeformPercent() / 100.0f : 1.0f; + animMeshes.push_back(animMesh); + } + } + } + const size_t numAnimMeshes = animMeshes.size(); + if (numAnimMeshes > 0) { + out_mesh->mNumAnimMeshes = static_cast<unsigned int>(numAnimMeshes); + out_mesh->mAnimMeshes = new aiAnimMesh*[numAnimMeshes]; + for (size_t i = 0; i < numAnimMeshes; i++) { + out_mesh->mAnimMeshes[i] = animMeshes.at(i); + } + } + return static_cast<unsigned int>(meshes.size() - 1); + } + + std::vector<unsigned int> FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd) + { + const MatIndexArray& mindices = mesh.GetMaterialIndices(); + ai_assert(mindices.size()); + + std::set<MatIndexArray::value_type> had; + std::vector<unsigned int> indices; + + for (MatIndexArray::value_type index : mindices) { + if (had.find(index) == had.end()) { + + indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform, nd)); + had.insert(index); + } + } + + return indices; + } + + unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, + MatIndexArray::value_type index, + const aiMatrix4x4& node_global_transform, + aiNode& nd) + { + aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); + + const MatIndexArray& mindices = mesh.GetMaterialIndices(); + const std::vector<aiVector3D>& vertices = mesh.GetVertices(); + const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts(); + + const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL; + + unsigned int count_faces = 0; + unsigned int count_vertices = 0; + + // count faces + std::vector<unsigned int>::const_iterator itf = faces.begin(); + for (MatIndexArray::const_iterator it = mindices.begin(), + end = mindices.end(); it != end; ++it, ++itf) + { + if ((*it) != index) { + continue; + } + ++count_faces; + count_vertices += *itf; + } + + ai_assert(count_faces); + ai_assert(count_vertices); + + // mapping from output indices to DOM indexing, needed to resolve weights + std::vector<unsigned int> reverseMapping; + + if (process_weights) { + reverseMapping.resize(count_vertices); + } + + // allocate output data arrays, but don't fill them yet + out_mesh->mNumVertices = count_vertices; + out_mesh->mVertices = new aiVector3D[count_vertices]; + + out_mesh->mNumFaces = count_faces; + aiFace* fac = out_mesh->mFaces = new aiFace[count_faces](); + + + // allocate normals + const std::vector<aiVector3D>& normals = mesh.GetNormals(); + if (normals.size()) { + ai_assert(normals.size() == vertices.size()); + out_mesh->mNormals = new aiVector3D[vertices.size()]; + } + + // allocate tangents, binormals. + const std::vector<aiVector3D>& tangents = mesh.GetTangents(); + const std::vector<aiVector3D>* binormals = &mesh.GetBinormals(); + std::vector<aiVector3D> tempBinormals; + + if (tangents.size()) { + if (!binormals->size()) { + if (normals.size()) { + // XXX this computes the binormals for the entire mesh, not only + // the part for which we need them. + tempBinormals.resize(normals.size()); + for (unsigned int i = 0; i < tangents.size(); ++i) { + tempBinormals[i] = normals[i] ^ tangents[i]; + } + + binormals = &tempBinormals; + } + else { + binormals = NULL; + } + } + + if (binormals) { + ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size()); + + out_mesh->mTangents = new aiVector3D[vertices.size()]; + out_mesh->mBitangents = new aiVector3D[vertices.size()]; + } + } + + // allocate texture coords + unsigned int num_uvs = 0; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i, ++num_uvs) { + const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i); + if (uvs.empty()) { + break; + } + + out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()]; + out_mesh->mNumUVComponents[i] = 2; + } + + // allocate vertex colors + unsigned int num_vcs = 0; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i, ++num_vcs) { + const std::vector<aiColor4D>& colors = mesh.GetVertexColors(i); + if (colors.empty()) { + break; + } + + out_mesh->mColors[i] = new aiColor4D[vertices.size()]; + } + + unsigned int cursor = 0, in_cursor = 0; + + itf = faces.begin(); + for (MatIndexArray::const_iterator it = mindices.begin(), end = mindices.end(); it != end; ++it, ++itf) + { + const unsigned int pcount = *itf; + if ((*it) != index) { + in_cursor += pcount; + continue; + } + + aiFace& f = *fac++; + + f.mNumIndices = pcount; + f.mIndices = new unsigned int[pcount]; + switch (pcount) + { + case 1: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 2: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 3: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + } + for (unsigned int i = 0; i < pcount; ++i, ++cursor, ++in_cursor) { + f.mIndices[i] = cursor; + + if (reverseMapping.size()) { + reverseMapping[cursor] = in_cursor; + } + + out_mesh->mVertices[cursor] = vertices[in_cursor]; + + if (out_mesh->mNormals) { + out_mesh->mNormals[cursor] = normals[in_cursor]; + } + + if (out_mesh->mTangents) { + out_mesh->mTangents[cursor] = tangents[in_cursor]; + out_mesh->mBitangents[cursor] = (*binormals)[in_cursor]; + } + + for (unsigned int j = 0; j < num_uvs; ++j) { + const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(j); + out_mesh->mTextureCoords[j][cursor] = aiVector3D(uvs[in_cursor].x, uvs[in_cursor].y, 0.0f); + } + + for (unsigned int j = 0; j < num_vcs; ++j) { + const std::vector<aiColor4D>& cols = mesh.GetVertexColors(j); + out_mesh->mColors[j][cursor] = cols[in_cursor]; + } + } + } + + ConvertMaterialForMesh(out_mesh, model, mesh, index); + + if (process_weights) { + ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping); + } + + return static_cast<unsigned int>(meshes.size() - 1); + } + + void FBXConverter::ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, + const aiMatrix4x4& node_global_transform, + unsigned int materialIndex, + std::vector<unsigned int>* outputVertStartIndices) + { + ai_assert(geo.DeformerSkin()); + + std::vector<size_t> out_indices; + std::vector<size_t> index_out_indices; + std::vector<size_t> count_out_indices; + + const Skin& sk = *geo.DeformerSkin(); + + std::vector<aiBone*> bones; + bones.reserve(sk.Clusters().size()); + + const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION; + ai_assert(no_mat_check || outputVertStartIndices); + + try { + + for (const Cluster* cluster : sk.Clusters()) { + ai_assert(cluster); + + const WeightIndexArray& indices = cluster->GetIndices(); + + if (indices.empty()) { + continue; + } + + const MatIndexArray& mats = geo.GetMaterialIndices(); + + bool ok = false; + + const size_t no_index_sentinel = std::numeric_limits<size_t>::max(); + + count_out_indices.clear(); + index_out_indices.clear(); + out_indices.clear(); + + // now check if *any* of these weights is contained in the output mesh, + // taking notes so we don't need to do it twice. + for (WeightIndexArray::value_type index : indices) { + + unsigned int count = 0; + const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count); + // ToOutputVertexIndex only returns NULL if index is out of bounds + // which should never happen + ai_assert(out_idx != NULL); + + index_out_indices.push_back(no_index_sentinel); + count_out_indices.push_back(0); + + for (unsigned int i = 0; i < count; ++i) { + if (no_mat_check || static_cast<size_t>(mats[geo.FaceForVertexIndex(out_idx[i])]) == materialIndex) { + + if (index_out_indices.back() == no_index_sentinel) { + index_out_indices.back() = out_indices.size(); + + } + + if (no_mat_check) { + out_indices.push_back(out_idx[i]); + } + else { + // this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn) + const std::vector<unsigned int>::iterator it = std::lower_bound( + outputVertStartIndices->begin(), + outputVertStartIndices->end(), + out_idx[i] + ); + + out_indices.push_back(std::distance(outputVertStartIndices->begin(), it)); + } + + ++count_out_indices.back(); + ok = true; + } + } + } + + // if we found at least one, generate the output bones + // XXX this could be heavily simplified by collecting the bone + // data in a single step. + if (ok) { + ConvertCluster(bones, model, *cluster, out_indices, index_out_indices, + count_out_indices, node_global_transform); + } + } + } + catch (std::exception&) { + std::for_each(bones.begin(), bones.end(), Util::delete_fun<aiBone>()); + throw; + } + + if (bones.empty()) { + return; + } + + out->mBones = new aiBone*[bones.size()](); + out->mNumBones = static_cast<unsigned int>(bones.size()); + + std::swap_ranges(bones.begin(), bones.end(), out->mBones); + } + + void FBXConverter::ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl, + std::vector<size_t>& out_indices, + std::vector<size_t>& index_out_indices, + std::vector<size_t>& count_out_indices, + const aiMatrix4x4& node_global_transform) + { + + aiBone* const bone = new aiBone(); + bones.push_back(bone); + + bone->mName = FixNodeName(cl.TargetNode()->Name()); + + bone->mOffsetMatrix = cl.TransformLink(); + bone->mOffsetMatrix.Inverse(); + + bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform; + + bone->mNumWeights = static_cast<unsigned int>(out_indices.size()); + aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()]; + + const size_t no_index_sentinel = std::numeric_limits<size_t>::max(); + const WeightArray& weights = cl.GetWeights(); + + const size_t c = index_out_indices.size(); + for (size_t i = 0; i < c; ++i) { + const size_t index_index = index_out_indices[i]; + + if (index_index == no_index_sentinel) { + continue; + } + + const size_t cc = count_out_indices[i]; + for (size_t j = 0; j < cc; ++j) { + aiVertexWeight& out_weight = *cursor++; + + out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]); + out_weight.mWeight = weights[i]; + } + } + } + + void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, + MatIndexArray::value_type materialIndex) + { + // locate source materials for this mesh + const std::vector<const Material*>& mats = model.GetMaterials(); + if (static_cast<unsigned int>(materialIndex) >= mats.size() || materialIndex < 0) { + FBXImporter::LogError("material index out of bounds, setting default material"); + out->mMaterialIndex = GetDefaultMaterial(); + return; + } + + const Material* const mat = mats[materialIndex]; + MaterialMap::const_iterator it = materials_converted.find(mat); + if (it != materials_converted.end()) { + out->mMaterialIndex = (*it).second; + return; + } + + out->mMaterialIndex = ConvertMaterial(*mat, &geo); + materials_converted[mat] = out->mMaterialIndex; + } + + unsigned int FBXConverter::GetDefaultMaterial() + { + if (defaultMaterialIndex) { + return defaultMaterialIndex - 1; + } + + aiMaterial* out_mat = new aiMaterial(); + materials.push_back(out_mat); + + const aiColor3D diffuse = aiColor3D(0.8f, 0.8f, 0.8f); + out_mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + + aiString s; + s.Set(AI_DEFAULT_MATERIAL_NAME); + + out_mat->AddProperty(&s, AI_MATKEY_NAME); + + defaultMaterialIndex = static_cast<unsigned int>(materials.size()); + return defaultMaterialIndex - 1; + } + + + unsigned int FBXConverter::ConvertMaterial(const Material& material, const MeshGeometry* const mesh) + { + const PropertyTable& props = material.Props(); + + // generate empty output material + aiMaterial* out_mat = new aiMaterial(); + materials_converted[&material] = static_cast<unsigned int>(materials.size()); + + materials.push_back(out_mat); + + aiString str; + + // strip Material:: prefix + std::string name = material.Name(); + if (name.substr(0, 10) == "Material::") { + name = name.substr(10); + } + + // set material name if not empty - this could happen + // and there should be no key for it in this case. + if (name.length()) { + str.Set(name); + out_mat->AddProperty(&str, AI_MATKEY_NAME); + } + + // shading stuff and colors + SetShadingPropertiesCommon(out_mat, props); + SetShadingPropertiesRaw( out_mat, props, material.Textures(), mesh ); + + // texture assignments + SetTextureProperties(out_mat, material.Textures(), mesh); + SetTextureProperties(out_mat, material.LayeredTextures(), mesh); + + return static_cast<unsigned int>(materials.size() - 1); + } + + unsigned int FBXConverter::ConvertVideo(const Video& video) + { + // generate empty output texture + aiTexture* out_tex = new aiTexture(); + textures.push_back(out_tex); + + // assuming the texture is compressed + out_tex->mWidth = static_cast<unsigned int>(video.ContentLength()); // total data size + out_tex->mHeight = 0; // fixed to 0 + + // steal the data from the Video to avoid an additional copy + out_tex->pcData = reinterpret_cast<aiTexel*>(const_cast<Video&>(video).RelinquishContent()); + + // try to extract a hint from the file extension + const std::string& filename = video.FileName().empty() ? video.RelativeFilename() : video.FileName(); + std::string ext = BaseImporter::GetExtension(filename); + + if (ext == "jpeg") { + ext = "jpg"; + } + + if (ext.size() <= 3) { + memcpy(out_tex->achFormatHint, ext.c_str(), ext.size()); + } + + out_tex->mFilename.Set(video.FileName().c_str()); + + return static_cast<unsigned int>(textures.size() - 1); + } + + aiString FBXConverter::GetTexturePath(const Texture* tex) + { + aiString path; + path.Set(tex->RelativeFilename()); + + const Video* media = tex->Media(); + if (media != nullptr) { + bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found) + unsigned int index; + + VideoMap::const_iterator it = textures_converted.find(media); + if (it != textures_converted.end()) { + index = (*it).second; + textureReady = true; + } + else { + if (media->ContentLength() > 0) { + index = ConvertVideo(*media); + textures_converted[media] = index; + textureReady = true; + } + } + + // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture), if the texture is ready + if (doc.Settings().useLegacyEmbeddedTextureNaming) { + if (textureReady) { + // TODO: check the possibility of using the flag "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING" + // In FBX files textures are now stored internally by Assimp with their filename included + // Now Assimp can lookup through the loaded textures after all data is processed + // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it + // This may occur on this case too, it has to be studied + path.data[0] = '*'; + path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index); + } + } + } + + return path; + } + + void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, + const std::string& propName, + aiTextureType target, const MeshGeometry* const mesh) + { + TextureMap::const_iterator it = textures.find(propName); + if (it == textures.end()) { + return; + } + + const Texture* const tex = (*it).second; + if (tex != 0) + { + aiString path = GetTexturePath(tex); + out_mat->AddProperty(&path, _AI_MATKEY_TEXTURE_BASE, target, 0); + + aiUVTransform uvTrafo; + // XXX handle all kinds of UV transformations + uvTrafo.mScaling = tex->UVScaling(); + uvTrafo.mTranslation = tex->UVTranslation(); + out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, 0); + + const PropertyTable& props = tex->Props(); + + int uvIndex = 0; + + bool ok; + const std::string& uvSet = PropertyGet<std::string>(props, "UVSet", ok); + if (ok) { + // "default" is the name which usually appears in the FbxFileTexture template + if (uvSet != "default" && uvSet.length()) { + // this is a bit awkward - we need to find a mesh that uses this + // material and scan its UV channels for the given UV name because + // assimp references UV channels by index, not by name. + + // XXX: the case that UV channels may appear in different orders + // in meshes is unhandled. A possible solution would be to sort + // the UV channels alphabetically, but this would have the side + // effect that the primary (first) UV channel would sometimes + // be moved, causing trouble when users read only the first + // UV channel and ignore UV channel assignments altogether. + + const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(), + std::find(materials.begin(), materials.end(), out_mat) + )); + + + uvIndex = -1; + if (!mesh) + { + for (const MeshMap::value_type& v : meshes_converted) { + const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*> (v.first); + if (!meshGeom) { + continue; + } + + const MatIndexArray& mats = meshGeom->GetMaterialIndices(); + if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { + continue; + } + + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (meshGeom->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = meshGeom->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast<int>(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + continue; + } + + if (uvIndex == -1) { + uvIndex = index; + } + else { + FBXImporter::LogWarn("the UV channel named " + uvSet + + " appears at different positions in meshes, results will be wrong"); + } + } + } + else + { + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (mesh->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = mesh->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast<int>(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + } + + if (uvIndex == -1) { + uvIndex = index; + } + } + + if (uvIndex == -1) { + FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); + uvIndex = 0; + } + } + } + + out_mat->AddProperty(&uvIndex, 1, _AI_MATKEY_UVWSRC_BASE, target, 0); + } + } + + void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, + const std::string& propName, + aiTextureType target, const MeshGeometry* const mesh) { + LayeredTextureMap::const_iterator it = layeredTextures.find(propName); + if (it == layeredTextures.end()) { + return; + } + + int texCount = (*it).second->textureCount(); + + // Set the blend mode for layered textures + int blendmode = (*it).second->GetBlendMode(); + out_mat->AddProperty(&blendmode, 1, _AI_MATKEY_TEXOP_BASE, target, 0); + + for (int texIndex = 0; texIndex < texCount; texIndex++) { + + const Texture* const tex = (*it).second->getTexture(texIndex); + + aiString path = GetTexturePath(tex); + out_mat->AddProperty(&path, _AI_MATKEY_TEXTURE_BASE, target, texIndex); + + aiUVTransform uvTrafo; + // XXX handle all kinds of UV transformations + uvTrafo.mScaling = tex->UVScaling(); + uvTrafo.mTranslation = tex->UVTranslation(); + out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, texIndex); + + const PropertyTable& props = tex->Props(); + + int uvIndex = 0; + + bool ok; + const std::string& uvSet = PropertyGet<std::string>(props, "UVSet", ok); + if (ok) { + // "default" is the name which usually appears in the FbxFileTexture template + if (uvSet != "default" && uvSet.length()) { + // this is a bit awkward - we need to find a mesh that uses this + // material and scan its UV channels for the given UV name because + // assimp references UV channels by index, not by name. + + // XXX: the case that UV channels may appear in different orders + // in meshes is unhandled. A possible solution would be to sort + // the UV channels alphabetically, but this would have the side + // effect that the primary (first) UV channel would sometimes + // be moved, causing trouble when users read only the first + // UV channel and ignore UV channel assignments altogether. + + const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(), + std::find(materials.begin(), materials.end(), out_mat) + )); + + uvIndex = -1; + if (!mesh) + { + for (const MeshMap::value_type& v : meshes_converted) { + const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*> (v.first); + if (!meshGeom) { + continue; + } + + const MatIndexArray& mats = meshGeom->GetMaterialIndices(); + if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { + continue; + } + + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (meshGeom->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = meshGeom->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast<int>(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + continue; + } + + if (uvIndex == -1) { + uvIndex = index; + } + else { + FBXImporter::LogWarn("the UV channel named " + uvSet + + " appears at different positions in meshes, results will be wrong"); + } + } + } + else + { + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (mesh->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = mesh->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast<int>(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + } + + if (uvIndex == -1) { + uvIndex = index; + } + } + + if (uvIndex == -1) { + FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); + uvIndex = 0; + } + } + } + + out_mat->AddProperty(&uvIndex, 1, _AI_MATKEY_UVWSRC_BASE, target, texIndex); + } + } + + void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh) + { + TrySetTextureProperties(out_mat, textures, "DiffuseColor", aiTextureType_DIFFUSE, mesh); + TrySetTextureProperties(out_mat, textures, "AmbientColor", aiTextureType_AMBIENT, mesh); + TrySetTextureProperties(out_mat, textures, "EmissiveColor", aiTextureType_EMISSIVE, mesh); + TrySetTextureProperties(out_mat, textures, "SpecularColor", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, textures, "SpecularFactor", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, textures, "TransparentColor", aiTextureType_OPACITY, mesh); + TrySetTextureProperties(out_mat, textures, "ReflectionColor", aiTextureType_REFLECTION, mesh); + TrySetTextureProperties(out_mat, textures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh); + TrySetTextureProperties(out_mat, textures, "NormalMap", aiTextureType_NORMALS, mesh); + TrySetTextureProperties(out_mat, textures, "Bump", aiTextureType_HEIGHT, mesh); + TrySetTextureProperties(out_mat, textures, "ShininessExponent", aiTextureType_SHININESS, mesh); + TrySetTextureProperties( out_mat, textures, "TransparencyFactor", aiTextureType_OPACITY, mesh ); + TrySetTextureProperties( out_mat, textures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh ); + //Maya counterparts + TrySetTextureProperties(out_mat, textures, "Maya|DiffuseTexture", aiTextureType_DIFFUSE, mesh); + TrySetTextureProperties(out_mat, textures, "Maya|NormalTexture", aiTextureType_NORMALS, mesh); + TrySetTextureProperties(out_mat, textures, "Maya|SpecularTexture", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, textures, "Maya|FalloffTexture", aiTextureType_OPACITY, mesh); + TrySetTextureProperties(out_mat, textures, "Maya|ReflectionMapTexture", aiTextureType_REFLECTION, mesh); + } + + void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh) + { + TrySetTextureProperties(out_mat, layeredTextures, "DiffuseColor", aiTextureType_DIFFUSE, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "AmbientColor", aiTextureType_AMBIENT, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "EmissiveColor", aiTextureType_EMISSIVE, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "SpecularColor", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "SpecularFactor", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "TransparentColor", aiTextureType_OPACITY, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "ReflectionColor", aiTextureType_REFLECTION, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "NormalMap", aiTextureType_NORMALS, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "Bump", aiTextureType_HEIGHT, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "ShininessExponent", aiTextureType_SHININESS, mesh); + TrySetTextureProperties( out_mat, layeredTextures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh ); + TrySetTextureProperties( out_mat, layeredTextures, "TransparencyFactor", aiTextureType_OPACITY, mesh ); + } + + aiColor3D FBXConverter::GetColorPropertyFactored(const PropertyTable& props, const std::string& colorName, + const std::string& factorName, bool& result, bool useTemplate) + { + result = true; + + bool ok; + aiVector3D BaseColor = PropertyGet<aiVector3D>(props, colorName, ok, useTemplate); + if (!ok) { + result = false; + return aiColor3D(0.0f, 0.0f, 0.0f); + } + + // if no factor name, return the colour as is + if (factorName.empty()) { + return aiColor3D(BaseColor.x, BaseColor.y, BaseColor.z); + } + + // otherwise it should be multiplied by the factor, if found. + float factor = PropertyGet<float>(props, factorName, ok, useTemplate); + if (ok) { + BaseColor *= factor; + } + return aiColor3D(BaseColor.x, BaseColor.y, BaseColor.z); + } + + aiColor3D FBXConverter::GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName, + bool& result) + { + return GetColorPropertyFactored(props, baseName + "Color", baseName + "Factor", result, true); + } + + aiColor3D FBXConverter::GetColorProperty(const PropertyTable& props, const std::string& colorName, + bool& result, bool useTemplate) + { + result = true; + bool ok; + const aiVector3D& ColorVec = PropertyGet<aiVector3D>(props, colorName, ok, useTemplate); + if (!ok) { + result = false; + return aiColor3D(0.0f, 0.0f, 0.0f); + } + return aiColor3D(ColorVec.x, ColorVec.y, ColorVec.z); + } + + void FBXConverter::SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props) + { + // Set shading properties. + // Modern FBX Files have two separate systems for defining these, + // with only the more comprehensive one described in the property template. + // Likely the other values are a legacy system, + // which is still always exported by the official FBX SDK. + // + // Blender's FBX import and export mostly ignore this legacy system, + // and as we only support recent versions of FBX anyway, we can do the same. + bool ok; + + const aiColor3D& Diffuse = GetColorPropertyFromMaterial(props, "Diffuse", ok); + if (ok) { + out_mat->AddProperty(&Diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + } + + const aiColor3D& Emissive = GetColorPropertyFromMaterial(props, "Emissive", ok); + if (ok) { + out_mat->AddProperty(&Emissive, 1, AI_MATKEY_COLOR_EMISSIVE); + } + + const aiColor3D& Ambient = GetColorPropertyFromMaterial(props, "Ambient", ok); + if (ok) { + out_mat->AddProperty(&Ambient, 1, AI_MATKEY_COLOR_AMBIENT); + } + + // we store specular factor as SHININESS_STRENGTH, so just get the color + const aiColor3D& Specular = GetColorProperty(props, "SpecularColor", ok, true); + if (ok) { + out_mat->AddProperty(&Specular, 1, AI_MATKEY_COLOR_SPECULAR); + } + + // and also try to get SHININESS_STRENGTH + const float SpecularFactor = PropertyGet<float>(props, "SpecularFactor", ok, true); + if (ok) { + out_mat->AddProperty(&SpecularFactor, 1, AI_MATKEY_SHININESS_STRENGTH); + } + + // and the specular exponent + const float ShininessExponent = PropertyGet<float>(props, "ShininessExponent", ok); + if (ok) { + out_mat->AddProperty(&ShininessExponent, 1, AI_MATKEY_SHININESS); + } + + // TransparentColor / TransparencyFactor... gee thanks FBX :rolleyes: + const aiColor3D& Transparent = GetColorPropertyFactored(props, "TransparentColor", "TransparencyFactor", ok); + float CalculatedOpacity = 1.0f; + if (ok) { + out_mat->AddProperty(&Transparent, 1, AI_MATKEY_COLOR_TRANSPARENT); + // as calculated by FBX SDK 2017: + CalculatedOpacity = 1.0f - ((Transparent.r + Transparent.g + Transparent.b) / 3.0f); + } + + // use of TransparencyFactor is inconsistent. + // Maya always stores it as 1.0, + // so we can't use it to set AI_MATKEY_OPACITY. + // Blender is more sensible and stores it as the alpha value. + // However both the FBX SDK and Blender always write an additional + // legacy "Opacity" field, so we can try to use that. + // + // If we can't find it, + // we can fall back to the value which the FBX SDK calculates + // from transparency colour (RGB) and factor (F) as + // 1.0 - F*((R+G+B)/3). + // + // There's no consistent way to interpret this opacity value, + // so it's up to clients to do the correct thing. + const float Opacity = PropertyGet<float>(props, "Opacity", ok); + if (ok) { + out_mat->AddProperty(&Opacity, 1, AI_MATKEY_OPACITY); + } + else if (CalculatedOpacity != 1.0) { + out_mat->AddProperty(&CalculatedOpacity, 1, AI_MATKEY_OPACITY); + } + + // reflection color and factor are stored separately + const aiColor3D& Reflection = GetColorProperty(props, "ReflectionColor", ok, true); + if (ok) { + out_mat->AddProperty(&Reflection, 1, AI_MATKEY_COLOR_REFLECTIVE); + } + + float ReflectionFactor = PropertyGet<float>(props, "ReflectionFactor", ok, true); + if (ok) { + out_mat->AddProperty(&ReflectionFactor, 1, AI_MATKEY_REFLECTIVITY); + } + + const float BumpFactor = PropertyGet<float>(props, "BumpFactor", ok); + if (ok) { + out_mat->AddProperty(&BumpFactor, 1, AI_MATKEY_BUMPSCALING); + } + + const float DispFactor = PropertyGet<float>(props, "DisplacementFactor", ok); + if (ok) { + out_mat->AddProperty(&DispFactor, 1, "$mat.displacementscaling", 0, 0); + } +} + + +void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTable& props, const TextureMap& textures, const MeshGeometry* const mesh) +{ + // Add all the unparsed properties with a "$raw." prefix + + const std::string prefix = "$raw."; + + for (const DirectPropertyMap::value_type& prop : props.GetUnparsedProperties()) { + + std::string name = prefix + prop.first; + + if (const TypedProperty<aiVector3D>* interpreted = prop.second->As<TypedProperty<aiVector3D> >()) + { + out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); + } + else if (const TypedProperty<aiColor3D>* interpreted = prop.second->As<TypedProperty<aiColor3D> >()) + { + out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); + } + else if (const TypedProperty<aiColor4D>* interpreted = prop.second->As<TypedProperty<aiColor4D> >()) + { + out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); + } + else if (const TypedProperty<float>* interpreted = prop.second->As<TypedProperty<float> >()) + { + out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); + } + else if (const TypedProperty<int>* interpreted = prop.second->As<TypedProperty<int> >()) + { + out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0); + } + else if (const TypedProperty<bool>* interpreted = prop.second->As<TypedProperty<bool> >()) + { + int value = interpreted->Value() ? 1 : 0; + out_mat->AddProperty(&value, 1, name.c_str(), 0, 0); + } + else if (const TypedProperty<std::string>* interpreted = prop.second->As<TypedProperty<std::string> >()) + { + const aiString value = aiString(interpreted->Value()); + out_mat->AddProperty(&value, name.c_str(), 0, 0); + } + } + + // Add the textures' properties + + for (TextureMap::const_iterator it = textures.begin(); it != textures.end(); it++) { + + std::string name = prefix + it->first; + + const Texture* const tex = (*it).second; + if (tex != nullptr) + { + aiString path; + path.Set(tex->RelativeFilename()); + + const Video* media = tex->Media(); + if (media != nullptr && media->ContentLength() > 0) { + unsigned int index; + + VideoMap::const_iterator it = textures_converted.find(media); + if (it != textures_converted.end()) { + index = (*it).second; + } + else { + index = ConvertVideo(*media); + textures_converted[media] = index; + } + + // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) + path.data[0] = '*'; + path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index); + } + + out_mat->AddProperty(&path, (name + "|file").c_str(), aiTextureType_UNKNOWN, 0); + + aiUVTransform uvTrafo; + // XXX handle all kinds of UV transformations + uvTrafo.mScaling = tex->UVScaling(); + uvTrafo.mTranslation = tex->UVTranslation(); + out_mat->AddProperty(&uvTrafo, 1, (name + "|uvtrafo").c_str(), aiTextureType_UNKNOWN, 0); + + int uvIndex = 0; + + bool uvFound = false; + const std::string& uvSet = PropertyGet<std::string>(tex->Props(), "UVSet", uvFound); + if (uvFound) { + // "default" is the name which usually appears in the FbxFileTexture template + if (uvSet != "default" && uvSet.length()) { + // this is a bit awkward - we need to find a mesh that uses this + // material and scan its UV channels for the given UV name because + // assimp references UV channels by index, not by name. + + // XXX: the case that UV channels may appear in different orders + // in meshes is unhandled. A possible solution would be to sort + // the UV channels alphabetically, but this would have the side + // effect that the primary (first) UV channel would sometimes + // be moved, causing trouble when users read only the first + // UV channel and ignore UV channel assignments altogether. + + std::vector<aiMaterial*>::iterator materialIt = std::find(materials.begin(), materials.end(), out_mat); + const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(), materialIt)); + + uvIndex = -1; + if (!mesh) + { + for (const MeshMap::value_type& v : meshes_converted) { + const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*>(v.first); + if (!meshGeom) { + continue; + } + + const MatIndexArray& mats = meshGeom->GetMaterialIndices(); + if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { + continue; + } + + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (meshGeom->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = meshGeom->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast<int>(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + continue; + } + + if (uvIndex == -1) { + uvIndex = index; + } + else { + FBXImporter::LogWarn("the UV channel named " + uvSet + " appears at different positions in meshes, results will be wrong"); + } + } + } + else + { + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (mesh->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = mesh->GetTextureCoordChannelName(i); + if (name == uvSet) { + index = static_cast<int>(i); + break; + } + } + if (index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + } + + if (uvIndex == -1) { + uvIndex = index; + } + } + + if (uvIndex == -1) { + FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); + uvIndex = 0; + } + } + } + + out_mat->AddProperty(&uvIndex, 1, (name + "|uvwsrc").c_str(), aiTextureType_UNKNOWN, 0); + } + } + } + + + double FBXConverter::FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal) { + switch (fp) { + case FileGlobalSettings::FrameRate_DEFAULT: + return 1.0; + + case FileGlobalSettings::FrameRate_120: + return 120.0; + + case FileGlobalSettings::FrameRate_100: + return 100.0; + + case FileGlobalSettings::FrameRate_60: + return 60.0; + + case FileGlobalSettings::FrameRate_50: + return 50.0; + + case FileGlobalSettings::FrameRate_48: + return 48.0; + + case FileGlobalSettings::FrameRate_30: + case FileGlobalSettings::FrameRate_30_DROP: + return 30.0; + + case FileGlobalSettings::FrameRate_NTSC_DROP_FRAME: + case FileGlobalSettings::FrameRate_NTSC_FULL_FRAME: + return 29.9700262; + + case FileGlobalSettings::FrameRate_PAL: + return 25.0; + + case FileGlobalSettings::FrameRate_CINEMA: + return 24.0; + + case FileGlobalSettings::FrameRate_1000: + return 1000.0; + + case FileGlobalSettings::FrameRate_CINEMA_ND: + return 23.976; + + case FileGlobalSettings::FrameRate_CUSTOM: + return customFPSVal; + + case FileGlobalSettings::FrameRate_MAX: // this is to silence compiler warnings + break; + } + + ai_assert(false); + + return -1.0f; + } + + + void FBXConverter::ConvertAnimations() + { + // first of all determine framerate + const FileGlobalSettings::FrameRate fps = doc.GlobalSettings().TimeMode(); + const float custom = doc.GlobalSettings().CustomFrameRate(); + anim_fps = FrameRateToDouble(fps, custom); + + const std::vector<const AnimationStack*>& animations = doc.AnimationStacks(); + for (const AnimationStack* stack : animations) { + ConvertAnimationStack(*stack); + } + } + + std::string FBXConverter::FixNodeName(const std::string& name) { + // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if + // this causes ambiguities, well possible between empty identifiers, + // such as "Model::" and ""). Make sure the behaviour is consistent + // across multiple calls to FixNodeName(). + if (name.substr(0, 7) == "Model::") { + std::string temp = name.substr(7); + return temp; + } + + return name; + } + + std::string FBXConverter::FixAnimMeshName(const std::string& name) { + if (name.length()) { + size_t indexOf = name.find_first_of("::"); + if (indexOf != std::string::npos && indexOf < name.size() - 2) { + return name.substr(indexOf + 2); + } + } + return name.length() ? name : "AnimMesh"; + } + + void FBXConverter::ConvertAnimationStack(const AnimationStack& st) + { + const AnimationLayerList& layers = st.Layers(); + if (layers.empty()) { + return; + } + + aiAnimation* const anim = new aiAnimation(); + animations.push_back(anim); + + // strip AnimationStack:: prefix + std::string name = st.Name(); + if (name.substr(0, 16) == "AnimationStack::") { + name = name.substr(16); + } + else if (name.substr(0, 11) == "AnimStack::") { + name = name.substr(11); + } + + anim->mName.Set(name); + + // need to find all nodes for which we need to generate node animations - + // it may happen that we need to merge multiple layers, though. + NodeMap node_map; + + // reverse mapping from curves to layers, much faster than querying + // the FBX DOM for it. + LayerMap layer_map; + + const char* prop_whitelist[] = { + "Lcl Scaling", + "Lcl Rotation", + "Lcl Translation", + "DeformPercent" + }; + + std::map<std::string, morphAnimData*> morphAnimDatas; + + for (const AnimationLayer* layer : layers) { + ai_assert(layer); + const AnimationCurveNodeList& nodes = layer->Nodes(prop_whitelist, 4); + for (const AnimationCurveNode* node : nodes) { + ai_assert(node); + const Model* const model = dynamic_cast<const Model*>(node->Target()); + if (model) { + const std::string& name = FixNodeName(model->Name()); + node_map[name].push_back(node); + layer_map[node] = layer; + continue; + } + const BlendShapeChannel* const bsc = dynamic_cast<const BlendShapeChannel*>(node->Target()); + if (bsc) { + ProcessMorphAnimDatas(&morphAnimDatas, bsc, node); + } + } + } + + // generate node animations + std::vector<aiNodeAnim*> node_anims; + + double min_time = 1e10; + double max_time = -1e10; + + int64_t start_time = st.LocalStart(); + int64_t stop_time = st.LocalStop(); + bool has_local_startstop = start_time != 0 || stop_time != 0; + if (!has_local_startstop) { + // no time range given, so accept every keyframe and use the actual min/max time + // the numbers are INT64_MIN/MAX, the 20000 is for safety because GenerateNodeAnimations uses an epsilon of 10000 + start_time = -9223372036854775807ll + 20000; + stop_time = 9223372036854775807ll - 20000; + } + + try { + for (const NodeMap::value_type& kv : node_map) { + GenerateNodeAnimations(node_anims, + kv.first, + kv.second, + layer_map, + start_time, stop_time, + max_time, + min_time); + } + } + catch (std::exception&) { + std::for_each(node_anims.begin(), node_anims.end(), Util::delete_fun<aiNodeAnim>()); + throw; + } + + if (node_anims.size() || morphAnimDatas.size()) { + if (node_anims.size()) { + anim->mChannels = new aiNodeAnim*[node_anims.size()](); + anim->mNumChannels = static_cast<unsigned int>(node_anims.size()); + std::swap_ranges(node_anims.begin(), node_anims.end(), anim->mChannels); + } + if (morphAnimDatas.size()) { + unsigned int numMorphMeshChannels = static_cast<unsigned int>(morphAnimDatas.size()); + anim->mMorphMeshChannels = new aiMeshMorphAnim*[numMorphMeshChannels]; + anim->mNumMorphMeshChannels = numMorphMeshChannels; + unsigned int i = 0; + for (auto morphAnimIt : morphAnimDatas) { + morphAnimData* animData = morphAnimIt.second; + unsigned int numKeys = static_cast<unsigned int>(animData->size()); + aiMeshMorphAnim* meshMorphAnim = new aiMeshMorphAnim(); + meshMorphAnim->mName.Set(morphAnimIt.first); + meshMorphAnim->mNumKeys = numKeys; + meshMorphAnim->mKeys = new aiMeshMorphKey[numKeys]; + unsigned int j = 0; + for (auto animIt : *animData) { + morphKeyData* keyData = animIt.second; + unsigned int numValuesAndWeights = static_cast<unsigned int>(keyData->values.size()); + meshMorphAnim->mKeys[j].mNumValuesAndWeights = numValuesAndWeights; + meshMorphAnim->mKeys[j].mValues = new unsigned int[numValuesAndWeights]; + meshMorphAnim->mKeys[j].mWeights = new double[numValuesAndWeights]; + meshMorphAnim->mKeys[j].mTime = CONVERT_FBX_TIME(animIt.first) * anim_fps; + for (unsigned int k = 0; k < numValuesAndWeights; k++) { + meshMorphAnim->mKeys[j].mValues[k] = keyData->values.at(k); + meshMorphAnim->mKeys[j].mWeights[k] = keyData->weights.at(k); + } + j++; + } + anim->mMorphMeshChannels[i++] = meshMorphAnim; + } + } + } + else { + // empty animations would fail validation, so drop them + delete anim; + animations.pop_back(); + FBXImporter::LogInfo("ignoring empty AnimationStack (using IK?): " + name); + return; + } + + double start_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(start_time) * anim_fps) : min_time; + double stop_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(stop_time) * anim_fps) : max_time; + + // adjust relative timing for animation + for (unsigned int c = 0; c < anim->mNumChannels; c++) { + aiNodeAnim* channel = anim->mChannels[c]; + for (uint32_t i = 0; i < channel->mNumPositionKeys; i++) { + channel->mPositionKeys[i].mTime -= start_time_fps; + } + for (uint32_t i = 0; i < channel->mNumRotationKeys; i++) { + channel->mRotationKeys[i].mTime -= start_time_fps; + } + for (uint32_t i = 0; i < channel->mNumScalingKeys; i++) { + channel->mScalingKeys[i].mTime -= start_time_fps; + } + } + for (unsigned int c = 0; c < anim->mNumMorphMeshChannels; c++) { + aiMeshMorphAnim* channel = anim->mMorphMeshChannels[c]; + for (uint32_t i = 0; i < channel->mNumKeys; i++) { + channel->mKeys[i].mTime -= start_time_fps; + } + } + + // for some mysterious reason, mDuration is simply the maximum key -- the + // validator always assumes animations to start at zero. + anim->mDuration = stop_time_fps - start_time_fps; + anim->mTicksPerSecond = anim_fps; + } + + // ------------------------------------------------------------------------------------------------ + void FBXConverter::ProcessMorphAnimDatas(std::map<std::string, morphAnimData*>* morphAnimDatas, const BlendShapeChannel* bsc, const AnimationCurveNode* node) { + std::vector<const Connection*> bscConnections = doc.GetConnectionsBySourceSequenced(bsc->ID(), "Deformer"); + for (const Connection* bscConnection : bscConnections) { + auto bs = dynamic_cast<const BlendShape*>(bscConnection->DestinationObject()); + if (bs) { + auto channelIt = std::find(bs->BlendShapeChannels().begin(), bs->BlendShapeChannels().end(), bsc); + if (channelIt != bs->BlendShapeChannels().end()) { + auto channelIndex = static_cast<unsigned int>(std::distance(bs->BlendShapeChannels().begin(), channelIt)); + std::vector<const Connection*> bsConnections = doc.GetConnectionsBySourceSequenced(bs->ID(), "Geometry"); + for (const Connection* bsConnection : bsConnections) { + auto geo = dynamic_cast<const Geometry*>(bsConnection->DestinationObject()); + if (geo) { + std::vector<const Connection*> geoConnections = doc.GetConnectionsBySourceSequenced(geo->ID(), "Model"); + for (const Connection* geoConnection : geoConnections) { + auto model = dynamic_cast<const Model*>(geoConnection->DestinationObject()); + if (model) { + auto geoIt = std::find(model->GetGeometry().begin(), model->GetGeometry().end(), geo); + auto geoIndex = static_cast<unsigned int>(std::distance(model->GetGeometry().begin(), geoIt)); + auto name = aiString(FixNodeName(model->Name() + "*")); + name.length = 1 + ASSIMP_itoa10(name.data + name.length, MAXLEN - 1, geoIndex); + morphAnimData* animData; + auto animIt = morphAnimDatas->find(name.C_Str()); + if (animIt == morphAnimDatas->end()) { + animData = new morphAnimData(); + morphAnimDatas->insert(std::make_pair(name.C_Str(), animData)); + } + else { + animData = animIt->second; + } + for (std::pair<std::string, const AnimationCurve*> curvesIt : node->Curves()) { + if (curvesIt.first == "d|DeformPercent") { + const AnimationCurve* animationCurve = curvesIt.second; + const KeyTimeList& keys = animationCurve->GetKeys(); + const KeyValueList& values = animationCurve->GetValues(); + unsigned int k = 0; + for (auto key : keys) { + morphKeyData* keyData; + auto keyIt = animData->find(key); + if (keyIt == animData->end()) { + keyData = new morphKeyData(); + animData->insert(std::make_pair(key, keyData)); + } + else { + keyData = keyIt->second; + } + keyData->values.push_back(channelIndex); + keyData->weights.push_back(values.at(k) / 100.0f); + k++; + } + } + } + } + } + } + } + } + } + } + } + + // ------------------------------------------------------------------------------------------------ +#ifdef ASSIMP_BUILD_DEBUG + // ------------------------------------------------------------------------------------------------ + // sanity check whether the input is ok + static void validateAnimCurveNodes(const std::vector<const AnimationCurveNode*>& curves, + bool strictMode) { + const Object* target(NULL); + for (const AnimationCurveNode* node : curves) { + if (!target) { + target = node->Target(); + } + if (node->Target() != target) { + FBXImporter::LogWarn("Node target is nullptr type."); + } + if (strictMode) { + ai_assert(node->Target() == target); + } + } + } +#endif // ASSIMP_BUILD_DEBUG + + // ------------------------------------------------------------------------------------------------ + void FBXConverter::GenerateNodeAnimations(std::vector<aiNodeAnim*>& node_anims, + const std::string& fixed_name, + const std::vector<const AnimationCurveNode*>& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time) + { + + NodeMap node_property_map; + ai_assert(curves.size()); + +#ifdef ASSIMP_BUILD_DEBUG + validateAnimCurveNodes(curves, doc.Settings().strictMode); +#endif + const AnimationCurveNode* curve_node = NULL; + for (const AnimationCurveNode* node : curves) { + ai_assert(node); + + if (node->TargetProperty().empty()) { + FBXImporter::LogWarn("target property for animation curve not set: " + node->Name()); + continue; + } + + curve_node = node; + if (node->Curves().empty()) { + FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode: " + node->Name()); + continue; + } + + node_property_map[node->TargetProperty()].push_back(node); + } + + ai_assert(curve_node); + ai_assert(curve_node->TargetAsModel()); + + const Model& target = *curve_node->TargetAsModel(); + + // check for all possible transformation components + NodeMap::const_iterator chain[TransformationComp_MAXIMUM]; + + bool has_any = false; + bool has_complex = false; + + for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) { + const TransformationComp comp = static_cast<TransformationComp>(i); + + // inverse pivots don't exist in the input, we just generate them + if (comp == TransformationComp_RotationPivotInverse || comp == TransformationComp_ScalingPivotInverse) { + chain[i] = node_property_map.end(); + continue; + } + + chain[i] = node_property_map.find(NameTransformationCompProperty(comp)); + if (chain[i] != node_property_map.end()) { + + // check if this curves contains redundant information by looking + // up the corresponding node's transformation chain. + if (doc.Settings().optimizeEmptyAnimationCurves && + IsRedundantAnimationData(target, comp, (*chain[i]).second)) { + + FBXImporter::LogDebug("dropping redundant animation channel for node " + target.Name()); + continue; + } + + has_any = true; + + if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling && comp != TransformationComp_Translation) + { + has_complex = true; + } + } + } + + if (!has_any) { + FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames"); + return; + } + + // this needs to play nicely with GenerateTransformationNodeChain() which will + // be invoked _later_ (animations come first). If this node has only rotation, + // scaling and translation _and_ there are no animated other components either, + // we can use a single node and also a single node animation channel. + if (!has_complex && !NeedsComplexTransformationChain(target)) { + + aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain, + node_property_map.end(), + layer_map, + start, stop, + max_time, + min_time, + true // input is TRS order, assimp is SRT + ); + + ai_assert(nd); + if (nd->mNumPositionKeys == 0 && nd->mNumRotationKeys == 0 && nd->mNumScalingKeys == 0) { + delete nd; + } + else { + node_anims.push_back(nd); + } + return; + } + + // otherwise, things get gruesome and we need separate animation channels + // for each part of the transformation chain. Remember which channels + // we generated and pass this information to the node conversion + // code to avoid nodes that have identity transform, but non-identity + // animations, being dropped. + unsigned int flags = 0, bit = 0x1; + for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) { + const TransformationComp comp = static_cast<TransformationComp>(i); + + if (chain[i] != node_property_map.end()) { + flags |= bit; + + ai_assert(comp != TransformationComp_RotationPivotInverse); + ai_assert(comp != TransformationComp_ScalingPivotInverse); + + const std::string& chain_name = NameTransformationChainNode(fixed_name, comp); + + aiNodeAnim* na = nullptr; + switch (comp) + { + case TransformationComp_Rotation: + case TransformationComp_PreRotation: + case TransformationComp_PostRotation: + case TransformationComp_GeometricRotation: + na = GenerateRotationNodeAnim(chain_name, + target, + (*chain[i]).second, + layer_map, + start, stop, + max_time, + min_time); + + break; + + case TransformationComp_RotationOffset: + case TransformationComp_RotationPivot: + case TransformationComp_ScalingOffset: + case TransformationComp_ScalingPivot: + case TransformationComp_Translation: + case TransformationComp_GeometricTranslation: + na = GenerateTranslationNodeAnim(chain_name, + target, + (*chain[i]).second, + layer_map, + start, stop, + max_time, + min_time); + + // pivoting requires us to generate an implicit inverse channel to undo the pivot translation + if (comp == TransformationComp_RotationPivot) { + const std::string& invName = NameTransformationChainNode(fixed_name, + TransformationComp_RotationPivotInverse); + + aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName, + target, + (*chain[i]).second, + layer_map, + start, stop, + max_time, + min_time, + true); + + ai_assert(inv); + if (inv->mNumPositionKeys == 0 && inv->mNumRotationKeys == 0 && inv->mNumScalingKeys == 0) { + delete inv; + } + else { + node_anims.push_back(inv); + } + + ai_assert(TransformationComp_RotationPivotInverse > i); + flags |= bit << (TransformationComp_RotationPivotInverse - i); + } + else if (comp == TransformationComp_ScalingPivot) { + const std::string& invName = NameTransformationChainNode(fixed_name, + TransformationComp_ScalingPivotInverse); + + aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName, + target, + (*chain[i]).second, + layer_map, + start, stop, + max_time, + min_time, + true); + + ai_assert(inv); + if (inv->mNumPositionKeys == 0 && inv->mNumRotationKeys == 0 && inv->mNumScalingKeys == 0) { + delete inv; + } + else { + node_anims.push_back(inv); + } + + ai_assert(TransformationComp_RotationPivotInverse > i); + flags |= bit << (TransformationComp_RotationPivotInverse - i); + } + + break; + + case TransformationComp_Scaling: + case TransformationComp_GeometricScaling: + na = GenerateScalingNodeAnim(chain_name, + target, + (*chain[i]).second, + layer_map, + start, stop, + max_time, + min_time); + + break; + + default: + ai_assert(false); + } + + ai_assert(na); + if (na->mNumPositionKeys == 0 && na->mNumRotationKeys == 0 && na->mNumScalingKeys == 0) { + delete na; + } + else { + node_anims.push_back(na); + } + continue; + } + } + + node_anim_chain_bits[fixed_name] = flags; + } + + + bool FBXConverter::IsRedundantAnimationData(const Model& target, + TransformationComp comp, + const std::vector<const AnimationCurveNode*>& curves) { + ai_assert(curves.size()); + + // look for animation nodes with + // * sub channels for all relevant components set + // * one key/value pair per component + // * combined values match up the corresponding value in the bind pose node transformation + // only such nodes are 'redundant' for this function. + + if (curves.size() > 1) { + return false; + } + + const AnimationCurveNode& nd = *curves.front(); + const AnimationCurveMap& sub_curves = nd.Curves(); + + const AnimationCurveMap::const_iterator dx = sub_curves.find("d|X"); + const AnimationCurveMap::const_iterator dy = sub_curves.find("d|Y"); + const AnimationCurveMap::const_iterator dz = sub_curves.find("d|Z"); + + if (dx == sub_curves.end() || dy == sub_curves.end() || dz == sub_curves.end()) { + return false; + } + + const KeyValueList& vx = (*dx).second->GetValues(); + const KeyValueList& vy = (*dy).second->GetValues(); + const KeyValueList& vz = (*dz).second->GetValues(); + + if (vx.size() != 1 || vy.size() != 1 || vz.size() != 1) { + return false; + } + + const aiVector3D dyn_val = aiVector3D(vx[0], vy[0], vz[0]); + const aiVector3D& static_val = PropertyGet<aiVector3D>(target.Props(), + NameTransformationCompProperty(comp), + TransformationCompDefaultValue(comp) + ); + + const float epsilon = 1e-6f; + return (dyn_val - static_val).SquareLength() < epsilon; + } + + + aiNodeAnim* FBXConverter::GenerateRotationNodeAnim(const std::string& name, + const Model& target, + const std::vector<const AnimationCurveNode*>& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time) + { + std::unique_ptr<aiNodeAnim> na(new aiNodeAnim()); + na->mNodeName.Set(name); + + ConvertRotationKeys(na.get(), curves, layer_map, start, stop, max_time, min_time, target.RotationOrder()); + + // dummy scaling key + na->mScalingKeys = new aiVectorKey[1]; + na->mNumScalingKeys = 1; + + na->mScalingKeys[0].mTime = 0.; + na->mScalingKeys[0].mValue = aiVector3D(1.0f, 1.0f, 1.0f); + + // dummy position key + na->mPositionKeys = new aiVectorKey[1]; + na->mNumPositionKeys = 1; + + na->mPositionKeys[0].mTime = 0.; + na->mPositionKeys[0].mValue = aiVector3D(); + + return na.release(); + } + + aiNodeAnim* FBXConverter::GenerateScalingNodeAnim(const std::string& name, + const Model& /*target*/, + const std::vector<const AnimationCurveNode*>& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time) + { + std::unique_ptr<aiNodeAnim> na(new aiNodeAnim()); + na->mNodeName.Set(name); + + ConvertScaleKeys(na.get(), curves, layer_map, start, stop, max_time, min_time); + + // dummy rotation key + na->mRotationKeys = new aiQuatKey[1]; + na->mNumRotationKeys = 1; + + na->mRotationKeys[0].mTime = 0.; + na->mRotationKeys[0].mValue = aiQuaternion(); + + // dummy position key + na->mPositionKeys = new aiVectorKey[1]; + na->mNumPositionKeys = 1; + + na->mPositionKeys[0].mTime = 0.; + na->mPositionKeys[0].mValue = aiVector3D(); + + return na.release(); + } + + aiNodeAnim* FBXConverter::GenerateTranslationNodeAnim(const std::string& name, + const Model& /*target*/, + const std::vector<const AnimationCurveNode*>& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time, + bool inverse) { + std::unique_ptr<aiNodeAnim> na(new aiNodeAnim()); + na->mNodeName.Set(name); + + ConvertTranslationKeys(na.get(), curves, layer_map, start, stop, max_time, min_time); + + if (inverse) { + for (unsigned int i = 0; i < na->mNumPositionKeys; ++i) { + na->mPositionKeys[i].mValue *= -1.0f; + } + } + + // dummy scaling key + na->mScalingKeys = new aiVectorKey[1]; + na->mNumScalingKeys = 1; + + na->mScalingKeys[0].mTime = 0.; + na->mScalingKeys[0].mValue = aiVector3D(1.0f, 1.0f, 1.0f); + + // dummy rotation key + na->mRotationKeys = new aiQuatKey[1]; + na->mNumRotationKeys = 1; + + na->mRotationKeys[0].mTime = 0.; + na->mRotationKeys[0].mValue = aiQuaternion(); + + return na.release(); + } + + aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name, + const Model& target, + NodeMap::const_iterator chain[TransformationComp_MAXIMUM], + NodeMap::const_iterator iter_end, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time, + bool reverse_order) + + { + std::unique_ptr<aiNodeAnim> na(new aiNodeAnim()); + na->mNodeName.Set(name); + + const PropertyTable& props = target.Props(); + + // need to convert from TRS order to SRT? + if (reverse_order) { + + aiVector3D def_scale = PropertyGet(props, "Lcl Scaling", aiVector3D(1.f, 1.f, 1.f)); + aiVector3D def_translate = PropertyGet(props, "Lcl Translation", aiVector3D(0.f, 0.f, 0.f)); + aiVector3D def_rot = PropertyGet(props, "Lcl Rotation", aiVector3D(0.f, 0.f, 0.f)); + + KeyFrameListList scaling; + KeyFrameListList translation; + KeyFrameListList rotation; + + if (chain[TransformationComp_Scaling] != iter_end) { + scaling = GetKeyframeList((*chain[TransformationComp_Scaling]).second, start, stop); + } + + if (chain[TransformationComp_Translation] != iter_end) { + translation = GetKeyframeList((*chain[TransformationComp_Translation]).second, start, stop); + } + + if (chain[TransformationComp_Rotation] != iter_end) { + rotation = GetKeyframeList((*chain[TransformationComp_Rotation]).second, start, stop); + } + + KeyFrameListList joined; + joined.insert(joined.end(), scaling.begin(), scaling.end()); + joined.insert(joined.end(), translation.begin(), translation.end()); + joined.insert(joined.end(), rotation.begin(), rotation.end()); + + const KeyTimeList& times = GetKeyTimeList(joined); + + aiQuatKey* out_quat = new aiQuatKey[times.size()]; + aiVectorKey* out_scale = new aiVectorKey[times.size()]; + aiVectorKey* out_translation = new aiVectorKey[times.size()]; + + if (times.size()) + { + ConvertTransformOrder_TRStoSRT(out_quat, out_scale, out_translation, + scaling, + translation, + rotation, + times, + max_time, + min_time, + target.RotationOrder(), + def_scale, + def_translate, + def_rot); + } + + // XXX remove duplicates / redundant keys which this operation did + // likely produce if not all three channels were equally dense. + + na->mNumScalingKeys = static_cast<unsigned int>(times.size()); + na->mNumRotationKeys = na->mNumScalingKeys; + na->mNumPositionKeys = na->mNumScalingKeys; + + na->mScalingKeys = out_scale; + na->mRotationKeys = out_quat; + na->mPositionKeys = out_translation; + } + else { + + // if a particular transformation is not given, grab it from + // the corresponding node to meet the semantics of aiNodeAnim, + // which requires all of rotation, scaling and translation + // to be set. + if (chain[TransformationComp_Scaling] != iter_end) { + ConvertScaleKeys(na.get(), (*chain[TransformationComp_Scaling]).second, + layer_map, + start, stop, + max_time, + min_time); + } + else { + na->mScalingKeys = new aiVectorKey[1]; + na->mNumScalingKeys = 1; + + na->mScalingKeys[0].mTime = 0.; + na->mScalingKeys[0].mValue = PropertyGet(props, "Lcl Scaling", + aiVector3D(1.f, 1.f, 1.f)); + } + + if (chain[TransformationComp_Rotation] != iter_end) { + ConvertRotationKeys(na.get(), (*chain[TransformationComp_Rotation]).second, + layer_map, + start, stop, + max_time, + min_time, + target.RotationOrder()); + } + else { + na->mRotationKeys = new aiQuatKey[1]; + na->mNumRotationKeys = 1; + + na->mRotationKeys[0].mTime = 0.; + na->mRotationKeys[0].mValue = EulerToQuaternion( + PropertyGet(props, "Lcl Rotation", aiVector3D(0.f, 0.f, 0.f)), + target.RotationOrder()); + } + + if (chain[TransformationComp_Translation] != iter_end) { + ConvertTranslationKeys(na.get(), (*chain[TransformationComp_Translation]).second, + layer_map, + start, stop, + max_time, + min_time); + } + else { + na->mPositionKeys = new aiVectorKey[1]; + na->mNumPositionKeys = 1; + + na->mPositionKeys[0].mTime = 0.; + na->mPositionKeys[0].mValue = PropertyGet(props, "Lcl Translation", + aiVector3D(0.f, 0.f, 0.f)); + } + + } + return na.release(); + } + + FBXConverter::KeyFrameListList FBXConverter::GetKeyframeList(const std::vector<const AnimationCurveNode*>& nodes, int64_t start, int64_t stop) + { + KeyFrameListList inputs; + inputs.reserve(nodes.size() * 3); + + //give some breathing room for rounding errors + int64_t adj_start = start - 10000; + int64_t adj_stop = stop + 10000; + + for (const AnimationCurveNode* node : nodes) { + ai_assert(node); + + const AnimationCurveMap& curves = node->Curves(); + for (const AnimationCurveMap::value_type& kv : curves) { + + unsigned int mapto; + if (kv.first == "d|X") { + mapto = 0; + } + else if (kv.first == "d|Y") { + mapto = 1; + } + else if (kv.first == "d|Z") { + mapto = 2; + } + else { + FBXImporter::LogWarn("ignoring scale animation curve, did not recognize target component"); + continue; + } + + const AnimationCurve* const curve = kv.second; + ai_assert(curve->GetKeys().size() == curve->GetValues().size() && curve->GetKeys().size()); + + //get values within the start/stop time window + std::shared_ptr<KeyTimeList> Keys(new KeyTimeList()); + std::shared_ptr<KeyValueList> Values(new KeyValueList()); + const size_t count = curve->GetKeys().size(); + Keys->reserve(count); + Values->reserve(count); + for (size_t n = 0; n < count; n++) + { + int64_t k = curve->GetKeys().at(n); + if (k >= adj_start && k <= adj_stop) + { + Keys->push_back(k); + Values->push_back(curve->GetValues().at(n)); + } + } + + inputs.push_back(std::make_tuple(Keys, Values, mapto)); + } + } + return inputs; // pray for NRVO :-) + } + + + KeyTimeList FBXConverter::GetKeyTimeList(const KeyFrameListList& inputs) { + ai_assert(!inputs.empty()); + + // reserve some space upfront - it is likely that the key-frame lists + // have matching time values, so max(of all key-frame lists) should + // be a good estimate. + KeyTimeList keys; + + size_t estimate = 0; + for (const KeyFrameList& kfl : inputs) { + estimate = std::max(estimate, std::get<0>(kfl)->size()); + } + + keys.reserve(estimate); + + std::vector<unsigned int> next_pos; + next_pos.resize(inputs.size(), 0); + + const size_t count = inputs.size(); + while (true) { + + int64_t min_tick = std::numeric_limits<int64_t>::max(); + for (size_t i = 0; i < count; ++i) { + const KeyFrameList& kfl = inputs[i]; + + if (std::get<0>(kfl)->size() > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) < min_tick) { + min_tick = std::get<0>(kfl)->at(next_pos[i]); + } + } + + if (min_tick == std::numeric_limits<int64_t>::max()) { + break; + } + keys.push_back(min_tick); + + for (size_t i = 0; i < count; ++i) { + const KeyFrameList& kfl = inputs[i]; + + + while (std::get<0>(kfl)->size() > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) == min_tick) { + ++next_pos[i]; + } + } + } + + return keys; + } + + void FBXConverter::InterpolateKeys(aiVectorKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, + const aiVector3D& def_value, + double& max_time, + double& min_time) { + ai_assert(!keys.empty()); + ai_assert(nullptr != valOut); + + std::vector<unsigned int> next_pos; + const size_t count(inputs.size()); + + next_pos.resize(inputs.size(), 0); + + for (KeyTimeList::value_type time : keys) { + ai_real result[3] = { def_value.x, def_value.y, def_value.z }; + + for (size_t i = 0; i < count; ++i) { + const KeyFrameList& kfl = inputs[i]; + + const size_t ksize = std::get<0>(kfl)->size(); + if (ksize == 0) { + continue; + } + if (ksize > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) == time) { + ++next_pos[i]; + } + + const size_t id0 = next_pos[i] > 0 ? next_pos[i] - 1 : 0; + const size_t id1 = next_pos[i] == ksize ? ksize - 1 : next_pos[i]; + + // use lerp for interpolation + const KeyValueList::value_type valueA = std::get<1>(kfl)->at(id0); + const KeyValueList::value_type valueB = std::get<1>(kfl)->at(id1); + + const KeyTimeList::value_type timeA = std::get<0>(kfl)->at(id0); + const KeyTimeList::value_type timeB = std::get<0>(kfl)->at(id1); + + const ai_real factor = timeB == timeA ? ai_real(0.) : static_cast<ai_real>((time - timeA)) / (timeB - timeA); + const ai_real interpValue = static_cast<ai_real>(valueA + (valueB - valueA) * factor); + + result[std::get<2>(kfl)] = interpValue; + } + + // magic value to convert fbx times to seconds + valOut->mTime = CONVERT_FBX_TIME(time) * anim_fps; + + min_time = std::min(min_time, valOut->mTime); + max_time = std::max(max_time, valOut->mTime); + + valOut->mValue.x = result[0]; + valOut->mValue.y = result[1]; + valOut->mValue.z = result[2]; + + ++valOut; + } + } + + void FBXConverter::InterpolateKeys(aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, + const aiVector3D& def_value, + double& maxTime, + double& minTime, + Model::RotOrder order) + { + ai_assert(!keys.empty()); + ai_assert(nullptr != valOut); + + std::unique_ptr<aiVectorKey[]> temp(new aiVectorKey[keys.size()]); + InterpolateKeys(temp.get(), keys, inputs, def_value, maxTime, minTime); + + aiMatrix4x4 m; + + aiQuaternion lastq; + + for (size_t i = 0, c = keys.size(); i < c; ++i) { + + valOut[i].mTime = temp[i].mTime; + + GetRotationMatrix(order, temp[i].mValue, m); + aiQuaternion quat = aiQuaternion(aiMatrix3x3(m)); + + // take shortest path by checking the inner product + // http://www.3dkingdoms.com/weekly/weekly.php?a=36 + if (quat.x * lastq.x + quat.y * lastq.y + quat.z * lastq.z + quat.w * lastq.w < 0) + { + quat.x = -quat.x; + quat.y = -quat.y; + quat.z = -quat.z; + quat.w = -quat.w; + } + lastq = quat; + + valOut[i].mValue = quat; + } + } + + void FBXConverter::ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale, + aiVectorKey* out_translation, + const KeyFrameListList& scaling, + const KeyFrameListList& translation, + const KeyFrameListList& rotation, + const KeyTimeList& times, + double& maxTime, + double& minTime, + Model::RotOrder order, + const aiVector3D& def_scale, + const aiVector3D& def_translate, + const aiVector3D& def_rotation) + { + if (rotation.size()) { + InterpolateKeys(out_quat, times, rotation, def_rotation, maxTime, minTime, order); + } + else { + for (size_t i = 0; i < times.size(); ++i) { + out_quat[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps; + out_quat[i].mValue = EulerToQuaternion(def_rotation, order); + } + } + + if (scaling.size()) { + InterpolateKeys(out_scale, times, scaling, def_scale, maxTime, minTime); + } + else { + for (size_t i = 0; i < times.size(); ++i) { + out_scale[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps; + out_scale[i].mValue = def_scale; + } + } + + if (translation.size()) { + InterpolateKeys(out_translation, times, translation, def_translate, maxTime, minTime); + } + else { + for (size_t i = 0; i < times.size(); ++i) { + out_translation[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps; + out_translation[i].mValue = def_translate; + } + } + + const size_t count = times.size(); + for (size_t i = 0; i < count; ++i) { + aiQuaternion& r = out_quat[i].mValue; + aiVector3D& s = out_scale[i].mValue; + aiVector3D& t = out_translation[i].mValue; + + aiMatrix4x4 mat, temp; + aiMatrix4x4::Translation(t, mat); + mat *= aiMatrix4x4(r.GetMatrix()); + mat *= aiMatrix4x4::Scaling(s, temp); + + mat.Decompose(s, r, t); + } + } + + aiQuaternion FBXConverter::EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order) + { + aiMatrix4x4 m; + GetRotationMatrix(order, rot, m); + + return aiQuaternion(aiMatrix3x3(m)); + } + + void FBXConverter::ConvertScaleKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime) + { + ai_assert(nodes.size()); + + // XXX for now, assume scale should be blended geometrically (i.e. two + // layers should be multiplied with each other). There is a FBX + // property in the layer to specify the behaviour, though. + + const KeyFrameListList& inputs = GetKeyframeList(nodes, start, stop); + const KeyTimeList& keys = GetKeyTimeList(inputs); + + na->mNumScalingKeys = static_cast<unsigned int>(keys.size()); + na->mScalingKeys = new aiVectorKey[keys.size()]; + if (keys.size() > 0) + InterpolateKeys(na->mScalingKeys, keys, inputs, aiVector3D(1.0f, 1.0f, 1.0f), maxTime, minTime); + } + + void FBXConverter::ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, + const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime) + { + ai_assert(nodes.size()); + + // XXX see notes in ConvertScaleKeys() + const KeyFrameListList& inputs = GetKeyframeList(nodes, start, stop); + const KeyTimeList& keys = GetKeyTimeList(inputs); + + na->mNumPositionKeys = static_cast<unsigned int>(keys.size()); + na->mPositionKeys = new aiVectorKey[keys.size()]; + if (keys.size() > 0) + InterpolateKeys(na->mPositionKeys, keys, inputs, aiVector3D(0.0f, 0.0f, 0.0f), maxTime, minTime); + } + + void FBXConverter::ConvertRotationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, + const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime, + Model::RotOrder order) + { + ai_assert(nodes.size()); + + // XXX see notes in ConvertScaleKeys() + const std::vector< KeyFrameList >& inputs = GetKeyframeList(nodes, start, stop); + const KeyTimeList& keys = GetKeyTimeList(inputs); + + na->mNumRotationKeys = static_cast<unsigned int>(keys.size()); + na->mRotationKeys = new aiQuatKey[keys.size()]; + if (!keys.empty()) { + InterpolateKeys(na->mRotationKeys, keys, inputs, aiVector3D(0.0f, 0.0f, 0.0f), maxTime, minTime, order); + } + } + + void FBXConverter::ConvertGlobalSettings() { + if (nullptr == out) { + return; + } + + out->mMetaData = aiMetadata::Alloc(15); + out->mMetaData->Set(0, "UpAxis", doc.GlobalSettings().UpAxis()); + out->mMetaData->Set(1, "UpAxisSign", doc.GlobalSettings().UpAxisSign()); + out->mMetaData->Set(2, "FrontAxis", doc.GlobalSettings().FrontAxis()); + out->mMetaData->Set(3, "FrontAxisSign", doc.GlobalSettings().FrontAxisSign()); + out->mMetaData->Set(4, "CoordAxis", doc.GlobalSettings().CoordAxis()); + out->mMetaData->Set(5, "CoordAxisSign", doc.GlobalSettings().CoordAxisSign()); + out->mMetaData->Set(6, "OriginalUpAxis", doc.GlobalSettings().OriginalUpAxis()); + out->mMetaData->Set(7, "OriginalUpAxisSign", doc.GlobalSettings().OriginalUpAxisSign()); + out->mMetaData->Set(8, "UnitScaleFactor", (double)doc.GlobalSettings().UnitScaleFactor()); + out->mMetaData->Set(9, "OriginalUnitScaleFactor", doc.GlobalSettings().OriginalUnitScaleFactor()); + out->mMetaData->Set(10, "AmbientColor", doc.GlobalSettings().AmbientColor()); + out->mMetaData->Set(11, "FrameRate", (int)doc.GlobalSettings().TimeMode()); + out->mMetaData->Set(12, "TimeSpanStart", doc.GlobalSettings().TimeSpanStart()); + out->mMetaData->Set(13, "TimeSpanStop", doc.GlobalSettings().TimeSpanStop()); + out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate()); + } + + void FBXConverter::TransferDataToScene() + { + ai_assert(!out->mMeshes); + ai_assert(!out->mNumMeshes); + + // note: the trailing () ensures initialization with NULL - not + // many C++ users seem to know this, so pointing it out to avoid + // confusion why this code works. + + if (meshes.size()) { + out->mMeshes = new aiMesh*[meshes.size()](); + out->mNumMeshes = static_cast<unsigned int>(meshes.size()); + + std::swap_ranges(meshes.begin(), meshes.end(), out->mMeshes); + } + + if (materials.size()) { + out->mMaterials = new aiMaterial*[materials.size()](); + out->mNumMaterials = static_cast<unsigned int>(materials.size()); + + std::swap_ranges(materials.begin(), materials.end(), out->mMaterials); + } + + if (animations.size()) { + out->mAnimations = new aiAnimation*[animations.size()](); + out->mNumAnimations = static_cast<unsigned int>(animations.size()); + + std::swap_ranges(animations.begin(), animations.end(), out->mAnimations); + } + + if (lights.size()) { + out->mLights = new aiLight*[lights.size()](); + out->mNumLights = static_cast<unsigned int>(lights.size()); + + std::swap_ranges(lights.begin(), lights.end(), out->mLights); + } + + if (cameras.size()) { + out->mCameras = new aiCamera*[cameras.size()](); + out->mNumCameras = static_cast<unsigned int>(cameras.size()); + + std::swap_ranges(cameras.begin(), cameras.end(), out->mCameras); + } + + if (textures.size()) { + out->mTextures = new aiTexture*[textures.size()](); + out->mNumTextures = static_cast<unsigned int>(textures.size()); + + std::swap_ranges(textures.begin(), textures.end(), out->mTextures); + } + } + + // ------------------------------------------------------------------------------------------------ + void ConvertToAssimpScene(aiScene* out, const Document& doc) + { + FBXConverter converter(out, doc); + } + + } // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBXConverter.h b/thirdparty/assimp/code/FBXConverter.h new file mode 100644 index 0000000000..398baa445f --- /dev/null +++ b/thirdparty/assimp/code/FBXConverter.h @@ -0,0 +1,457 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDConverter.h + * @brief FBX DOM to aiScene conversion + */ +#ifndef INCLUDED_AI_FBX_CONVERTER_H +#define INCLUDED_AI_FBX_CONVERTER_H + +#include "FBXParser.h" +#include "FBXMeshGeometry.h" +#include "FBXDocument.h" +#include "FBXUtil.h" +#include "FBXProperties.h" +#include "FBXImporter.h" +#include <assimp/anim.h> +#include <assimp/material.h> +#include <assimp/light.h> +#include <assimp/texture.h> +#include <assimp/camera.h> +#include <assimp/StringComparison.h> + +struct aiScene; +struct aiNode; +struct aiMaterial; + +struct morphKeyData { + std::vector<unsigned int> values; + std::vector<float> weights; +}; +typedef std::map<int64_t, morphKeyData*> morphAnimData; + +namespace Assimp { +namespace FBX { + +class Document; + +using NodeNameCache = std::set<std::string>; + +/** + * Convert a FBX #Document to #aiScene + * @param out Empty scene to be populated + * @param doc Parsed FBX document + */ +void ConvertToAssimpScene(aiScene* out, const Document& doc); + +/** Dummy class to encapsulate the conversion process */ +class FBXConverter { +public: + /** + * The different parts that make up the final local transformation of a fbx-node + */ + enum TransformationComp { + TransformationComp_GeometricScalingInverse = 0, + TransformationComp_GeometricRotationInverse, + TransformationComp_GeometricTranslationInverse, + TransformationComp_Translation, + TransformationComp_RotationOffset, + TransformationComp_RotationPivot, + TransformationComp_PreRotation, + TransformationComp_Rotation, + TransformationComp_PostRotation, + TransformationComp_RotationPivotInverse, + TransformationComp_ScalingOffset, + TransformationComp_ScalingPivot, + TransformationComp_Scaling, + TransformationComp_ScalingPivotInverse, + TransformationComp_GeometricTranslation, + TransformationComp_GeometricRotation, + TransformationComp_GeometricScaling, + + TransformationComp_MAXIMUM + }; + +public: + FBXConverter(aiScene* out, const Document& doc); + ~FBXConverter(); + +private: + // ------------------------------------------------------------------------------------------------ + // find scene root and trigger recursive scene conversion + void ConvertRootNode(); + + // ------------------------------------------------------------------------------------------------ + // collect and assign child nodes + void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4()); + + // ------------------------------------------------------------------------------------------------ + void ConvertLights(const Model& model, const std::string &orig_name ); + + // ------------------------------------------------------------------------------------------------ + void ConvertCameras(const Model& model, const std::string &orig_name ); + + // ------------------------------------------------------------------------------------------------ + void ConvertLight( const Light& light, const std::string &orig_name ); + + // ------------------------------------------------------------------------------------------------ + void ConvertCamera( const Camera& cam, const std::string &orig_name ); + + // ------------------------------------------------------------------------------------------------ + void GetUniqueName( const std::string &name, std::string& uniqueName ); + + // ------------------------------------------------------------------------------------------------ + // this returns unified names usable within assimp identifiers (i.e. no space characters - + // while these would be allowed, they are a potential trouble spot so better not use them). + const char* NameTransformationComp(TransformationComp comp); + + // ------------------------------------------------------------------------------------------------ + // note: this returns the REAL fbx property names + const char* NameTransformationCompProperty(TransformationComp comp); + + // ------------------------------------------------------------------------------------------------ + aiVector3D TransformationCompDefaultValue(TransformationComp comp); + + // ------------------------------------------------------------------------------------------------ + void GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out); + // ------------------------------------------------------------------------------------------------ + /** + * checks if a node has more than just scaling, rotation and translation components + */ + bool NeedsComplexTransformationChain(const Model& model); + + // ------------------------------------------------------------------------------------------------ + // note: name must be a FixNodeName() result + std::string NameTransformationChainNode(const std::string& name, TransformationComp comp); + + // ------------------------------------------------------------------------------------------------ + /** + * note: memory for output_nodes will be managed by the caller + */ + void GenerateTransformationNodeChain(const Model& model, std::vector<aiNode*>& output_nodes, std::vector<aiNode*>& post_output_nodes); + + // ------------------------------------------------------------------------------------------------ + void SetupNodeMetadata(const Model& model, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform); + + // ------------------------------------------------------------------------------------------------ + // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed + std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + std::vector<unsigned int> ConvertLine(const LineGeometry& line, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, + const aiMatrix4x4& node_global_transform, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, + MatIndexArray::value_type index, + const aiMatrix4x4& node_global_transform, aiNode& nd); + + // ------------------------------------------------------------------------------------------------ + static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */ + static_cast<unsigned int>(-1); + + // ------------------------------------------------------------------------------------------------ + /** + * - if materialIndex == NO_MATERIAL_SEPARATION, materials are not taken into + * account when determining which weights to include. + * - outputVertStartIndices is only used when a material index is specified, it gives for + * each output vertex the DOM index it maps to. + */ + void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, + const aiMatrix4x4& node_global_transform = aiMatrix4x4(), + unsigned int materialIndex = NO_MATERIAL_SEPARATION, + std::vector<unsigned int>* outputVertStartIndices = NULL); + + // ------------------------------------------------------------------------------------------------ + void ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl, + std::vector<size_t>& out_indices, + std::vector<size_t>& index_out_indices, + std::vector<size_t>& count_out_indices, + const aiMatrix4x4& node_global_transform); + + // ------------------------------------------------------------------------------------------------ + void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, + MatIndexArray::value_type materialIndex); + + // ------------------------------------------------------------------------------------------------ + unsigned int GetDefaultMaterial(); + + // ------------------------------------------------------------------------------------------------ + // Material -> aiMaterial + unsigned int ConvertMaterial(const Material& material, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + // Video -> aiTexture + unsigned int ConvertVideo(const Video& video); + + // ------------------------------------------------------------------------------------------------ + // convert embedded texture if necessary and return actual texture path + aiString GetTexturePath(const Texture* tex); + + // ------------------------------------------------------------------------------------------------ + void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, + const std::string& propName, + aiTextureType target, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + void TrySetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, + const std::string& propName, + aiTextureType target, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + void SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + void SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + aiColor3D GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName, + bool& result); + aiColor3D GetColorPropertyFactored(const PropertyTable& props, const std::string& colorName, + const std::string& factorName, bool& result, bool useTemplate = true); + aiColor3D GetColorProperty(const PropertyTable& props, const std::string& colorName, + bool& result, bool useTemplate = true); + + // ------------------------------------------------------------------------------------------------ + void SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props); + void SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTable& props, const TextureMap& textures, const MeshGeometry* const mesh); + + // ------------------------------------------------------------------------------------------------ + // get the number of fps for a FrameRate enumerated value + static double FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal = -1.0); + + // ------------------------------------------------------------------------------------------------ + // convert animation data to aiAnimation et al + void ConvertAnimations(); + + // ------------------------------------------------------------------------------------------------ + // takes a fbx node name and returns the identifier to be used in the assimp output scene. + // the function is guaranteed to provide consistent results over multiple invocations + // UNLESS RenameNode() is called for a particular node name. + std::string FixNodeName(const std::string& name); + std::string FixAnimMeshName(const std::string& name); + + typedef std::map<const AnimationCurveNode*, const AnimationLayer*> LayerMap; + + // XXX: better use multi_map .. + typedef std::map<std::string, std::vector<const AnimationCurveNode*> > NodeMap; + + // ------------------------------------------------------------------------------------------------ + void ConvertAnimationStack(const AnimationStack& st); + + // ------------------------------------------------------------------------------------------------ + void ProcessMorphAnimDatas(std::map<std::string, morphAnimData*>* morphAnimDatas, const BlendShapeChannel* bsc, const AnimationCurveNode* node); + + // ------------------------------------------------------------------------------------------------ + void GenerateNodeAnimations(std::vector<aiNodeAnim*>& node_anims, + const std::string& fixed_name, + const std::vector<const AnimationCurveNode*>& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time); + + // ------------------------------------------------------------------------------------------------ + bool IsRedundantAnimationData(const Model& target, + TransformationComp comp, + const std::vector<const AnimationCurveNode*>& curves); + + // ------------------------------------------------------------------------------------------------ + aiNodeAnim* GenerateRotationNodeAnim(const std::string& name, + const Model& target, + const std::vector<const AnimationCurveNode*>& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time); + + // ------------------------------------------------------------------------------------------------ + aiNodeAnim* GenerateScalingNodeAnim(const std::string& name, + const Model& /*target*/, + const std::vector<const AnimationCurveNode*>& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time); + + // ------------------------------------------------------------------------------------------------ + aiNodeAnim* GenerateTranslationNodeAnim(const std::string& name, + const Model& /*target*/, + const std::vector<const AnimationCurveNode*>& curves, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time, + bool inverse = false); + + // ------------------------------------------------------------------------------------------------ + // generate node anim, extracting only Rotation, Scaling and Translation from the given chain + aiNodeAnim* GenerateSimpleNodeAnim(const std::string& name, + const Model& target, + NodeMap::const_iterator chain[TransformationComp_MAXIMUM], + NodeMap::const_iterator iter_end, + const LayerMap& layer_map, + int64_t start, int64_t stop, + double& max_time, + double& min_time, + bool reverse_order = false); + + // key (time), value, mapto (component index) + typedef std::tuple<std::shared_ptr<KeyTimeList>, std::shared_ptr<KeyValueList>, unsigned int > KeyFrameList; + typedef std::vector<KeyFrameList> KeyFrameListList; + + // ------------------------------------------------------------------------------------------------ + KeyFrameListList GetKeyframeList(const std::vector<const AnimationCurveNode*>& nodes, int64_t start, int64_t stop); + + // ------------------------------------------------------------------------------------------------ + KeyTimeList GetKeyTimeList(const KeyFrameListList& inputs); + + // ------------------------------------------------------------------------------------------------ + void InterpolateKeys(aiVectorKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, + const aiVector3D& def_value, + double& max_time, + double& min_time); + + // ------------------------------------------------------------------------------------------------ + void InterpolateKeys(aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs, + const aiVector3D& def_value, + double& maxTime, + double& minTime, + Model::RotOrder order); + + // ------------------------------------------------------------------------------------------------ + void ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale, + aiVectorKey* out_translation, + const KeyFrameListList& scaling, + const KeyFrameListList& translation, + const KeyFrameListList& rotation, + const KeyTimeList& times, + double& maxTime, + double& minTime, + Model::RotOrder order, + const aiVector3D& def_scale, + const aiVector3D& def_translate, + const aiVector3D& def_rotation); + + // ------------------------------------------------------------------------------------------------ + // euler xyz -> quat + aiQuaternion EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order); + + // ------------------------------------------------------------------------------------------------ + void ConvertScaleKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime); + + // ------------------------------------------------------------------------------------------------ + void ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, + const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime); + + // ------------------------------------------------------------------------------------------------ + void ConvertRotationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, + const LayerMap& /*layers*/, + int64_t start, int64_t stop, + double& maxTime, + double& minTime, + Model::RotOrder order); + + void ConvertGlobalSettings(); + + // ------------------------------------------------------------------------------------------------ + // copy generated meshes, animations, lights, cameras and textures to the output scene + void TransferDataToScene(); + +private: + + // 0: not assigned yet, others: index is value - 1 + unsigned int defaultMaterialIndex; + + std::vector<aiMesh*> meshes; + std::vector<aiMaterial*> materials; + std::vector<aiAnimation*> animations; + std::vector<aiLight*> lights; + std::vector<aiCamera*> cameras; + std::vector<aiTexture*> textures; + + + typedef std::map<const Material*, unsigned int> MaterialMap; + MaterialMap materials_converted; + + typedef std::map<const Video*, unsigned int> VideoMap; + VideoMap textures_converted; + + typedef std::map<const Geometry*, std::vector<unsigned int> > MeshMap; + MeshMap meshes_converted; + + // fixed node name -> which trafo chain components have animations? + typedef std::map<std::string, unsigned int> NodeAnimBitMap; + NodeAnimBitMap node_anim_chain_bits; + + NodeNameCache mNodeNames; + double anim_fps; + + aiScene* const out; + const FBX::Document& doc; +}; + +} +} + +#endif // INCLUDED_AI_FBX_CONVERTER_H diff --git a/thirdparty/assimp/code/FBXDeformer.cpp b/thirdparty/assimp/code/FBXDeformer.cpp new file mode 100644 index 0000000000..6927553450 --- /dev/null +++ b/thirdparty/assimp/code/FBXDeformer.cpp @@ -0,0 +1,213 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXNoteAttribute.cpp + * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXMeshGeometry.h" +#include "FBXImporter.h" +#include "FBXDocumentUtil.h" + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name) + : Object(id,element,name) +{ + const Scope& sc = GetRequiredScope(element); + + const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2)); + props = GetPropertyTable(doc,"Deformer.Fbx" + classname,element,sc,true); +} + + +// ------------------------------------------------------------------------------------------------ +Deformer::~Deformer() +{ + +} + + +// ------------------------------------------------------------------------------------------------ +Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Deformer(id,element,doc,name) +, node() +{ + const Scope& sc = GetRequiredScope(element); + + const Element* const Indexes = sc["Indexes"]; + const Element* const Weights = sc["Weights"]; + + const Element& Transform = GetRequiredElement(sc,"Transform",&element); + const Element& TransformLink = GetRequiredElement(sc,"TransformLink",&element); + + transform = ReadMatrix(Transform); + transformLink = ReadMatrix(TransformLink); + + // it is actually possible that there be Deformer's with no weights + if (!!Indexes != !!Weights) { + DOMError("either Indexes or Weights are missing from Cluster",&element); + } + + if(Indexes) { + ParseVectorDataArray(indices,*Indexes); + ParseVectorDataArray(weights,*Weights); + } + + if(indices.size() != weights.size()) { + DOMError("sizes of index and weight array don't match up",&element); + } + + // read assigned node + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Model"); + for(const Connection* con : conns) { + const Model* const mod = ProcessSimpleConnection<Model>(*con, false, "Model -> Cluster", element); + if(mod) { + node = mod; + break; + } + } + + if (!node) { + DOMError("failed to read target Node for Cluster",&element); + } +} + + +// ------------------------------------------------------------------------------------------------ +Cluster::~Cluster() +{ + +} + + +// ------------------------------------------------------------------------------------------------ +Skin::Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Deformer(id,element,doc,name) +, accuracy( 0.0f ) { + const Scope& sc = GetRequiredScope(element); + + const Element* const Link_DeformAcuracy = sc["Link_DeformAcuracy"]; + if(Link_DeformAcuracy) { + accuracy = ParseTokenAsFloat(GetRequiredToken(*Link_DeformAcuracy,0)); + } + + // resolve assigned clusters + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer"); + + clusters.reserve(conns.size()); + for(const Connection* con : conns) { + + const Cluster* const cluster = ProcessSimpleConnection<Cluster>(*con, false, "Cluster -> Skin", element); + if(cluster) { + clusters.push_back(cluster); + continue; + } + } +} + + +// ------------------------------------------------------------------------------------------------ +Skin::~Skin() +{ + +} +// ------------------------------------------------------------------------------------------------ +BlendShape::BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name) + : Deformer(id, element, doc, name) +{ + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer"); + blendShapeChannels.reserve(conns.size()); + for (const Connection* con : conns) { + const BlendShapeChannel* const bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element); + if (bspc) { + blendShapeChannels.push_back(bspc); + continue; + } + } +} +// ------------------------------------------------------------------------------------------------ +BlendShape::~BlendShape() +{ + +} +// ------------------------------------------------------------------------------------------------ +BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name) + : Deformer(id, element, doc, name) +{ + const Scope& sc = GetRequiredScope(element); + const Element* const DeformPercent = sc["DeformPercent"]; + if (DeformPercent) { + percent = ParseTokenAsFloat(GetRequiredToken(*DeformPercent, 0)); + } + const Element* const FullWeights = sc["FullWeights"]; + if (FullWeights) { + ParseVectorDataArray(fullWeights, *FullWeights); + } + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(), "Geometry"); + shapeGeometries.reserve(conns.size()); + for (const Connection* con : conns) { + const ShapeGeometry* const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element); + if (sg) { + shapeGeometries.push_back(sg); + continue; + } + } +} +// ------------------------------------------------------------------------------------------------ +BlendShapeChannel::~BlendShapeChannel() +{ + +} +// ------------------------------------------------------------------------------------------------ +} +} +#endif + diff --git a/thirdparty/assimp/code/FBXDocument.cpp b/thirdparty/assimp/code/FBXDocument.cpp new file mode 100644 index 0000000000..1af08fe6d8 --- /dev/null +++ b/thirdparty/assimp/code/FBXDocument.cpp @@ -0,0 +1,726 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the* + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDocument.cpp + * @brief Implementation of the FBX DOM classes + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXDocument.h" +#include "FBXMeshGeometry.h" +#include "FBXParser.h" +#include "FBXUtil.h" +#include "FBXImporter.h" +#include "FBXImportSettings.h" +#include "FBXDocumentUtil.h" +#include "FBXProperties.h" + +#include <memory> +#include <functional> +#include <map> + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc) +: doc(doc) +, element(element) +, id(id) +, flags() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +LazyObject::~LazyObject() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +const Object* LazyObject::Get(bool dieOnError) +{ + if(IsBeingConstructed() || FailedToConstruct()) { + return nullptr; + } + + if (object.get()) { + return object.get(); + } + + // if this is the root object, we return a dummy since there + // is no root object int he fbx file - it is just referenced + // with id 0. + if(id == 0L) { + object.reset(new Object(id, element, "Model::RootNode")); + return object.get(); + } + + const Token& key = element.KeyToken(); + const TokenList& tokens = element.Tokens(); + + if(tokens.size() < 3) { + DOMError("expected at least 3 tokens: id, name and class tag",&element); + } + + const char* err; + std::string name = ParseTokenAsString(*tokens[1],err); + if (err) { + DOMError(err,&element); + } + + // small fix for binary reading: binary fbx files don't use + // prefixes such as Model:: in front of their names. The + // loading code expects this at many places, though! + // so convert the binary representation (a 0x0001) to the + // double colon notation. + if(tokens[1]->IsBinary()) { + for (size_t i = 0; i < name.length(); ++i) { + if (name[i] == 0x0 && name[i+1] == 0x1) { + name = name.substr(i+2) + "::" + name.substr(0,i); + } + } + } + + const std::string classtag = ParseTokenAsString(*tokens[2],err); + if (err) { + DOMError(err,&element); + } + + // prevent recursive calls + flags |= BEING_CONSTRUCTED; + + try { + // this needs to be relatively fast since it happens a lot, + // so avoid constructing strings all the time. + const char* obtype = key.begin(); + const size_t length = static_cast<size_t>(key.end()-key.begin()); + + // For debugging + //dumpObjectClassInfo( objtype, classtag ); + + if (!strncmp(obtype,"Geometry",length)) { + if (!strcmp(classtag.c_str(),"Mesh")) { + object.reset(new MeshGeometry(id,element,name,doc)); + } + if (!strcmp(classtag.c_str(), "Shape")) { + object.reset(new ShapeGeometry(id, element, name, doc)); + } + if (!strcmp(classtag.c_str(), "Line")) { + object.reset(new LineGeometry(id, element, name, doc)); + } + } + else if (!strncmp(obtype,"NodeAttribute",length)) { + if (!strcmp(classtag.c_str(),"Camera")) { + object.reset(new Camera(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(),"CameraSwitcher")) { + object.reset(new CameraSwitcher(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(),"Light")) { + object.reset(new Light(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(),"Null")) { + object.reset(new Null(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(),"LimbNode")) { + object.reset(new LimbNode(id,element,doc,name)); + } + } + else if (!strncmp(obtype,"Deformer",length)) { + if (!strcmp(classtag.c_str(),"Cluster")) { + object.reset(new Cluster(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(),"Skin")) { + object.reset(new Skin(id,element,doc,name)); + } + else if (!strcmp(classtag.c_str(), "BlendShape")) { + object.reset(new BlendShape(id, element, doc, name)); + } + else if (!strcmp(classtag.c_str(), "BlendShapeChannel")) { + object.reset(new BlendShapeChannel(id, element, doc, name)); + } + } + else if ( !strncmp( obtype, "Model", length ) ) { + // FK and IK effectors are not supported + if ( strcmp( classtag.c_str(), "IKEffector" ) && strcmp( classtag.c_str(), "FKEffector" ) ) { + object.reset( new Model( id, element, doc, name ) ); + } + } + else if (!strncmp(obtype,"Material",length)) { + object.reset(new Material(id,element,doc,name)); + } + else if (!strncmp(obtype,"Texture",length)) { + object.reset(new Texture(id,element,doc,name)); + } + else if (!strncmp(obtype,"LayeredTexture",length)) { + object.reset(new LayeredTexture(id,element,doc,name)); + } + else if (!strncmp(obtype,"Video",length)) { + object.reset(new Video(id,element,doc,name)); + } + else if (!strncmp(obtype,"AnimationStack",length)) { + object.reset(new AnimationStack(id,element,name,doc)); + } + else if (!strncmp(obtype,"AnimationLayer",length)) { + object.reset(new AnimationLayer(id,element,name,doc)); + } + // note: order matters for these two + else if (!strncmp(obtype,"AnimationCurve",length)) { + object.reset(new AnimationCurve(id,element,name,doc)); + } + else if (!strncmp(obtype,"AnimationCurveNode",length)) { + object.reset(new AnimationCurveNode(id,element,name,doc)); + } + } + catch(std::exception& ex) { + flags &= ~BEING_CONSTRUCTED; + flags |= FAILED_TO_CONSTRUCT; + + if(dieOnError || doc.Settings().strictMode) { + throw; + } + + // note: the error message is already formatted, so raw logging is ok + if(!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_ERROR(ex.what()); + } + return NULL; + } + + if (!object.get()) { + //DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element); + } + + flags &= ~BEING_CONSTRUCTED; + return object.get(); +} + +// ------------------------------------------------------------------------------------------------ +Object::Object(uint64_t id, const Element& element, const std::string& name) +: element(element) +, name(name) +, id(id) +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +Object::~Object() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr<const PropertyTable> props) +: props(props) +, doc(doc) +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +FileGlobalSettings::~FileGlobalSettings() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +Document::Document(const Parser& parser, const ImportSettings& settings) +: settings(settings) +, parser(parser) +{ + // Cannot use array default initialization syntax because vc8 fails on it + for (auto &timeStamp : creationTimeStamp) { + timeStamp = 0; + } + + ReadHeader(); + ReadPropertyTemplates(); + + ReadGlobalSettings(); + + // This order is important, connections need parsed objects to check + // whether connections are ok or not. Objects may not be evaluated yet, + // though, since this may require valid connections. + ReadObjects(); + ReadConnections(); +} + +// ------------------------------------------------------------------------------------------------ +Document::~Document() +{ + for(ObjectMap::value_type& v : objects) { + delete v.second; + } + + for(ConnectionMap::value_type& v : src_connections) { + delete v.second; + } + // |dest_connections| contain the same Connection objects as the |src_connections| +} + +// ------------------------------------------------------------------------------------------------ +static const unsigned int LowerSupportedVersion = 7100; +static const unsigned int UpperSupportedVersion = 7400; + +void Document::ReadHeader() { + // Read ID objects from "Objects" section + const Scope& sc = parser.GetRootScope(); + const Element* const ehead = sc["FBXHeaderExtension"]; + if(!ehead || !ehead->Compound()) { + DOMError("no FBXHeaderExtension dictionary found"); + } + + const Scope& shead = *ehead->Compound(); + fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead,"FBXVersion",ehead),0)); + + // While we may have some success with newer files, we don't support + // the older 6.n fbx format + if(fbxVersion < LowerSupportedVersion ) { + DOMError("unsupported, old format version, supported are only FBX 2011, FBX 2012 and FBX 2013"); + } + if(fbxVersion > UpperSupportedVersion ) { + if(Settings().strictMode) { + DOMError("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013" + " (turn off strict mode to try anyhow) "); + } + else { + DOMWarning("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013," + " trying to read it nevertheless"); + } + } + + const Element* const ecreator = shead["Creator"]; + if(ecreator) { + creator = ParseTokenAsString(GetRequiredToken(*ecreator,0)); + } + + const Element* const etimestamp = shead["CreationTimeStamp"]; + if(etimestamp && etimestamp->Compound()) { + const Scope& stimestamp = *etimestamp->Compound(); + creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Year"),0)); + creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Month"),0)); + creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Day"),0)); + creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Hour"),0)); + creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Minute"),0)); + creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Second"),0)); + creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Millisecond"),0)); + } +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadGlobalSettings() +{ + const Scope& sc = parser.GetRootScope(); + const Element* const ehead = sc["GlobalSettings"]; + if ( nullptr == ehead || !ehead->Compound() ) { + DOMWarning( "no GlobalSettings dictionary found" ); + globals.reset(new FileGlobalSettings(*this, std::make_shared<const PropertyTable>())); + return; + } + + std::shared_ptr<const PropertyTable> props = GetPropertyTable( *this, "", *ehead, *ehead->Compound(), true ); + + //double v = PropertyGet<float>( *props.get(), std::string("UnitScaleFactor"), 1.0 ); + + if(!props) { + DOMError("GlobalSettings dictionary contains no property table"); + } + + globals.reset(new FileGlobalSettings(*this, props)); +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadObjects() +{ + // read ID objects from "Objects" section + const Scope& sc = parser.GetRootScope(); + const Element* const eobjects = sc["Objects"]; + if(!eobjects || !eobjects->Compound()) { + DOMError("no Objects dictionary found"); + } + + // add a dummy entry to represent the Model::RootNode object (id 0), + // which is only indirectly defined in the input file + objects[0] = new LazyObject(0L, *eobjects, *this); + + const Scope& sobjects = *eobjects->Compound(); + for(const ElementMap::value_type& el : sobjects.Elements()) { + + // extract ID + const TokenList& tok = el.second->Tokens(); + + if (tok.empty()) { + DOMError("expected ID after object key",el.second); + } + + const char* err; + const uint64_t id = ParseTokenAsID(*tok[0], err); + if(err) { + DOMError(err,el.second); + } + + // id=0 is normally implicit + if(id == 0L) { + DOMError("encountered object with implicitly defined id 0",el.second); + } + + if(objects.find(id) != objects.end()) { + DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second); + } + + objects[id] = new LazyObject(id, *el.second, *this); + + // grab all animation stacks upfront since there is no listing of them + if(!strcmp(el.first.c_str(),"AnimationStack")) { + animationStacks.push_back(id); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadPropertyTemplates() +{ + const Scope& sc = parser.GetRootScope(); + // read property templates from "Definitions" section + const Element* const edefs = sc["Definitions"]; + if(!edefs || !edefs->Compound()) { + DOMWarning("no Definitions dictionary found"); + return; + } + + const Scope& sdefs = *edefs->Compound(); + const ElementCollection otypes = sdefs.GetCollection("ObjectType"); + for(ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) { + const Element& el = *(*it).second; + const Scope* sc = el.Compound(); + if(!sc) { + DOMWarning("expected nested scope in ObjectType, ignoring",&el); + continue; + } + + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + DOMWarning("expected name for ObjectType element, ignoring",&el); + continue; + } + + const std::string& oname = ParseTokenAsString(*tok[0]); + + const ElementCollection templs = sc->GetCollection("PropertyTemplate"); + for(ElementMap::const_iterator it = templs.first; it != templs.second; ++it) { + const Element& el = *(*it).second; + const Scope* sc = el.Compound(); + if(!sc) { + DOMWarning("expected nested scope in PropertyTemplate, ignoring",&el); + continue; + } + + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + DOMWarning("expected name for PropertyTemplate element, ignoring",&el); + continue; + } + + const std::string& pname = ParseTokenAsString(*tok[0]); + + const Element* Properties70 = (*sc)["Properties70"]; + if(Properties70) { + std::shared_ptr<const PropertyTable> props = std::make_shared<const PropertyTable>( + *Properties70,std::shared_ptr<const PropertyTable>(static_cast<const PropertyTable*>(NULL)) + ); + + templates[oname+"."+pname] = props; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void Document::ReadConnections() +{ + const Scope& sc = parser.GetRootScope(); + // read property templates from "Definitions" section + const Element* const econns = sc["Connections"]; + if(!econns || !econns->Compound()) { + DOMError("no Connections dictionary found"); + } + + uint64_t insertionOrder = 0l; + const Scope& sconns = *econns->Compound(); + const ElementCollection conns = sconns.GetCollection("C"); + for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) { + const Element& el = *(*it).second; + const std::string& type = ParseTokenAsString(GetRequiredToken(el,0)); + + // PP = property-property connection, ignored for now + // (tokens: "PP", ID1, "Property1", ID2, "Property2") + if ( type == "PP" ) { + continue; + } + + const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1)); + const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2)); + + // OO = object-object connection + // OP = object-property connection, in which case the destination property follows the object ID + const std::string& prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el,3)) : ""); + + if(objects.find(src) == objects.end()) { + DOMWarning("source object for connection does not exist",&el); + continue; + } + + // dest may be 0 (root node) but we added a dummy object before + if(objects.find(dest) == objects.end()) { + DOMWarning("destination object for connection does not exist",&el); + continue; + } + + // add new connection + const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this); + src_connections.insert(ConnectionMap::value_type(src,c)); + dest_connections.insert(ConnectionMap::value_type(dest,c)); + } +} + +// ------------------------------------------------------------------------------------------------ +const std::vector<const AnimationStack*>& Document::AnimationStacks() const +{ + if (!animationStacksResolved.empty() || animationStacks.empty()) { + return animationStacksResolved; + } + + animationStacksResolved.reserve(animationStacks.size()); + for(uint64_t id : animationStacks) { + LazyObject* const lazy = GetObject(id); + const AnimationStack* stack; + if(!lazy || !(stack = lazy->Get<AnimationStack>())) { + DOMWarning("failed to read AnimationStack object"); + continue; + } + animationStacksResolved.push_back(stack); + } + + return animationStacksResolved; +} + +// ------------------------------------------------------------------------------------------------ +LazyObject* Document::GetObject(uint64_t id) const +{ + ObjectMap::const_iterator it = objects.find(id); + return it == objects.end() ? nullptr : (*it).second; +} + +#define MAX_CLASSNAMES 6 + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap& conns) const +{ + std::vector<const Connection*> temp; + + const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range = + conns.equal_range(id); + + temp.reserve(std::distance(range.first,range.second)); + for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { + temp.push_back((*it).second); + } + + std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare)); + + return temp; // NRVO should handle this +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bool is_src, + const ConnectionMap& conns, + const char* const* classnames, + size_t count) const + +{ + ai_assert(classnames); + ai_assert( count != 0 ); + ai_assert( count <= MAX_CLASSNAMES); + + size_t lengths[MAX_CLASSNAMES]; + + const size_t c = count; + for (size_t i = 0; i < c; ++i) { + lengths[ i ] = strlen(classnames[i]); + } + + std::vector<const Connection*> temp; + const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range = + conns.equal_range(id); + + temp.reserve(std::distance(range.first,range.second)); + for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { + const Token& key = (is_src + ? (*it).second->LazyDestinationObject() + : (*it).second->LazySourceObject() + ).GetElement().KeyToken(); + + const char* obtype = key.begin(); + + for (size_t i = 0; i < c; ++i) { + ai_assert(classnames[i]); + if(static_cast<size_t>(std::distance(key.begin(),key.end())) == lengths[i] && !strncmp(classnames[i],obtype,lengths[i])) { + obtype = nullptr; + break; + } + } + + if(obtype) { + continue; + } + + temp.push_back((*it).second); + } + + std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare)); + return temp; // NRVO should handle this +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source) const +{ + return GetConnectionsSequenced(source, ConnectionsBySource()); +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t src, const char* classname) const +{ + const char* arr[] = {classname}; + return GetConnectionsBySourceSequenced(src, arr,1); +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source, + const char* const* classnames, size_t count) const +{ + return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count); +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest, + const char* classname) const +{ + const char* arr[] = {classname}; + return GetConnectionsByDestinationSequenced(dest, arr,1); +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const +{ + return GetConnectionsSequenced(dest, ConnectionsByDestination()); +} + +// ------------------------------------------------------------------------------------------------ +std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest, + const char* const* classnames, size_t count) const + +{ + return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count); +} + +// ------------------------------------------------------------------------------------------------ +Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop, + const Document& doc) + +: insertionOrder(insertionOrder) +, prop(prop) +, src(src) +, dest(dest) +, doc(doc) +{ + ai_assert(doc.Objects().find(src) != doc.Objects().end()); + // dest may be 0 (root node) + ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end()); +} + +// ------------------------------------------------------------------------------------------------ +Connection::~Connection() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +LazyObject& Connection::LazySourceObject() const +{ + LazyObject* const lazy = doc.GetObject(src); + ai_assert(lazy); + return *lazy; +} + +// ------------------------------------------------------------------------------------------------ +LazyObject& Connection::LazyDestinationObject() const +{ + LazyObject* const lazy = doc.GetObject(dest); + ai_assert(lazy); + return *lazy; +} + +// ------------------------------------------------------------------------------------------------ +const Object* Connection::SourceObject() const +{ + LazyObject* const lazy = doc.GetObject(src); + ai_assert(lazy); + return lazy->Get(); +} + +// ------------------------------------------------------------------------------------------------ +const Object* Connection::DestinationObject() const +{ + LazyObject* const lazy = doc.GetObject(dest); + ai_assert(lazy); + return lazy->Get(); +} + +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBXDocument.h b/thirdparty/assimp/code/FBXDocument.h new file mode 100644 index 0000000000..c849defdcd --- /dev/null +++ b/thirdparty/assimp/code/FBXDocument.h @@ -0,0 +1,1180 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDocument.h + * @brief FBX DOM + */ +#ifndef INCLUDED_AI_FBX_DOCUMENT_H +#define INCLUDED_AI_FBX_DOCUMENT_H + +#include <numeric> +#include <stdint.h> +#include <assimp/mesh.h> +#include "FBXProperties.h" +#include "FBXParser.h" + +#define _AI_CONCAT(a,b) a ## b +#define AI_CONCAT(a,b) _AI_CONCAT(a,b) + +namespace Assimp { +namespace FBX { + +class Parser; +class Object; +struct ImportSettings; + +class PropertyTable; +class Document; +class Material; +class ShapeGeometry; +class LineGeometry; +class Geometry; + +class Video; + +class AnimationCurve; +class AnimationCurveNode; +class AnimationLayer; +class AnimationStack; + +class BlendShapeChannel; +class BlendShape; +class Skin; +class Cluster; + + +/** Represents a delay-parsed FBX objects. Many objects in the scene + * are not needed by assimp, so it makes no sense to parse them + * upfront. */ +class LazyObject { +public: + LazyObject(uint64_t id, const Element& element, const Document& doc); + + ~LazyObject(); + + const Object* Get(bool dieOnError = false); + + template <typename T> + const T* Get(bool dieOnError = false) { + const Object* const ob = Get(dieOnError); + return ob ? dynamic_cast<const T*>(ob) : NULL; + } + + uint64_t ID() const { + return id; + } + + bool IsBeingConstructed() const { + return (flags & BEING_CONSTRUCTED) != 0; + } + + bool FailedToConstruct() const { + return (flags & FAILED_TO_CONSTRUCT) != 0; + } + + const Element& GetElement() const { + return element; + } + + const Document& GetDocument() const { + return doc; + } + +private: + const Document& doc; + const Element& element; + std::unique_ptr<const Object> object; + + const uint64_t id; + + enum Flags { + BEING_CONSTRUCTED = 0x1, + FAILED_TO_CONSTRUCT = 0x2 + }; + + unsigned int flags; +}; + +/** Base class for in-memory (DOM) representations of FBX objects */ +class Object { +public: + Object(uint64_t id, const Element& element, const std::string& name); + + virtual ~Object(); + + const Element& SourceElement() const { + return element; + } + + const std::string& Name() const { + return name; + } + + uint64_t ID() const { + return id; + } + +protected: + const Element& element; + const std::string name; + const uint64_t id; +}; + +/** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table, + * fixed members are added by deriving classes. */ +class NodeAttribute : public Object { +public: + NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~NodeAttribute(); + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + +private: + std::shared_ptr<const PropertyTable> props; +}; + +/** DOM base class for FBX camera settings attached to a node */ +class CameraSwitcher : public NodeAttribute { +public: + CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~CameraSwitcher(); + + int CameraID() const { + return cameraId; + } + + const std::string& CameraName() const { + return cameraName; + } + + const std::string& CameraIndexName() const { + return cameraIndexName; + } + +private: + int cameraId; + std::string cameraName; + std::string cameraIndexName; +}; + +#define fbx_stringize(a) #a + +#define fbx_simple_property(name, type, default_value) \ + type name() const { \ + return PropertyGet<type>(Props(), fbx_stringize(name), (default_value)); \ + } + +// XXX improve logging +#define fbx_simple_enum_property(name, type, default_value) \ + type name() const { \ + const int ival = PropertyGet<int>(Props(), fbx_stringize(name), static_cast<int>(default_value)); \ + if (ival < 0 || ival >= AI_CONCAT(type, _MAX)) { \ + ai_assert(static_cast<int>(default_value) >= 0 && static_cast<int>(default_value) < AI_CONCAT(type, _MAX)); \ + return static_cast<type>(default_value); \ + } \ + return static_cast<type>(ival); \ +} + + +/** DOM base class for FBX cameras attached to a node */ +class Camera : public NodeAttribute { +public: + Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Camera(); + + fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0)) + fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0)) + fbx_simple_property(InterestPosition, aiVector3D, aiVector3D(0,0,0)) + + fbx_simple_property(AspectWidth, float, 1.0f) + fbx_simple_property(AspectHeight, float, 1.0f) + fbx_simple_property(FilmWidth, float, 1.0f) + fbx_simple_property(FilmHeight, float, 1.0f) + + fbx_simple_property(NearPlane, float, 0.1f) + fbx_simple_property(FarPlane, float, 100.0f) + + fbx_simple_property(FilmAspectRatio, float, 1.0f) + fbx_simple_property(ApertureMode, int, 0) + + fbx_simple_property(FieldOfView, float, 1.0f) + fbx_simple_property(FocalLength, float, 1.0f) +}; + +/** DOM base class for FBX null markers attached to a node */ +class Null : public NodeAttribute { +public: + Null(uint64_t id, const Element& element, const Document& doc, const std::string& name); + virtual ~Null(); +}; + +/** DOM base class for FBX limb node markers attached to a node */ +class LimbNode : public NodeAttribute { +public: + LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name); + virtual ~LimbNode(); +}; + +/** DOM base class for FBX lights attached to a node */ +class Light : public NodeAttribute { +public: + Light(uint64_t id, const Element& element, const Document& doc, const std::string& name); + virtual ~Light(); + + enum Type + { + Type_Point, + Type_Directional, + Type_Spot, + Type_Area, + Type_Volume, + + Type_MAX // end-of-enum sentinel + }; + + enum Decay + { + Decay_None, + Decay_Linear, + Decay_Quadratic, + Decay_Cubic, + + Decay_MAX // end-of-enum sentinel + }; + + fbx_simple_property(Color, aiVector3D, aiVector3D(1,1,1)) + fbx_simple_enum_property(LightType, Type, 0) + fbx_simple_property(CastLightOnObject, bool, false) + fbx_simple_property(DrawVolumetricLight, bool, true) + fbx_simple_property(DrawGroundProjection, bool, true) + fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false) + fbx_simple_property(Intensity, float, 100.0f) + fbx_simple_property(InnerAngle, float, 0.0f) + fbx_simple_property(OuterAngle, float, 45.0f) + fbx_simple_property(Fog, int, 50) + fbx_simple_enum_property(DecayType, Decay, 2) + fbx_simple_property(DecayStart, float, 1.0f) + fbx_simple_property(FileName, std::string, "") + + fbx_simple_property(EnableNearAttenuation, bool, false) + fbx_simple_property(NearAttenuationStart, float, 0.0f) + fbx_simple_property(NearAttenuationEnd, float, 0.0f) + fbx_simple_property(EnableFarAttenuation, bool, false) + fbx_simple_property(FarAttenuationStart, float, 0.0f) + fbx_simple_property(FarAttenuationEnd, float, 0.0f) + + fbx_simple_property(CastShadows, bool, true) + fbx_simple_property(ShadowColor, aiVector3D, aiVector3D(0,0,0)) + + fbx_simple_property(AreaLightShape, int, 0) + + fbx_simple_property(LeftBarnDoor, float, 20.0f) + fbx_simple_property(RightBarnDoor, float, 20.0f) + fbx_simple_property(TopBarnDoor, float, 20.0f) + fbx_simple_property(BottomBarnDoor, float, 20.0f) + fbx_simple_property(EnableBarnDoor, bool, true) +}; + +/** DOM base class for FBX models (even though its semantics are more "node" than "model" */ +class Model : public Object { +public: + enum RotOrder { + RotOrder_EulerXYZ = 0, + RotOrder_EulerXZY, + RotOrder_EulerYZX, + RotOrder_EulerYXZ, + RotOrder_EulerZXY, + RotOrder_EulerZYX, + + RotOrder_SphericXYZ, + + RotOrder_MAX // end-of-enum sentinel + }; + + enum TransformInheritance { + TransformInheritance_RrSs = 0, + TransformInheritance_RSrs, + TransformInheritance_Rrs, + + TransformInheritance_MAX // end-of-enum sentinel + }; + + Model(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Model(); + + fbx_simple_property(QuaternionInterpolate, int, 0) + + fbx_simple_property(RotationOffset, aiVector3D, aiVector3D()) + fbx_simple_property(RotationPivot, aiVector3D, aiVector3D()) + fbx_simple_property(ScalingOffset, aiVector3D, aiVector3D()) + fbx_simple_property(ScalingPivot, aiVector3D, aiVector3D()) + fbx_simple_property(TranslationActive, bool, false) + + fbx_simple_property(TranslationMin, aiVector3D, aiVector3D()) + fbx_simple_property(TranslationMax, aiVector3D, aiVector3D()) + + fbx_simple_property(TranslationMinX, bool, false) + fbx_simple_property(TranslationMaxX, bool, false) + fbx_simple_property(TranslationMinY, bool, false) + fbx_simple_property(TranslationMaxY, bool, false) + fbx_simple_property(TranslationMinZ, bool, false) + fbx_simple_property(TranslationMaxZ, bool, false) + + fbx_simple_enum_property(RotationOrder, RotOrder, 0) + fbx_simple_property(RotationSpaceForLimitOnly, bool, false) + fbx_simple_property(RotationStiffnessX, float, 0.0f) + fbx_simple_property(RotationStiffnessY, float, 0.0f) + fbx_simple_property(RotationStiffnessZ, float, 0.0f) + fbx_simple_property(AxisLen, float, 0.0f) + + fbx_simple_property(PreRotation, aiVector3D, aiVector3D()) + fbx_simple_property(PostRotation, aiVector3D, aiVector3D()) + fbx_simple_property(RotationActive, bool, false) + + fbx_simple_property(RotationMin, aiVector3D, aiVector3D()) + fbx_simple_property(RotationMax, aiVector3D, aiVector3D()) + + fbx_simple_property(RotationMinX, bool, false) + fbx_simple_property(RotationMaxX, bool, false) + fbx_simple_property(RotationMinY, bool, false) + fbx_simple_property(RotationMaxY, bool, false) + fbx_simple_property(RotationMinZ, bool, false) + fbx_simple_property(RotationMaxZ, bool, false) + fbx_simple_enum_property(InheritType, TransformInheritance, 0) + + fbx_simple_property(ScalingActive, bool, false) + fbx_simple_property(ScalingMin, aiVector3D, aiVector3D()) + fbx_simple_property(ScalingMax, aiVector3D, aiVector3D(1.f,1.f,1.f)) + fbx_simple_property(ScalingMinX, bool, false) + fbx_simple_property(ScalingMaxX, bool, false) + fbx_simple_property(ScalingMinY, bool, false) + fbx_simple_property(ScalingMaxY, bool, false) + fbx_simple_property(ScalingMinZ, bool, false) + fbx_simple_property(ScalingMaxZ, bool, false) + + fbx_simple_property(GeometricTranslation, aiVector3D, aiVector3D()) + fbx_simple_property(GeometricRotation, aiVector3D, aiVector3D()) + fbx_simple_property(GeometricScaling, aiVector3D, aiVector3D(1.f, 1.f, 1.f)) + + fbx_simple_property(MinDampRangeX, float, 0.0f) + fbx_simple_property(MinDampRangeY, float, 0.0f) + fbx_simple_property(MinDampRangeZ, float, 0.0f) + fbx_simple_property(MaxDampRangeX, float, 0.0f) + fbx_simple_property(MaxDampRangeY, float, 0.0f) + fbx_simple_property(MaxDampRangeZ, float, 0.0f) + + fbx_simple_property(MinDampStrengthX, float, 0.0f) + fbx_simple_property(MinDampStrengthY, float, 0.0f) + fbx_simple_property(MinDampStrengthZ, float, 0.0f) + fbx_simple_property(MaxDampStrengthX, float, 0.0f) + fbx_simple_property(MaxDampStrengthY, float, 0.0f) + fbx_simple_property(MaxDampStrengthZ, float, 0.0f) + + fbx_simple_property(PreferredAngleX, float, 0.0f) + fbx_simple_property(PreferredAngleY, float, 0.0f) + fbx_simple_property(PreferredAngleZ, float, 0.0f) + + fbx_simple_property(Show, bool, true) + fbx_simple_property(LODBox, bool, false) + fbx_simple_property(Freeze, bool, false) + + const std::string& Shading() const { + return shading; + } + + const std::string& Culling() const { + return culling; + } + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + /** Get material links */ + const std::vector<const Material*>& GetMaterials() const { + return materials; + } + + /** Get geometry links */ + const std::vector<const Geometry*>& GetGeometry() const { + return geometry; + } + + /** Get node attachments */ + const std::vector<const NodeAttribute*>& GetAttributes() const { + return attributes; + } + + /** convenience method to check if the node has a Null node marker */ + bool IsNull() const; + +private: + void ResolveLinks(const Element& element, const Document& doc); + +private: + std::vector<const Material*> materials; + std::vector<const Geometry*> geometry; + std::vector<const NodeAttribute*> attributes; + + std::string shading; + std::string culling; + std::shared_ptr<const PropertyTable> props; +}; + +/** DOM class for generic FBX textures */ +class Texture : public Object { +public: + Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Texture(); + + const std::string& Type() const { + return type; + } + + const std::string& FileName() const { + return fileName; + } + + const std::string& RelativeFilename() const { + return relativeFileName; + } + + const std::string& AlphaSource() const { + return alphaSource; + } + + const aiVector2D& UVTranslation() const { + return uvTrans; + } + + const aiVector2D& UVScaling() const { + return uvScaling; + } + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + // return a 4-tuple + const unsigned int* Crop() const { + return crop; + } + + const Video* Media() const { + return media; + } + +private: + aiVector2D uvTrans; + aiVector2D uvScaling; + + std::string type; + std::string relativeFileName; + std::string fileName; + std::string alphaSource; + std::shared_ptr<const PropertyTable> props; + + unsigned int crop[4]; + + const Video* media; +}; + +/** DOM class for layered FBX textures */ +class LayeredTexture : public Object { +public: + LayeredTexture(uint64_t id, const Element& element, const Document& doc, const std::string& name); + virtual ~LayeredTexture(); + + // Can only be called after construction of the layered texture object due to construction flag. + void fillTexture(const Document& doc); + + enum BlendMode { + BlendMode_Translucent, + BlendMode_Additive, + BlendMode_Modulate, + BlendMode_Modulate2, + BlendMode_Over, + BlendMode_Normal, + BlendMode_Dissolve, + BlendMode_Darken, + BlendMode_ColorBurn, + BlendMode_LinearBurn, + BlendMode_DarkerColor, + BlendMode_Lighten, + BlendMode_Screen, + BlendMode_ColorDodge, + BlendMode_LinearDodge, + BlendMode_LighterColor, + BlendMode_SoftLight, + BlendMode_HardLight, + BlendMode_VividLight, + BlendMode_LinearLight, + BlendMode_PinLight, + BlendMode_HardMix, + BlendMode_Difference, + BlendMode_Exclusion, + BlendMode_Subtract, + BlendMode_Divide, + BlendMode_Hue, + BlendMode_Saturation, + BlendMode_Color, + BlendMode_Luminosity, + BlendMode_Overlay, + BlendMode_BlendModeCount + }; + + const Texture* getTexture(int index=0) const + { + return textures[index]; + + } + int textureCount() const { + return static_cast<int>(textures.size()); + } + BlendMode GetBlendMode() const + { + return blendMode; + } + float Alpha() + { + return alpha; + } +private: + std::vector<const Texture*> textures; + BlendMode blendMode; + float alpha; +}; + +typedef std::fbx_unordered_map<std::string, const Texture*> TextureMap; +typedef std::fbx_unordered_map<std::string, const LayeredTexture*> LayeredTextureMap; + + +/** DOM class for generic FBX videos */ +class Video : public Object { +public: + Video(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Video(); + + const std::string& Type() const { + return type; + } + + const std::string& FileName() const { + return fileName; + } + + const std::string& RelativeFilename() const { + return relativeFileName; + } + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + const uint8_t* Content() const { + ai_assert(content); + return content; + } + + uint32_t ContentLength() const { + return contentLength; + } + + uint8_t* RelinquishContent() { + uint8_t* ptr = content; + content = 0; + return ptr; + } + +private: + std::string type; + std::string relativeFileName; + std::string fileName; + std::shared_ptr<const PropertyTable> props; + + uint32_t contentLength; + uint8_t* content; +}; + +/** DOM class for generic FBX materials */ +class Material : public Object { +public: + Material(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Material(); + + const std::string& GetShadingModel() const { + return shading; + } + + bool IsMultilayer() const { + return multilayer; + } + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + const TextureMap& Textures() const { + return textures; + } + + const LayeredTextureMap& LayeredTextures() const { + return layeredTextures; + } + +private: + std::string shading; + bool multilayer; + std::shared_ptr<const PropertyTable> props; + + TextureMap textures; + LayeredTextureMap layeredTextures; +}; + +typedef std::vector<int64_t> KeyTimeList; +typedef std::vector<float> KeyValueList; + +/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */ +class AnimationCurve : public Object { +public: + AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc); + virtual ~AnimationCurve(); + + /** get list of keyframe positions (time). + * Invariant: |GetKeys()| > 0 */ + const KeyTimeList& GetKeys() const { + return keys; + } + + /** get list of keyframe values. + * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/ + const KeyValueList& GetValues() const { + return values; + } + + const std::vector<float>& GetAttributes() const { + return attributes; + } + + const std::vector<unsigned int>& GetFlags() const { + return flags; + } + +private: + KeyTimeList keys; + KeyValueList values; + std::vector<float> attributes; + std::vector<unsigned int> flags; +}; + +// property-name -> animation curve +typedef std::map<std::string, const AnimationCurve*> AnimationCurveMap; + +/** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */ +class AnimationCurveNode : public Object { +public: + /* the optional white list specifies a list of property names for which the caller + wants animations for. If the curve node does not match one of these, std::range_error + will be thrown. */ + AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, const Document& doc, + const char* const * target_prop_whitelist = NULL, size_t whitelist_size = 0); + + virtual ~AnimationCurveNode(); + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + + const AnimationCurveMap& Curves() const; + + /** Object the curve is assigned to, this can be NULL if the + * target object has no DOM representation or could not + * be read for other reasons.*/ + const Object* Target() const { + return target; + } + + const Model* TargetAsModel() const { + return dynamic_cast<const Model*>(target); + } + + const NodeAttribute* TargetAsNodeAttribute() const { + return dynamic_cast<const NodeAttribute*>(target); + } + + /** Property of Target() that is being animated*/ + const std::string& TargetProperty() const { + return prop; + } + +private: + const Object* target; + std::shared_ptr<const PropertyTable> props; + mutable AnimationCurveMap curves; + + std::string prop; + const Document& doc; +}; + +typedef std::vector<const AnimationCurveNode*> AnimationCurveNodeList; + +/** Represents a FBX animation layer (i.e. a list of node animations) */ +class AnimationLayer : public Object { +public: + AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc); + virtual ~AnimationLayer(); + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + /* the optional white list specifies a list of property names for which the caller + wants animations for. Curves not matching this list will not be added to the + animation layer. */ + AnimationCurveNodeList Nodes(const char* const * target_prop_whitelist = nullptr, size_t whitelist_size = 0) const; + +private: + std::shared_ptr<const PropertyTable> props; + const Document& doc; +}; + +typedef std::vector<const AnimationLayer*> AnimationLayerList; + +/** Represents a FBX animation stack (i.e. a list of animation layers) */ +class AnimationStack : public Object { +public: + AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc); + virtual ~AnimationStack(); + + fbx_simple_property(LocalStart, int64_t, 0L) + fbx_simple_property(LocalStop, int64_t, 0L) + fbx_simple_property(ReferenceStart, int64_t, 0L) + fbx_simple_property(ReferenceStop, int64_t, 0L) + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + const AnimationLayerList& Layers() const { + return layers; + } + +private: + std::shared_ptr<const PropertyTable> props; + AnimationLayerList layers; +}; + + +/** DOM class for deformers */ +class Deformer : public Object { +public: + Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name); + virtual ~Deformer(); + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + +private: + std::shared_ptr<const PropertyTable> props; +}; + +typedef std::vector<float> WeightArray; +typedef std::vector<unsigned int> WeightIndexArray; + + +/** DOM class for BlendShapeChannel deformers */ +class BlendShapeChannel : public Deformer { +public: + BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~BlendShapeChannel(); + + float DeformPercent() const { + return percent; + } + + const WeightArray& GetFullWeights() const { + return fullWeights; + } + + const std::vector<const ShapeGeometry*>& GetShapeGeometries() const { + return shapeGeometries; + } + +private: + float percent; + WeightArray fullWeights; + std::vector<const ShapeGeometry*> shapeGeometries; +}; + +/** DOM class for BlendShape deformers */ +class BlendShape : public Deformer { +public: + BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~BlendShape(); + + const std::vector<const BlendShapeChannel*>& BlendShapeChannels() const { + return blendShapeChannels; + } + +private: + std::vector<const BlendShapeChannel*> blendShapeChannels; +}; + +/** DOM class for skin deformer clusters (aka sub-deformers) */ +class Cluster : public Deformer { +public: + Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Cluster(); + + /** get the list of deformer weights associated with this cluster. + * Use #GetIndices() to get the associated vertices. Both arrays + * have the same size (and may also be empty). */ + const WeightArray& GetWeights() const { + return weights; + } + + /** get indices into the vertex data of the geometry associated + * with this cluster. Use #GetWeights() to get the associated weights. + * Both arrays have the same size (and may also be empty). */ + const WeightIndexArray& GetIndices() const { + return indices; + } + + /** */ + const aiMatrix4x4& Transform() const { + return transform; + } + + const aiMatrix4x4& TransformLink() const { + return transformLink; + } + + const Model* TargetNode() const { + return node; + } + +private: + WeightArray weights; + WeightIndexArray indices; + + aiMatrix4x4 transform; + aiMatrix4x4 transformLink; + + const Model* node; +}; + +/** DOM class for skin deformers */ +class Skin : public Deformer { +public: + Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name); + + virtual ~Skin(); + + float DeformAccuracy() const { + return accuracy; + } + + const std::vector<const Cluster*>& Clusters() const { + return clusters; + } + +private: + float accuracy; + std::vector<const Cluster*> clusters; +}; + +/** Represents a link between two FBX objects. */ +class Connection { +public: + Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop, const Document& doc); + + ~Connection(); + + // note: a connection ensures that the source and dest objects exist, but + // not that they have DOM representations, so the return value of one of + // these functions can still be NULL. + const Object* SourceObject() const; + const Object* DestinationObject() const; + + // these, however, are always guaranteed to be valid + LazyObject& LazySourceObject() const; + LazyObject& LazyDestinationObject() const; + + + /** return the name of the property the connection is attached to. + * this is an empty string for object to object (OO) connections. */ + const std::string& PropertyName() const { + return prop; + } + + uint64_t InsertionOrder() const { + return insertionOrder; + } + + int CompareTo(const Connection* c) const { + ai_assert( nullptr != c ); + + // note: can't subtract because this would overflow uint64_t + if(InsertionOrder() > c->InsertionOrder()) { + return 1; + } + else if(InsertionOrder() < c->InsertionOrder()) { + return -1; + } + return 0; + } + + bool Compare(const Connection* c) const { + ai_assert( nullptr != c ); + + return InsertionOrder() < c->InsertionOrder(); + } + +public: + uint64_t insertionOrder; + const std::string prop; + + uint64_t src, dest; + const Document& doc; +}; + +// XXX again, unique_ptr would be useful. shared_ptr is too +// bloated since the objects have a well-defined single owner +// during their entire lifetime (Document). FBX files have +// up to many thousands of objects (most of which we never use), +// so the memory overhead for them should be kept at a minimum. +typedef std::map<uint64_t, LazyObject*> ObjectMap; +typedef std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > PropertyTemplateMap; + +typedef std::multimap<uint64_t, const Connection*> ConnectionMap; + +/** DOM class for global document settings, a single instance per document can + * be accessed via Document.Globals(). */ +class FileGlobalSettings { +public: + FileGlobalSettings(const Document& doc, std::shared_ptr<const PropertyTable> props); + + ~FileGlobalSettings(); + + const PropertyTable& Props() const { + ai_assert(props.get()); + return *props.get(); + } + + const Document& GetDocument() const { + return doc; + } + + fbx_simple_property(UpAxis, int, 1) + fbx_simple_property(UpAxisSign, int, 1) + fbx_simple_property(FrontAxis, int, 2) + fbx_simple_property(FrontAxisSign, int, 1) + fbx_simple_property(CoordAxis, int, 0) + fbx_simple_property(CoordAxisSign, int, 1) + fbx_simple_property(OriginalUpAxis, int, 0) + fbx_simple_property(OriginalUpAxisSign, int, 1) + fbx_simple_property(UnitScaleFactor, float, 1) + fbx_simple_property(OriginalUnitScaleFactor, float, 1) + fbx_simple_property(AmbientColor, aiVector3D, aiVector3D(0,0,0)) + fbx_simple_property(DefaultCamera, std::string, "") + + + enum FrameRate { + FrameRate_DEFAULT = 0, + FrameRate_120 = 1, + FrameRate_100 = 2, + FrameRate_60 = 3, + FrameRate_50 = 4, + FrameRate_48 = 5, + FrameRate_30 = 6, + FrameRate_30_DROP = 7, + FrameRate_NTSC_DROP_FRAME = 8, + FrameRate_NTSC_FULL_FRAME = 9, + FrameRate_PAL = 10, + FrameRate_CINEMA = 11, + FrameRate_1000 = 12, + FrameRate_CINEMA_ND = 13, + FrameRate_CUSTOM = 14, + + FrameRate_MAX// end-of-enum sentinel + }; + + fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT) + fbx_simple_property(TimeSpanStart, uint64_t, 0L) + fbx_simple_property(TimeSpanStop, uint64_t, 0L) + fbx_simple_property(CustomFrameRate, float, -1.0f) + +private: + std::shared_ptr<const PropertyTable> props; + const Document& doc; +}; + +/** DOM root for a FBX file */ +class Document { +public: + Document(const Parser& parser, const ImportSettings& settings); + + ~Document(); + + LazyObject* GetObject(uint64_t id) const; + + bool IsBinary() const { + return parser.IsBinary(); + } + + unsigned int FBXVersion() const { + return fbxVersion; + } + + const std::string& Creator() const { + return creator; + } + + // elements (in this order): Year, Month, Day, Hour, Second, Millisecond + const unsigned int* CreationTimeStamp() const { + return creationTimeStamp; + } + + const FileGlobalSettings& GlobalSettings() const { + ai_assert(globals.get()); + return *globals.get(); + } + + const PropertyTemplateMap& Templates() const { + return templates; + } + + const ObjectMap& Objects() const { + return objects; + } + + const ImportSettings& Settings() const { + return settings; + } + + const ConnectionMap& ConnectionsBySource() const { + return src_connections; + } + + const ConnectionMap& ConnectionsByDestination() const { + return dest_connections; + } + + // note: the implicit rule in all DOM classes is to always resolve + // from destination to source (since the FBX object hierarchy is, + // with very few exceptions, a DAG, this avoids cycles). In all + // cases that may involve back-facing edges in the object graph, + // use LazyObject::IsBeingConstructed() to check. + + std::vector<const Connection*> GetConnectionsBySourceSequenced(uint64_t source) const; + std::vector<const Connection*> GetConnectionsByDestinationSequenced(uint64_t dest) const; + + std::vector<const Connection*> GetConnectionsBySourceSequenced(uint64_t source, const char* classname) const; + std::vector<const Connection*> GetConnectionsByDestinationSequenced(uint64_t dest, const char* classname) const; + + std::vector<const Connection*> GetConnectionsBySourceSequenced(uint64_t source, + const char* const* classnames, size_t count) const; + std::vector<const Connection*> GetConnectionsByDestinationSequenced(uint64_t dest, + const char* const* classnames, + size_t count) const; + + const std::vector<const AnimationStack*>& AnimationStacks() const; + +private: + std::vector<const Connection*> GetConnectionsSequenced(uint64_t id, const ConnectionMap&) const; + std::vector<const Connection*> GetConnectionsSequenced(uint64_t id, bool is_src, + const ConnectionMap&, + const char* const* classnames, + size_t count) const; + void ReadHeader(); + void ReadObjects(); + void ReadPropertyTemplates(); + void ReadConnections(); + void ReadGlobalSettings(); + +private: + const ImportSettings& settings; + + ObjectMap objects; + const Parser& parser; + + PropertyTemplateMap templates; + ConnectionMap src_connections; + ConnectionMap dest_connections; + + unsigned int fbxVersion; + std::string creator; + unsigned int creationTimeStamp[7]; + + std::vector<uint64_t> animationStacks; + mutable std::vector<const AnimationStack*> animationStacksResolved; + + std::unique_ptr<FileGlobalSettings> globals; +}; + +} // Namespace FBX +} // Namespace Assimp + +#endif // INCLUDED_AI_FBX_DOCUMENT_H diff --git a/thirdparty/assimp/code/FBXDocumentUtil.cpp b/thirdparty/assimp/code/FBXDocumentUtil.cpp new file mode 100644 index 0000000000..f84691479a --- /dev/null +++ b/thirdparty/assimp/code/FBXDocumentUtil.cpp @@ -0,0 +1,135 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDocumentUtil.cpp + * @brief Implementation of the FBX DOM utility functions declared in FBXDocumentUtil.h + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXUtil.h" +#include "FBXDocumentUtil.h" +#include "FBXProperties.h" + + +namespace Assimp { +namespace FBX { +namespace Util { + +// ------------------------------------------------------------------------------------------------ +// signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError. +void DOMError(const std::string& message, const Token& token) +{ + throw DeadlyImportError(Util::AddTokenText("FBX-DOM",message,&token)); +} + +// ------------------------------------------------------------------------------------------------ +void DOMError(const std::string& message, const Element* element /*= NULL*/) +{ + if(element) { + DOMError(message,element->KeyToken()); + } + throw DeadlyImportError("FBX-DOM " + message); +} + + +// ------------------------------------------------------------------------------------------------ +// print warning, do return +void DOMWarning(const std::string& message, const Token& token) +{ + if(DefaultLogger::get()) { + ASSIMP_LOG_WARN(Util::AddTokenText("FBX-DOM",message,&token)); + } +} + +// ------------------------------------------------------------------------------------------------ +void DOMWarning(const std::string& message, const Element* element /*= NULL*/) +{ + if(element) { + DOMWarning(message,element->KeyToken()); + return; + } + if(DefaultLogger::get()) { + ASSIMP_LOG_WARN("FBX-DOM: " + message); + } +} + + +// ------------------------------------------------------------------------------------------------ +// fetch a property table and the corresponding property template +std::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc, + const std::string& templateName, + const Element &element, + const Scope& sc, + bool no_warn /*= false*/) +{ + const Element* const Properties70 = sc["Properties70"]; + std::shared_ptr<const PropertyTable> templateProps = std::shared_ptr<const PropertyTable>( + static_cast<const PropertyTable*>(NULL)); + + if(templateName.length()) { + PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName); + if(it != doc.Templates().end()) { + templateProps = (*it).second; + } + } + + if(!Properties70 || !Properties70->Compound()) { + if(!no_warn) { + DOMWarning("property table (Properties70) not found",&element); + } + if(templateProps) { + return templateProps; + } + else { + return std::make_shared<const PropertyTable>(); + } + } + return std::make_shared<const PropertyTable>(*Properties70,templateProps); +} +} // !Util +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBXDocumentUtil.h b/thirdparty/assimp/code/FBXDocumentUtil.h new file mode 100644 index 0000000000..2450109e59 --- /dev/null +++ b/thirdparty/assimp/code/FBXDocumentUtil.h @@ -0,0 +1,120 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXDocumentUtil.h + * @brief FBX internal utilities used by the DOM reading code + */ +#ifndef INCLUDED_AI_FBX_DOCUMENT_UTIL_H +#define INCLUDED_AI_FBX_DOCUMENT_UTIL_H + +#include <assimp/defs.h> +#include <string> +#include <memory> +#include "FBXDocument.h" + +struct Token; +struct Element; + +namespace Assimp { +namespace FBX { +namespace Util { + +/* DOM/Parse error reporting - does not return */ +AI_WONT_RETURN void DOMError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX; +AI_WONT_RETURN void DOMError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX; + +// does return +void DOMWarning(const std::string& message, const Token& token); +void DOMWarning(const std::string& message, const Element* element = NULL); + + +// fetch a property table and the corresponding property template +std::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc, + const std::string& templateName, + const Element &element, + const Scope& sc, + bool no_warn = false); + +// ------------------------------------------------------------------------------------------------ +template <typename T> +inline +const T* ProcessSimpleConnection(const Connection& con, + bool is_object_property_conn, + const char* name, + const Element& element, + const char** propNameOut = nullptr) +{ + if (is_object_property_conn && !con.PropertyName().length()) { + DOMWarning("expected incoming " + std::string(name) + + " link to be an object-object connection, ignoring", + &element + ); + return nullptr; + } + else if (!is_object_property_conn && con.PropertyName().length()) { + DOMWarning("expected incoming " + std::string(name) + + " link to be an object-property connection, ignoring", + &element + ); + return nullptr; + } + + if(is_object_property_conn && propNameOut) { + // note: this is ok, the return value of PropertyValue() is guaranteed to + // remain valid and unchanged as long as the document exists. + *propNameOut = con.PropertyName().c_str(); + } + + const Object* const ob = con.SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for incoming " + std::string(name) + + " link, ignoring", + &element); + return nullptr; + } + + return dynamic_cast<const T*>(ob); +} + +} //!Util +} //!FBX +} //!Assimp + +#endif diff --git a/thirdparty/assimp/code/FBXExportNode.cpp b/thirdparty/assimp/code/FBXExportNode.cpp new file mode 100644 index 0000000000..e5215466a1 --- /dev/null +++ b/thirdparty/assimp/code/FBXExportNode.cpp @@ -0,0 +1,568 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportNode.h" +#include "FBXCommon.h" + +#include <assimp/StreamWriter.h> // StreamWriterLE +#include <assimp/Exceptional.h> // DeadlyExportError +#include <assimp/ai_assert.h> +#include <assimp/StringUtils.h> // ai_snprintf + +#include <string> +#include <ostream> +#include <sstream> // ostringstream +#include <memory> // shared_ptr + +// AddP70<type> helpers... there's no usable pattern here, +// so all are defined as separate functions. +// Even "animatable" properties are often completely different +// from the standard (nonanimated) property definition, +// so they are specified with an 'A' suffix. + +void FBX::Node::AddP70int( + const std::string& name, int32_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "int", "Integer", "", value); + AddChild(n); +} + +void FBX::Node::AddP70bool( + const std::string& name, bool value +) { + FBX::Node n("P"); + n.AddProperties(name, "bool", "", "", int32_t(value)); + AddChild(n); +} + +void FBX::Node::AddP70double( + const std::string& name, double value +) { + FBX::Node n("P"); + n.AddProperties(name, "double", "Number", "", value); + AddChild(n); +} + +void FBX::Node::AddP70numberA( + const std::string& name, double value +) { + FBX::Node n("P"); + n.AddProperties(name, "Number", "", "A", value); + AddChild(n); +} + +void FBX::Node::AddP70color( + const std::string& name, double r, double g, double b +) { + FBX::Node n("P"); + n.AddProperties(name, "ColorRGB", "Color", "", r, g, b); + AddChild(n); +} + +void FBX::Node::AddP70colorA( + const std::string& name, double r, double g, double b +) { + FBX::Node n("P"); + n.AddProperties(name, "Color", "", "A", r, g, b); + AddChild(n); +} + +void FBX::Node::AddP70vector( + const std::string& name, double x, double y, double z +) { + FBX::Node n("P"); + n.AddProperties(name, "Vector3D", "Vector", "", x, y, z); + AddChild(n); +} + +void FBX::Node::AddP70vectorA( + const std::string& name, double x, double y, double z +) { + FBX::Node n("P"); + n.AddProperties(name, "Vector", "", "A", x, y, z); + AddChild(n); +} + +void FBX::Node::AddP70string( + const std::string& name, const std::string& value +) { + FBX::Node n("P"); + n.AddProperties(name, "KString", "", "", value); + AddChild(n); +} + +void FBX::Node::AddP70enum( + const std::string& name, int32_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "enum", "", "", value); + AddChild(n); +} + +void FBX::Node::AddP70time( + const std::string& name, int64_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "KTime", "Time", "", value); + AddChild(n); +} + + +// public member functions for writing nodes to stream + +void FBX::Node::Dump( + std::shared_ptr<Assimp::IOStream> outfile, + bool binary, int indent +) { + if (binary) { + Assimp::StreamWriterLE outstream(outfile); + DumpBinary(outstream); + } else { + std::ostringstream ss; + DumpAscii(ss, indent); + std::string s = ss.str(); + outfile->Write(s.c_str(), s.size(), 1); + } +} + +void FBX::Node::Dump( + Assimp::StreamWriterLE &outstream, + bool binary, int indent +) { + if (binary) { + DumpBinary(outstream); + } else { + std::ostringstream ss; + DumpAscii(ss, indent); + outstream.PutString(ss.str()); + } +} + + +// public member functions for low-level writing + +void FBX::Node::Begin( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + if (binary) { + BeginBinary(s); + } else { + // assume we're at the correct place to start already + (void)indent; + std::ostringstream ss; + BeginAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::DumpProperties( + Assimp::StreamWriterLE& s, + bool binary, int indent +) { + if (binary) { + DumpPropertiesBinary(s); + } else { + std::ostringstream ss; + DumpPropertiesAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::EndProperties( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + EndProperties(s, binary, indent, properties.size()); +} + +void FBX::Node::EndProperties( + Assimp::StreamWriterLE &s, + bool binary, int indent, + size_t num_properties +) { + if (binary) { + EndPropertiesBinary(s, num_properties); + } else { + // nothing to do + (void)indent; + } +} + +void FBX::Node::BeginChildren( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + if (binary) { + // nothing to do + } else { + std::ostringstream ss; + BeginChildrenAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::DumpChildren( + Assimp::StreamWriterLE& s, + bool binary, int indent +) { + if (binary) { + DumpChildrenBinary(s); + } else { + std::ostringstream ss; + DumpChildrenAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::End( + Assimp::StreamWriterLE &s, + bool binary, int indent, + bool has_children +) { + if (binary) { + EndBinary(s, has_children); + } else { + std::ostringstream ss; + EndAscii(ss, indent, has_children); + s.PutString(ss.str()); + } +} + + +// public member functions for writing to binary fbx + +void FBX::Node::DumpBinary(Assimp::StreamWriterLE &s) +{ + // write header section (with placeholders for some things) + BeginBinary(s); + + // write properties + DumpPropertiesBinary(s); + + // go back and fill in property related placeholders + EndPropertiesBinary(s, properties.size()); + + // write children + DumpChildrenBinary(s); + + // finish, filling in end offset placeholder + EndBinary(s, force_has_children || !children.empty()); +} + + +// public member functions for writing to ascii fbx + +void FBX::Node::DumpAscii(std::ostream &s, int indent) +{ + // write name + BeginAscii(s, indent); + + // write properties + DumpPropertiesAscii(s, indent); + + if (force_has_children || !children.empty()) { + // begin children (with a '{') + BeginChildrenAscii(s, indent + 1); + // write children + DumpChildrenAscii(s, indent + 1); + } + + // finish (also closing the children bracket '}') + EndAscii(s, indent, force_has_children || !children.empty()); +} + + +// private member functions for low-level writing to fbx + +void FBX::Node::BeginBinary(Assimp::StreamWriterLE &s) +{ + // remember start pos so we can come back and write the end pos + this->start_pos = s.Tell(); + + // placeholders for end pos and property section info + s.PutU4(0); // end pos + s.PutU4(0); // number of properties + s.PutU4(0); // total property section length + + // node name + s.PutU1(uint8_t(name.size())); // length of node name + s.PutString(name); // node name as raw bytes + + // property data comes after here + this->property_start = s.Tell(); +} + +void FBX::Node::DumpPropertiesBinary(Assimp::StreamWriterLE& s) +{ + for (auto &p : properties) { + p.DumpBinary(s); + } +} + +void FBX::Node::EndPropertiesBinary( + Assimp::StreamWriterLE &s, + size_t num_properties +) { + if (num_properties == 0) { return; } + size_t pos = s.Tell(); + ai_assert(pos > property_start); + size_t property_section_size = pos - property_start; + s.Seek(start_pos + 4); + s.PutU4(uint32_t(num_properties)); + s.PutU4(uint32_t(property_section_size)); + s.Seek(pos); +} + +void FBX::Node::DumpChildrenBinary(Assimp::StreamWriterLE& s) +{ + for (FBX::Node& child : children) { + child.DumpBinary(s); + } +} + +void FBX::Node::EndBinary( + Assimp::StreamWriterLE &s, + bool has_children +) { + // if there were children, add a null record + if (has_children) { s.PutString(FBX::NULL_RECORD); } + + // now go back and write initial pos + this->end_pos = s.Tell(); + s.Seek(start_pos); + s.PutU4(uint32_t(end_pos)); + s.Seek(end_pos); +} + + +void FBX::Node::BeginAscii(std::ostream& s, int indent) +{ + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << name << ": "; +} + +void FBX::Node::DumpPropertiesAscii(std::ostream &s, int indent) +{ + for (size_t i = 0; i < properties.size(); ++i) { + if (i > 0) { s << ", "; } + properties[i].DumpAscii(s, indent); + } +} + +void FBX::Node::BeginChildrenAscii(std::ostream& s, int indent) +{ + // only call this if there are actually children + s << " {"; + (void)indent; +} + +void FBX::Node::DumpChildrenAscii(std::ostream& s, int indent) +{ + // children will need a lot of padding and corralling + if (children.size() || force_has_children) { + for (size_t i = 0; i < children.size(); ++i) { + // no compression in ascii files, so skip this node if it exists + if (children[i].name == "EncryptionType") { continue; } + // the child can dump itself + children[i].DumpAscii(s, indent); + } + } +} + +void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children) +{ + if (!has_children) { return; } // nothing to do + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "}"; +} + +// private helpers for static member functions + +// ascii property node from vector of doubles +void FBX::Node::WritePropertyNodeAscii( + const std::string& name, + const std::vector<double>& v, + Assimp::StreamWriterLE& s, + int indent +){ + char buffer[32]; + FBX::Node node(name); + node.Begin(s, false, indent); + std::string vsize = to_string(v.size()); + // *<size> { + s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); + // indent + 1 + for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); } + // a: value,value,value,... + s.PutString("a: "); + int count = 0; + for (size_t i = 0; i < v.size(); ++i) { + if (i > 0) { s.PutChar(','); } + int len = ai_snprintf(buffer, sizeof(buffer), "%f", v[i]); + count += len; + if (count > 2048) { s.PutChar('\n'); count = 0; } + if (len < 0 || len > 31) { + // this should never happen + throw DeadlyExportError("failed to convert double to string"); + } + for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); } + } + // } + s.PutChar('\n'); + for (int i = 0; i < indent; ++i) { s.PutChar('\t'); } + s.PutChar('}'); s.PutChar(' '); + node.End(s, false, indent, false); +} + +// ascii property node from vector of int32_t +void FBX::Node::WritePropertyNodeAscii( + const std::string& name, + const std::vector<int32_t>& v, + Assimp::StreamWriterLE& s, + int indent +){ + char buffer[32]; + FBX::Node node(name); + node.Begin(s, false, indent); + std::string vsize = to_string(v.size()); + // *<size> { + s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); + // indent + 1 + for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); } + // a: value,value,value,... + s.PutString("a: "); + int count = 0; + for (size_t i = 0; i < v.size(); ++i) { + if (i > 0) { s.PutChar(','); } + int len = ai_snprintf(buffer, sizeof(buffer), "%d", v[i]); + count += len; + if (count > 2048) { s.PutChar('\n'); count = 0; } + if (len < 0 || len > 31) { + // this should never happen + throw DeadlyExportError("failed to convert double to string"); + } + for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); } + } + // } + s.PutChar('\n'); + for (int i = 0; i < indent; ++i) { s.PutChar('\t'); } + s.PutChar('}'); s.PutChar(' '); + node.End(s, false, indent, false); +} + +// binary property node from vector of doubles +// TODO: optional zip compression! +void FBX::Node::WritePropertyNodeBinary( + const std::string& name, + const std::vector<double>& v, + Assimp::StreamWriterLE& s +){ + FBX::Node node(name); + node.BeginBinary(s); + s.PutU1('d'); + s.PutU4(uint32_t(v.size())); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + s.PutU4(uint32_t(v.size()) * 8); // data size + for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); } + node.EndPropertiesBinary(s, 1); + node.EndBinary(s, false); +} + +// binary property node from vector of int32_t +// TODO: optional zip compression! +void FBX::Node::WritePropertyNodeBinary( + const std::string& name, + const std::vector<int32_t>& v, + Assimp::StreamWriterLE& s +){ + FBX::Node node(name); + node.BeginBinary(s); + s.PutU1('i'); + s.PutU4(uint32_t(v.size())); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + s.PutU4(uint32_t(v.size()) * 4); // data size + for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); } + node.EndPropertiesBinary(s, 1); + node.EndBinary(s, false); +} + +// public static member functions + +// convenience function to create and write a property node, +// holding a single property which is an array of values. +// does not copy the data, so is efficient for large arrays. +void FBX::Node::WritePropertyNode( + const std::string& name, + const std::vector<double>& v, + Assimp::StreamWriterLE& s, + bool binary, int indent +){ + if (binary) { + FBX::Node::WritePropertyNodeBinary(name, v, s); + } else { + FBX::Node::WritePropertyNodeAscii(name, v, s, indent); + } +} + +// convenience function to create and write a property node, +// holding a single property which is an array of values. +// does not copy the data, so is efficient for large arrays. +void FBX::Node::WritePropertyNode( + const std::string& name, + const std::vector<int32_t>& v, + Assimp::StreamWriterLE& s, + bool binary, int indent +){ + if (binary) { + FBX::Node::WritePropertyNodeBinary(name, v, s); + } else { + FBX::Node::WritePropertyNodeAscii(name, v, s, indent); + } +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/FBXExportNode.h b/thirdparty/assimp/code/FBXExportNode.h new file mode 100644 index 0000000000..e1ebc36969 --- /dev/null +++ b/thirdparty/assimp/code/FBXExportNode.h @@ -0,0 +1,271 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXExportNode.h +* Declares the FBX::Node helper class for fbx export. +*/ +#ifndef AI_FBXEXPORTNODE_H_INC +#define AI_FBXEXPORTNODE_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportProperty.h" + +#include <assimp/StreamWriter.h> // StreamWriterLE + +#include <string> +#include <vector> + +namespace FBX { + class Node; +} + +class FBX::Node +{ +public: // public data members + // TODO: accessors + std::string name; // node name + std::vector<FBX::Property> properties; // node properties + std::vector<FBX::Node> children; // child nodes + + // some nodes always pretend they have children... + bool force_has_children = false; + +public: // constructors + /// The default class constructor. + Node() = default; + + /// The class constructor with the name. + Node(const std::string& n) + : name(n) + , properties() + , children() + , force_has_children( false ) { + // empty + } + + // convenience template to construct with properties directly + template <typename... More> + Node(const std::string& n, const More... more) + : name(n) + , properties() + , children() + , force_has_children(false) { + AddProperties(more...); + } + +public: // functions to add properties or children + // add a single property to the node + template <typename T> + void AddProperty(T value) { + properties.emplace_back(value); + } + + // convenience function to add multiple properties at once + template <typename T, typename... More> + void AddProperties(T value, More... more) { + properties.emplace_back(value); + AddProperties(more...); + } + void AddProperties() {} + + // add a child node directly + void AddChild(const Node& node) { children.push_back(node); } + + // convenience function to add a child node with a single property + template <typename... More> + void AddChild( + const std::string& name, + More... more + ) { + FBX::Node c(name); + c.AddProperties(more...); + children.push_back(c); + } + +public: // support specifically for dealing with Properties70 nodes + + // it really is simpler to make these all separate functions. + // the versions with 'A' suffixes are for animatable properties. + // those often follow a completely different format internally in FBX. + void AddP70int(const std::string& name, int32_t value); + void AddP70bool(const std::string& name, bool value); + void AddP70double(const std::string& name, double value); + void AddP70numberA(const std::string& name, double value); + void AddP70color(const std::string& name, double r, double g, double b); + void AddP70colorA(const std::string& name, double r, double g, double b); + void AddP70vector(const std::string& name, double x, double y, double z); + void AddP70vectorA(const std::string& name, double x, double y, double z); + void AddP70string(const std::string& name, const std::string& value); + void AddP70enum(const std::string& name, int32_t value); + void AddP70time(const std::string& name, int64_t value); + + // template for custom P70 nodes. + // anything that doesn't fit in the above can be created manually. + template <typename... More> + void AddP70( + const std::string& name, + const std::string& type, + const std::string& type2, + const std::string& flags, + More... more + ) { + Node n("P"); + n.AddProperties(name, type, type2, flags, more...); + AddChild(n); + } + +public: // member functions for writing data to a file or stream + + // write the full node to the given file or stream + void Dump( + std::shared_ptr<Assimp::IOStream> outfile, + bool binary, int indent + ); + void Dump(Assimp::StreamWriterLE &s, bool binary, int indent); + + // these other functions are for writing data piece by piece. + // they must be used carefully. + // for usage examples see FBXExporter.cpp. + void Begin(Assimp::StreamWriterLE &s, bool binary, int indent); + void DumpProperties(Assimp::StreamWriterLE& s, bool binary, int indent); + void EndProperties(Assimp::StreamWriterLE &s, bool binary, int indent); + void EndProperties( + Assimp::StreamWriterLE &s, bool binary, int indent, + size_t num_properties + ); + void BeginChildren(Assimp::StreamWriterLE &s, bool binary, int indent); + void DumpChildren(Assimp::StreamWriterLE& s, bool binary, int indent); + void End( + Assimp::StreamWriterLE &s, bool binary, int indent, + bool has_children + ); + +private: // internal functions used for writing + + void DumpBinary(Assimp::StreamWriterLE &s); + void DumpAscii(Assimp::StreamWriterLE &s, int indent); + void DumpAscii(std::ostream &s, int indent); + + void BeginBinary(Assimp::StreamWriterLE &s); + void DumpPropertiesBinary(Assimp::StreamWriterLE& s); + void EndPropertiesBinary(Assimp::StreamWriterLE &s); + void EndPropertiesBinary(Assimp::StreamWriterLE &s, size_t num_properties); + void DumpChildrenBinary(Assimp::StreamWriterLE& s); + void EndBinary(Assimp::StreamWriterLE &s, bool has_children); + + void BeginAscii(std::ostream &s, int indent); + void DumpPropertiesAscii(std::ostream &s, int indent); + void BeginChildrenAscii(std::ostream &s, int indent); + void DumpChildrenAscii(std::ostream &s, int indent); + void EndAscii(std::ostream &s, int indent, bool has_children); + +private: // data used for binary dumps + size_t start_pos; // starting position in stream + size_t end_pos; // ending position in stream + size_t property_start; // starting position of property section + +public: // static member functions + + // convenience function to create a node with a single property, + // and write it to the stream. + template <typename T> + static void WritePropertyNode( + const std::string& name, + const T value, + Assimp::StreamWriterLE& s, + bool binary, int indent + ) { + FBX::Property p(value); + FBX::Node node(name, p); + node.Dump(s, binary, indent); + } + + // convenience function to create and write a property node, + // holding a single property which is an array of values. + // does not copy the data, so is efficient for large arrays. + static void WritePropertyNode( + const std::string& name, + const std::vector<double>& v, + Assimp::StreamWriterLE& s, + bool binary, int indent + ); + + // convenience function to create and write a property node, + // holding a single property which is an array of values. + // does not copy the data, so is efficient for large arrays. + static void WritePropertyNode( + const std::string& name, + const std::vector<int32_t>& v, + Assimp::StreamWriterLE& s, + bool binary, int indent + ); + +private: // static helper functions + static void WritePropertyNodeAscii( + const std::string& name, + const std::vector<double>& v, + Assimp::StreamWriterLE& s, + int indent + ); + static void WritePropertyNodeAscii( + const std::string& name, + const std::vector<int32_t>& v, + Assimp::StreamWriterLE& s, + int indent + ); + static void WritePropertyNodeBinary( + const std::string& name, + const std::vector<double>& v, + Assimp::StreamWriterLE& s + ); + static void WritePropertyNodeBinary( + const std::string& name, + const std::vector<int32_t>& v, + Assimp::StreamWriterLE& s + ); + +}; + + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTNODE_H_INC diff --git a/thirdparty/assimp/code/FBXExportProperty.cpp b/thirdparty/assimp/code/FBXExportProperty.cpp new file mode 100644 index 0000000000..9981d6b1c6 --- /dev/null +++ b/thirdparty/assimp/code/FBXExportProperty.cpp @@ -0,0 +1,364 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportProperty.h" + +#include <assimp/StreamWriter.h> // StreamWriterLE +#include <assimp/Exceptional.h> // DeadlyExportError + +#include <string> +#include <vector> +#include <ostream> +#include <locale> +#include <sstream> // ostringstream + + +// constructors for single element properties + +FBX::Property::Property(bool v) + : type('C'), data(1) +{ + data = {uint8_t(v)}; +} + +FBX::Property::Property(int16_t v) : type('Y'), data(2) +{ + uint8_t* d = data.data(); + (reinterpret_cast<int16_t*>(d))[0] = v; +} + +FBX::Property::Property(int32_t v) : type('I'), data(4) +{ + uint8_t* d = data.data(); + (reinterpret_cast<int32_t*>(d))[0] = v; +} + +FBX::Property::Property(float v) : type('F'), data(4) +{ + uint8_t* d = data.data(); + (reinterpret_cast<float*>(d))[0] = v; +} + +FBX::Property::Property(double v) : type('D'), data(8) +{ + uint8_t* d = data.data(); + (reinterpret_cast<double*>(d))[0] = v; +} + +FBX::Property::Property(int64_t v) : type('L'), data(8) +{ + uint8_t* d = data.data(); + (reinterpret_cast<int64_t*>(d))[0] = v; +} + + +// constructors for array-type properties + +FBX::Property::Property(const char* c, bool raw) + : Property(std::string(c), raw) +{} + +// strings can either be saved as "raw" (R) data, or "string" (S) data +FBX::Property::Property(const std::string& s, bool raw) + : type(raw ? 'R' : 'S'), data(s.size()) +{ + for (size_t i = 0; i < s.size(); ++i) { + data[i] = uint8_t(s[i]); + } +} + +FBX::Property::Property(const std::vector<uint8_t>& r) + : type('R'), data(r) +{} + +FBX::Property::Property(const std::vector<int32_t>& va) + : type('i'), data(4*va.size()) +{ + int32_t* d = reinterpret_cast<int32_t*>(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + +FBX::Property::Property(const std::vector<int64_t>& va) + : type('l'), data(8*va.size()) +{ + int64_t* d = reinterpret_cast<int64_t*>(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + +FBX::Property::Property(const std::vector<float>& va) + : type('f'), data(4*va.size()) +{ + float* d = reinterpret_cast<float*>(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + +FBX::Property::Property(const std::vector<double>& va) + : type('d'), data(8*va.size()) +{ + double* d = reinterpret_cast<double*>(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + +FBX::Property::Property(const aiMatrix4x4& vm) + : type('d'), data(8*16) +{ + double* d = reinterpret_cast<double*>(data.data()); + for (unsigned int c = 0; c < 4; ++c) { + for (unsigned int r = 0; r < 4; ++r) { + d[4*c+r] = vm[r][c]; + } + } +} + +// public member functions + +size_t FBX::Property::size() +{ + switch (type) { + case 'C': case 'Y': case 'I': case 'F': case 'D': case 'L': + return data.size() + 1; + case 'S': case 'R': + return data.size() + 5; + case 'i': case 'd': + return data.size() + 13; + default: + throw DeadlyExportError("Requested size on property of unknown type"); + } +} + +void FBX::Property::DumpBinary(Assimp::StreamWriterLE &s) +{ + s.PutU1(type); + uint8_t* d = data.data(); + size_t N; + switch (type) { + case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(d))); return; + case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return; + case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return; + case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return; + case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return; + case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return; + case 'S': + case 'R': + s.PutU4(uint32_t(data.size())); + for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); } + return; + case 'i': + N = data.size() / 4; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutI4((reinterpret_cast<int32_t*>(d))[i]); + } + return; + case 'l': + N = data.size() / 8; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutI8((reinterpret_cast<int64_t*>(d))[i]); + } + return; + case 'f': + N = data.size() / 4; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutF4((reinterpret_cast<float*>(d))[i]); + } + return; + case 'd': + N = data.size() / 8; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutF8((reinterpret_cast<double*>(d))[i]); + } + return; + default: + std::ostringstream err; + err << "Tried to dump property with invalid type '"; + err << type << "'!"; + throw DeadlyExportError(err.str()); + } +} + +void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent) +{ + std::ostringstream ss; + ss.imbue(std::locale::classic()); + ss.precision(15); // this seems to match official FBX SDK exports + DumpAscii(ss, indent); + outstream.PutString(ss.str()); +} + +void FBX::Property::DumpAscii(std::ostream& s, int indent) +{ + // no writing type... or anything. just shove it into the stream. + uint8_t* d = data.data(); + size_t N; + size_t swap = data.size(); + size_t count = 0; + switch (type) { + case 'C': + if (*(reinterpret_cast<uint8_t*>(d))) { s << 'T'; } + else { s << 'F'; } + return; + case 'Y': s << *(reinterpret_cast<int16_t*>(d)); return; + case 'I': s << *(reinterpret_cast<int32_t*>(d)); return; + case 'F': s << *(reinterpret_cast<float*>(d)); return; + case 'D': s << *(reinterpret_cast<double*>(d)); return; + case 'L': s << *(reinterpret_cast<int64_t*>(d)); return; + case 'S': + // first search to see if it has "\x00\x01" in it - + // which separates fields which are reversed in the ascii version. + // yeah. + // FBX, yeah. + for (size_t i = 0; i < data.size(); ++i) { + if (data[i] == '\0') { + swap = i; + break; + } + } + case 'R': + s << '"'; + // we might as well check this now, + // probably it will never happen + for (size_t i = 0; i < data.size(); ++i) { + char c = data[i]; + if (c == '"') { + throw runtime_error("can't handle quotes in property string"); + } + } + // first write the SWAPPED member (if any) + for (size_t i = swap + 2; i < data.size(); ++i) { + char c = data[i]; + s << c; + } + // then a separator + if (swap != data.size()) { + s << "::"; + } + // then the initial member + for (size_t i = 0; i < swap; ++i) { + char c = data[i]; + s << c; + } + s << '"'; + return; + case 'i': + N = data.size() / 4; // number of elements + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast<int32_t*>(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'l': + N = data.size() / 8; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast<int64_t*>(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'f': + N = data.size() / 4; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast<float*>(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'd': + N = data.size() / 8; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + // set precision to something that can handle doubles + s.precision(15); + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast<double*>(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + default: + std::ostringstream err; + err << "Tried to dump property with invalid type '"; + err << type << "'!"; + throw runtime_error(err.str()); + } +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/FBXExportProperty.h b/thirdparty/assimp/code/FBXExportProperty.h new file mode 100644 index 0000000000..9c9d37c362 --- /dev/null +++ b/thirdparty/assimp/code/FBXExportProperty.h @@ -0,0 +1,129 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXExportProperty.h +* Declares the FBX::Property helper class for fbx export. +*/ +#ifndef AI_FBXEXPORTPROPERTY_H_INC +#define AI_FBXEXPORTPROPERTY_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + + +#include <assimp/types.h> // aiMatrix4x4 +#include <assimp/StreamWriter.h> // StreamWriterLE + +#include <string> +#include <vector> +#include <ostream> +#include <type_traits> // is_void + +namespace FBX { + class Property; +} + +/** FBX::Property + * + * Holds a value of any of FBX's recognized types, + * each represented by a particular one-character code. + * C : 1-byte uint8, usually 0x00 or 0x01 to represent boolean false and true + * Y : 2-byte int16 + * I : 4-byte int32 + * F : 4-byte float + * D : 8-byte double + * L : 8-byte int64 + * i : array of int32 + * f : array of float + * d : array of double + * l : array of int64 + * b : array of 1-byte booleans (0x00 or 0x01) + * S : string (array of 1-byte char) + * R : raw data (array of bytes) + */ +class FBX::Property +{ +public: + // constructors for basic types. + // all explicit to avoid accidental typecasting + explicit Property(bool v); + // TODO: determine if there is actually a byte type, + // or if this always means <bool>. 'C' seems to imply <char>, + // so possibly the above was intended to represent both. + explicit Property(int16_t v); + explicit Property(int32_t v); + explicit Property(float v); + explicit Property(double v); + explicit Property(int64_t v); + // strings can either be stored as 'R' (raw) or 'S' (string) type + explicit Property(const char* c, bool raw=false); + explicit Property(const std::string& s, bool raw=false); + explicit Property(const std::vector<uint8_t>& r); + explicit Property(const std::vector<int32_t>& va); + explicit Property(const std::vector<int64_t>& va); + explicit Property(const std::vector<double>& va); + explicit Property(const std::vector<float>& va); + explicit Property(const aiMatrix4x4& vm); + + // this will catch any type not defined above, + // so that we don't accidentally convert something we don't want. + // for example (const char*) --> (bool)... seriously wtf C++ + template <class T> + explicit Property(T v) : type('X') { + static_assert(std::is_void<T>::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION"); + } // note: no line wrap so it appears verbatim on the compiler error + + // the size of this property node in a binary file, in bytes + size_t size(); + + // write this property node as binary data to the given stream + void DumpBinary(Assimp::StreamWriterLE &s); + void DumpAscii(Assimp::StreamWriterLE &s, int indent=0); + void DumpAscii(std::ostream &s, int indent=0); + // note: make sure the ostream is in classic "C" locale + +private: + char type; + std::vector<uint8_t> data; +}; + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTPROPERTY_H_INC diff --git a/thirdparty/assimp/code/FBXExporter.cpp b/thirdparty/assimp/code/FBXExporter.cpp new file mode 100644 index 0000000000..acb1227144 --- /dev/null +++ b/thirdparty/assimp/code/FBXExporter.cpp @@ -0,0 +1,2480 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExporter.h" +#include "FBXExportNode.h" +#include "FBXExportProperty.h" +#include "FBXCommon.h" + +#include <assimp/version.h> // aiGetVersion +#include <assimp/IOSystem.hpp> +#include <assimp/Exporter.hpp> +#include <assimp/DefaultLogger.hpp> +#include <assimp/StreamWriter.h> // StreamWriterLE +#include <assimp/Exceptional.h> // DeadlyExportError +#include <assimp/material.h> // aiTextureType +#include <assimp/scene.h> +#include <assimp/mesh.h> + +// Header files, standard library. +#include <memory> // shared_ptr +#include <string> +#include <sstream> // stringstream +#include <ctime> // localtime, tm_* +#include <map> +#include <set> +#include <vector> +#include <array> +#include <unordered_set> + +// RESOURCES: +// https://code.blender.org/2013/08/fbx-binary-file-format-specification/ +// https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure + +const ai_real DEG = ai_real( 57.29577951308232087679815481 ); // degrees per radian + +// some constants that we'll use for writing metadata +namespace FBX { + const std::string EXPORT_VERSION_STR = "7.4.0"; + const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015 + // FBX files have some hashed values that depend on the creation time field, + // but for now we don't actually know how to generate these. + // what we can do is set them to a known-working version. + // this is the data that Blender uses in their FBX export process. + const std::string GENERIC_CTIME = "1970-01-01 10:00:00:000"; + const std::string GENERIC_FILEID = + "\x28\xb3\x2a\xeb\xb6\x24\xcc\xc2\xbf\xc8\xb0\x2a\xa9\x2b\xfc\xf1"; + const std::string GENERIC_FOOTID = + "\xfa\xbc\xab\x09\xd0\xc8\xd4\x66\xb1\x76\xfb\x83\x1c\xf7\x26\x7e"; + const std::string FOOT_MAGIC = + "\xf8\x5a\x8c\x6a\xde\xf5\xd9\x7e\xec\xe9\x0c\xe3\x75\x8f\x29\x0b"; + const std::string COMMENT_UNDERLINE = + ";------------------------------------------------------------------"; +} + +using namespace Assimp; +using namespace FBX; + +namespace Assimp { + + // --------------------------------------------------------------------- + // Worker function for exporting a scene to binary FBX. + // Prototyped and registered in Exporter.cpp + void ExportSceneFBX ( + const char* pFile, + IOSystem* pIOSystem, + const aiScene* pScene, + const ExportProperties* pProperties + ){ + // initialize the exporter + FBXExporter exporter(pScene, pProperties); + + // perform binary export + exporter.ExportBinary(pFile, pIOSystem); + } + + // --------------------------------------------------------------------- + // Worker function for exporting a scene to ASCII FBX. + // Prototyped and registered in Exporter.cpp + void ExportSceneFBXA ( + const char* pFile, + IOSystem* pIOSystem, + const aiScene* pScene, + const ExportProperties* pProperties + ){ + // initialize the exporter + FBXExporter exporter(pScene, pProperties); + + // perform ascii export + exporter.ExportAscii(pFile, pIOSystem); + } + +} // end of namespace Assimp + +FBXExporter::FBXExporter ( const aiScene* pScene, const ExportProperties* pProperties ) +: binary(false) +, mScene(pScene) +, mProperties(pProperties) +, outfile() +, connections() +, mesh_uids() +, material_uids() +, node_uids() { + // will probably need to determine UIDs, connections, etc here. + // basically anything that needs to be known + // before we start writing sections to the stream. +} + +void FBXExporter::ExportBinary ( + const char* pFile, + IOSystem* pIOSystem +){ + // remember that we're exporting in binary mode + binary = true; + + // we're not currently using these preferences, + // but clang will cry about it if we never touch it. + // TODO: some of these might be relevant to export + (void)mProperties; + + // open the indicated file for writing (in binary mode) + outfile.reset(pIOSystem->Open(pFile,"wb")); + if (!outfile) { + throw DeadlyExportError( + "could not open output .fbx file: " + std::string(pFile) + ); + } + + // first a binary-specific file header + WriteBinaryHeader(); + + // the rest of the file is in node entries. + // we have to serialize each entry before we write to the output, + // as the first thing we write is the byte offset of the _next_ entry. + // Either that or we can skip back to write the offset when we finish. + WriteAllNodes(); + + // finally we have a binary footer to the file + WriteBinaryFooter(); + + // explicitly release file pointer, + // so we don't have to rely on class destruction. + outfile.reset(); +} + +void FBXExporter::ExportAscii ( + const char* pFile, + IOSystem* pIOSystem +){ + // remember that we're exporting in ascii mode + binary = false; + + // open the indicated file for writing in text mode + outfile.reset(pIOSystem->Open(pFile,"wt")); + if (!outfile) { + throw DeadlyExportError( + "could not open output .fbx file: " + std::string(pFile) + ); + } + + // write the ascii header + WriteAsciiHeader(); + + // write all the sections + WriteAllNodes(); + + // make sure the file ends with a newline. + // note: if the file is opened in text mode, + // this should do the right cross-platform thing. + outfile->Write("\n", 1, 1); + + // explicitly release file pointer, + // so we don't have to rely on class destruction. + outfile.reset(); +} + +void FBXExporter::WriteAsciiHeader() +{ + // basically just a comment at the top of the file + std::stringstream head; + head << "; FBX " << EXPORT_VERSION_STR << " project file\n"; + head << "; Created by the Open Asset Import Library (Assimp)\n"; + head << "; http://assimp.org\n"; + head << "; -------------------------------------------------\n"; + const std::string ascii_header = head.str(); + outfile->Write(ascii_header.c_str(), ascii_header.size(), 1); +} + +void FBXExporter::WriteAsciiSectionHeader(const std::string& title) +{ + StreamWriterLE outstream(outfile); + std::stringstream s; + s << "\n\n; " << title << '\n'; + s << FBX::COMMENT_UNDERLINE << "\n"; + outstream.PutString(s.str()); +} + +void FBXExporter::WriteBinaryHeader() +{ + // first a specific sequence of 23 bytes, always the same + const char binary_header[24] = "Kaydara FBX Binary\x20\x20\x00\x1a\x00"; + outfile->Write(binary_header, 1, 23); + + // then FBX version number, "multiplied" by 1000, as little-endian uint32. + // so 7.3 becomes 7300 == 0x841C0000, 7.4 becomes 7400 == 0xE81C0000, etc + { + StreamWriterLE outstream(outfile); + outstream.PutU4(EXPORT_VERSION_INT); + } // StreamWriter destructor writes the data to the file + + // after this the node data starts immediately + // (probably with the FBXHEaderExtension node) +} + +void FBXExporter::WriteBinaryFooter() +{ + outfile->Write(NULL_RECORD.c_str(), NULL_RECORD.size(), 1); + + outfile->Write(GENERIC_FOOTID.c_str(), GENERIC_FOOTID.size(), 1); + + // here some padding is added for alignment to 16 bytes. + // if already aligned, the full 16 bytes is added. + size_t pos = outfile->Tell(); + size_t pad = 16 - (pos % 16); + for (size_t i = 0; i < pad; ++i) { + outfile->Write("\x00", 1, 1); + } + + // not sure what this is, but it seems to always be 0 in modern files + for (size_t i = 0; i < 4; ++i) { + outfile->Write("\x00", 1, 1); + } + + // now the file version again + { + StreamWriterLE outstream(outfile); + outstream.PutU4(EXPORT_VERSION_INT); + } // StreamWriter destructor writes the data to the file + + // and finally some binary footer added to all files + for (size_t i = 0; i < 120; ++i) { + outfile->Write("\x00", 1, 1); + } + outfile->Write(FOOT_MAGIC.c_str(), FOOT_MAGIC.size(), 1); +} + +void FBXExporter::WriteAllNodes () +{ + // header + // (and fileid, creation time, creator, if binary) + WriteHeaderExtension(); + + // global settings + WriteGlobalSettings(); + + // documents + WriteDocuments(); + + // references + WriteReferences(); + + // definitions + WriteDefinitions(); + + // objects + WriteObjects(); + + // connections + WriteConnections(); + + // WriteTakes? (deprecated since at least 2015 (fbx 7.4)) +} + +//FBXHeaderExtension top-level node +void FBXExporter::WriteHeaderExtension () +{ + if (!binary) { + // no title, follows directly from the top comment + } + FBX::Node n("FBXHeaderExtension"); + StreamWriterLE outstream(outfile); + int indent = 0; + + // begin node + n.Begin(outstream, binary, indent); + + // write properties + // (none) + + // finish properties + n.EndProperties(outstream, binary, indent, 0); + + // begin children + n.BeginChildren(outstream, binary, indent); + + indent = 1; + + // write child nodes + FBX::Node::WritePropertyNode( + "FBXHeaderVersion", int32_t(1003), outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "FBXVersion", int32_t(EXPORT_VERSION_INT), outstream, binary, indent + ); + if (binary) { + FBX::Node::WritePropertyNode( + "EncryptionType", int32_t(0), outstream, binary, indent + ); + } + + FBX::Node CreationTimeStamp("CreationTimeStamp"); + time_t rawtime; + time(&rawtime); + struct tm * now = localtime(&rawtime); + CreationTimeStamp.AddChild("Version", int32_t(1000)); + CreationTimeStamp.AddChild("Year", int32_t(now->tm_year + 1900)); + CreationTimeStamp.AddChild("Month", int32_t(now->tm_mon + 1)); + CreationTimeStamp.AddChild("Day", int32_t(now->tm_mday)); + CreationTimeStamp.AddChild("Hour", int32_t(now->tm_hour)); + CreationTimeStamp.AddChild("Minute", int32_t(now->tm_min)); + CreationTimeStamp.AddChild("Second", int32_t(now->tm_sec)); + CreationTimeStamp.AddChild("Millisecond", int32_t(0)); + CreationTimeStamp.Dump(outstream, binary, indent); + + std::stringstream creator; + creator << "Open Asset Import Library (Assimp) " << aiGetVersionMajor() + << "." << aiGetVersionMinor() << "." << aiGetVersionRevision(); + FBX::Node::WritePropertyNode( + "Creator", creator.str(), outstream, binary, indent + ); + + //FBX::Node sceneinfo("SceneInfo"); + //sceneinfo.AddProperty("GlobalInfo" + FBX::SEPARATOR + "SceneInfo"); + // not sure if any of this is actually needed, + // so just write an empty node for now. + //sceneinfo.Dump(outstream, binary, indent); + + indent = 0; + + // finish node + n.End(outstream, binary, indent, true); + + // that's it for FBXHeaderExtension... + if (!binary) { return; } + + // but binary files also need top-level FileID, CreationTime, Creator: + std::vector<uint8_t> raw(GENERIC_FILEID.size()); + for (size_t i = 0; i < GENERIC_FILEID.size(); ++i) { + raw[i] = uint8_t(GENERIC_FILEID[i]); + } + FBX::Node::WritePropertyNode( + "FileId", raw, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "CreationTime", GENERIC_CTIME, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "Creator", creator.str(), outstream, binary, indent + ); +} + +void FBXExporter::WriteGlobalSettings () +{ + if (!binary) { + // no title, follows directly from the header extension + } + FBX::Node gs("GlobalSettings"); + gs.AddChild("Version", int32_t(1000)); + + FBX::Node p("Properties70"); + p.AddP70int("UpAxis", 1); + p.AddP70int("UpAxisSign", 1); + p.AddP70int("FrontAxis", 2); + p.AddP70int("FrontAxisSign", 1); + p.AddP70int("CoordAxis", 0); + p.AddP70int("CoordAxisSign", 1); + p.AddP70int("OriginalUpAxis", 1); + p.AddP70int("OriginalUpAxisSign", 1); + p.AddP70double("UnitScaleFactor", 1.0); + p.AddP70double("OriginalUnitScaleFactor", 1.0); + p.AddP70color("AmbientColor", 0.0, 0.0, 0.0); + p.AddP70string("DefaultCamera", "Producer Perspective"); + p.AddP70enum("TimeMode", 11); + p.AddP70enum("TimeProtocol", 2); + p.AddP70enum("SnapOnFrameMode", 0); + p.AddP70time("TimeSpanStart", 0); // TODO: animation support + p.AddP70time("TimeSpanStop", FBX::SECOND); // TODO: animation support + p.AddP70double("CustomFrameRate", -1.0); + p.AddP70("TimeMarker", "Compound", "", ""); // not sure what this is + p.AddP70int("CurrentTimeMarker", -1); + gs.AddChild(p); + + gs.Dump(outfile, binary, 0); +} + +void FBXExporter::WriteDocuments () +{ + if (!binary) { + WriteAsciiSectionHeader("Documents Description"); + } + + // not sure what the use of multiple documents would be, + // or whether any end-application supports it + FBX::Node docs("Documents"); + docs.AddChild("Count", int32_t(1)); + FBX::Node doc("Document"); + + // generate uid + int64_t uid = generate_uid(); + doc.AddProperties(uid, "", "Scene"); + FBX::Node p("Properties70"); + p.AddP70("SourceObject", "object", "", ""); // what is this even for? + p.AddP70string("ActiveAnimStackName", ""); // should do this properly? + doc.AddChild(p); + + // UID for root node in scene hierarchy. + // always set to 0 in the case of a single document. + // not sure what happens if more than one document exists, + // but that won't matter to us as we're exporting a single scene. + doc.AddChild("RootNode", int64_t(0)); + + docs.AddChild(doc); + docs.Dump(outfile, binary, 0); +} + +void FBXExporter::WriteReferences () +{ + if (!binary) { + WriteAsciiSectionHeader("Document References"); + } + // always empty for now. + // not really sure what this is for. + FBX::Node n("References"); + n.force_has_children = true; + n.Dump(outfile, binary, 0); +} + + +// --------------------------------------------------------------- +// some internal helper functions used for writing the definitions +// (before any actual data is written) +// --------------------------------------------------------------- + +size_t count_nodes(const aiNode* n) { + size_t count = 1; + for (size_t i = 0; i < n->mNumChildren; ++i) { + count += count_nodes(n->mChildren[i]); + } + return count; +} + +bool has_phong_mat(const aiScene* scene) +{ + // just search for any material with a shininess exponent + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + float shininess = 0; + mat->Get(AI_MATKEY_SHININESS, shininess); + if (shininess > 0) { + return true; + } + } + return false; +} + +size_t count_images(const aiScene* scene) { + std::unordered_set<std::string> images; + aiString texpath; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + const aiTextureType textype = static_cast<aiTextureType>(tt); + const size_t texcount = mat->GetTextureCount(textype); + for (unsigned int j = 0; j < texcount; ++j) { + mat->GetTexture(textype, j, &texpath); + images.insert(std::string(texpath.C_Str())); + } + } + } + return images.size(); +} + +size_t count_textures(const aiScene* scene) { + size_t count = 0; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + // TODO: handle layered textures + if (mat->GetTextureCount(static_cast<aiTextureType>(tt)) > 0) { + count += 1; + } + } + } + return count; +} + +size_t count_deformers(const aiScene* scene) { + size_t count = 0; + for (size_t i = 0; i < scene->mNumMeshes; ++i) { + const size_t n = scene->mMeshes[i]->mNumBones; + if (n) { + // 1 main deformer, 1 subdeformer per bone + count += n + 1; + } + } + return count; +} + +void FBXExporter::WriteDefinitions () +{ + // basically this is just bookkeeping: + // determining how many of each type of object there are + // and specifying the base properties to use when otherwise unspecified. + + // ascii section header + if (!binary) { + WriteAsciiSectionHeader("Object definitions"); + } + + // we need to count the objects + int32_t count; + int32_t total_count = 0; + + // and store them + std::vector<FBX::Node> object_nodes; + FBX::Node n, pt, p; + + // GlobalSettings + // this seems to always be here in Maya exports + n = FBX::Node("ObjectType", "GlobalSettings"); + count = 1; + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + + // AnimationStack / FbxAnimStack + // this seems to always be here in Maya exports, + // but no harm seems to come of leaving it out. + count = mScene->mNumAnimations; + if (count) { + n = FBX::Node("ObjectType", "AnimationStack"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxAnimStack"); + p = FBX::Node("Properties70"); + p.AddP70string("Description", ""); + p.AddP70time("LocalStart", 0); + p.AddP70time("LocalStop", 0); + p.AddP70time("ReferenceStart", 0); + p.AddP70time("ReferenceStop", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // AnimationLayer / FbxAnimLayer + // this seems to always be here in Maya exports, + // but no harm seems to come of leaving it out. + // Assimp doesn't support animation layers, + // so there will be one per aiAnimation + count = mScene->mNumAnimations; + if (count) { + n = FBX::Node("ObjectType", "AnimationLayer"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FBXAnimLayer"); + p = FBX::Node("Properties70"); + p.AddP70("Weight", "Number", "", "A", double(100)); + p.AddP70bool("Mute", 0); + p.AddP70bool("Solo", 0); + p.AddP70bool("Lock", 0); + p.AddP70color("Color", 0.8, 0.8, 0.8); + p.AddP70("BlendMode", "enum", "", "", int32_t(0)); + p.AddP70("RotationAccumulationMode", "enum", "", "", int32_t(0)); + p.AddP70("ScaleAccumulationMode", "enum", "", "", int32_t(0)); + p.AddP70("BlendModeBypass", "ULongLong", "", "", int64_t(0)); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // NodeAttribute + // this is completely absurd. + // there can only be one "NodeAttribute" template, + // but FbxSkeleton, FbxCamera, FbxLight all are "NodeAttributes". + // so if only one exists we should set the template for that, + // otherwise... we just pick one :/. + // the others have to set all their properties every instance, + // because there's no template. + count = 1; // TODO: select properly + if (count) { + // FbxSkeleton + n = FBX::Node("ObjectType", "NodeAttribute"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxSkeleton"); + p = FBX::Node("Properties70"); + p.AddP70color("Color", 0.8, 0.8, 0.8); + p.AddP70double("Size", 33.333333333333); + p.AddP70("LimbLength", "double", "Number", "H", double(1)); + // note: not sure what the "H" flag is for - hidden? + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Model / FbxNode + // <~~ node hierarchy + count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) + if (count) { + n = FBX::Node("ObjectType", "Model"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxNode"); + p = FBX::Node("Properties70"); + p.AddP70enum("QuaternionInterpolate", 0); + p.AddP70vector("RotationOffset", 0.0, 0.0, 0.0); + p.AddP70vector("RotationPivot", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingOffset", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingPivot", 0.0, 0.0, 0.0); + p.AddP70bool("TranslationActive", 0); + p.AddP70vector("TranslationMin", 0.0, 0.0, 0.0); + p.AddP70vector("TranslationMax", 0.0, 0.0, 0.0); + p.AddP70bool("TranslationMinX", 0); + p.AddP70bool("TranslationMinY", 0); + p.AddP70bool("TranslationMinZ", 0); + p.AddP70bool("TranslationMaxX", 0); + p.AddP70bool("TranslationMaxY", 0); + p.AddP70bool("TranslationMaxZ", 0); + p.AddP70enum("RotationOrder", 0); + p.AddP70bool("RotationSpaceForLimitOnly", 0); + p.AddP70double("RotationStiffnessX", 0.0); + p.AddP70double("RotationStiffnessY", 0.0); + p.AddP70double("RotationStiffnessZ", 0.0); + p.AddP70double("AxisLen", 10.0); + p.AddP70vector("PreRotation", 0.0, 0.0, 0.0); + p.AddP70vector("PostRotation", 0.0, 0.0, 0.0); + p.AddP70bool("RotationActive", 0); + p.AddP70vector("RotationMin", 0.0, 0.0, 0.0); + p.AddP70vector("RotationMax", 0.0, 0.0, 0.0); + p.AddP70bool("RotationMinX", 0); + p.AddP70bool("RotationMinY", 0); + p.AddP70bool("RotationMinZ", 0); + p.AddP70bool("RotationMaxX", 0); + p.AddP70bool("RotationMaxY", 0); + p.AddP70bool("RotationMaxZ", 0); + p.AddP70enum("InheritType", 0); + p.AddP70bool("ScalingActive", 0); + p.AddP70vector("ScalingMin", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingMax", 1.0, 1.0, 1.0); + p.AddP70bool("ScalingMinX", 0); + p.AddP70bool("ScalingMinY", 0); + p.AddP70bool("ScalingMinZ", 0); + p.AddP70bool("ScalingMaxX", 0); + p.AddP70bool("ScalingMaxY", 0); + p.AddP70bool("ScalingMaxZ", 0); + p.AddP70vector("GeometricTranslation", 0.0, 0.0, 0.0); + p.AddP70vector("GeometricRotation", 0.0, 0.0, 0.0); + p.AddP70vector("GeometricScaling", 1.0, 1.0, 1.0); + p.AddP70double("MinDampRangeX", 0.0); + p.AddP70double("MinDampRangeY", 0.0); + p.AddP70double("MinDampRangeZ", 0.0); + p.AddP70double("MaxDampRangeX", 0.0); + p.AddP70double("MaxDampRangeY", 0.0); + p.AddP70double("MaxDampRangeZ", 0.0); + p.AddP70double("MinDampStrengthX", 0.0); + p.AddP70double("MinDampStrengthY", 0.0); + p.AddP70double("MinDampStrengthZ", 0.0); + p.AddP70double("MaxDampStrengthX", 0.0); + p.AddP70double("MaxDampStrengthY", 0.0); + p.AddP70double("MaxDampStrengthZ", 0.0); + p.AddP70double("PreferedAngleX", 0.0); + p.AddP70double("PreferedAngleY", 0.0); + p.AddP70double("PreferedAngleZ", 0.0); + p.AddP70("LookAtProperty", "object", "", ""); + p.AddP70("UpVectorProperty", "object", "", ""); + p.AddP70bool("Show", 1); + p.AddP70bool("NegativePercentShapeSupport", 1); + p.AddP70int("DefaultAttributeIndex", -1); + p.AddP70bool("Freeze", 0); + p.AddP70bool("LODBox", 0); + p.AddP70( + "Lcl Translation", "Lcl Translation", "", "A", + double(0), double(0), double(0) + ); + p.AddP70( + "Lcl Rotation", "Lcl Rotation", "", "A", + double(0), double(0), double(0) + ); + p.AddP70( + "Lcl Scaling", "Lcl Scaling", "", "A", + double(1), double(1), double(1) + ); + p.AddP70("Visibility", "Visibility", "", "A", double(1)); + p.AddP70( + "Visibility Inheritance", "Visibility Inheritance", "", "", + int32_t(1) + ); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Geometry / FbxMesh + // <~~ aiMesh + count = mScene->mNumMeshes; + if (count) { + n = FBX::Node("ObjectType", "Geometry"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxMesh"); + p = FBX::Node("Properties70"); + p.AddP70color("Color", 0, 0, 0); + p.AddP70vector("BBoxMin", 0, 0, 0); + p.AddP70vector("BBoxMax", 0, 0, 0); + p.AddP70bool("Primary Visibility", 1); + p.AddP70bool("Casts Shadows", 1); + p.AddP70bool("Receive Shadows", 1); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Material / FbxSurfacePhong, FbxSurfaceLambert, FbxSurfaceMaterial + // <~~ aiMaterial + // basically if there's any phong material this is defined as phong, + // and otherwise lambert. + // More complex materials cause a bare-bones FbxSurfaceMaterial definition + // and are treated specially, as they're not really supported by FBX. + // TODO: support Maya's Stingray PBS material + count = mScene->mNumMaterials; + if (count) { + bool has_phong = has_phong_mat(mScene); + n = FBX::Node("ObjectType", "Material"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate"); + if (has_phong) { + pt.AddProperty("FbxSurfacePhong"); + } else { + pt.AddProperty("FbxSurfaceLambert"); + } + p = FBX::Node("Properties70"); + if (has_phong) { + p.AddP70string("ShadingModel", "Phong"); + } else { + p.AddP70string("ShadingModel", "Lambert"); + } + p.AddP70bool("MultiLayer", 0); + p.AddP70colorA("EmissiveColor", 0.0, 0.0, 0.0); + p.AddP70numberA("EmissiveFactor", 1.0); + p.AddP70colorA("AmbientColor", 0.2, 0.2, 0.2); + p.AddP70numberA("AmbientFactor", 1.0); + p.AddP70colorA("DiffuseColor", 0.8, 0.8, 0.8); + p.AddP70numberA("DiffuseFactor", 1.0); + p.AddP70vector("Bump", 0.0, 0.0, 0.0); + p.AddP70vector("NormalMap", 0.0, 0.0, 0.0); + p.AddP70double("BumpFactor", 1.0); + p.AddP70colorA("TransparentColor", 0.0, 0.0, 0.0); + p.AddP70numberA("TransparencyFactor", 0.0); + p.AddP70color("DisplacementColor", 0.0, 0.0, 0.0); + p.AddP70double("DisplacementFactor", 1.0); + p.AddP70color("VectorDisplacementColor", 0.0, 0.0, 0.0); + p.AddP70double("VectorDisplacementFactor", 1.0); + if (has_phong) { + p.AddP70colorA("SpecularColor", 0.2, 0.2, 0.2); + p.AddP70numberA("SpecularFactor", 1.0); + p.AddP70numberA("ShininessExponent", 20.0); + p.AddP70colorA("ReflectionColor", 0.0, 0.0, 0.0); + p.AddP70numberA("ReflectionFactor", 1.0); + } + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Video / FbxVideo + // one for each image file. + count = int32_t(count_images(mScene)); + if (count) { + n = FBX::Node("ObjectType", "Video"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxVideo"); + p = FBX::Node("Properties70"); + p.AddP70bool("ImageSequence", 0); + p.AddP70int("ImageSequenceOffset", 0); + p.AddP70double("FrameRate", 0.0); + p.AddP70int("LastFrame", 0); + p.AddP70int("Width", 0); + p.AddP70int("Height", 0); + p.AddP70("Path", "KString", "XRefUrl", "", ""); + p.AddP70int("StartFrame", 0); + p.AddP70int("StopFrame", 0); + p.AddP70double("PlaySpeed", 0.0); + p.AddP70time("Offset", 0); + p.AddP70enum("InterlaceMode", 0); + p.AddP70bool("FreeRunning", 0); + p.AddP70bool("Loop", 0); + p.AddP70enum("AccessMode", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Texture / FbxFileTexture + // <~~ aiTexture + count = int32_t(count_textures(mScene)); + if (count) { + n = FBX::Node("ObjectType", "Texture"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxFileTexture"); + p = FBX::Node("Properties70"); + p.AddP70enum("TextureTypeUse", 0); + p.AddP70numberA("Texture alpha", 1.0); + p.AddP70enum("CurrentMappingType", 0); + p.AddP70enum("WrapModeU", 0); + p.AddP70enum("WrapModeV", 0); + p.AddP70bool("UVSwap", 0); + p.AddP70bool("PremultiplyAlpha", 1); + p.AddP70vectorA("Translation", 0.0, 0.0, 0.0); + p.AddP70vectorA("Rotation", 0.0, 0.0, 0.0); + p.AddP70vectorA("Scaling", 1.0, 1.0, 1.0); + p.AddP70vector("TextureRotationPivot", 0.0, 0.0, 0.0); + p.AddP70vector("TextureScalingPivot", 0.0, 0.0, 0.0); + p.AddP70enum("CurrentTextureBlendMode", 1); + p.AddP70string("UVSet", "default"); + p.AddP70bool("UseMaterial", 0); + p.AddP70bool("UseMipMap", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // AnimationCurveNode / FbxAnimCurveNode + count = mScene->mNumAnimations * 3; + if (count) { + n = FBX::Node("ObjectType", "AnimationCurveNode"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxAnimCurveNode"); + p = FBX::Node("Properties70"); + p.AddP70("d", "Compound", "", ""); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // AnimationCurve / FbxAnimCurve + count = mScene->mNumAnimations * 9; + if (count) { + n = FBX::Node("ObjectType", "AnimationCurve"); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + + // Pose + count = 0; + for (size_t i = 0; i < mScene->mNumMeshes; ++i) { + aiMesh* mesh = mScene->mMeshes[i]; + if (mesh->HasBones()) { ++count; } + } + if (count) { + n = FBX::Node("ObjectType", "Pose"); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + + // Deformer + count = int32_t(count_deformers(mScene)); + if (count) { + n = FBX::Node("ObjectType", "Deformer"); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + + // (template) + count = 0; + if (count) { + n = FBX::Node("ObjectType", ""); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", ""); + p = FBX::Node("Properties70"); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // now write it all + FBX::Node defs("Definitions"); + defs.AddChild("Version", int32_t(100)); + defs.AddChild("Count", int32_t(total_count)); + for (auto &n : object_nodes) { defs.AddChild(n); } + defs.Dump(outfile, binary, 0); +} + + +// ------------------------------------------------------------------- +// some internal helper functions used for writing the objects section +// (which holds the actual data) +// ------------------------------------------------------------------- + +aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node) +{ + for (size_t i = 0; i < node->mNumMeshes; ++i) { + if (node->mMeshes[i] == meshIndex) { + return node; + } + } + for (size_t i = 0; i < node->mNumChildren; ++i) { + aiNode* ret = get_node_for_mesh(meshIndex, node->mChildren[i]); + if (ret) { return ret; } + } + return nullptr; +} + +aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene) +{ + std::vector<const aiNode*> node_chain; + while (node != scene->mRootNode) { + node_chain.push_back(node); + node = node->mParent; + } + aiMatrix4x4 transform; + for (auto n = node_chain.rbegin(); n != node_chain.rend(); ++n) { + transform *= (*n)->mTransformation; + } + return transform; +} + +int64_t to_ktime(double ticks, const aiAnimation* anim) { + if (anim->mTicksPerSecond <= 0) { + return static_cast<int64_t>(ticks) * FBX::SECOND; + } + return (static_cast<int64_t>(ticks) / static_cast<int64_t>(anim->mTicksPerSecond)) * FBX::SECOND; +} + +int64_t to_ktime(double time) { + return (static_cast<int64_t>(time * FBX::SECOND)); +} + +void FBXExporter::WriteObjects () +{ + if (!binary) { + WriteAsciiSectionHeader("Object properties"); + } + // numbers should match those given in definitions! make sure to check + StreamWriterLE outstream(outfile); + FBX::Node object_node("Objects"); + int indent = 0; + object_node.Begin(outstream, binary, indent); + object_node.EndProperties(outstream, binary, indent); + object_node.BeginChildren(outstream, binary, indent); + + // geometry (aiMesh) + mesh_uids.clear(); + indent = 1; + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + // it's all about this mesh + aiMesh* m = mScene->mMeshes[mi]; + + // start the node record + FBX::Node n("Geometry"); + int64_t uid = generate_uid(); + mesh_uids.push_back(uid); + n.AddProperty(uid); + n.AddProperty(FBX::SEPARATOR + "Geometry"); + n.AddProperty("Mesh"); + n.Begin(outstream, binary, indent); + n.DumpProperties(outstream, binary, indent); + n.EndProperties(outstream, binary, indent); + n.BeginChildren(outstream, binary, indent); + indent = 2; + + // output vertex data - each vertex should be unique (probably) + std::vector<double> flattened_vertices; + // index of original vertex in vertex data vector + std::vector<int32_t> vertex_indices; + // map of vertex value to its index in the data vector + std::map<aiVector3D,size_t> index_by_vertex_value; + int32_t index = 0; + for (size_t vi = 0; vi < m->mNumVertices; ++vi) { + aiVector3D vtx = m->mVertices[vi]; + auto elem = index_by_vertex_value.find(vtx); + if (elem == index_by_vertex_value.end()) { + vertex_indices.push_back(index); + index_by_vertex_value[vtx] = index; + flattened_vertices.push_back(vtx[0]); + flattened_vertices.push_back(vtx[1]); + flattened_vertices.push_back(vtx[2]); + ++index; + } else { + vertex_indices.push_back(int32_t(elem->second)); + } + } + FBX::Node::WritePropertyNode( + "Vertices", flattened_vertices, outstream, binary, indent + ); + + // output polygon data as a flattened array of vertex indices. + // the last vertex index of each polygon is negated and - 1 + std::vector<int32_t> polygon_data; + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices - 1; ++pvi) { + polygon_data.push_back(vertex_indices[f.mIndices[pvi]]); + } + polygon_data.push_back( + -1 - vertex_indices[f.mIndices[f.mNumIndices-1]] + ); + } + FBX::Node::WritePropertyNode( + "PolygonVertexIndex", polygon_data, outstream, binary, indent + ); + + // here could be edges but they're insane. + // it's optional anyway, so let's ignore it. + + FBX::Node::WritePropertyNode( + "GeometryVersion", int32_t(124), outstream, binary, indent + ); + + // normals, if any + if (m->HasNormals()) { + FBX::Node normals("LayerElementNormal", int32_t(0)); + normals.Begin(outstream, binary, indent); + normals.DumpProperties(outstream, binary, indent); + normals.EndProperties(outstream, binary, indent); + normals.BeginChildren(outstream, binary, indent); + indent = 3; + FBX::Node::WritePropertyNode( + "Version", int32_t(101), outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "Name", "", outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "MappingInformationType", "ByPolygonVertex", + outstream, binary, indent + ); + // TODO: vertex-normals or indexed normals when appropriate + FBX::Node::WritePropertyNode( + "ReferenceInformationType", "Direct", + outstream, binary, indent + ); + std::vector<double> normal_data; + normal_data.reserve(3 * polygon_data.size()); + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { + const aiVector3D &n = m->mNormals[f.mIndices[pvi]]; + normal_data.push_back(n.x); + normal_data.push_back(n.y); + normal_data.push_back(n.z); + } + } + FBX::Node::WritePropertyNode( + "Normals", normal_data, outstream, binary, indent + ); + // note: version 102 has a NormalsW also... not sure what it is, + // so we can stick with version 101 for now. + indent = 2; + normals.End(outstream, binary, indent, true); + } + + // uvs, if any + for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) { + if (m->mNumUVComponents[uvi] > 2) { + // FBX only supports 2-channel UV maps... + // or at least i'm not sure how to indicate a different number + std::stringstream err; + err << "Only 2-channel UV maps supported by FBX,"; + err << " but mesh " << mi; + if (m->mName.length) { + err << " (" << m->mName.C_Str() << ")"; + } + err << " UV map " << uvi; + err << " has " << m->mNumUVComponents[uvi]; + err << " components! Data will be preserved,"; + err << " but may be incorrectly interpreted on load."; + ASSIMP_LOG_WARN(err.str()); + } + FBX::Node uv("LayerElementUV", int32_t(uvi)); + uv.Begin(outstream, binary, indent); + uv.DumpProperties(outstream, binary, indent); + uv.EndProperties(outstream, binary, indent); + uv.BeginChildren(outstream, binary, indent); + indent = 3; + FBX::Node::WritePropertyNode( + "Version", int32_t(101), outstream, binary, indent + ); + // it doesn't seem like assimp keeps the uv map name, + // so just leave it blank. + FBX::Node::WritePropertyNode( + "Name", "", outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "MappingInformationType", "ByPolygonVertex", + outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "ReferenceInformationType", "IndexToDirect", + outstream, binary, indent + ); + + std::vector<double> uv_data; + std::vector<int32_t> uv_indices; + std::map<aiVector3D,int32_t> index_by_uv; + int32_t index = 0; + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { + const aiVector3D &uv = + m->mTextureCoords[uvi][f.mIndices[pvi]]; + auto elem = index_by_uv.find(uv); + if (elem == index_by_uv.end()) { + index_by_uv[uv] = index; + uv_indices.push_back(index); + for (unsigned int x = 0; x < m->mNumUVComponents[uvi]; ++x) { + uv_data.push_back(uv[x]); + } + ++index; + } else { + uv_indices.push_back(elem->second); + } + } + } + FBX::Node::WritePropertyNode( + "UV", uv_data, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "UVIndex", uv_indices, outstream, binary, indent + ); + indent = 2; + uv.End(outstream, binary, indent, true); + } + + // i'm not really sure why this material section exists, + // as the material is linked via "Connections". + // it seems to always have the same "0" value. + FBX::Node mat("LayerElementMaterial", int32_t(0)); + mat.AddChild("Version", int32_t(101)); + mat.AddChild("Name", ""); + mat.AddChild("MappingInformationType", "AllSame"); + mat.AddChild("ReferenceInformationType", "IndexToDirect"); + std::vector<int32_t> mat_indices = {0}; + mat.AddChild("Materials", mat_indices); + mat.Dump(outstream, binary, indent); + + // finally we have the layer specifications, + // which select the normals / UV set / etc to use. + // TODO: handle multiple uv sets correctly? + FBX::Node layer("Layer", int32_t(0)); + layer.AddChild("Version", int32_t(100)); + FBX::Node le("LayerElement"); + le.AddChild("Type", "LayerElementNormal"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + le = FBX::Node("LayerElement"); + le.AddChild("Type", "LayerElementMaterial"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + le = FBX::Node("LayerElement"); + le.AddChild("Type", "LayerElementUV"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + layer.Dump(outstream, binary, indent); + + // finish the node record + indent = 1; + n.End(outstream, binary, indent, true); + } + + // aiMaterial + material_uids.clear(); + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + // it's all about this material + aiMaterial* m = mScene->mMaterials[i]; + + // these are used to receive material data + float f; aiColor3D c; + + // start the node record + FBX::Node n("Material"); + + int64_t uid = generate_uid(); + material_uids.push_back(uid); + n.AddProperty(uid); + + aiString name; + m->Get(AI_MATKEY_NAME, name); + n.AddProperty(name.C_Str() + FBX::SEPARATOR + "Material"); + + n.AddProperty(""); + + n.AddChild("Version", int32_t(102)); + f = 0; + m->Get(AI_MATKEY_SHININESS, f); + bool phong = (f > 0); + if (phong) { + n.AddChild("ShadingModel", "phong"); + } else { + n.AddChild("ShadingModel", "lambert"); + } + n.AddChild("MultiLayer", int32_t(0)); + + FBX::Node p("Properties70"); + + // materials exported using the FBX SDK have two sets of fields. + // there are the properties specified in the PropertyTemplate, + // which are those supported by the modernFBX SDK, + // and an extra set of properties with simpler names. + // The extra properties are a legacy material system from pre-2009. + // + // In the modern system, each property has "color" and "factor". + // Generally the interpretation of these seems to be + // that the colour is multiplied by the factor before use, + // but this is not always clear-cut. + // + // Usually assimp only stores the colour, + // so we can just leave the factors at the default "1.0". + + // first we can export the "standard" properties + if (m->Get(AI_MATKEY_COLOR_AMBIENT, c) == aiReturn_SUCCESS) { + p.AddP70colorA("AmbientColor", c.r, c.g, c.b); + //p.AddP70numberA("AmbientFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_DIFFUSE, c) == aiReturn_SUCCESS) { + p.AddP70colorA("DiffuseColor", c.r, c.g, c.b); + //p.AddP70numberA("DiffuseFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) { + // "TransparentColor" / "TransparencyFactor"... + // thanks FBX, for your insightful interpretation of consistency + p.AddP70colorA("TransparentColor", c.r, c.g, c.b); + // TransparencyFactor defaults to 0.0, so set it to 1.0. + // note: Maya always sets this to 1.0, + // so we can't use it sensibly as "Opacity". + // In stead we rely on the legacy "Opacity" value, below. + // Blender also relies on "Opacity" not "TransparencyFactor", + // probably for a similar reason. + p.AddP70numberA("TransparencyFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_REFLECTIVE, c) == aiReturn_SUCCESS) { + p.AddP70colorA("ReflectionColor", c.r, c.g, c.b); + } + if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ReflectionFactor", f); + } + if (phong) { + if (m->Get(AI_MATKEY_COLOR_SPECULAR, c) == aiReturn_SUCCESS) { + p.AddP70colorA("SpecularColor", c.r, c.g, c.b); + } + if (m->Get(AI_MATKEY_SHININESS_STRENGTH, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ShininessFactor", f); + } + if (m->Get(AI_MATKEY_SHININESS, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ShininessExponent", f); + } + if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ReflectionFactor", f); + } + } + + // Now the legacy system. + // For safety let's include it. + // thrse values don't exist in the property template, + // and usually are completely ignored when loading. + // One notable exception is the "Opacity" property, + // which Blender uses as (1.0 - alpha). + c.r = 0.0f; c.g = 0.0f; c.b = 0.0f; + m->Get(AI_MATKEY_COLOR_EMISSIVE, c); + p.AddP70vector("Emissive", c.r, c.g, c.b); + c.r = 0.2f; c.g = 0.2f; c.b = 0.2f; + m->Get(AI_MATKEY_COLOR_AMBIENT, c); + p.AddP70vector("Ambient", c.r, c.g, c.b); + c.r = 0.8f; c.g = 0.8f; c.b = 0.8f; + m->Get(AI_MATKEY_COLOR_DIFFUSE, c); + p.AddP70vector("Diffuse", c.r, c.g, c.b); + // The FBX SDK determines "Opacity" from transparency colour (RGB) + // and factor (F) as: O = (1.0 - F * ((R + G + B) / 3)). + // However we actually have an opacity value, + // so we should take it from AI_MATKEY_OPACITY if possible. + // It might make more sense to use TransparencyFactor, + // but Blender actually loads "Opacity" correctly, so let's use it. + f = 1.0f; + if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) { + f = 1.0f - ((c.r + c.g + c.b) / 3.0f); + } + m->Get(AI_MATKEY_OPACITY, f); + p.AddP70double("Opacity", f); + if (phong) { + // specular color is multiplied by shininess_strength + c.r = 0.2f; c.g = 0.2f; c.b = 0.2f; + m->Get(AI_MATKEY_COLOR_SPECULAR, c); + f = 1.0f; + m->Get(AI_MATKEY_SHININESS_STRENGTH, f); + p.AddP70vector("Specular", f*c.r, f*c.g, f*c.b); + f = 20.0f; + m->Get(AI_MATKEY_SHININESS, f); + p.AddP70double("Shininess", f); + // Legacy "Reflectivity" is F*F*((R+G+B)/3), + // where F is the proportion of light reflected (AKA reflectivity), + // and RGB is the reflective colour of the material. + // No idea why, but we might as well set it the same way. + f = 0.0f; + m->Get(AI_MATKEY_REFLECTIVITY, f); + c.r = 1.0f, c.g = 1.0f, c.b = 1.0f; + m->Get(AI_MATKEY_COLOR_REFLECTIVE, c); + p.AddP70double("Reflectivity", f*f*((c.r+c.g+c.b)/3.0)); + } + + n.AddChild(p); + + n.Dump(outstream, binary, indent); + } + + // we need to look up all the images we're using, + // so we can generate uids, and eliminate duplicates. + std::map<std::string, int64_t> uid_by_image; + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + aiString texpath; + aiMaterial* mat = mScene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + const aiTextureType textype = static_cast<aiTextureType>(tt); + const size_t texcount = mat->GetTextureCount(textype); + for (size_t j = 0; j < texcount; ++j) { + mat->GetTexture(textype, (unsigned int)j, &texpath); + const std::string texstring = texpath.C_Str(); + auto elem = uid_by_image.find(texstring); + if (elem == uid_by_image.end()) { + uid_by_image[texstring] = generate_uid(); + } + } + } + } + + // FbxVideo - stores images used by textures. + for (const auto &it : uid_by_image) { + if (it.first.compare(0, 1, "*") == 0) { + // TODO: embedded textures + continue; + } + FBX::Node n("Video"); + const int64_t& uid = it.second; + const std::string name = ""; // TODO: ... name??? + n.AddProperties(uid, name + FBX::SEPARATOR + "Video", "Clip"); + n.AddChild("Type", "Clip"); + FBX::Node p("Properties70"); + // TODO: get full path... relative path... etc... ugh... + // for now just use the same path for everything, + // and hopefully one of them will work out. + const std::string& path = it.first; + p.AddP70("Path", "KString", "XRefUrl", "", path); + n.AddChild(p); + n.AddChild("UseMipMap", int32_t(0)); + n.AddChild("Filename", path); + n.AddChild("RelativeFilename", path); + n.Dump(outstream, binary, indent); + } + + // Textures + // referenced by material_index/texture_type pairs. + std::map<std::pair<size_t,size_t>,int64_t> texture_uids; + const std::map<aiTextureType,std::string> prop_name_by_tt = { + {aiTextureType_DIFFUSE, "DiffuseColor"}, + {aiTextureType_SPECULAR, "SpecularColor"}, + {aiTextureType_AMBIENT, "AmbientColor"}, + {aiTextureType_EMISSIVE, "EmissiveColor"}, + {aiTextureType_HEIGHT, "Bump"}, + {aiTextureType_NORMALS, "NormalMap"}, + {aiTextureType_SHININESS, "ShininessExponent"}, + {aiTextureType_OPACITY, "TransparentColor"}, + {aiTextureType_DISPLACEMENT, "DisplacementColor"}, + //{aiTextureType_LIGHTMAP, "???"}, + {aiTextureType_REFLECTION, "ReflectionColor"} + //{aiTextureType_UNKNOWN, ""} + }; + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + // textures are attached to materials + aiMaterial* mat = mScene->mMaterials[i]; + int64_t material_uid = material_uids[i]; + + for ( + size_t j = aiTextureType_DIFFUSE; + j < aiTextureType_UNKNOWN; + ++j + ) { + const aiTextureType tt = static_cast<aiTextureType>(j); + size_t n = mat->GetTextureCount(tt); + + if (n < 1) { // no texture of this type + continue; + } + + if (n > 1) { + // TODO: multilayer textures + std::stringstream err; + err << "Multilayer textures not supported (for now),"; + err << " skipping texture type " << j; + err << " of material " << i; + ASSIMP_LOG_WARN(err.str()); + } + + // get image path for this (single-image) texture + aiString tpath; + if (mat->GetTexture(tt, 0, &tpath) != aiReturn_SUCCESS) { + std::stringstream err; + err << "Failed to get texture 0 for texture of type " << tt; + err << " on material " << i; + err << ", however GetTextureCount returned 1."; + throw DeadlyExportError(err.str()); + } + const std::string texture_path(tpath.C_Str()); + + // get connected image uid + auto elem = uid_by_image.find(texture_path); + if (elem == uid_by_image.end()) { + // this should never happen + std::stringstream err; + err << "Failed to find video element for texture with path"; + err << " \"" << texture_path << "\""; + err << ", type " << j << ", material " << i; + throw DeadlyExportError(err.str()); + } + const int64_t image_uid = elem->second; + + // get the name of the material property to connect to + auto elem2 = prop_name_by_tt.find(tt); + if (elem2 == prop_name_by_tt.end()) { + // don't know how to handle this type of texture, + // so skip it. + std::stringstream err; + err << "Not sure how to handle texture of type " << j; + err << " on material " << i; + err << ", skipping..."; + ASSIMP_LOG_WARN(err.str()); + continue; + } + const std::string& prop_name = elem2->second; + + // generate a uid for this texture + const int64_t texture_uid = generate_uid(); + + // link the texture to the material + connections.emplace_back( + "C", "OP", texture_uid, material_uid, prop_name + ); + + // link the image data to the texture + connections.emplace_back("C", "OO", image_uid, texture_uid); + + // now write the actual texture node + FBX::Node tnode("Texture"); + // TODO: some way to determine texture name? + const std::string texture_name = "" + FBX::SEPARATOR + "Texture"; + tnode.AddProperties(texture_uid, texture_name, ""); + // there really doesn't seem to be a better type than this: + tnode.AddChild("Type", "TextureVideoClip"); + tnode.AddChild("Version", int32_t(202)); + tnode.AddChild("TextureName", texture_name); + FBX::Node p("Properties70"); + p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify + //p.AddP70string("UVSet", ""); // TODO: how should this work? + p.AddP70bool("UseMaterial", 1); + tnode.AddChild(p); + // can't easily detrmine which texture path will be correct, + // so just store what we have in every field. + // these being incorrect is a common problem with FBX anyway. + tnode.AddChild("FileName", texture_path); + tnode.AddChild("RelativeFilename", texture_path); + tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0)); + tnode.AddChild("ModelUVScaling", double(1.0), double(1.0)); + tnode.AddChild("Texture_Alpha_Source", "None"); + tnode.AddChild( + "Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0) + ); + tnode.Dump(outstream, binary, indent); + } + } + + // bones. + // + // output structure: + // subset of node hierarchy that are "skeleton", + // i.e. do not have meshes but only bones. + // but.. i'm not sure how anyone could guarantee that... + // + // input... + // well, for each mesh it has "bones", + // and the bone names correspond to nodes. + // of course we also need the parent nodes, + // as they give some of the transform........ + // + // well. we can assume a sane input, i suppose. + // + // so input is the bone node hierarchy, + // with an extra thing for the transformation of the MESH in BONE space. + // + // output is a set of bone nodes, + // a "bindpose" which indicates the default local transform of all bones, + // and a set of "deformers". + // each deformer is parented to a mesh geometry, + // and has one or more "subdeformer"s as children. + // each subdeformer has one bone node as a child, + // and represents the influence of that bone on the grandparent mesh. + // the subdeformer has a list of indices, and weights, + // with indices specifying vertex indices, + // and weights specifying the corresponding influence of this bone. + // it also has Transform and TransformLink elements, + // specifying the transform of the MESH in BONE space, + // and the transformation of the BONE in WORLD space, + // likely in the bindpose. + // + // the input bone structure is different but similar, + // storing the number of weights for this bone, + // and an array of (vertex index, weight) pairs. + // + // one sticky point is that the number of vertices may not match, + // because assimp splits vertices by normal, uv, etc. + + // first we should mark the skeleton for each mesh. + // the skeleton must include not only the aiBones, + // but also all their parent nodes. + // anything that affects the position of any bone node must be included. + std::vector<std::set<const aiNode*>> skeleton_by_mesh(mScene->mNumMeshes); + // at the same time we can build a list of all the skeleton nodes, + // which will be used later to mark them as type "limbNode". + std::unordered_set<const aiNode*> limbnodes; + // and a map of nodes by bone name, as finding them is annoying. + std::map<std::string,aiNode*> node_by_bone; + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + const aiMesh* m = mScene->mMeshes[mi]; + std::set<const aiNode*> skeleton; + for (size_t bi =0; bi < m->mNumBones; ++bi) { + const aiBone* b = m->mBones[bi]; + const std::string name(b->mName.C_Str()); + auto elem = node_by_bone.find(name); + aiNode* n; + if (elem != node_by_bone.end()) { + n = elem->second; + } else { + n = mScene->mRootNode->FindNode(b->mName); + if (!n) { + // this should never happen + std::stringstream err; + err << "Failed to find node for bone: \"" << name << "\""; + throw DeadlyExportError(err.str()); + } + node_by_bone[name] = n; + limbnodes.insert(n); + } + skeleton.insert(n); + // mark all parent nodes as skeleton as well, + // up until we find the root node, + // or else the node containing the mesh, + // or else the parent of a node containig the mesh. + for ( + const aiNode* parent = n->mParent; + parent && parent != mScene->mRootNode; + parent = parent->mParent + ) { + // if we've already done this node we can skip it all + if (skeleton.count(parent)) { + break; + } + // ignore fbx transform nodes as these will be collapsed later + // TODO: cache this by aiNode* + const std::string node_name(parent->mName.C_Str()); + if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { + continue; + } + // otherwise check if this is the root of the skeleton + bool end = false; + // is the mesh part of this node? + for (size_t i = 0; i < parent->mNumMeshes; ++i) { + if (parent->mMeshes[i] == mi) { + end = true; + break; + } + } + // is the mesh in one of the children of this node? + for (size_t j = 0; j < parent->mNumChildren; ++j) { + aiNode* child = parent->mChildren[j]; + for (size_t i = 0; i < child->mNumMeshes; ++i) { + if (child->mMeshes[i] == mi) { + end = true; + break; + } + } + if (end) { break; } + } + limbnodes.insert(parent); + skeleton.insert(parent); + // if it was the skeleton root we can finish here + if (end) { break; } + } + } + skeleton_by_mesh[mi] = skeleton; + } + + // we'll need the uids for the bone nodes, so generate them now + for (size_t i = 0; i < mScene->mNumMeshes; ++i) { + auto &s = skeleton_by_mesh[i]; + for (const aiNode* n : s) { + auto elem = node_uids.find(n); + if (elem == node_uids.end()) { + node_uids[n] = generate_uid(); + } + } + } + + // now, for each aiMesh, we need to export a deformer, + // and for each aiBone a subdeformer, + // which should have all the skinning info. + // these will need to be connected properly to the mesh, + // and we can do that all now. + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + const aiMesh* m = mScene->mMeshes[mi]; + if (!m->HasBones()) { + continue; + } + // make a deformer for this mesh + int64_t deformer_uid = generate_uid(); + FBX::Node dnode("Deformer"); + dnode.AddProperties(deformer_uid, FBX::SEPARATOR + "Deformer", "Skin"); + dnode.AddChild("Version", int32_t(101)); + // "acuracy"... this is not a typo.... + dnode.AddChild("Link_DeformAcuracy", double(50)); + dnode.AddChild("SkinningType", "Linear"); // TODO: other modes? + dnode.Dump(outstream, binary, indent); + + // connect it + connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]); + + // we will be indexing by vertex... + // but there might be a different number of "vertices" + // between assimp and our output FBX. + // this code is cut-and-pasted from the geometry section above... + // ideally this should not be so. + // --- + // index of original vertex in vertex data vector + std::vector<int32_t> vertex_indices; + // map of vertex value to its index in the data vector + std::map<aiVector3D,size_t> index_by_vertex_value; + int32_t index = 0; + for (size_t vi = 0; vi < m->mNumVertices; ++vi) { + aiVector3D vtx = m->mVertices[vi]; + auto elem = index_by_vertex_value.find(vtx); + if (elem == index_by_vertex_value.end()) { + vertex_indices.push_back(index); + index_by_vertex_value[vtx] = index; + ++index; + } else { + vertex_indices.push_back(int32_t(elem->second)); + } + } + + // TODO, FIXME: this won't work if anything is not in the bind pose. + // for now if such a situation is detected, we throw an exception. + std::set<const aiBone*> not_in_bind_pose; + std::set<const aiNode*> no_offset_matrix; + + // first get this mesh's position in world space, + // as we'll need it for each subdeformer. + // + // ...of course taking the position of the MESH doesn't make sense, + // as it can be instanced to many nodes. + // All we can do is assume no instancing, + // and take the first node we find that contains the mesh. + aiNode* mesh_node = get_node_for_mesh((unsigned int)mi, mScene->mRootNode); + aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene); + + // now make a subdeformer for each bone in the skeleton + const std::set<const aiNode*> &skeleton = skeleton_by_mesh[mi]; + for (const aiNode* bone_node : skeleton) { + // if there's a bone for this node, find it + const aiBone* b = nullptr; + for (size_t bi = 0; bi < m->mNumBones; ++bi) { + // TODO: this probably should index by something else + const std::string name(m->mBones[bi]->mName.C_Str()); + if (node_by_bone[name] == bone_node) { + b = m->mBones[bi]; + break; + } + } + if (!b) { + no_offset_matrix.insert(bone_node); + } + + // start the subdeformer node + const int64_t subdeformer_uid = generate_uid(); + FBX::Node sdnode("Deformer"); + sdnode.AddProperties( + subdeformer_uid, FBX::SEPARATOR + "SubDeformer", "Cluster" + ); + sdnode.AddChild("Version", int32_t(100)); + sdnode.AddChild("UserData", "", ""); + + // add indices and weights, if any + if (b) { + std::vector<int32_t> subdef_indices; + std::vector<double> subdef_weights; + int32_t last_index = -1; + for (size_t wi = 0; wi < b->mNumWeights; ++wi) { + int32_t vi = vertex_indices[b->mWeights[wi].mVertexId]; + if (vi == last_index) { + // only for vertices we exported to fbx + // TODO, FIXME: this assumes identically-located vertices + // will always deform in the same way. + // as assimp doesn't store a separate list of "positions", + // there's not much that can be done about this + // other than assuming that identical position means + // identical vertex. + continue; + } + subdef_indices.push_back(vi); + subdef_weights.push_back(b->mWeights[wi].mWeight); + last_index = vi; + } + // yes, "indexes" + sdnode.AddChild("Indexes", subdef_indices); + sdnode.AddChild("Weights", subdef_weights); + } + + // transform is the transform of the mesh, but in bone space. + // if the skeleton is in the bind pose, + // we can take the inverse of the world-space bone transform + // and multiply by the world-space transform of the mesh. + aiMatrix4x4 bone_xform = get_world_transform(bone_node, mScene); + aiMatrix4x4 inverse_bone_xform = bone_xform; + inverse_bone_xform.Inverse(); + aiMatrix4x4 tr = inverse_bone_xform * mesh_xform; + + // this should be the same as the bone's mOffsetMatrix. + // if it's not the same, the skeleton isn't in the bind pose. + const float epsilon = 1e-4f; // some error is to be expected + bool bone_xform_okay = true; + if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) { + not_in_bind_pose.insert(b); + bone_xform_okay = false; + } + + // if we have a bone we should use the mOffsetMatrix, + // otherwise try to just use the calculated transform. + if (b) { + sdnode.AddChild("Transform", b->mOffsetMatrix); + } else { + sdnode.AddChild("Transform", tr); + } + // note: it doesn't matter if we mix these, + // because if they disagree we'll throw an exception later. + // it could be that the skeleton is not in the bone pose + // but all bones are still defined, + // in which case this would use the mOffsetMatrix for everything + // and a correct skeleton would still be output. + + // transformlink should be the position of the bone in world space. + // if the bone is in the bind pose (or nonexistent), + // we can just use the matrix we already calculated + if (bone_xform_okay) { + sdnode.AddChild("TransformLink", bone_xform); + // otherwise we can only work it out using the mesh position. + } else { + aiMatrix4x4 trl = b->mOffsetMatrix; + trl.Inverse(); + trl *= mesh_xform; + sdnode.AddChild("TransformLink", trl); + } + // note: this means we ALWAYS rely on the mesh node transform + // being unchanged from the time the skeleton was bound. + // there's not really any way around this at the moment. + + // done + sdnode.Dump(outstream, binary, indent); + + // lastly, connect to the parent deformer + connections.emplace_back( + "C", "OO", subdeformer_uid, deformer_uid + ); + + // we also need to connect the limb node to the subdeformer. + connections.emplace_back( + "C", "OO", node_uids[bone_node], subdeformer_uid + ); + } + + // if we cannot create a valid FBX file, simply die. + // this will both prevent unnecessary bug reports, + // and tell the user what they can do to fix the situation + // (i.e. export their model in the bind pose). + if (no_offset_matrix.size() && not_in_bind_pose.size()) { + std::stringstream err; + err << "Not enough information to construct bind pose"; + err << " for mesh " << mi << "!"; + err << " Transform matrix for bone \""; + err << (*not_in_bind_pose.begin())->mName.C_Str() << "\""; + if (not_in_bind_pose.size() > 1) { + err << " (and " << not_in_bind_pose.size() - 1 << " more)"; + } + err << " does not match mOffsetMatrix,"; + err << " and node \""; + err << (*no_offset_matrix.begin())->mName.C_Str() << "\""; + if (no_offset_matrix.size() > 1) { + err << " (and " << no_offset_matrix.size() - 1 << " more)"; + } + err << " has no offset matrix to rely on."; + err << " Please ensure bones are in the bind pose to export."; + throw DeadlyExportError(err.str()); + } + + } + + // BindPose + // + // This is a legacy system, which should be unnecessary. + // + // Somehow including it slows file loading by the official FBX SDK, + // and as it can reconstruct it from the deformers anyway, + // this is not currently included. + // + // The code is kept here in case it's useful in the future, + // but it's pretty much a hack anyway, + // as assimp doesn't store bindpose information for full skeletons. + // + /*for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + aiMesh* mesh = mScene->mMeshes[mi]; + if (! mesh->HasBones()) { continue; } + int64_t bindpose_uid = generate_uid(); + FBX::Node bpnode("Pose"); + bpnode.AddProperty(bindpose_uid); + // note: this uid is never linked or connected to anything. + bpnode.AddProperty(FBX::SEPARATOR + "Pose"); // blank name + bpnode.AddProperty("BindPose"); + + bpnode.AddChild("Type", "BindPose"); + bpnode.AddChild("Version", int32_t(100)); + + aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode); + + // next get the whole skeleton for this mesh. + // we need it all to define the bindpose section. + // the FBX SDK will complain if it's missing, + // and also if parents of used bones don't have a subdeformer. + // order shouldn't matter. + std::set<aiNode*> skeleton; + for (size_t bi = 0; bi < mesh->mNumBones; ++bi) { + // bone node should have already been indexed + const aiBone* b = mesh->mBones[bi]; + const std::string bone_name(b->mName.C_Str()); + aiNode* parent = node_by_bone[bone_name]; + // insert all nodes down to the root or mesh node + while ( + parent + && parent != mScene->mRootNode + && parent != mesh_node + ) { + skeleton.insert(parent); + parent = parent->mParent; + } + } + + // number of pose nodes. includes one for the mesh itself. + bpnode.AddChild("NbPoseNodes", int32_t(1 + skeleton.size())); + + // the first pose node is always the mesh itself + FBX::Node pose("PoseNode"); + pose.AddChild("Node", mesh_uids[mi]); + aiMatrix4x4 mesh_node_xform = get_world_transform(mesh_node, mScene); + pose.AddChild("Matrix", mesh_node_xform); + bpnode.AddChild(pose); + + for (aiNode* bonenode : skeleton) { + // does this node have a uid yet? + int64_t node_uid; + auto node_uid_iter = node_uids.find(bonenode); + if (node_uid_iter != node_uids.end()) { + node_uid = node_uid_iter->second; + } else { + node_uid = generate_uid(); + node_uids[bonenode] = node_uid; + } + + // make a pose thingy + pose = FBX::Node("PoseNode"); + pose.AddChild("Node", node_uid); + aiMatrix4x4 node_xform = get_world_transform(bonenode, mScene); + pose.AddChild("Matrix", node_xform); + bpnode.AddChild(pose); + } + + // now write it + bpnode.Dump(outstream, binary, indent); + }*/ + + // TODO: cameras, lights + + // write nodes (i.e. model hierarchy) + // start at root node + WriteModelNodes( + outstream, mScene->mRootNode, 0, limbnodes + ); + + // animations + // + // in FBX there are: + // * AnimationStack - corresponds to an aiAnimation + // * AnimationLayer - a combinable animation component + // * AnimationCurveNode - links the property to be animated + // * AnimationCurve - defines animation data for a single property value + // + // the CurveNode also provides the default value for a property, + // such as the X, Y, Z coordinates for animatable translation. + // + // the Curve only specifies values for one component of the property, + // so there will be a separate AnimationCurve for X, Y, and Z. + // + // Assimp has: + // * aiAnimation - basically corresponds to an AnimationStack + // * aiNodeAnim - defines all animation for one aiNode + // * aiVectorKey/aiQuatKey - define the keyframe data for T/R/S + // + // assimp has no equivalent for AnimationLayer, + // and these are flattened on FBX import. + // we can assume there will be one per AnimationStack. + // + // the aiNodeAnim contains all animation data for a single aiNode, + // which will correspond to three AnimationCurveNode's: + // one each for translation, rotation and scale. + // The data for each of these will be put in 9 AnimationCurve's, + // T.X, T.Y, T.Z, R.X, R.Y, R.Z, etc. + + // AnimationStack / aiAnimation + std::vector<int64_t> animation_stack_uids(mScene->mNumAnimations); + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + int64_t animstack_uid = generate_uid(); + animation_stack_uids[ai] = animstack_uid; + const aiAnimation* anim = mScene->mAnimations[ai]; + + FBX::Node asnode("AnimationStack"); + std::string name = anim->mName.C_Str() + FBX::SEPARATOR + "AnimStack"; + asnode.AddProperties(animstack_uid, name, ""); + FBX::Node p("Properties70"); + p.AddP70time("LocalStart", 0); // assimp doesn't store this + p.AddP70time("LocalStop", to_ktime(anim->mDuration, anim)); + p.AddP70time("ReferenceStart", 0); + p.AddP70time("ReferenceStop", to_ktime(anim->mDuration, anim)); + asnode.AddChild(p); + + // this node absurdly always pretends it has children + // (in this case it does, but just in case...) + asnode.force_has_children = true; + asnode.Dump(outstream, binary, indent); + + // note: animation stacks are not connected to anything + } + + // AnimationLayer - one per aiAnimation + std::vector<int64_t> animation_layer_uids(mScene->mNumAnimations); + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + int64_t animlayer_uid = generate_uid(); + animation_layer_uids[ai] = animlayer_uid; + FBX::Node alnode("AnimationLayer"); + alnode.AddProperties(animlayer_uid, FBX::SEPARATOR + "AnimLayer", ""); + + // this node absurdly always pretends it has children + alnode.force_has_children = true; + alnode.Dump(outstream, binary, indent); + + // connect to the relevant animstack + connections.emplace_back( + "C", "OO", animlayer_uid, animation_stack_uids[ai] + ); + } + + // AnimCurveNode - three per aiNodeAnim + std::vector<std::vector<std::array<int64_t,3>>> curve_node_uids; + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + const aiAnimation* anim = mScene->mAnimations[ai]; + const int64_t layer_uid = animation_layer_uids[ai]; + std::vector<std::array<int64_t,3>> nodeanim_uids; + for (size_t nai = 0; nai < anim->mNumChannels; ++nai) { + const aiNodeAnim* na = anim->mChannels[nai]; + // get the corresponding aiNode + const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName); + // and its transform + const aiMatrix4x4 node_xfm = get_world_transform(node, mScene); + aiVector3D T, R, S; + node_xfm.Decompose(S, R, T); + + // AnimationCurveNode uids + std::array<int64_t,3> ids; + ids[0] = generate_uid(); // T + ids[1] = generate_uid(); // R + ids[2] = generate_uid(); // S + + // translation + WriteAnimationCurveNode(outstream, + ids[0], "T", T, "Lcl Translation", + layer_uid, node_uids[node] + ); + + // rotation + WriteAnimationCurveNode(outstream, + ids[1], "R", R, "Lcl Rotation", + layer_uid, node_uids[node] + ); + + // scale + WriteAnimationCurveNode(outstream, + ids[2], "S", S, "Lcl Scale", + layer_uid, node_uids[node] + ); + + // store the uids for later use + nodeanim_uids.push_back(ids); + } + curve_node_uids.push_back(nodeanim_uids); + } + + // AnimCurve - defines actual keyframe data. + // there's a separate curve for every component of every vector, + // for example a transform curvenode will have separate X/Y/Z AnimCurve's + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + const aiAnimation* anim = mScene->mAnimations[ai]; + for (size_t nai = 0; nai < anim->mNumChannels; ++nai) { + const aiNodeAnim* na = anim->mChannels[nai]; + // get the corresponding aiNode + const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName); + // and its transform + const aiMatrix4x4 node_xfm = get_world_transform(node, mScene); + aiVector3D T, R, S; + node_xfm.Decompose(S, R, T); + const std::array<int64_t,3>& ids = curve_node_uids[ai][nai]; + + std::vector<int64_t> times; + std::vector<float> xval, yval, zval; + + // position/translation + for (size_t ki = 0; ki < na->mNumPositionKeys; ++ki) { + const aiVectorKey& k = na->mPositionKeys[ki]; + times.push_back(to_ktime(k.mTime)); + xval.push_back(k.mValue.x); + yval.push_back(k.mValue.y); + zval.push_back(k.mValue.z); + } + // one curve each for X, Y, Z + WriteAnimationCurve(outstream, T.x, times, xval, ids[0], "d|X"); + WriteAnimationCurve(outstream, T.y, times, yval, ids[0], "d|Y"); + WriteAnimationCurve(outstream, T.z, times, zval, ids[0], "d|Z"); + + // rotation + times.clear(); xval.clear(); yval.clear(); zval.clear(); + for (size_t ki = 0; ki < na->mNumRotationKeys; ++ki) { + const aiQuatKey& k = na->mRotationKeys[ki]; + times.push_back(to_ktime(k.mTime)); + // TODO: aiQuaternion method to convert to Euler... + aiMatrix4x4 m(k.mValue.GetMatrix()); + aiVector3D qs, qr, qt; + m.Decompose(qs, qr, qt); + qr *= DEG; + xval.push_back(qr.x); + yval.push_back(qr.y); + zval.push_back(qr.z); + } + WriteAnimationCurve(outstream, R.x, times, xval, ids[1], "d|X"); + WriteAnimationCurve(outstream, R.y, times, yval, ids[1], "d|Y"); + WriteAnimationCurve(outstream, R.z, times, zval, ids[1], "d|Z"); + + // scaling/scale + times.clear(); xval.clear(); yval.clear(); zval.clear(); + for (size_t ki = 0; ki < na->mNumScalingKeys; ++ki) { + const aiVectorKey& k = na->mScalingKeys[ki]; + times.push_back(to_ktime(k.mTime)); + xval.push_back(k.mValue.x); + yval.push_back(k.mValue.y); + zval.push_back(k.mValue.z); + } + WriteAnimationCurve(outstream, S.x, times, xval, ids[2], "d|X"); + WriteAnimationCurve(outstream, S.y, times, yval, ids[2], "d|Y"); + WriteAnimationCurve(outstream, S.z, times, zval, ids[2], "d|Z"); + } + } + + indent = 0; + object_node.End(outstream, binary, indent, true); +} + +// convenience map of magic node name strings to FBX properties, +// including the expected type of transform. +const std::map<std::string,std::pair<std::string,char>> transform_types = { + {"Translation", {"Lcl Translation", 't'}}, + {"RotationOffset", {"RotationOffset", 't'}}, + {"RotationPivot", {"RotationPivot", 't'}}, + {"PreRotation", {"PreRotation", 'r'}}, + {"Rotation", {"Lcl Rotation", 'r'}}, + {"PostRotation", {"PostRotation", 'r'}}, + {"RotationPivotInverse", {"RotationPivotInverse", 'i'}}, + {"ScalingOffset", {"ScalingOffset", 't'}}, + {"ScalingPivot", {"ScalingPivot", 't'}}, + {"Scaling", {"Lcl Scaling", 's'}}, + {"ScalingPivotInverse", {"ScalingPivotInverse", 'i'}}, + {"GeometricScaling", {"GeometricScaling", 's'}}, + {"GeometricRotation", {"GeometricRotation", 'r'}}, + {"GeometricTranslation", {"GeometricTranslation", 't'}}, + {"GeometricTranslationInverse", {"GeometricTranslationInverse", 'i'}}, + {"GeometricRotationInverse", {"GeometricRotationInverse", 'i'}}, + {"GeometricScalingInverse", {"GeometricScalingInverse", 'i'}} +}; + +// write a single model node to the stream +void FBXExporter::WriteModelNode( + StreamWriterLE& outstream, + bool binary, + const aiNode* node, + int64_t node_uid, + const std::string& type, + const std::vector<std::pair<std::string,aiVector3D>>& transform_chain, + TransformInheritance inherit_type +){ + const aiVector3D zero = {0, 0, 0}; + const aiVector3D one = {1, 1, 1}; + FBX::Node m("Model"); + std::string name = node->mName.C_Str() + FBX::SEPARATOR + "Model"; + m.AddProperties(node_uid, name, type); + m.AddChild("Version", int32_t(232)); + FBX::Node p("Properties70"); + p.AddP70bool("RotationActive", 1); + p.AddP70int("DefaultAttributeIndex", 0); + p.AddP70enum("InheritType", inherit_type); + if (transform_chain.empty()) { + // decompose 4x4 transform matrix into TRS + aiVector3D t, r, s; + node->mTransformation.Decompose(s, r, t); + if (t != zero) { + p.AddP70( + "Lcl Translation", "Lcl Translation", "", "A", + double(t.x), double(t.y), double(t.z) + ); + } + if (r != zero) { + p.AddP70( + "Lcl Rotation", "Lcl Rotation", "", "A", + double(DEG*r.x), double(DEG*r.y), double(DEG*r.z) + ); + } + if (s != one) { + p.AddP70( + "Lcl Scaling", "Lcl Scaling", "", "A", + double(s.x), double(s.y), double(s.z) + ); + } + } else { + // apply the transformation chain. + // these transformation elements are created when importing FBX, + // which has a complex transformation hierarchy for each node. + // as such we can bake the hierarchy back into the node on export. + for (auto &item : transform_chain) { + auto elem = transform_types.find(item.first); + if (elem == transform_types.end()) { + // then this is a bug + std::stringstream err; + err << "unrecognized FBX transformation type: "; + err << item.first; + throw DeadlyExportError(err.str()); + } + const std::string &name = elem->second.first; + const aiVector3D &v = item.second; + if (name.compare(0, 4, "Lcl ") == 0) { + // special handling for animatable properties + p.AddP70( + name, name, "", "A", + double(v.x), double(v.y), double(v.z) + ); + } else { + p.AddP70vector(name, v.x, v.y, v.z); + } + } + } + m.AddChild(p); + + // not sure what these are for, + // but they seem to be omnipresent + m.AddChild("Shading", Property(true)); + m.AddChild("Culling", Property("CullingOff")); + + m.Dump(outstream, binary, 1); +} + +// wrapper for WriteModelNodes to create and pass a blank transform chain +void FBXExporter::WriteModelNodes( + StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set<const aiNode*>& limbnodes +) { + std::vector<std::pair<std::string,aiVector3D>> chain; + WriteModelNodes(s, node, parent_uid, limbnodes, chain); +} + +void FBXExporter::WriteModelNodes( + StreamWriterLE& outstream, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set<const aiNode*>& limbnodes, + std::vector<std::pair<std::string,aiVector3D>>& transform_chain +) { + // first collapse any expanded transformation chains created by FBX import. + std::string node_name(node->mName.C_Str()); + if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { + auto pos = node_name.find(MAGIC_NODE_TAG) + MAGIC_NODE_TAG.size() + 1; + std::string type_name = node_name.substr(pos); + auto elem = transform_types.find(type_name); + if (elem == transform_types.end()) { + // then this is a bug and should be fixed + std::stringstream err; + err << "unrecognized FBX transformation node"; + err << " of type " << type_name << " in node " << node_name; + throw DeadlyExportError(err.str()); + } + aiVector3D t, r, s; + node->mTransformation.Decompose(s, r, t); + switch (elem->second.second) { + case 'i': // inverse + // we don't need to worry about the inverse matrices + break; + case 't': // translation + transform_chain.emplace_back(elem->first, t); + break; + case 'r': // rotation + r *= float(DEG); + transform_chain.emplace_back(elem->first, r); + break; + case 's': // scale + transform_chain.emplace_back(elem->first, s); + break; + default: + // this should never happen + std::stringstream err; + err << "unrecognized FBX transformation type code: "; + err << elem->second.second; + throw DeadlyExportError(err.str()); + } + // now continue on to any child nodes + for (unsigned i = 0; i < node->mNumChildren; ++i) { + WriteModelNodes( + outstream, + node->mChildren[i], + parent_uid, + limbnodes, + transform_chain + ); + } + return; + } + + int64_t node_uid = 0; + // generate uid and connect to parent, if not the root node, + if (node != mScene->mRootNode) { + auto elem = node_uids.find(node); + if (elem != node_uids.end()) { + node_uid = elem->second; + } else { + node_uid = generate_uid(); + node_uids[node] = node_uid; + } + connections.emplace_back("C", "OO", node_uid, parent_uid); + } + + // what type of node is this? + if (node == mScene->mRootNode) { + // handled later + } else if (node->mNumMeshes == 1) { + // connect to child mesh, which should have been written previously + connections.emplace_back( + "C", "OO", mesh_uids[node->mMeshes[0]], node_uid + ); + // also connect to the material for the child mesh + connections.emplace_back( + "C", "OO", + material_uids[mScene->mMeshes[node->mMeshes[0]]->mMaterialIndex], + node_uid + ); + // write model node + WriteModelNode( + outstream, binary, node, node_uid, "Mesh", transform_chain + ); + } else if (limbnodes.count(node)) { + WriteModelNode( + outstream, binary, node, node_uid, "LimbNode", transform_chain + ); + // we also need to write a nodeattribute to mark it as a skeleton + int64_t node_attribute_uid = generate_uid(); + FBX::Node na("NodeAttribute"); + na.AddProperties( + node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode" + ); + na.AddChild("TypeFlags", Property("Skeleton")); + na.Dump(outstream, binary, 1); + // and connect them + connections.emplace_back("C", "OO", node_attribute_uid, node_uid); + } else { + // generate a null node so we can add children to it + WriteModelNode( + outstream, binary, node, node_uid, "Null", transform_chain + ); + } + + // if more than one child mesh, make nodes for each mesh + if (node->mNumMeshes > 1 || node == mScene->mRootNode) { + for (size_t i = 0; i < node->mNumMeshes; ++i) { + // make a new model node + int64_t new_node_uid = generate_uid(); + // connect to parent node + connections.emplace_back("C", "OO", new_node_uid, node_uid); + // connect to child mesh, which should have been written previously + connections.emplace_back( + "C", "OO", mesh_uids[node->mMeshes[i]], new_node_uid + ); + // also connect to the material for the child mesh + connections.emplace_back( + "C", "OO", + material_uids[ + mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex + ], + new_node_uid + ); + // write model node + FBX::Node m("Model"); + // take name from mesh name, if it exists + std::string name = mScene->mMeshes[node->mMeshes[i]]->mName.C_Str(); + name += FBX::SEPARATOR + "Model"; + m.AddProperties(new_node_uid, name, "Mesh"); + m.AddChild("Version", int32_t(232)); + FBX::Node p("Properties70"); + p.AddP70enum("InheritType", 1); + m.AddChild(p); + m.Dump(outstream, binary, 1); + } + } + + // now recurse into children + for (size_t i = 0; i < node->mNumChildren; ++i) { + WriteModelNodes( + outstream, node->mChildren[i], node_uid, limbnodes + ); + } +} + + +void FBXExporter::WriteAnimationCurveNode( + StreamWriterLE& outstream, + int64_t uid, + std::string name, // "T", "R", or "S" + aiVector3D default_value, + std::string property_name, // "Lcl Translation" etc + int64_t layer_uid, + int64_t node_uid +) { + FBX::Node n("AnimationCurveNode"); + n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", ""); + FBX::Node p("Properties70"); + p.AddP70numberA("d|X", default_value.x); + p.AddP70numberA("d|Y", default_value.y); + p.AddP70numberA("d|Z", default_value.z); + n.AddChild(p); + n.Dump(outstream, binary, 1); + // connect to layer + this->connections.emplace_back("C", "OO", uid, layer_uid); + // connect to bone + this->connections.emplace_back("C", "OP", uid, node_uid, property_name); +} + + +void FBXExporter::WriteAnimationCurve( + StreamWriterLE& outstream, + double default_value, + const std::vector<int64_t>& times, + const std::vector<float>& values, + int64_t curvenode_uid, + const std::string& property_link // "d|X", "d|Y", etc +) { + FBX::Node n("AnimationCurve"); + int64_t curve_uid = generate_uid(); + n.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + n.AddChild("Default", default_value); + n.AddChild("KeyVer", int32_t(4009)); + n.AddChild("KeyTime", times); + n.AddChild("KeyValueFloat", values); + // TODO: keyattr flags and data (STUB for now) + n.AddChild("KeyAttrFlags", std::vector<int32_t>{0}); + n.AddChild("KeyAttrDataFloat", std::vector<float>{0,0,0,0}); + n.AddChild( + "KeyAttrRefCount", + std::vector<int32_t>{static_cast<int32_t>(times.size())} + ); + n.Dump(outstream, binary, 1); + this->connections.emplace_back( + "C", "OP", curve_uid, curvenode_uid, property_link + ); +} + + +void FBXExporter::WriteConnections () +{ + // we should have completed the connection graph already, + // so basically just dump it here + if (!binary) { + WriteAsciiSectionHeader("Object connections"); + } + // TODO: comments with names in the ascii version + FBX::Node conn("Connections"); + StreamWriterLE outstream(outfile); + conn.Begin(outstream, binary, 0); + conn.BeginChildren(outstream, binary, 0); + for (auto &n : connections) { + n.Dump(outstream, binary, 1); + } + conn.End(outstream, binary, 0, !connections.empty()); + connections.clear(); +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/thirdparty/assimp/code/FBXExporter.h b/thirdparty/assimp/code/FBXExporter.h new file mode 100644 index 0000000000..71fb55c57f --- /dev/null +++ b/thirdparty/assimp/code/FBXExporter.h @@ -0,0 +1,178 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXExporter.h +* Declares the exporter class to write a scene to an fbx file +*/ +#ifndef AI_FBXEXPORTER_H_INC +#define AI_FBXEXPORTER_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportNode.h" // FBX::Node +#include "FBXCommon.h" // FBX::TransformInheritance + +#include <assimp/types.h> +//#include <assimp/material.h> +#include <assimp/StreamWriter.h> // StreamWriterLE +#include <assimp/Exceptional.h> // DeadlyExportError + +#include <vector> +#include <map> +#include <unordered_set> +#include <memory> // shared_ptr +#include <sstream> // stringstream + +struct aiScene; +struct aiNode; +//struct aiMaterial; + +namespace Assimp +{ + class IOSystem; + class IOStream; + class ExportProperties; + + // --------------------------------------------------------------------- + /** Helper class to export a given scene to an FBX file. */ + // --------------------------------------------------------------------- + class FBXExporter + { + public: + /// Constructor for a specific scene to export + FBXExporter(const aiScene* pScene, const ExportProperties* pProperties); + + // call one of these methods to export + void ExportBinary(const char* pFile, IOSystem* pIOSystem); + void ExportAscii(const char* pFile, IOSystem* pIOSystem); + + private: + bool binary; // whether current export is in binary or ascii format + const aiScene* mScene; // the scene to export + const ExportProperties* mProperties; // currently unused + std::shared_ptr<IOStream> outfile; // file to write to + + std::vector<FBX::Node> connections; // connection storage + + std::vector<int64_t> mesh_uids; + std::vector<int64_t> material_uids; + std::map<const aiNode*,int64_t> node_uids; + + // this crude unique-ID system is actually fine + int64_t last_uid = 999999; + int64_t generate_uid() { return ++last_uid; } + + // binary files have a specific header and footer, + // in addition to the actual data + void WriteBinaryHeader(); + void WriteBinaryFooter(); + + // ascii files have a comment at the top + void WriteAsciiHeader(); + + // WriteAllNodes does the actual export. + // It just calls all the Write<Section> methods below in order. + void WriteAllNodes(); + + // Methods to write individual sections. + // The order here matches the order inside an FBX file. + // Each method corresponds to a top-level FBX section, + // except WriteHeader which also includes some binary-only sections + // and WriteFooter which is binary data only. + void WriteHeaderExtension(); + // WriteFileId(); // binary-only, included in WriteHeader + // WriteCreationTime(); // binary-only, included in WriteHeader + // WriteCreator(); // binary-only, included in WriteHeader + void WriteGlobalSettings(); + void WriteDocuments(); + void WriteReferences(); + void WriteDefinitions(); + void WriteObjects(); + void WriteConnections(); + // WriteTakes(); // deprecated since at least 2015 (fbx 7.4) + + // helpers + void WriteAsciiSectionHeader(const std::string& title); + void WriteModelNodes( + Assimp::StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set<const aiNode*>& limbnodes + ); + void WriteModelNodes( // usually don't call this directly + StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set<const aiNode*>& limbnodes, + std::vector<std::pair<std::string,aiVector3D>>& transform_chain + ); + void WriteModelNode( // nor this + StreamWriterLE& s, + bool binary, + const aiNode* node, + int64_t node_uid, + const std::string& type, + const std::vector<std::pair<std::string,aiVector3D>>& xfm_chain, + FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs + ); + void WriteAnimationCurveNode( + StreamWriterLE& outstream, + int64_t uid, + std::string name, // "T", "R", or "S" + aiVector3D default_value, + std::string property_name, // "Lcl Translation" etc + int64_t animation_layer_uid, + int64_t node_uid + ); + void WriteAnimationCurve( + StreamWriterLE& outstream, + double default_value, + const std::vector<int64_t>& times, + const std::vector<float>& values, + int64_t curvenode_id, + const std::string& property_link // "d|X", "d|Y", etc + ); + }; +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTER_H_INC diff --git a/thirdparty/assimp/code/FBXImportSettings.h b/thirdparty/assimp/code/FBXImportSettings.h new file mode 100644 index 0000000000..d5e1c20608 --- /dev/null +++ b/thirdparty/assimp/code/FBXImportSettings.h @@ -0,0 +1,153 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXImportSettings.h + * @brief FBX importer runtime configuration + */ +#ifndef INCLUDED_AI_FBX_IMPORTSETTINGS_H +#define INCLUDED_AI_FBX_IMPORTSETTINGS_H + +namespace Assimp { +namespace FBX { + +/** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */ +struct ImportSettings +{ + ImportSettings() + : strictMode(true) + , readAllLayers(true) + , readAllMaterials(false) + , readMaterials(true) + , readTextures(true) + , readCameras(true) + , readLights(true) + , readAnimations(true) + , readWeights(true) + , preservePivots(true) + , optimizeEmptyAnimationCurves(true) + , useLegacyEmbeddedTextureNaming(false) + {} + + + /** enable strict mode: + * - only accept fbx 2012, 2013 files + * - on the slightest error, give up. + * + * Basically, strict mode means that the fbx file will actually + * be validated. Strict mode is off by default. */ + bool strictMode; + + /** specifies whether all geometry layers are read and scanned for + * usable data channels. The FBX spec indicates that many readers + * will only read the first channel and that this is in some way + * the recommended way- in reality, however, it happens a lot that + * vertex data is spread among multiple layers. The default + * value for this option is true.*/ + bool readAllLayers; + + /** specifies whether all materials are read, or only those that + * are referenced by at least one mesh. Reading all materials + * may make FBX reading a lot slower since all objects + * need to be processed . + * This bit is ignored unless readMaterials=true*/ + bool readAllMaterials; + + + /** import materials (true) or skip them and assign a default + * material. The default value is true.*/ + bool readMaterials; + + /** import embedded textures? Default value is true.*/ + bool readTextures; + + /** import cameras? Default value is true.*/ + bool readCameras; + + /** import light sources? Default value is true.*/ + bool readLights; + + /** import animations (i.e. animation curves, the node + * skeleton is always imported). Default value is true. */ + bool readAnimations; + + /** read bones (vertex weights and deform info). + * Default value is true. */ + bool readWeights; + + /** preserve transformation pivots and offsets. Since these can + * not directly be represented in assimp, additional dummy + * nodes will be generated. Note that settings this to false + * can make animation import a lot slower. The default value + * is true. + * + * The naming scheme for the generated nodes is: + * <OriginalName>_$AssimpFbx$_<TransformName> + * + * where <TransformName> is one of + * RotationPivot + * RotationOffset + * PreRotation + * PostRotation + * ScalingPivot + * ScalingOffset + * Translation + * Scaling + * Rotation + **/ + bool preservePivots; + + /** do not import animation curves that specify a constant + * values matching the corresponding node transformation. + * The default value is true. */ + bool optimizeEmptyAnimationCurves; + + /** use legacy naming for embedded textures eg: (*0, *1, *2) + **/ + bool useLegacyEmbeddedTextureNaming; +}; + + +} // !FBX +} // !Assimp + +#endif + diff --git a/thirdparty/assimp/code/FBXImporter.cpp b/thirdparty/assimp/code/FBXImporter.cpp new file mode 100644 index 0000000000..2cc8bffc29 --- /dev/null +++ b/thirdparty/assimp/code/FBXImporter.cpp @@ -0,0 +1,197 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. +r +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXImporter.cpp + * @brief Implementation of the FBX importer. + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXImporter.h" + +#include "FBXTokenizer.h" +#include "FBXParser.h" +#include "FBXUtil.h" +#include "FBXDocument.h" +#include "FBXConverter.h" + +#include <assimp/StreamReader.h> +#include <assimp/MemoryIOWrapper.h> +#include <assimp/Importer.hpp> +#include <assimp/importerdesc.h> + +namespace Assimp { + +template<> +const char* LogFunctions<FBXImporter>::Prefix() { + static auto prefix = "FBX: "; + return prefix; +} + +} + +using namespace Assimp; +using namespace Assimp::Formatter; +using namespace Assimp::FBX; + +namespace { + +static const aiImporterDesc desc = { + "Autodesk FBX Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "fbx" +}; +} + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by #Importer +FBXImporter::FBXImporter() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FBXImporter::~FBXImporter() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +{ + const std::string& extension = GetExtension(pFile); + if (extension == std::string( desc.mFileExtensions ) ) { + return true; + } + + else if ((!extension.length() || checkSig) && pIOHandler) { + // at least ASCII-FBX files usually have a 'FBX' somewhere in their head + const char* tokens[] = {"fbx"}; + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// List all extensions handled by this loader +const aiImporterDesc* FBXImporter::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration properties for the loader +void FBXImporter::SetupProperties(const Importer* pImp) +{ + settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); + settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); + settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); + settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true); + settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true); + settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true); + settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true); + settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false); + settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); + settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true); + settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false); +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + std::unique_ptr<IOStream> stream(pIOHandler->Open(pFile,"rb")); + if (!stream) { + ThrowException("Could not open file for reading"); + } + + // read entire file into memory - no streaming for this, fbx + // files can grow large, but the assimp output data structure + // then becomes very large, too. Assimp doesn't support + // streaming for its output data structures so the net win with + // streaming input data would be very low. + std::vector<char> contents; + contents.resize(stream->FileSize()+1); + stream->Read( &*contents.begin(), 1, contents.size()-1 ); + contents[ contents.size() - 1 ] = 0; + const char* const begin = &*contents.begin(); + + // broadphase tokenizing pass in which we identify the core + // syntax elements of FBX (brackets, commas, key:value mappings) + TokenList tokens; + try { + + bool is_binary = false; + if (!strncmp(begin,"Kaydara FBX Binary",18)) { + is_binary = true; + TokenizeBinary(tokens,begin,static_cast<unsigned int>(contents.size())); + } + else { + Tokenize(tokens,begin); + } + + // use this information to construct a very rudimentary + // parse-tree representing the FBX scope structure + Parser parser(tokens, is_binary); + + // take the raw parse-tree and convert it to a FBX DOM + Document doc(parser,settings); + + // convert the FBX DOM to aiScene + ConvertToAssimpScene(pScene,doc); + + std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>()); + } + catch(std::exception&) { + std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>()); + throw; + } +} + +#endif // !ASSIMP_BUILD_NO_FBX_IMPORTER diff --git a/thirdparty/assimp/code/FBXImporter.h b/thirdparty/assimp/code/FBXImporter.h new file mode 100644 index 0000000000..c365b2cddf --- /dev/null +++ b/thirdparty/assimp/code/FBXImporter.h @@ -0,0 +1,100 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXImporter.h + * @brief Declaration of the FBX main importer class + */ +#ifndef INCLUDED_AI_FBX_IMPORTER_H +#define INCLUDED_AI_FBX_IMPORTER_H + +#include <assimp/BaseImporter.h> +#include <assimp/LogAux.h> + +#include "FBXImportSettings.h" + +namespace Assimp { + +// TinyFormatter.h +namespace Formatter { + template <typename T,typename TR, typename A> class basic_formatter; + typedef class basic_formatter< char, std::char_traits<char>, std::allocator<char> > format; +} + +// ------------------------------------------------------------------------------------------- +/** Load the Autodesk FBX file format. + + See http://en.wikipedia.org/wiki/FBX +*/ +// ------------------------------------------------------------------------------------------- +class FBXImporter : public BaseImporter, public LogFunctions<FBXImporter> +{ +public: + FBXImporter(); + virtual ~FBXImporter(); + + // -------------------- + bool CanRead( const std::string& pFile, + IOSystem* pIOHandler, + bool checkSig + ) const; + +protected: + + // -------------------- + const aiImporterDesc* GetInfo () const; + + // -------------------- + void SetupProperties(const Importer* pImp); + + // -------------------- + void InternReadFile( const std::string& pFile, + aiScene* pScene, + IOSystem* pIOHandler + ); + +private: + FBX::ImportSettings settings; +}; // !class FBXImporter + +} // end of namespace Assimp +#endif // !INCLUDED_AI_FBX_IMPORTER_H + diff --git a/thirdparty/assimp/code/FBXMaterial.cpp b/thirdparty/assimp/code/FBXMaterial.cpp new file mode 100644 index 0000000000..08347740fa --- /dev/null +++ b/thirdparty/assimp/code/FBXMaterial.cpp @@ -0,0 +1,351 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXMaterial.cpp + * @brief Assimp::FBX::Material and Assimp::FBX::Texture implementation + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXImporter.h" +#include "FBXImportSettings.h" +#include "FBXDocumentUtil.h" +#include "FBXProperties.h" +#include <assimp/ByteSwapper.h> + +#include <algorithm> // std::transform + +namespace Assimp { +namespace FBX { + + using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Material::Material(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Object(id,element,name) +{ + const Scope& sc = GetRequiredScope(element); + + const Element* const ShadingModel = sc["ShadingModel"]; + const Element* const MultiLayer = sc["MultiLayer"]; + + if(MultiLayer) { + multilayer = !!ParseTokenAsInt(GetRequiredToken(*MultiLayer,0)); + } + + if(ShadingModel) { + shading = ParseTokenAsString(GetRequiredToken(*ShadingModel,0)); + } + else { + DOMWarning("shading mode not specified, assuming phong",&element); + shading = "phong"; + } + + std::string templateName; + + // lower-case shading because Blender (for example) writes "Phong" + std::transform(shading.begin(), shading.end(), shading.begin(), ::tolower); + if(shading == "phong") { + templateName = "Material.FbxSurfacePhong"; + } + else if(shading == "lambert") { + templateName = "Material.FbxSurfaceLambert"; + } + else { + DOMWarning("shading mode not recognized: " + shading,&element); + } + + props = GetPropertyTable(doc,templateName,element,sc); + + // resolve texture links + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID()); + for(const Connection* con : conns) { + + // texture link to properties, not objects + if (!con->PropertyName().length()) { + continue; + } + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for texture link, ignoring",&element); + continue; + } + + const Texture* const tex = dynamic_cast<const Texture*>(ob); + if(!tex) { + const LayeredTexture* const layeredTexture = dynamic_cast<const LayeredTexture*>(ob); + if(!layeredTexture) { + DOMWarning("source object for texture link is not a texture or layered texture, ignoring",&element); + continue; + } + const std::string& prop = con->PropertyName(); + if (layeredTextures.find(prop) != layeredTextures.end()) { + DOMWarning("duplicate layered texture link: " + prop,&element); + } + + layeredTextures[prop] = layeredTexture; + ((LayeredTexture*)layeredTexture)->fillTexture(doc); + } + else + { + const std::string& prop = con->PropertyName(); + if (textures.find(prop) != textures.end()) { + DOMWarning("duplicate texture link: " + prop,&element); + } + + textures[prop] = tex; + } + + } +} + + +// ------------------------------------------------------------------------------------------------ +Material::~Material() +{ +} + + +// ------------------------------------------------------------------------------------------------ +Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Object(id,element,name) +, uvScaling(1.0f,1.0f) +, media(0) +{ + const Scope& sc = GetRequiredScope(element); + + const Element* const Type = sc["Type"]; + const Element* const FileName = sc["FileName"]; + const Element* const RelativeFilename = sc["RelativeFilename"]; + const Element* const ModelUVTranslation = sc["ModelUVTranslation"]; + const Element* const ModelUVScaling = sc["ModelUVScaling"]; + const Element* const Texture_Alpha_Source = sc["Texture_Alpha_Source"]; + const Element* const Cropping = sc["Cropping"]; + + if(Type) { + type = ParseTokenAsString(GetRequiredToken(*Type,0)); + } + + if(FileName) { + fileName = ParseTokenAsString(GetRequiredToken(*FileName,0)); + } + + if(RelativeFilename) { + relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0)); + } + + if(ModelUVTranslation) { + uvTrans = aiVector2D(ParseTokenAsFloat(GetRequiredToken(*ModelUVTranslation,0)), + ParseTokenAsFloat(GetRequiredToken(*ModelUVTranslation,1)) + ); + } + + if(ModelUVScaling) { + uvScaling = aiVector2D(ParseTokenAsFloat(GetRequiredToken(*ModelUVScaling,0)), + ParseTokenAsFloat(GetRequiredToken(*ModelUVScaling,1)) + ); + } + + if(Cropping) { + crop[0] = ParseTokenAsInt(GetRequiredToken(*Cropping,0)); + crop[1] = ParseTokenAsInt(GetRequiredToken(*Cropping,1)); + crop[2] = ParseTokenAsInt(GetRequiredToken(*Cropping,2)); + crop[3] = ParseTokenAsInt(GetRequiredToken(*Cropping,3)); + } + else { + // vc8 doesn't support the crop() syntax in initialization lists + // (and vc9 WARNS about the new (i.e. compliant) behaviour). + crop[0] = crop[1] = crop[2] = crop[3] = 0; + } + + if(Texture_Alpha_Source) { + alphaSource = ParseTokenAsString(GetRequiredToken(*Texture_Alpha_Source,0)); + } + + props = GetPropertyTable(doc,"Texture.FbxFileTexture",element,sc); + + // resolve video links + if(doc.Settings().readTextures) { + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID()); + for(const Connection* con : conns) { + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for texture link, ignoring",&element); + continue; + } + + const Video* const video = dynamic_cast<const Video*>(ob); + if(video) { + media = video; + } + } + } +} + + +Texture::~Texture() +{ + +} + +LayeredTexture::LayeredTexture(uint64_t id, const Element& element, const Document& /*doc*/, const std::string& name) +: Object(id,element,name) +,blendMode(BlendMode_Modulate) +,alpha(1) +{ + const Scope& sc = GetRequiredScope(element); + + const Element* const BlendModes = sc["BlendModes"]; + const Element* const Alphas = sc["Alphas"]; + + + if(BlendModes!=0) + { + blendMode = (BlendMode)ParseTokenAsInt(GetRequiredToken(*BlendModes,0)); + } + if(Alphas!=0) + { + alpha = ParseTokenAsFloat(GetRequiredToken(*Alphas,0)); + } +} + +LayeredTexture::~LayeredTexture() +{ + +} + +void LayeredTexture::fillTexture(const Document& doc) +{ + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID()); + for(size_t i = 0; i < conns.size();++i) + { + const Connection* con = conns.at(i); + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for texture link, ignoring",&element); + continue; + } + + const Texture* const tex = dynamic_cast<const Texture*>(ob); + + textures.push_back(tex); + } +} + + +// ------------------------------------------------------------------------------------------------ +Video::Video(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Object(id,element,name) +, contentLength(0) +, content(0) +{ + const Scope& sc = GetRequiredScope(element); + + const Element* const Type = sc["Type"]; + const Element* const FileName = sc.FindElementCaseInsensitive("FileName"); //some files retain the information as "Filename", others "FileName", who knows + const Element* const RelativeFilename = sc["RelativeFilename"]; + const Element* const Content = sc["Content"]; + + if(Type) { + type = ParseTokenAsString(GetRequiredToken(*Type,0)); + } + + if(FileName) { + fileName = ParseTokenAsString(GetRequiredToken(*FileName,0)); + } + + if(RelativeFilename) { + relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0)); + } + + if(Content) { + //this field is omitted when the embedded texture is already loaded, let's ignore if it's not found + try { + const Token& token = GetRequiredToken(*Content, 0); + const char* data = token.begin(); + if (!token.IsBinary()) { + DOMWarning("video content is not binary data, ignoring", &element); + } + else if (static_cast<size_t>(token.end() - data) < 5) { + DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element); + } + else if (*data != 'R') { + DOMWarning("video content is not raw binary data, ignoring", &element); + } + else { + // read number of elements + uint32_t len = 0; + ::memcpy(&len, data + 1, sizeof(len)); + AI_SWAP4(len); + + contentLength = len; + + content = new uint8_t[len]; + ::memcpy(content, data + 5, len); + } + } catch (const runtime_error& runtimeError) + { + //we don't need the content data for contents that has already been loaded + ASSIMP_LOG_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ", + runtimeError.what()); + } + } + + props = GetPropertyTable(doc,"Video.FbxVideo",element,sc); +} + + +Video::~Video() +{ + if(content) { + delete[] content; + } +} + +} //!FBX +} //!Assimp + +#endif diff --git a/thirdparty/assimp/code/FBXMeshGeometry.cpp b/thirdparty/assimp/code/FBXMeshGeometry.cpp new file mode 100644 index 0000000000..d75476b826 --- /dev/null +++ b/thirdparty/assimp/code/FBXMeshGeometry.cpp @@ -0,0 +1,711 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXMeshGeometry.cpp + * @brief Assimp::FBX::MeshGeometry implementation + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include <functional> + +#include "FBXMeshGeometry.h" +#include "FBXDocument.h" +#include "FBXImporter.h" +#include "FBXImportSettings.h" +#include "FBXDocumentUtil.h" + + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) + : Object(id, element, name) + , skin() +{ + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer"); + for(const Connection* con : conns) { + const Skin* const sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element); + if(sk) { + skin = sk; + } + const BlendShape* const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element); + if (bsp) { + blendShapes.push_back(bsp); + } + } +} + +// ------------------------------------------------------------------------------------------------ +Geometry::~Geometry() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +const std::vector<const BlendShape*>& Geometry::GetBlendShapes() const { + return blendShapes; +} + +// ------------------------------------------------------------------------------------------------ +const Skin* Geometry::DeformerSkin() const { + return skin; +} + +// ------------------------------------------------------------------------------------------------ +MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) +: Geometry(id, element,name, doc) +{ + const Scope* sc = element.Compound(); + if (!sc) { + DOMError("failed to read Geometry object (class: Mesh), no data scope found"); + } + + // must have Mesh elements: + const Element& Vertices = GetRequiredElement(*sc,"Vertices",&element); + const Element& PolygonVertexIndex = GetRequiredElement(*sc,"PolygonVertexIndex",&element); + + // optional Mesh elements: + const ElementCollection& Layer = sc->GetCollection("Layer"); + + std::vector<aiVector3D> tempVerts; + ParseVectorDataArray(tempVerts,Vertices); + + if(tempVerts.empty()) { + FBXImporter::LogWarn("encountered mesh with no vertices"); + return; + } + + std::vector<int> tempFaces; + ParseVectorDataArray(tempFaces,PolygonVertexIndex); + + if(tempFaces.empty()) { + FBXImporter::LogWarn("encountered mesh with no faces"); + return; + } + + m_vertices.reserve(tempFaces.size()); + m_faces.reserve(tempFaces.size() / 3); + + m_mapping_offsets.resize(tempVerts.size()); + m_mapping_counts.resize(tempVerts.size(),0); + m_mappings.resize(tempFaces.size()); + + const size_t vertex_count = tempVerts.size(); + + // generate output vertices, computing an adjacency table to + // preserve the mapping from fbx indices to *this* indexing. + unsigned int count = 0; + for(int index : tempFaces) { + const int absi = index < 0 ? (-index - 1) : index; + if(static_cast<size_t>(absi) >= vertex_count) { + DOMError("polygon vertex index out of range",&PolygonVertexIndex); + } + + m_vertices.push_back(tempVerts[absi]); + ++count; + + ++m_mapping_counts[absi]; + + if (index < 0) { + m_faces.push_back(count); + count = 0; + } + } + + unsigned int cursor = 0; + for (size_t i = 0, e = tempVerts.size(); i < e; ++i) { + m_mapping_offsets[i] = cursor; + cursor += m_mapping_counts[i]; + + m_mapping_counts[i] = 0; + } + + cursor = 0; + for(int index : tempFaces) { + const int absi = index < 0 ? (-index - 1) : index; + m_mappings[m_mapping_offsets[absi] + m_mapping_counts[absi]++] = cursor++; + } + + // if settings.readAllLayers is true: + // * read all layers, try to load as many vertex channels as possible + // if settings.readAllLayers is false: + // * read only the layer with index 0, but warn about any further layers + for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) { + const TokenList& tokens = (*it).second->Tokens(); + + const char* err; + const int index = ParseTokenAsInt(*tokens[0], err); + if(err) { + DOMError(err,&element); + } + + if(doc.Settings().readAllLayers || index == 0) { + const Scope& layer = GetRequiredScope(*(*it).second); + ReadLayer(layer); + } + else { + FBXImporter::LogWarn("ignoring additional geometry layers"); + } + } +} + +// ------------------------------------------------------------------------------------------------ +MeshGeometry::~MeshGeometry() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +const std::vector<aiVector3D>& MeshGeometry::GetVertices() const { + return m_vertices; +} + +// ------------------------------------------------------------------------------------------------ +const std::vector<aiVector3D>& MeshGeometry::GetNormals() const { + return m_normals; +} + +// ------------------------------------------------------------------------------------------------ +const std::vector<aiVector3D>& MeshGeometry::GetTangents() const { + return m_tangents; +} + +// ------------------------------------------------------------------------------------------------ +const std::vector<aiVector3D>& MeshGeometry::GetBinormals() const { + return m_binormals; +} + +// ------------------------------------------------------------------------------------------------ +const std::vector<unsigned int>& MeshGeometry::GetFaceIndexCounts() const { + return m_faces; +} + +// ------------------------------------------------------------------------------------------------ +const std::vector<aiVector2D>& MeshGeometry::GetTextureCoords( unsigned int index ) const { + static const std::vector<aiVector2D> empty; + return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : m_uvs[ index ]; +} + +std::string MeshGeometry::GetTextureCoordChannelName( unsigned int index ) const { + return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? "" : m_uvNames[ index ]; +} + +const std::vector<aiColor4D>& MeshGeometry::GetVertexColors( unsigned int index ) const { + static const std::vector<aiColor4D> empty; + return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : m_colors[ index ]; +} + +const MatIndexArray& MeshGeometry::GetMaterialIndices() const { + return m_materials; +} +// ------------------------------------------------------------------------------------------------ +const unsigned int* MeshGeometry::ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const { + if ( in_index >= m_mapping_counts.size() ) { + return NULL; + } + + ai_assert( m_mapping_counts.size() == m_mapping_offsets.size() ); + count = m_mapping_counts[ in_index ]; + + ai_assert( m_mapping_offsets[ in_index ] + count <= m_mappings.size() ); + + return &m_mappings[ m_mapping_offsets[ in_index ] ]; +} + +// ------------------------------------------------------------------------------------------------ +unsigned int MeshGeometry::FaceForVertexIndex( unsigned int in_index ) const { + ai_assert( in_index < m_vertices.size() ); + + // in the current conversion pattern this will only be needed if + // weights are present, so no need to always pre-compute this table + if ( m_facesVertexStartIndices.empty() ) { + m_facesVertexStartIndices.resize( m_faces.size() + 1, 0 ); + + std::partial_sum( m_faces.begin(), m_faces.end(), m_facesVertexStartIndices.begin() + 1 ); + m_facesVertexStartIndices.pop_back(); + } + + ai_assert( m_facesVertexStartIndices.size() == m_faces.size() ); + const std::vector<unsigned int>::iterator it = std::upper_bound( + m_facesVertexStartIndices.begin(), + m_facesVertexStartIndices.end(), + in_index + ); + + return static_cast< unsigned int >( std::distance( m_facesVertexStartIndices.begin(), it - 1 ) ); +} + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadLayer(const Scope& layer) +{ + const ElementCollection& LayerElement = layer.GetCollection("LayerElement"); + for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) { + const Scope& elayer = GetRequiredScope(*(*eit).second); + + ReadLayerElement(elayer); + } +} + + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadLayerElement(const Scope& layerElement) +{ + const Element& Type = GetRequiredElement(layerElement,"Type"); + const Element& TypedIndex = GetRequiredElement(layerElement,"TypedIndex"); + + const std::string& type = ParseTokenAsString(GetRequiredToken(Type,0)); + const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex,0)); + + const Scope& top = GetRequiredScope(element); + const ElementCollection candidates = top.GetCollection(type); + + for (ElementMap::const_iterator it = candidates.first; it != candidates.second; ++it) { + const int index = ParseTokenAsInt(GetRequiredToken(*(*it).second,0)); + if(index == typedIndex) { + ReadVertexData(type,typedIndex,GetRequiredScope(*(*it).second)); + return; + } + } + + FBXImporter::LogError(Formatter::format("failed to resolve vertex layer element: ") + << type << ", index: " << typedIndex); +} + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scope& source) +{ + const std::string& MappingInformationType = ParseTokenAsString(GetRequiredToken( + GetRequiredElement(source,"MappingInformationType"),0) + ); + + const std::string& ReferenceInformationType = ParseTokenAsString(GetRequiredToken( + GetRequiredElement(source,"ReferenceInformationType"),0) + ); + + if (type == "LayerElementUV") { + if(index >= AI_MAX_NUMBER_OF_TEXTURECOORDS) { + FBXImporter::LogError(Formatter::format("ignoring UV layer, maximum number of UV channels exceeded: ") + << index << " (limit is " << AI_MAX_NUMBER_OF_TEXTURECOORDS << ")" ); + return; + } + + const Element* Name = source["Name"]; + m_uvNames[index] = ""; + if(Name) { + m_uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0)); + } + + ReadVertexDataUV(m_uvs[index],source, + MappingInformationType, + ReferenceInformationType + ); + } + else if (type == "LayerElementMaterial") { + if (m_materials.size() > 0) { + FBXImporter::LogError("ignoring additional material layer"); + return; + } + + std::vector<int> temp_materials; + + ReadVertexDataMaterials(temp_materials,source, + MappingInformationType, + ReferenceInformationType + ); + + // sometimes, there will be only negative entries. Drop the material + // layer in such a case (I guess it means a default material should + // be used). This is what the converter would do anyway, and it + // avoids losing the material if there are more material layers + // coming of which at least one contains actual data (did observe + // that with one test file). + const size_t count_neg = std::count_if(temp_materials.begin(),temp_materials.end(),[](int n) { return n < 0; }); + if(count_neg == temp_materials.size()) { + FBXImporter::LogWarn("ignoring dummy material layer (all entries -1)"); + return; + } + + std::swap(temp_materials, m_materials); + } + else if (type == "LayerElementNormal") { + if (m_normals.size() > 0) { + FBXImporter::LogError("ignoring additional normal layer"); + return; + } + + ReadVertexDataNormals(m_normals,source, + MappingInformationType, + ReferenceInformationType + ); + } + else if (type == "LayerElementTangent") { + if (m_tangents.size() > 0) { + FBXImporter::LogError("ignoring additional tangent layer"); + return; + } + + ReadVertexDataTangents(m_tangents,source, + MappingInformationType, + ReferenceInformationType + ); + } + else if (type == "LayerElementBinormal") { + if (m_binormals.size() > 0) { + FBXImporter::LogError("ignoring additional binormal layer"); + return; + } + + ReadVertexDataBinormals(m_binormals,source, + MappingInformationType, + ReferenceInformationType + ); + } + else if (type == "LayerElementColor") { + if(index >= AI_MAX_NUMBER_OF_COLOR_SETS) { + FBXImporter::LogError(Formatter::format("ignoring vertex color layer, maximum number of color sets exceeded: ") + << index << " (limit is " << AI_MAX_NUMBER_OF_COLOR_SETS << ")" ); + return; + } + + ReadVertexDataColors(m_colors[index],source, + MappingInformationType, + ReferenceInformationType + ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Lengthy utility function to read and resolve a FBX vertex data array - that is, the +// output is in polygon vertex order. This logic is used for reading normals, UVs, colors, +// tangents .. +template <typename T> +void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType, + const char* dataElementName, + const char* indexDataElementName, + size_t vertex_count, + const std::vector<unsigned int>& mapping_counts, + const std::vector<unsigned int>& mapping_offsets, + const std::vector<unsigned int>& mappings) +{ + bool isDirect = ReferenceInformationType == "Direct"; + bool isIndexToDirect = ReferenceInformationType == "IndexToDirect"; + + // fall-back to direct data if there is no index data element + if ( isIndexToDirect && !HasElement( source, indexDataElementName ) ) { + isDirect = true; + isIndexToDirect = false; + } + + // handle permutations of Mapping and Reference type - it would be nice to + // deal with this more elegantly and with less redundancy, but right + // now it seems unavoidable. + if (MappingInformationType == "ByVertice" && isDirect) { + if (!HasElement(source, dataElementName)) { + return; + } + std::vector<T> tempData; + ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); + + data_out.resize(vertex_count); + for (size_t i = 0, e = tempData.size(); i < e; ++i) { + + const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i]; + for (unsigned int j = istart; j < iend; ++j) { + data_out[mappings[j]] = tempData[i]; + } + } + } + else if (MappingInformationType == "ByVertice" && isIndexToDirect) { + std::vector<T> tempData; + ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); + + data_out.resize(vertex_count); + + std::vector<int> uvIndices; + ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); + for (size_t i = 0, e = uvIndices.size(); i < e; ++i) { + + const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i]; + for (unsigned int j = istart; j < iend; ++j) { + if (static_cast<size_t>(uvIndices[i]) >= tempData.size()) { + DOMError("index out of range",&GetRequiredElement(source,indexDataElementName)); + } + data_out[mappings[j]] = tempData[uvIndices[i]]; + } + } + } + else if (MappingInformationType == "ByPolygonVertex" && isDirect) { + std::vector<T> tempData; + ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); + + if (tempData.size() != vertex_count) { + FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ") + << tempData.size() << ", expected " << vertex_count + ); + return; + } + + data_out.swap(tempData); + } + else if (MappingInformationType == "ByPolygonVertex" && isIndexToDirect) { + std::vector<T> tempData; + ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); + + data_out.resize(vertex_count); + + std::vector<int> uvIndices; + ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); + + if (uvIndices.size() != vertex_count) { + FBXImporter::LogError("length of input data unexpected for ByPolygonVertex mapping"); + return; + } + + const T empty; + unsigned int next = 0; + for(int i : uvIndices) { + if ( -1 == i ) { + data_out[ next++ ] = empty; + continue; + } + if (static_cast<size_t>(i) >= tempData.size()) { + DOMError("index out of range",&GetRequiredElement(source,indexDataElementName)); + } + + data_out[next++] = tempData[i]; + } + } + else { + FBXImporter::LogError(Formatter::format("ignoring vertex data channel, access type not implemented: ") + << MappingInformationType << "," << ReferenceInformationType); + } +} + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadVertexDataNormals(std::vector<aiVector3D>& normals_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + ResolveVertexDataArray(normals_out,source,MappingInformationType,ReferenceInformationType, + "Normals", + "NormalsIndex", + m_vertices.size(), + m_mapping_counts, + m_mapping_offsets, + m_mappings); +} + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadVertexDataUV(std::vector<aiVector2D>& uv_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + ResolveVertexDataArray(uv_out,source,MappingInformationType,ReferenceInformationType, + "UV", + "UVIndex", + m_vertices.size(), + m_mapping_counts, + m_mapping_offsets, + m_mappings); +} + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadVertexDataColors(std::vector<aiColor4D>& colors_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + ResolveVertexDataArray(colors_out,source,MappingInformationType,ReferenceInformationType, + "Colors", + "ColorIndex", + m_vertices.size(), + m_mapping_counts, + m_mapping_offsets, + m_mappings); +} + +// ------------------------------------------------------------------------------------------------ +static const std::string TangentIndexToken = "TangentIndex"; +static const std::string TangentsIndexToken = "TangentsIndex"; + +void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent"; + const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken.c_str() : TangentIndexToken.c_str(); + ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType, + str, + strIdx, + m_vertices.size(), + m_mapping_counts, + m_mapping_offsets, + m_mappings); +} + +// ------------------------------------------------------------------------------------------------ +static const std::string BinormalIndexToken = "BinormalIndex"; +static const std::string BinormalsIndexToken = "BinormalsIndex"; + +void MeshGeometry::ReadVertexDataBinormals(std::vector<aiVector3D>& binormals_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal"; + const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken.c_str() : BinormalIndexToken.c_str(); + ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType, + str, + strIdx, + m_vertices.size(), + m_mapping_counts, + m_mapping_offsets, + m_mappings); +} + + +// ------------------------------------------------------------------------------------------------ +void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType) +{ + const size_t face_count = m_faces.size(); + ai_assert(face_count); + + // materials are handled separately. First of all, they are assigned per-face + // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect + // has a slightly different meaning for materials. + ParseVectorDataArray(materials_out,GetRequiredElement(source,"Materials")); + + if (MappingInformationType == "AllSame") { + // easy - same material for all faces + if (materials_out.empty()) { + FBXImporter::LogError(Formatter::format("expected material index, ignoring")); + return; + } + else if (materials_out.size() > 1) { + FBXImporter::LogWarn(Formatter::format("expected only a single material index, ignoring all except the first one")); + materials_out.clear(); + } + + m_materials.assign(m_vertices.size(),materials_out[0]); + } + else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") { + m_materials.resize(face_count); + + if(materials_out.size() != face_count) { + FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ") + << materials_out.size() << ", expected " << face_count + ); + return; + } + } + else { + FBXImporter::LogError(Formatter::format("ignoring material assignments, access type not implemented: ") + << MappingInformationType << "," << ReferenceInformationType); + } +} +// ------------------------------------------------------------------------------------------------ +ShapeGeometry::ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) + : Geometry(id, element, name, doc) +{ + const Scope* sc = element.Compound(); + if (!sc) { + DOMError("failed to read Geometry object (class: Shape), no data scope found"); + } + const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element); + const Element& Normals = GetRequiredElement(*sc, "Normals", &element); + const Element& Vertices = GetRequiredElement(*sc, "Vertices", &element); + ParseVectorDataArray(m_indices, Indexes); + ParseVectorDataArray(m_vertices, Vertices); + ParseVectorDataArray(m_normals, Normals); +} + +// ------------------------------------------------------------------------------------------------ +ShapeGeometry::~ShapeGeometry() { + // empty +} +// ------------------------------------------------------------------------------------------------ +const std::vector<aiVector3D>& ShapeGeometry::GetVertices() const { + return m_vertices; +} +// ------------------------------------------------------------------------------------------------ +const std::vector<aiVector3D>& ShapeGeometry::GetNormals() const { + return m_normals; +} +// ------------------------------------------------------------------------------------------------ +const std::vector<unsigned int>& ShapeGeometry::GetIndices() const { + return m_indices; +} +// ------------------------------------------------------------------------------------------------ +LineGeometry::LineGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) + : Geometry(id, element, name, doc) +{ + const Scope* sc = element.Compound(); + if (!sc) { + DOMError("failed to read Geometry object (class: Line), no data scope found"); + } + const Element& Points = GetRequiredElement(*sc, "Points", &element); + const Element& PointsIndex = GetRequiredElement(*sc, "PointsIndex", &element); + ParseVectorDataArray(m_vertices, Points); + ParseVectorDataArray(m_indices, PointsIndex); +} + +// ------------------------------------------------------------------------------------------------ +LineGeometry::~LineGeometry() { + // empty +} +// ------------------------------------------------------------------------------------------------ +const std::vector<aiVector3D>& LineGeometry::GetVertices() const { + return m_vertices; +} +// ------------------------------------------------------------------------------------------------ +const std::vector<int>& LineGeometry::GetIndices() const { + return m_indices; +} +} // !FBX +} // !Assimp +#endif + diff --git a/thirdparty/assimp/code/FBXMeshGeometry.h b/thirdparty/assimp/code/FBXMeshGeometry.h new file mode 100644 index 0000000000..d6d4512177 --- /dev/null +++ b/thirdparty/assimp/code/FBXMeshGeometry.h @@ -0,0 +1,235 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXImporter.h +* @brief Declaration of the FBX main importer class +*/ +#ifndef INCLUDED_AI_FBX_MESHGEOMETRY_H +#define INCLUDED_AI_FBX_MESHGEOMETRY_H + +#include "FBXParser.h" +#include "FBXDocument.h" + +namespace Assimp { +namespace FBX { + +/** + * DOM base class for all kinds of FBX geometry + */ +class Geometry : public Object +{ +public: + Geometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); + virtual ~Geometry(); + + /** Get the Skin attached to this geometry or NULL */ + const Skin* DeformerSkin() const; + + /** Get the BlendShape attached to this geometry or NULL */ + const std::vector<const BlendShape*>& GetBlendShapes() const; + +private: + const Skin* skin; + std::vector<const BlendShape*> blendShapes; + +}; + +typedef std::vector<int> MatIndexArray; + + +/** + * DOM class for FBX geometry of type "Mesh" + */ +class MeshGeometry : public Geometry +{ +public: + /** The class constructor */ + MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); + + /** The class destructor */ + virtual ~MeshGeometry(); + + /** Get a list of all vertex points, non-unique*/ + const std::vector<aiVector3D>& GetVertices() const; + + /** Get a list of all vertex normals or an empty array if + * no normals are specified. */ + const std::vector<aiVector3D>& GetNormals() const; + + /** Get a list of all vertex tangents or an empty array + * if no tangents are specified */ + const std::vector<aiVector3D>& GetTangents() const; + + /** Get a list of all vertex bi-normals or an empty array + * if no bi-normals are specified */ + const std::vector<aiVector3D>& GetBinormals() const; + + /** Return list of faces - each entry denotes a face and specifies + * how many vertices it has. Vertices are taken from the + * vertex data arrays in sequential order. */ + const std::vector<unsigned int>& GetFaceIndexCounts() const; + + /** Get a UV coordinate slot, returns an empty array if + * the requested slot does not exist. */ + const std::vector<aiVector2D>& GetTextureCoords( unsigned int index ) const; + + /** Get a UV coordinate slot, returns an empty array if + * the requested slot does not exist. */ + std::string GetTextureCoordChannelName( unsigned int index ) const; + + /** Get a vertex color coordinate slot, returns an empty array if + * the requested slot does not exist. */ + const std::vector<aiColor4D>& GetVertexColors( unsigned int index ) const; + + /** Get per-face-vertex material assignments */ + const MatIndexArray& GetMaterialIndices() const; + + /** Convert from a fbx file vertex index (for example from a #Cluster weight) or NULL + * if the vertex index is not valid. */ + const unsigned int* ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const; + + /** Determine the face to which a particular output vertex index belongs. + * This mapping is always unique. */ + unsigned int FaceForVertexIndex( unsigned int in_index ) const; +private: + void ReadLayer( const Scope& layer ); + void ReadLayerElement( const Scope& layerElement ); + void ReadVertexData( const std::string& type, int index, const Scope& source ); + + void ReadVertexDataUV( std::vector<aiVector2D>& uv_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + + void ReadVertexDataNormals( std::vector<aiVector3D>& normals_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + + void ReadVertexDataColors( std::vector<aiColor4D>& colors_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + + void ReadVertexDataTangents( std::vector<aiVector3D>& tangents_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + + void ReadVertexDataBinormals( std::vector<aiVector3D>& binormals_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + + void ReadVertexDataMaterials( MatIndexArray& materials_out, const Scope& source, + const std::string& MappingInformationType, + const std::string& ReferenceInformationType ); + +private: + // cached data arrays + MatIndexArray m_materials; + std::vector<aiVector3D> m_vertices; + std::vector<unsigned int> m_faces; + mutable std::vector<unsigned int> m_facesVertexStartIndices; + std::vector<aiVector3D> m_tangents; + std::vector<aiVector3D> m_binormals; + std::vector<aiVector3D> m_normals; + + std::string m_uvNames[ AI_MAX_NUMBER_OF_TEXTURECOORDS ]; + std::vector<aiVector2D> m_uvs[ AI_MAX_NUMBER_OF_TEXTURECOORDS ]; + std::vector<aiColor4D> m_colors[ AI_MAX_NUMBER_OF_COLOR_SETS ]; + + std::vector<unsigned int> m_mapping_counts; + std::vector<unsigned int> m_mapping_offsets; + std::vector<unsigned int> m_mappings; +}; + +/** +* DOM class for FBX geometry of type "Shape" +*/ +class ShapeGeometry : public Geometry +{ +public: + /** The class constructor */ + ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc); + + /** The class destructor */ + virtual ~ShapeGeometry(); + + /** Get a list of all vertex points, non-unique*/ + const std::vector<aiVector3D>& GetVertices() const; + + /** Get a list of all vertex normals or an empty array if + * no normals are specified. */ + const std::vector<aiVector3D>& GetNormals() const; + + /** Return list of vertex indices. */ + const std::vector<unsigned int>& GetIndices() const; + +private: + std::vector<aiVector3D> m_vertices; + std::vector<aiVector3D> m_normals; + std::vector<unsigned int> m_indices; +}; +/** +* DOM class for FBX geometry of type "Line" +*/ +class LineGeometry : public Geometry +{ +public: + /** The class constructor */ + LineGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc); + + /** The class destructor */ + virtual ~LineGeometry(); + + /** Get a list of all vertex points, non-unique*/ + const std::vector<aiVector3D>& GetVertices() const; + + /** Return list of vertex indices. */ + const std::vector<int>& GetIndices() const; + +private: + std::vector<aiVector3D> m_vertices; + std::vector<int> m_indices; +}; + +} +} + +#endif // INCLUDED_AI_FBX_MESHGEOMETRY_H + diff --git a/thirdparty/assimp/code/FBXModel.cpp b/thirdparty/assimp/code/FBXModel.cpp new file mode 100644 index 0000000000..589af36ac7 --- /dev/null +++ b/thirdparty/assimp/code/FBXModel.cpp @@ -0,0 +1,153 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXModel.cpp + * @brief Assimp::FBX::Model implementation + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXMeshGeometry.h" +#include "FBXDocument.h" +#include "FBXImporter.h" +#include "FBXDocumentUtil.h" + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Model::Model(uint64_t id, const Element& element, const Document& doc, const std::string& name) + : Object(id,element,name) + , shading("Y") +{ + const Scope& sc = GetRequiredScope(element); + const Element* const Shading = sc["Shading"]; + const Element* const Culling = sc["Culling"]; + + if(Shading) { + shading = GetRequiredToken(*Shading,0).StringContents(); + } + + if (Culling) { + culling = ParseTokenAsString(GetRequiredToken(*Culling,0)); + } + + props = GetPropertyTable(doc,"Model.FbxNode",element,sc); + ResolveLinks(element,doc); +} + +// ------------------------------------------------------------------------------------------------ +Model::~Model() +{ + +} + +// ------------------------------------------------------------------------------------------------ +void Model::ResolveLinks(const Element& element, const Document& doc) +{ + const char* const arr[] = {"Geometry","Material","NodeAttribute"}; + + // resolve material + const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),arr, 3); + + materials.reserve(conns.size()); + geometry.reserve(conns.size()); + attributes.reserve(conns.size()); + for(const Connection* con : conns) { + + // material and geometry links should be Object-Object connections + if (con->PropertyName().length()) { + continue; + } + + const Object* const ob = con->SourceObject(); + if(!ob) { + DOMWarning("failed to read source object for incoming Model link, ignoring",&element); + continue; + } + + const Material* const mat = dynamic_cast<const Material*>(ob); + if(mat) { + materials.push_back(mat); + continue; + } + + const Geometry* const geo = dynamic_cast<const Geometry*>(ob); + if(geo) { + geometry.push_back(geo); + continue; + } + + const NodeAttribute* const att = dynamic_cast<const NodeAttribute*>(ob); + if(att) { + attributes.push_back(att); + continue; + } + + DOMWarning("source object for model link is neither Material, NodeAttribute nor Geometry, ignoring",&element); + continue; + } +} + +// ------------------------------------------------------------------------------------------------ +bool Model::IsNull() const +{ + const std::vector<const NodeAttribute*>& attrs = GetAttributes(); + for(const NodeAttribute* att : attrs) { + + const Null* null_tag = dynamic_cast<const Null*>(att); + if(null_tag) { + return true; + } + } + + return false; +} + + +} //!FBX +} //!Assimp + +#endif diff --git a/thirdparty/assimp/code/FBXNodeAttribute.cpp b/thirdparty/assimp/code/FBXNodeAttribute.cpp new file mode 100644 index 0000000000..b72e5637ee --- /dev/null +++ b/thirdparty/assimp/code/FBXNodeAttribute.cpp @@ -0,0 +1,170 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXNoteAttribute.cpp + * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXImporter.h" +#include "FBXDocumentUtil.h" + +namespace Assimp { +namespace FBX { + +using namespace Util; + +// ------------------------------------------------------------------------------------------------ +NodeAttribute::NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: Object(id,element,name) +, props() +{ + const Scope& sc = GetRequiredScope(element); + + const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2)); + + // hack on the deriving type but Null/LimbNode attributes are the only case in which + // the property table is by design absent and no warning should be generated + // for it. + const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode"); + props = GetPropertyTable(doc,"NodeAttribute.Fbx" + classname,element,sc, is_null_or_limb); +} + + +// ------------------------------------------------------------------------------------------------ +NodeAttribute::~NodeAttribute() +{ + // empty +} + + +// ------------------------------------------------------------------------------------------------ +CameraSwitcher::CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name) + : NodeAttribute(id,element,doc,name) +{ + const Scope& sc = GetRequiredScope(element); + const Element* const CameraId = sc["CameraId"]; + const Element* const CameraName = sc["CameraName"]; + const Element* const CameraIndexName = sc["CameraIndexName"]; + + if(CameraId) { + cameraId = ParseTokenAsInt(GetRequiredToken(*CameraId,0)); + } + + if(CameraName) { + cameraName = GetRequiredToken(*CameraName,0).StringContents(); + } + + if(CameraIndexName && CameraIndexName->Tokens().size()) { + cameraIndexName = GetRequiredToken(*CameraIndexName,0).StringContents(); + } +} + +// ------------------------------------------------------------------------------------------------ +CameraSwitcher::~CameraSwitcher() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +Camera::Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: NodeAttribute(id,element,doc,name) +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +Camera::~Camera() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +Light::Light(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: NodeAttribute(id,element,doc,name) +{ + // empty +} + + +// ------------------------------------------------------------------------------------------------ +Light::~Light() +{ +} + + +// ------------------------------------------------------------------------------------------------ +Null::Null(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: NodeAttribute(id,element,doc,name) +{ + +} + + +// ------------------------------------------------------------------------------------------------ +Null::~Null() +{ + +} + + +// ------------------------------------------------------------------------------------------------ +LimbNode::LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name) +: NodeAttribute(id,element,doc,name) +{ + +} + + +// ------------------------------------------------------------------------------------------------ +LimbNode::~LimbNode() +{ + +} + +} +} + +#endif diff --git a/thirdparty/assimp/code/FBXParser.cpp b/thirdparty/assimp/code/FBXParser.cpp new file mode 100644 index 0000000000..b255c47347 --- /dev/null +++ b/thirdparty/assimp/code/FBXParser.cpp @@ -0,0 +1,1313 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXParser.cpp + * @brief Implementation of the FBX parser and the rudimentary DOM that we use + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#ifdef ASSIMP_BUILD_NO_OWN_ZLIB +# include <zlib.h> +#else +# include "../contrib/zlib/zlib.h" +#endif + +#include "FBXTokenizer.h" +#include "FBXParser.h" +#include "FBXUtil.h" + +#include <assimp/ParsingUtils.h> +#include <assimp/fast_atof.h> +#include <assimp/ByteSwapper.h> + +#include <iostream> + +using namespace Assimp; +using namespace Assimp::FBX; + +namespace { + + // ------------------------------------------------------------------------------------------------ + // signal parse error, this is always unrecoverable. Throws DeadlyImportError. + AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX; + AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) + { + throw DeadlyImportError(Util::AddTokenText("FBX-Parser",message,&token)); + } + + // ------------------------------------------------------------------------------------------------ + AI_WONT_RETURN void ParseError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX; + AI_WONT_RETURN void ParseError(const std::string& message, const Element* element) + { + if(element) { + ParseError(message,element->KeyToken()); + } + throw DeadlyImportError("FBX-Parser " + message); + } + + + // ------------------------------------------------------------------------------------------------ + void ParseError(const std::string& message, TokenPtr token) + { + if(token) { + ParseError(message, *token); + } + ParseError(message); + } + + // Initially, we did reinterpret_cast, breaking strict aliasing rules. + // This actually caused trouble on Android, so let's be safe this time. + // https://github.com/assimp/assimp/issues/24 + template <typename T> + T SafeParse(const char* data, const char* end) { + // Actual size validation happens during Tokenization so + // this is valid as an assertion. + (void)(end); + ai_assert(static_cast<size_t>(end - data) >= sizeof(T)); + T result = static_cast<T>(0); + ::memcpy(&result, data, sizeof(T)); + return result; + } +} + +namespace Assimp { +namespace FBX { + +// ------------------------------------------------------------------------------------------------ +Element::Element(const Token& key_token, Parser& parser) +: key_token(key_token) +{ + TokenPtr n = NULL; + do { + n = parser.AdvanceToNextToken(); + if(!n) { + ParseError("unexpected end of file, expected closing bracket",parser.LastToken()); + } + + if (n->Type() == TokenType_DATA) { + tokens.push_back(n); + TokenPtr prev = n; + n = parser.AdvanceToNextToken(); + if(!n) { + ParseError("unexpected end of file, expected bracket, comma or key",parser.LastToken()); + } + + const TokenType ty = n->Type(); + + // some exporters are missing a comma on the next line + if (ty == TokenType_DATA && prev->Type() == TokenType_DATA && (n->Line() == prev->Line() + 1)) { + tokens.push_back(n); + continue; + } + + if (ty != TokenType_OPEN_BRACKET && ty != TokenType_CLOSE_BRACKET && ty != TokenType_COMMA && ty != TokenType_KEY) { + ParseError("unexpected token; expected bracket, comma or key",n); + } + } + + if (n->Type() == TokenType_OPEN_BRACKET) { + compound.reset(new Scope(parser)); + + // current token should be a TOK_CLOSE_BRACKET + n = parser.CurrentToken(); + ai_assert(n); + + if (n->Type() != TokenType_CLOSE_BRACKET) { + ParseError("expected closing bracket",n); + } + + parser.AdvanceToNextToken(); + return; + } + } + while(n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET); +} + +// ------------------------------------------------------------------------------------------------ +Element::~Element() +{ + // no need to delete tokens, they are owned by the parser +} + +// ------------------------------------------------------------------------------------------------ +Scope::Scope(Parser& parser,bool topLevel) +{ + if(!topLevel) { + TokenPtr t = parser.CurrentToken(); + if (t->Type() != TokenType_OPEN_BRACKET) { + ParseError("expected open bracket",t); + } + } + + TokenPtr n = parser.AdvanceToNextToken(); + if(n == NULL) { + ParseError("unexpected end of file"); + } + + // note: empty scopes are allowed + while(n->Type() != TokenType_CLOSE_BRACKET) { + if (n->Type() != TokenType_KEY) { + ParseError("unexpected token, expected TOK_KEY",n); + } + + const std::string& str = n->StringContents(); + elements.insert(ElementMap::value_type(str,new_Element(*n,parser))); + + // Element() should stop at the next Key token (or right after a Close token) + n = parser.CurrentToken(); + if(n == NULL) { + if (topLevel) { + return; + } + ParseError("unexpected end of file",parser.LastToken()); + } + } +} + +// ------------------------------------------------------------------------------------------------ +Scope::~Scope() +{ + for(ElementMap::value_type& v : elements) { + delete v.second; + } +} + +// ------------------------------------------------------------------------------------------------ +Parser::Parser (const TokenList& tokens, bool is_binary) +: tokens(tokens) +, last() +, current() +, cursor(tokens.begin()) +, is_binary(is_binary) +{ + root.reset(new Scope(*this,true)); +} + +// ------------------------------------------------------------------------------------------------ +Parser::~Parser() +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +TokenPtr Parser::AdvanceToNextToken() +{ + last = current; + if (cursor == tokens.end()) { + current = NULL; + } else { + current = *cursor++; + } + return current; +} + +// ------------------------------------------------------------------------------------------------ +TokenPtr Parser::CurrentToken() const +{ + return current; +} + +// ------------------------------------------------------------------------------------------------ +TokenPtr Parser::LastToken() const +{ + return last; +} + +// ------------------------------------------------------------------------------------------------ +uint64_t ParseTokenAsID(const Token& t, const char*& err_out) +{ + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0L; + } + + if(t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'L') { + err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; + return 0L; + } + + BE_NCONST uint64_t id = SafeParse<uint64_t>(data+1, t.end()); + AI_SWAP8(id); + return id; + } + + // XXX: should use size_t here + unsigned int length = static_cast<unsigned int>(t.end() - t.begin()); + ai_assert(length > 0); + + const char* out = nullptr; + const uint64_t id = strtoul10_64(t.begin(),&out,&length); + if (out > t.end()) { + err_out = "failed to parse ID (text)"; + return 0L; + } + + return id; +} + +// ------------------------------------------------------------------------------------------------ +size_t ParseTokenAsDim(const Token& t, const char*& err_out) +{ + // same as ID parsing, except there is a trailing asterisk + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0; + } + + if(t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'L') { + err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; + return 0; + } + + BE_NCONST uint64_t id = SafeParse<uint64_t>(data+1, t.end()); + AI_SWAP8(id); + return static_cast<size_t>(id); + } + + if(*t.begin() != '*') { + err_out = "expected asterisk before array dimension"; + return 0; + } + + // XXX: should use size_t here + unsigned int length = static_cast<unsigned int>(t.end() - t.begin()); + if(length == 0) { + err_out = "expected valid integer number after asterisk"; + return 0; + } + + const char* out = nullptr; + const size_t id = static_cast<size_t>(strtoul10_64(t.begin() + 1,&out,&length)); + if (out > t.end()) { + err_out = "failed to parse ID"; + return 0; + } + + return id; +} + + +// ------------------------------------------------------------------------------------------------ +float ParseTokenAsFloat(const Token& t, const char*& err_out) +{ + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0.0f; + } + + if(t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'F' && data[0] != 'D') { + err_out = "failed to parse F(loat) or D(ouble), unexpected data type (binary)"; + return 0.0f; + } + + if (data[0] == 'F') { + return SafeParse<float>(data+1, t.end()); + } + else { + return static_cast<float>( SafeParse<double>(data+1, t.end()) ); + } + } + + // need to copy the input string to a temporary buffer + // first - next in the fbx token stream comes ',', + // which fast_atof could interpret as decimal point. +#define MAX_FLOAT_LENGTH 31 + char temp[MAX_FLOAT_LENGTH + 1]; + const size_t length = static_cast<size_t>(t.end()-t.begin()); + std::copy(t.begin(),t.end(),temp); + temp[std::min(static_cast<size_t>(MAX_FLOAT_LENGTH),length)] = '\0'; + + return fast_atof(temp); +} + + +// ------------------------------------------------------------------------------------------------ +int ParseTokenAsInt(const Token& t, const char*& err_out) +{ + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0; + } + + if(t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'I') { + err_out = "failed to parse I(nt), unexpected data type (binary)"; + return 0; + } + + BE_NCONST int32_t ival = SafeParse<int32_t>(data+1, t.end()); + AI_SWAP4(ival); + return static_cast<int>(ival); + } + + ai_assert(static_cast<size_t>(t.end() - t.begin()) > 0); + + const char* out; + const int intval = strtol10(t.begin(),&out); + if (out != t.end()) { + err_out = "failed to parse ID"; + return 0; + } + + return intval; +} + + +// ------------------------------------------------------------------------------------------------ +int64_t ParseTokenAsInt64(const Token& t, const char*& err_out) +{ + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return 0L; + } + + if (t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'L') { + err_out = "failed to parse Int64, unexpected data type"; + return 0L; + } + + BE_NCONST int64_t id = SafeParse<int64_t>(data + 1, t.end()); + AI_SWAP8(id); + return id; + } + + // XXX: should use size_t here + unsigned int length = static_cast<unsigned int>(t.end() - t.begin()); + ai_assert(length > 0); + + const char* out = nullptr; + const int64_t id = strtol10_64(t.begin(), &out, &length); + if (out > t.end()) { + err_out = "failed to parse Int64 (text)"; + return 0L; + } + + return id; +} + +// ------------------------------------------------------------------------------------------------ +std::string ParseTokenAsString(const Token& t, const char*& err_out) +{ + err_out = NULL; + + if (t.Type() != TokenType_DATA) { + err_out = "expected TOK_DATA token"; + return ""; + } + + if(t.IsBinary()) + { + const char* data = t.begin(); + if (data[0] != 'S') { + err_out = "failed to parse S(tring), unexpected data type (binary)"; + return ""; + } + + // read string length + BE_NCONST int32_t len = SafeParse<int32_t>(data+1, t.end()); + AI_SWAP4(len); + + ai_assert(t.end() - data == 5 + len); + return std::string(data + 5, len); + } + + const size_t length = static_cast<size_t>(t.end() - t.begin()); + if(length < 2) { + err_out = "token is too short to hold a string"; + return ""; + } + + const char* s = t.begin(), *e = t.end() - 1; + if (*s != '\"' || *e != '\"') { + err_out = "expected double quoted string"; + return ""; + } + + return std::string(s+1,length-2); +} + + +namespace { + +// ------------------------------------------------------------------------------------------------ +// read the type code and element count of a binary data array and stop there +void ReadBinaryDataArrayHead(const char*& data, const char* end, char& type, uint32_t& count, + const Element& el) +{ + if (static_cast<size_t>(end-data) < 5) { + ParseError("binary data array is too short, need five (5) bytes for type signature and element count",&el); + } + + // data type + type = *data; + + // read number of elements + BE_NCONST uint32_t len = SafeParse<uint32_t>(data+1, end); + AI_SWAP4(len); + + count = len; + data += 5; +} + + +// ------------------------------------------------------------------------------------------------ +// read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header) +void ReadBinaryDataArray(char type, uint32_t count, const char*& data, const char* end, + std::vector<char>& buff, + const Element& /*el*/) +{ + BE_NCONST uint32_t encmode = SafeParse<uint32_t>(data, end); + AI_SWAP4(encmode); + data += 4; + + // next comes the compressed length + BE_NCONST uint32_t comp_len = SafeParse<uint32_t>(data, end); + AI_SWAP4(comp_len); + data += 4; + + ai_assert(data + comp_len == end); + + // determine the length of the uncompressed data by looking at the type signature + uint32_t stride = 0; + switch(type) + { + case 'f': + case 'i': + stride = 4; + break; + + case 'd': + case 'l': + stride = 8; + break; + + default: + ai_assert(false); + }; + + const uint32_t full_length = stride * count; + buff.resize(full_length); + + if(encmode == 0) { + ai_assert(full_length == comp_len); + + // plain data, no compression + std::copy(data, end, buff.begin()); + } + else if(encmode == 1) { + // zlib/deflate, next comes ZIP head (0x78 0x01) + // see http://www.ietf.org/rfc/rfc1950.txt + + z_stream zstream; + zstream.opaque = Z_NULL; + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.data_type = Z_BINARY; + + // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib + if(Z_OK != inflateInit(&zstream)) { + ParseError("failure initializing zlib"); + } + + zstream.next_in = reinterpret_cast<Bytef*>( const_cast<char*>(data) ); + zstream.avail_in = comp_len; + + zstream.avail_out = static_cast<uInt>(buff.size()); + zstream.next_out = reinterpret_cast<Bytef*>(&*buff.begin()); + const int ret = inflate(&zstream, Z_FINISH); + + if (ret != Z_STREAM_END && ret != Z_OK) { + ParseError("failure decompressing compressed data section"); + } + + // terminate zlib + inflateEnd(&zstream); + } +#ifdef ASSIMP_BUILD_DEBUG + else { + // runtime check for this happens at tokenization stage + ai_assert(false); + } +#endif + + data += comp_len; + ai_assert(data == end); +} + +} // !anon + + +// ------------------------------------------------------------------------------------------------ +// read an array of float3 tuples +void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el) +{ + out.resize( 0 ); + + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(count % 3 != 0) { + ParseError("number of floats is not a multiple of three (3) (binary)",&el); + } + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count3 = count / 3; + out.reserve(count3); + + if (type == 'd') { + const double* d = reinterpret_cast<const double*>(&buff[0]); + for (unsigned int i = 0; i < count3; ++i, d += 3) { + out.push_back(aiVector3D(static_cast<float>(d[0]), + static_cast<float>(d[1]), + static_cast<float>(d[2]))); + } + // for debugging + /*for ( size_t i = 0; i < out.size(); i++ ) { + aiVector3D vec3( out[ i ] ); + std::stringstream stream; + stream << " vec3.x = " << vec3.x << " vec3.y = " << vec3.y << " vec3.z = " << vec3.z << std::endl; + DefaultLogger::get()->info( stream.str() ); + }*/ + } + else if (type == 'f') { + const float* f = reinterpret_cast<const float*>(&buff[0]); + for (unsigned int i = 0; i < count3; ++i, f += 3) { + out.push_back(aiVector3D(f[0],f[1],f[2])); + } + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // may throw bad_alloc if the input is rubbish, but this need + // not to be prevented - importing would fail but we wouldn't + // crash since assimp handles this case properly. + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + if (a.Tokens().size() % 3 != 0) { + ParseError("number of floats is not a multiple of three (3)",&el); + } + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + aiVector3D v; + v.x = ParseTokenAsFloat(**it++); + v.y = ParseTokenAsFloat(**it++); + v.z = ParseTokenAsFloat(**it++); + + out.push_back(v); + } +} + + +// ------------------------------------------------------------------------------------------------ +// read an array of color4 tuples +void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(count % 4 != 0) { + ParseError("number of floats is not a multiple of four (4) (binary)",&el); + } + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count4 = count / 4; + out.reserve(count4); + + if (type == 'd') { + const double* d = reinterpret_cast<const double*>(&buff[0]); + for (unsigned int i = 0; i < count4; ++i, d += 4) { + out.push_back(aiColor4D(static_cast<float>(d[0]), + static_cast<float>(d[1]), + static_cast<float>(d[2]), + static_cast<float>(d[3]))); + } + } + else if (type == 'f') { + const float* f = reinterpret_cast<const float*>(&buff[0]); + for (unsigned int i = 0; i < count4; ++i, f += 4) { + out.push_back(aiColor4D(f[0],f[1],f[2],f[3])); + } + } + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() above + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + if (a.Tokens().size() % 4 != 0) { + ParseError("number of floats is not a multiple of four (4)",&el); + } + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + aiColor4D v; + v.r = ParseTokenAsFloat(**it++); + v.g = ParseTokenAsFloat(**it++); + v.b = ParseTokenAsFloat(**it++); + v.a = ParseTokenAsFloat(**it++); + + out.push_back(v); + } +} + + +// ------------------------------------------------------------------------------------------------ +// read an array of float2 tuples +void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(count % 2 != 0) { + ParseError("number of floats is not a multiple of two (2) (binary)",&el); + } + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count2 = count / 2; + out.reserve(count2); + + if (type == 'd') { + const double* d = reinterpret_cast<const double*>(&buff[0]); + for (unsigned int i = 0; i < count2; ++i, d += 2) { + out.push_back(aiVector2D(static_cast<float>(d[0]), + static_cast<float>(d[1]))); + } + } + else if (type == 'f') { + const float* f = reinterpret_cast<const float*>(&buff[0]); + for (unsigned int i = 0; i < count2; ++i, f += 2) { + out.push_back(aiVector2D(f[0],f[1])); + } + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() above + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + if (a.Tokens().size() % 2 != 0) { + ParseError("number of floats is not a multiple of two (2)",&el); + } + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + aiVector2D v; + v.x = ParseTokenAsFloat(**it++); + v.y = ParseTokenAsFloat(**it++); + + out.push_back(v); + } +} + + +// ------------------------------------------------------------------------------------------------ +// read an array of ints +void ParseVectorDataArray(std::vector<int>& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(!count) { + return; + } + + if (type != 'i') { + ParseError("expected int array (binary)",&el); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * 4); + + out.reserve(count); + + const int32_t* ip = reinterpret_cast<const int32_t*>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + BE_NCONST int32_t val = *ip; + AI_SWAP4(val); + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + const int ival = ParseTokenAsInt(**it++); + out.push_back(ival); + } +} + + +// ------------------------------------------------------------------------------------------------ +// read an array of floats +void ParseVectorDataArray(std::vector<float>& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + if (type == 'd') { + const double* d = reinterpret_cast<const double*>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++d) { + out.push_back(static_cast<float>(*d)); + } + } + else if (type == 'f') { + const float* f = reinterpret_cast<const float*>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++f) { + out.push_back(*f); + } + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + const float ival = ParseTokenAsFloat(**it++); + out.push_back(ival); + } +} + + +// ------------------------------------------------------------------------------------------------ +// read an array of uints +void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(!count) { + return; + } + + if (type != 'i') { + ParseError("expected (u)int array (binary)",&el); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * 4); + + out.reserve(count); + + const int32_t* ip = reinterpret_cast<const int32_t*>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + BE_NCONST int32_t val = *ip; + if(val < 0) { + ParseError("encountered negative integer index (binary)"); + } + + AI_SWAP4(val); + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + const int ival = ParseTokenAsInt(**it++); + if(ival < 0) { + ParseError("encountered negative integer index"); + } + out.push_back(static_cast<unsigned int>(ival)); + } +} + + +// ------------------------------------------------------------------------------------------------ +// read an array of uint64_ts +void ParseVectorDataArray(std::vector<uint64_t>& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(!count) { + return; + } + + if (type != 'l') { + ParseError("expected long array (binary)",&el); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * 8); + + out.reserve(count); + + const uint64_t* ip = reinterpret_cast<const uint64_t*>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + BE_NCONST uint64_t val = *ip; + AI_SWAP8(val); + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope,"a",&el); + + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { + const uint64_t ival = ParseTokenAsID(**it++); + + out.push_back(ival); + } +} + +// ------------------------------------------------------------------------------------------------ +// read an array of int64_ts +void ParseVectorDataArray(std::vector<int64_t>& out, const Element& el) +{ + out.resize( 0 ); + const TokenList& tok = el.Tokens(); + if (tok.empty()) { + ParseError("unexpected empty element", &el); + } + + if (tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if (!count) { + return; + } + + if (type != 'l') { + ParseError("expected long array (binary)", &el); + } + + std::vector<char> buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * 8); + + out.reserve(count); + + const int64_t* ip = reinterpret_cast<const int64_t*>(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + BE_NCONST int64_t val = *ip; + AI_SWAP8(val); + out.push_back(val); + } + + return; + } + + const size_t dim = ParseTokenAsDim(*tok[0]); + + // see notes in ParseVectorDataArray() + out.reserve(dim); + + const Scope& scope = GetRequiredScope(el); + const Element& a = GetRequiredElement(scope, "a", &el); + + for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end;) { + const int64_t ival = ParseTokenAsInt64(**it++); + + out.push_back(ival); + } +} + +// ------------------------------------------------------------------------------------------------ +aiMatrix4x4 ReadMatrix(const Element& element) +{ + std::vector<float> values; + ParseVectorDataArray(values,element); + + if(values.size() != 16) { + ParseError("expected 16 matrix elements"); + } + + aiMatrix4x4 result; + + + result.a1 = values[0]; + result.a2 = values[1]; + result.a3 = values[2]; + result.a4 = values[3]; + + result.b1 = values[4]; + result.b2 = values[5]; + result.b3 = values[6]; + result.b4 = values[7]; + + result.c1 = values[8]; + result.c2 = values[9]; + result.c3 = values[10]; + result.c4 = values[11]; + + result.d1 = values[12]; + result.d2 = values[13]; + result.d3 = values[14]; + result.d4 = values[15]; + + result.Transpose(); + return result; +} + + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsString() with ParseError handling +std::string ParseTokenAsString(const Token& t) +{ + const char* err; + const std::string& i = ParseTokenAsString(t,err); + if(err) { + ParseError(err,t); + } + return i; +} + +bool HasElement( const Scope& sc, const std::string& index ) { + const Element* el = sc[ index ]; + if ( nullptr == el ) { + return false; + } + + return true; +} + +// ------------------------------------------------------------------------------------------------ +// extract a required element from a scope, abort if the element cannot be found +const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element /*= NULL*/) +{ + const Element* el = sc[index]; + if(!el) { + ParseError("did not find required element \"" + index + "\"",element); + } + return *el; +} + + +// ------------------------------------------------------------------------------------------------ +// extract required compound scope +const Scope& GetRequiredScope(const Element& el) +{ + const Scope* const s = el.Compound(); + if(!s) { + ParseError("expected compound scope",&el); + } + + return *s; +} + + +// ------------------------------------------------------------------------------------------------ +// get token at a particular index +const Token& GetRequiredToken(const Element& el, unsigned int index) +{ + const TokenList& t = el.Tokens(); + if(index >= t.size()) { + ParseError(Formatter::format( "missing token at index " ) << index,&el); + } + + return *t[index]; +} + + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsID() with ParseError handling +uint64_t ParseTokenAsID(const Token& t) +{ + const char* err; + const uint64_t i = ParseTokenAsID(t,err); + if(err) { + ParseError(err,t); + } + return i; +} + + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsDim() with ParseError handling +size_t ParseTokenAsDim(const Token& t) +{ + const char* err; + const size_t i = ParseTokenAsDim(t,err); + if(err) { + ParseError(err,t); + } + return i; +} + + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsFloat() with ParseError handling +float ParseTokenAsFloat(const Token& t) +{ + const char* err; + const float i = ParseTokenAsFloat(t,err); + if(err) { + ParseError(err,t); + } + return i; +} + + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsInt() with ParseError handling +int ParseTokenAsInt(const Token& t) +{ + const char* err; + const int i = ParseTokenAsInt(t,err); + if(err) { + ParseError(err,t); + } + return i; +} + + + +// ------------------------------------------------------------------------------------------------ +// wrapper around ParseTokenAsInt64() with ParseError handling +int64_t ParseTokenAsInt64(const Token& t) +{ + const char* err; + const int64_t i = ParseTokenAsInt64(t, err); + if (err) { + ParseError(err, t); + } + return i; +} + +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBXParser.h b/thirdparty/assimp/code/FBXParser.h new file mode 100644 index 0000000000..7b0cf72039 --- /dev/null +++ b/thirdparty/assimp/code/FBXParser.h @@ -0,0 +1,235 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXParser.h + * @brief FBX parsing code + */ +#ifndef INCLUDED_AI_FBX_PARSER_H +#define INCLUDED_AI_FBX_PARSER_H + +#include <stdint.h> +#include <map> +#include <memory> +#include <assimp/LogAux.h> +#include <assimp/fast_atof.h> + +#include "FBXCompileConfig.h" +#include "FBXTokenizer.h" + +namespace Assimp { +namespace FBX { + +class Scope; +class Parser; +class Element; + +// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03 +typedef std::vector< Scope* > ScopeList; +typedef std::fbx_unordered_multimap< std::string, Element* > ElementMap; + +typedef std::pair<ElementMap::const_iterator,ElementMap::const_iterator> ElementCollection; + +# define new_Scope new Scope +# define new_Element new Element + + +/** FBX data entity that consists of a key:value tuple. + * + * Example: + * @verbatim + * AnimationCurve: 23, "AnimCurve::", "" { + * [..] + * } + * @endverbatim + * + * As can be seen in this sample, elements can contain nested #Scope + * as their trailing member. **/ +class Element +{ +public: + Element(const Token& key_token, Parser& parser); + ~Element(); + + const Scope* Compound() const { + return compound.get(); + } + + const Token& KeyToken() const { + return key_token; + } + + const TokenList& Tokens() const { + return tokens; + } + +private: + const Token& key_token; + TokenList tokens; + std::unique_ptr<Scope> compound; +}; + +/** FBX data entity that consists of a 'scope', a collection + * of not necessarily unique #Element instances. + * + * Example: + * @verbatim + * GlobalSettings: { + * Version: 1000 + * Properties70: + * [...] + * } + * @endverbatim */ +class Scope +{ +public: + Scope(Parser& parser, bool topLevel = false); + ~Scope(); + + const Element* operator[] (const std::string& index) const { + ElementMap::const_iterator it = elements.find(index); + return it == elements.end() ? NULL : (*it).second; + } + + const Element* FindElementCaseInsensitive(const std::string& elementName) const { + const char* elementNameCStr = elementName.c_str(); + for (auto element = elements.begin(); element != elements.end(); ++element) + { + if (!ASSIMP_strincmp(element->first.c_str(), elementNameCStr, MAXLEN)) { + return element->second; + } + } + return NULL; + } + + ElementCollection GetCollection(const std::string& index) const { + return elements.equal_range(index); + } + + const ElementMap& Elements() const { + return elements; + } + +private: + ElementMap elements; +}; + +/** FBX parsing class, takes a list of input tokens and generates a hierarchy + * of nested #Scope instances, representing the fbx DOM.*/ +class Parser +{ +public: + /** Parse given a token list. Does not take ownership of the tokens - + * the objects must persist during the entire parser lifetime */ + Parser (const TokenList& tokens,bool is_binary); + ~Parser(); + + const Scope& GetRootScope() const { + return *root.get(); + } + + bool IsBinary() const { + return is_binary; + } + +private: + friend class Scope; + friend class Element; + + TokenPtr AdvanceToNextToken(); + TokenPtr LastToken() const; + TokenPtr CurrentToken() const; + +private: + const TokenList& tokens; + + TokenPtr last, current; + TokenList::const_iterator cursor; + std::unique_ptr<Scope> root; + + const bool is_binary; +}; + + +/* token parsing - this happens when building the DOM out of the parse-tree*/ +uint64_t ParseTokenAsID(const Token& t, const char*& err_out); +size_t ParseTokenAsDim(const Token& t, const char*& err_out); + +float ParseTokenAsFloat(const Token& t, const char*& err_out); +int ParseTokenAsInt(const Token& t, const char*& err_out); +int64_t ParseTokenAsInt64(const Token& t, const char*& err_out); +std::string ParseTokenAsString(const Token& t, const char*& err_out); + +/* wrapper around ParseTokenAsXXX() with DOMError handling */ +uint64_t ParseTokenAsID(const Token& t); +size_t ParseTokenAsDim(const Token& t); +float ParseTokenAsFloat(const Token& t); +int ParseTokenAsInt(const Token& t); +int64_t ParseTokenAsInt64(const Token& t); +std::string ParseTokenAsString(const Token& t); + +/* read data arrays */ +void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el); +void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el); +void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el); +void ParseVectorDataArray(std::vector<int>& out, const Element& el); +void ParseVectorDataArray(std::vector<float>& out, const Element& el); +void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el); +void ParseVectorDataArray(std::vector<uint64_t>& out, const Element& e); +void ParseVectorDataArray(std::vector<int64_t>& out, const Element& el); + +bool HasElement( const Scope& sc, const std::string& index ); + +// extract a required element from a scope, abort if the element cannot be found +const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element = NULL); + +// extract required compound scope +const Scope& GetRequiredScope(const Element& el); +// get token at a particular index +const Token& GetRequiredToken(const Element& el, unsigned int index); + +// read a 4x4 matrix from an array of 16 floats +aiMatrix4x4 ReadMatrix(const Element& element); + +} // ! FBX +} // ! Assimp + +#endif // ! INCLUDED_AI_FBX_PARSER_H diff --git a/thirdparty/assimp/code/FBXProperties.cpp b/thirdparty/assimp/code/FBXProperties.cpp new file mode 100644 index 0000000000..8d7036b6a9 --- /dev/null +++ b/thirdparty/assimp/code/FBXProperties.cpp @@ -0,0 +1,235 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXProperties.cpp + * @brief Implementation of the FBX dynamic properties system + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +#include "FBXTokenizer.h" +#include "FBXParser.h" +#include "FBXDocument.h" +#include "FBXDocumentUtil.h" +#include "FBXProperties.h" + +namespace Assimp { +namespace FBX { + + using namespace Util; + +// ------------------------------------------------------------------------------------------------ +Property::Property() +{ +} + +// ------------------------------------------------------------------------------------------------ +Property::~Property() +{ +} + +namespace { + +// ------------------------------------------------------------------------------------------------ +// read a typed property out of a FBX element. The return value is NULL if the property cannot be read. +Property* ReadTypedProperty(const Element& element) +{ + ai_assert(element.KeyToken().StringContents() == "P"); + + const TokenList& tok = element.Tokens(); + ai_assert(tok.size() >= 5); + + const std::string& s = ParseTokenAsString(*tok[1]); + const char* const cs = s.c_str(); + if (!strcmp(cs,"KString")) { + return new TypedProperty<std::string>(ParseTokenAsString(*tok[4])); + } + else if (!strcmp(cs,"bool") || !strcmp(cs,"Bool")) { + return new TypedProperty<bool>(ParseTokenAsInt(*tok[4]) != 0); + } + else if (!strcmp(cs, "int") || !strcmp(cs, "Int") || !strcmp(cs, "enum") || !strcmp(cs, "Enum")) { + return new TypedProperty<int>(ParseTokenAsInt(*tok[4])); + } + else if (!strcmp(cs, "ULongLong")) { + return new TypedProperty<uint64_t>(ParseTokenAsID(*tok[4])); + } + else if (!strcmp(cs, "KTime")) { + return new TypedProperty<int64_t>(ParseTokenAsInt64(*tok[4])); + } + else if (!strcmp(cs,"Vector3D") || + !strcmp(cs,"ColorRGB") || + !strcmp(cs,"Vector") || + !strcmp(cs,"Color") || + !strcmp(cs,"Lcl Translation") || + !strcmp(cs,"Lcl Rotation") || + !strcmp(cs,"Lcl Scaling") + ) { + return new TypedProperty<aiVector3D>(aiVector3D( + ParseTokenAsFloat(*tok[4]), + ParseTokenAsFloat(*tok[5]), + ParseTokenAsFloat(*tok[6])) + ); + } + else if (!strcmp(cs,"double") || !strcmp(cs,"Number") || !strcmp(cs,"Float") || !strcmp(cs,"FieldOfView") || !strcmp( cs, "UnitScaleFactor" ) ) { + return new TypedProperty<float>(ParseTokenAsFloat(*tok[4])); + } + return NULL; +} + + +// ------------------------------------------------------------------------------------------------ +// peek into an element and check if it contains a FBX property, if so return its name. +std::string PeekPropertyName(const Element& element) +{ + ai_assert(element.KeyToken().StringContents() == "P"); + const TokenList& tok = element.Tokens(); + if(tok.size() < 4) { + return ""; + } + + return ParseTokenAsString(*tok[0]); +} + +} //! anon + + +// ------------------------------------------------------------------------------------------------ +PropertyTable::PropertyTable() +: templateProps() +, element() +{ +} + +// ------------------------------------------------------------------------------------------------ +PropertyTable::PropertyTable(const Element& element, std::shared_ptr<const PropertyTable> templateProps) +: templateProps(templateProps) +, element(&element) +{ + const Scope& scope = GetRequiredScope(element); + for(const ElementMap::value_type& v : scope.Elements()) { + if(v.first != "P") { + DOMWarning("expected only P elements in property table",v.second); + continue; + } + + const std::string& name = PeekPropertyName(*v.second); + if(!name.length()) { + DOMWarning("could not read property name",v.second); + continue; + } + + LazyPropertyMap::const_iterator it = lazyProps.find(name); + if (it != lazyProps.end()) { + DOMWarning("duplicate property name, will hide previous value: " + name,v.second); + continue; + } + + lazyProps[name] = v.second; + } +} + + +// ------------------------------------------------------------------------------------------------ +PropertyTable::~PropertyTable() +{ + for(PropertyMap::value_type& v : props) { + delete v.second; + } +} + + +// ------------------------------------------------------------------------------------------------ +const Property* PropertyTable::Get(const std::string& name) const +{ + PropertyMap::const_iterator it = props.find(name); + if (it == props.end()) { + // hasn't been parsed yet? + LazyPropertyMap::const_iterator lit = lazyProps.find(name); + if(lit != lazyProps.end()) { + props[name] = ReadTypedProperty(*(*lit).second); + it = props.find(name); + + ai_assert(it != props.end()); + } + + if (it == props.end()) { + // check property template + if(templateProps) { + return templateProps->Get(name); + } + + return NULL; + } + } + + return (*it).second; +} + +DirectPropertyMap PropertyTable::GetUnparsedProperties() const +{ + DirectPropertyMap result; + + // Loop through all the lazy properties (which is all the properties) + for(const LazyPropertyMap::value_type& element : lazyProps) { + + // Skip parsed properties + if (props.end() != props.find(element.first)) continue; + + // Read the element's value. + // Wrap the naked pointer (since the call site is required to acquire ownership) + // std::unique_ptr from C++11 would be preferred both as a wrapper and a return value. + std::shared_ptr<Property> prop = std::shared_ptr<Property>(ReadTypedProperty(*element.second)); + + // Element could not be read. Skip it. + if (!prop) continue; + + // Add to result + result[element.first] = prop; + } + + return result; +} + +} //! FBX +} //! Assimp + +#endif diff --git a/thirdparty/assimp/code/FBXProperties.h b/thirdparty/assimp/code/FBXProperties.h new file mode 100644 index 0000000000..58755542fc --- /dev/null +++ b/thirdparty/assimp/code/FBXProperties.h @@ -0,0 +1,185 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXProperties.h + * @brief FBX dynamic properties + */ +#ifndef INCLUDED_AI_FBX_PROPERTIES_H +#define INCLUDED_AI_FBX_PROPERTIES_H + +#include "FBXCompileConfig.h" +#include <memory> +#include <string> + +namespace Assimp { +namespace FBX { + +// Forward declarations +class Element; + +/** Represents a dynamic property. Type info added by deriving classes, + * see #TypedProperty. + Example: + @verbatim + P: "ShininessExponent", "double", "Number", "",0.5 + @endvebatim +*/ +class Property { +protected: + Property(); + +public: + virtual ~Property(); + +public: + template <typename T> + const T* As() const { + return dynamic_cast<const T*>(this); + } +}; + +template<typename T> +class TypedProperty : public Property { +public: + explicit TypedProperty(const T& value) + : value(value) { + // empty + } + + const T& Value() const { + return value; + } + +private: + T value; +}; + + +typedef std::fbx_unordered_map<std::string,std::shared_ptr<Property> > DirectPropertyMap; +typedef std::fbx_unordered_map<std::string,const Property*> PropertyMap; +typedef std::fbx_unordered_map<std::string,const Element*> LazyPropertyMap; + +/** + * Represents a property table as can be found in the newer FBX files (Properties60, Properties70) + */ +class PropertyTable { +public: + // in-memory property table with no source element + PropertyTable(); + PropertyTable(const Element& element, std::shared_ptr<const PropertyTable> templateProps); + ~PropertyTable(); + + const Property* Get(const std::string& name) const; + + // PropertyTable's need not be coupled with FBX elements so this can be NULL + const Element* GetElement() const { + return element; + } + + const PropertyTable* TemplateProps() const { + return templateProps.get(); + } + + DirectPropertyMap GetUnparsedProperties() const; + +private: + LazyPropertyMap lazyProps; + mutable PropertyMap props; + const std::shared_ptr<const PropertyTable> templateProps; + const Element* const element; +}; + +// ------------------------------------------------------------------------------------------------ +template <typename T> +inline +T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) { + const Property* const prop = in.Get(name); + if( nullptr == prop) { + return defaultValue; + } + + // strong typing, no need to be lenient + const TypedProperty<T>* const tprop = prop->As< TypedProperty<T> >(); + if( nullptr == tprop) { + return defaultValue; + } + + return tprop->Value(); +} + +// ------------------------------------------------------------------------------------------------ +template <typename T> +inline +T PropertyGet(const PropertyTable& in, const std::string& name, bool& result, bool useTemplate=false ) { + const Property* prop = in.Get(name); + if( nullptr == prop) { + if ( ! useTemplate ) { + result = false; + return T(); + } + const PropertyTable* templ = in.TemplateProps(); + if ( nullptr == templ ) { + result = false; + return T(); + } + prop = templ->Get(name); + if ( nullptr == prop ) { + result = false; + return T(); + } + } + + // strong typing, no need to be lenient + const TypedProperty<T>* const tprop = prop->As< TypedProperty<T> >(); + if( nullptr == tprop) { + result = false; + return T(); + } + + result = true; + return tprop->Value(); +} + +} //! FBX +} //! Assimp + +#endif // INCLUDED_AI_FBX_PROPERTIES_H diff --git a/thirdparty/assimp/code/FBXTokenizer.cpp b/thirdparty/assimp/code/FBXTokenizer.cpp new file mode 100644 index 0000000000..252cce3557 --- /dev/null +++ b/thirdparty/assimp/code/FBXTokenizer.cpp @@ -0,0 +1,248 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXTokenizer.cpp + * @brief Implementation of the FBX broadphase lexer + */ + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +// tab width for logging columns +#define ASSIMP_FBX_TAB_WIDTH 4 + +#include <assimp/ParsingUtils.h> + +#include "FBXTokenizer.h" +#include "FBXUtil.h" +#include <assimp/Exceptional.h> + +namespace Assimp { +namespace FBX { + +// ------------------------------------------------------------------------------------------------ +Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column) + : +#ifdef DEBUG + contents(sbegin, static_cast<size_t>(send-sbegin)), +#endif + sbegin(sbegin) + , send(send) + , type(type) + , line(line) + , column(column) +{ + ai_assert(sbegin); + ai_assert(send); + + // tokens must be of non-zero length + ai_assert(static_cast<size_t>(send-sbegin) > 0); +} + +// ------------------------------------------------------------------------------------------------ +Token::~Token() +{ +} + +namespace { + +// ------------------------------------------------------------------------------------------------ +// signal tokenization error, this is always unrecoverable. Throws DeadlyImportError. +AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) AI_WONT_RETURN_SUFFIX; +AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) +{ + throw DeadlyImportError(Util::AddLineAndColumn("FBX-Tokenize",message,line,column)); +} + + +// process a potential data token up to 'cur', adding it to 'output_tokens'. +// ------------------------------------------------------------------------------------------------ +void ProcessDataToken( TokenList& output_tokens, const char*& start, const char*& end, + unsigned int line, + unsigned int column, + TokenType type = TokenType_DATA, + bool must_have_token = false) +{ + if (start && end) { + // sanity check: + // tokens should have no whitespace outside quoted text and [start,end] should + // properly delimit the valid range. + bool in_double_quotes = false; + for (const char* c = start; c != end + 1; ++c) { + if (*c == '\"') { + in_double_quotes = !in_double_quotes; + } + + if (!in_double_quotes && IsSpaceOrNewLine(*c)) { + TokenizeError("unexpected whitespace in token", line, column); + } + } + + if (in_double_quotes) { + TokenizeError("non-terminated double quotes", line, column); + } + + output_tokens.push_back(new_Token(start,end + 1,type,line,column)); + } + else if (must_have_token) { + TokenizeError("unexpected character, expected data token", line, column); + } + + start = end = NULL; +} + +} + +// ------------------------------------------------------------------------------------------------ +void Tokenize(TokenList& output_tokens, const char* input) +{ + ai_assert(input); + + // line and column numbers numbers are one-based + unsigned int line = 1; + unsigned int column = 1; + + bool comment = false; + bool in_double_quotes = false; + bool pending_data_token = false; + + const char* token_begin = NULL, *token_end = NULL; + for (const char* cur = input;*cur;column += (*cur == '\t' ? ASSIMP_FBX_TAB_WIDTH : 1), ++cur) { + const char c = *cur; + + if (IsLineEnd(c)) { + comment = false; + + column = 0; + ++line; + } + + if(comment) { + continue; + } + + if(in_double_quotes) { + if (c == '\"') { + in_double_quotes = false; + token_end = cur; + + ProcessDataToken(output_tokens,token_begin,token_end,line,column); + pending_data_token = false; + } + continue; + } + + switch(c) + { + case '\"': + if (token_begin) { + TokenizeError("unexpected double-quote", line, column); + } + token_begin = cur; + in_double_quotes = true; + continue; + + case ';': + ProcessDataToken(output_tokens,token_begin,token_end,line,column); + comment = true; + continue; + + case '{': + ProcessDataToken(output_tokens,token_begin,token_end, line, column); + output_tokens.push_back(new_Token(cur,cur+1,TokenType_OPEN_BRACKET,line,column)); + continue; + + case '}': + ProcessDataToken(output_tokens,token_begin,token_end,line,column); + output_tokens.push_back(new_Token(cur,cur+1,TokenType_CLOSE_BRACKET,line,column)); + continue; + + case ',': + if (pending_data_token) { + ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_DATA,true); + } + output_tokens.push_back(new_Token(cur,cur+1,TokenType_COMMA,line,column)); + continue; + + case ':': + if (pending_data_token) { + ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_KEY,true); + } + else { + TokenizeError("unexpected colon", line, column); + } + continue; + } + + if (IsSpaceOrNewLine(c)) { + + if (token_begin) { + // peek ahead and check if the next token is a colon in which + // case this counts as KEY token. + TokenType type = TokenType_DATA; + for (const char* peek = cur; *peek && IsSpaceOrNewLine(*peek); ++peek) { + if (*peek == ':') { + type = TokenType_KEY; + cur = peek; + break; + } + } + + ProcessDataToken(output_tokens,token_begin,token_end,line,column,type); + } + + pending_data_token = false; + } + else { + token_end = cur; + if (!token_begin) { + token_begin = cur; + } + + pending_data_token = true; + } + } +} + +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBXTokenizer.h b/thirdparty/assimp/code/FBXTokenizer.h new file mode 100644 index 0000000000..2af29743f4 --- /dev/null +++ b/thirdparty/assimp/code/FBXTokenizer.h @@ -0,0 +1,187 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXTokenizer.h + * @brief FBX lexer + */ +#ifndef INCLUDED_AI_FBX_TOKENIZER_H +#define INCLUDED_AI_FBX_TOKENIZER_H + +#include "FBXCompileConfig.h" +#include <assimp/ai_assert.h> +#include <vector> +#include <string> + +namespace Assimp { +namespace FBX { + +/** Rough classification for text FBX tokens used for constructing the + * basic scope hierarchy. */ +enum TokenType +{ + // { + TokenType_OPEN_BRACKET = 0, + + // } + TokenType_CLOSE_BRACKET, + + // '"blablubb"', '2', '*14' - very general token class, + // further processing happens at a later stage. + TokenType_DATA, + + // + TokenType_BINARY_DATA, + + // , + TokenType_COMMA, + + // blubb: + TokenType_KEY +}; + + +/** Represents a single token in a FBX file. Tokens are + * classified by the #TokenType enumerated types. + * + * Offers iterator protocol. Tokens are immutable. */ +class Token +{ +private: + static const unsigned int BINARY_MARKER = static_cast<unsigned int>(-1); + +public: + /** construct a textual token */ + Token(const char* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column); + + /** construct a binary token */ + Token(const char* sbegin, const char* send, TokenType type, unsigned int offset); + + ~Token(); + +public: + std::string StringContents() const { + return std::string(begin(),end()); + } + + bool IsBinary() const { + return column == BINARY_MARKER; + } + + const char* begin() const { + return sbegin; + } + + const char* end() const { + return send; + } + + TokenType Type() const { + return type; + } + + unsigned int Offset() const { + ai_assert(IsBinary()); + return offset; + } + + unsigned int Line() const { + ai_assert(!IsBinary()); + return line; + } + + unsigned int Column() const { + ai_assert(!IsBinary()); + return column; + } + +private: + +#ifdef DEBUG + // full string copy for the sole purpose that it nicely appears + // in msvc's debugger window. + const std::string contents; +#endif + + + const char* const sbegin; + const char* const send; + const TokenType type; + + union { + const unsigned int line; + unsigned int offset; + }; + const unsigned int column; +}; + +// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03 +typedef const Token* TokenPtr; +typedef std::vector< TokenPtr > TokenList; + +#define new_Token new Token + + +/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens. + * + * Skips over comments and generates line and column numbers. + * + * @param output_tokens Receives a list of all tokens in the input data. + * @param input_buffer Textual input buffer to be processed, 0-terminated. + * @throw DeadlyImportError if something goes wrong */ +void Tokenize(TokenList& output_tokens, const char* input); + + +/** Tokenizer function for binary FBX files. + * + * Emits a token list suitable for direct parsing. + * + * @param output_tokens Receives a list of all tokens in the input data. + * @param input_buffer Binary input buffer to be processed. + * @param length Length of input buffer, in bytes. There is no 0-terminal. + * @throw DeadlyImportError if something goes wrong */ +void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int length); + + +} // ! FBX +} // ! Assimp + +#endif // ! INCLUDED_AI_FBX_PARSER_H diff --git a/thirdparty/assimp/code/FBXUtil.cpp b/thirdparty/assimp/code/FBXUtil.cpp new file mode 100644 index 0000000000..c184c4a00b --- /dev/null +++ b/thirdparty/assimp/code/FBXUtil.cpp @@ -0,0 +1,120 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXUtil.cpp + * @brief Implementation of internal FBX utility functions + */ + +#include "FBXUtil.h" +#include "FBXTokenizer.h" + +#include <assimp/TinyFormatter.h> +#include <string> + +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER + +namespace Assimp { +namespace FBX { +namespace Util { + +// ------------------------------------------------------------------------------------------------ +const char* TokenTypeString(TokenType t) +{ + switch(t) { + case TokenType_OPEN_BRACKET: + return "TOK_OPEN_BRACKET"; + + case TokenType_CLOSE_BRACKET: + return "TOK_CLOSE_BRACKET"; + + case TokenType_DATA: + return "TOK_DATA"; + + case TokenType_COMMA: + return "TOK_COMMA"; + + case TokenType_KEY: + return "TOK_KEY"; + + case TokenType_BINARY_DATA: + return "TOK_BINARY_DATA"; + } + + ai_assert(false); + return ""; +} + + +// ------------------------------------------------------------------------------------------------ +std::string AddOffset(const std::string& prefix, const std::string& text, unsigned int offset) +{ + return static_cast<std::string>( (Formatter::format() << prefix << " (offset 0x" << std::hex << offset << ") " << text) ); +} + +// ------------------------------------------------------------------------------------------------ +std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column) +{ + return static_cast<std::string>( (Formatter::format() << prefix << " (line " << line << " << col " << column << ") " << text) ); +} + +// ------------------------------------------------------------------------------------------------ +std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok) +{ + if(tok->IsBinary()) { + return static_cast<std::string>( (Formatter::format() << prefix << + " (" << TokenTypeString(tok->Type()) << + ", offset 0x" << std::hex << tok->Offset() << ") " << + text) ); + } + + return static_cast<std::string>( (Formatter::format() << prefix << + " (" << TokenTypeString(tok->Type()) << + ", line " << tok->Line() << + ", col " << tok->Column() << ") " << + text) ); +} + +} // !Util +} // !FBX +} // !Assimp + +#endif diff --git a/thirdparty/assimp/code/FBXUtil.h b/thirdparty/assimp/code/FBXUtil.h new file mode 100644 index 0000000000..1a37d346b4 --- /dev/null +++ b/thirdparty/assimp/code/FBXUtil.h @@ -0,0 +1,105 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FBXUtil.h + * @brief FBX utility functions for internal use + */ +#ifndef INCLUDED_AI_FBX_UTIL_H +#define INCLUDED_AI_FBX_UTIL_H + +#include "FBXCompileConfig.h" +#include "FBXTokenizer.h" + +namespace Assimp { +namespace FBX { + + +namespace Util { + + +/** helper for std::for_each to delete all heap-allocated items in a container */ +template<typename T> +struct delete_fun +{ + void operator()(const volatile T* del) { + delete del; + } +}; + +/** Get a string representation for a #TokenType. */ +const char* TokenTypeString(TokenType t); + + + +/** Format log/error messages using a given offset in the source binary file + * + * @param prefix Message prefix to be preprended to the location info. + * @param text Message text + * @param line Line index, 1-based + * @param column Column index, 1-based + * @return A string of the following format: {prefix} (offset 0x{offset}) {text}*/ +std::string AddOffset(const std::string& prefix, const std::string& text, unsigned int offset); + + +/** Format log/error messages using a given line location in the source file. + * + * @param prefix Message prefix to be preprended to the location info. + * @param text Message text + * @param line Line index, 1-based + * @param column Column index, 1-based + * @return A string of the following format: {prefix} (line {line}, col {column}) {text}*/ +std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column); + + +/** Format log/error messages using a given cursor token. + * + * @param prefix Message prefix to be preprended to the location info. + * @param text Message text + * @param tok Token where parsing/processing stopped + * @return A string of the following format: {prefix} ({token-type}, line {line}, col {column}) {text}*/ +std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok); + +} +} +} + +#endif // ! INCLUDED_AI_FBX_UTIL_H diff --git a/thirdparty/assimp/code/FIReader.cpp b/thirdparty/assimp/code/FIReader.cpp new file mode 100644 index 0000000000..2116316ca3 --- /dev/null +++ b/thirdparty/assimp/code/FIReader.cpp @@ -0,0 +1,1834 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file FIReader.cpp +/// \brief Reader for Fast Infoset encoded binary XML files. +/// \date 2017 +/// \author Patrick Daehne + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "FIReader.hpp" +#include <assimp/StringUtils.h> + +// Workaround for issue #1361 +// https://github.com/assimp/assimp/issues/1361 +#ifdef __ANDROID__ +# define _GLIBCXX_USE_C99 1 +#endif + +#include <assimp/Exceptional.h> +#include <assimp/IOStream.hpp> +#include <assimp/types.h> +#include <assimp/MemoryIOWrapper.h> +#include <assimp/irrXMLWrapper.h> +#include "../contrib/utf8cpp/source/utf8.h" +#include <assimp/fast_atof.h> +#include <stack> +#include <map> +#include <iostream> +#include <sstream> +#include <iomanip> + +namespace Assimp { + +static const std::string parseErrorMessage = "Fast Infoset parse error"; + +static const char *xmlDeclarations[] = { + "<?xml encoding='finf'?>", + "<?xml encoding='finf' standalone='yes'?>", + "<?xml encoding='finf' standalone='no'?>", + "<?xml version='1.0' encoding='finf'?>", + "<?xml version='1.0' encoding='finf' standalone='yes'?>", + "<?xml version='1.0' encoding='finf' standalone='no'?>", + "<?xml version='1.1' encoding='finf'?>", + "<?xml version='1.1' encoding='finf' standalone='yes'?>", + "<?xml version='1.1' encoding='finf' standalone='no'?>" +}; + +static size_t parseMagic(const uint8_t *data, const uint8_t *dataEnd) { + if (dataEnd - data < 4) { + return 0; + } + uint32_t magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + switch (magic) { + case 0xe0000001: + return 4; + case 0x3c3f786d: // "<?xm" + { + size_t xmlDeclarationsLength = sizeof(xmlDeclarations) / sizeof(xmlDeclarations[0]); + for (size_t i = 0; i < xmlDeclarationsLength; ++i) { + auto xmlDeclaration = xmlDeclarations[i]; + ptrdiff_t xmlDeclarationLength = strlen(xmlDeclaration); + if ((dataEnd - data >= xmlDeclarationLength) && (memcmp(xmlDeclaration, data, xmlDeclarationLength) == 0)) { + data += xmlDeclarationLength; + if (dataEnd - data < 4) { + return 0; + } + magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + return magic == 0xe0000001 ? xmlDeclarationLength + 4 : 0; + } + } + return 0; + } + default: + return 0; + } +} + +static std::string parseUTF8String(const uint8_t *data, size_t len) { + return std::string((char*)data, len); +} + +static std::string parseUTF16String(const uint8_t *data, size_t len) { + if (len & 1) { + throw DeadlyImportError(parseErrorMessage); + } + size_t numShorts = len / 2; + std::vector<short> utf16; + utf16.reserve(numShorts); + for (size_t i = 0; i < numShorts; ++i) { + short v = (data[0] << 8) | data[1]; + utf16.push_back(v); + data += 2; + } + std::string result; + utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(result)); + return result; +} + +struct FIStringValueImpl: public FIStringValue { + inline FIStringValueImpl(std::string &&value_) { value = std::move(value_); } + virtual const std::string &toString() const /*override*/ { return value; } +}; + +std::shared_ptr<FIStringValue> FIStringValue::create(std::string &&value) { + return std::make_shared<FIStringValueImpl>(std::move(value)); +} + +struct FIHexValueImpl: public FIHexValue { + mutable std::string strValue; + mutable bool strValueValid; + inline FIHexValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); } + virtual const std::string &toString() const /*override*/ { + if (!strValueValid) { + strValueValid = true; + std::ostringstream os; + os << std::hex << std::uppercase << std::setfill('0'); + std::for_each(value.begin(), value.end(), [&](uint8_t c) { os << std::setw(2) << static_cast<int>(c); }); + strValue = os.str(); + } + return strValue; + }; +}; + +std::shared_ptr<FIHexValue> FIHexValue::create(std::vector<uint8_t> &&value) { + return std::make_shared<FIHexValueImpl>(std::move(value)); +} + +struct FIBase64ValueImpl: public FIBase64Value { + mutable std::string strValue; + mutable bool strValueValid; + inline FIBase64ValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); } + virtual const std::string &toString() const /*override*/ { + if (!strValueValid) { + strValueValid = true; + std::ostringstream os; + uint8_t c1 = 0, c2; + int imod3 = 0; + std::vector<uint8_t>::size_type valueSize = value.size(); + for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) { + c2 = value[i]; + switch (imod3) { + case 0: + os << basis_64[c2 >> 2]; + imod3 = 1; + break; + case 1: + os << basis_64[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)]; + imod3 = 2; + break; + case 2: + os << basis_64[((c1 & 0x0f) << 2) | ((c2 & 0xc0) >> 6)] << basis_64[c2 & 0x3f]; + imod3 = 0; + break; + } + c1 = c2; + } + switch (imod3) { + case 1: + os << basis_64[(c1 & 0x03) << 4] << "=="; + break; + case 2: + os << basis_64[(c1 & 0x0f) << 2] << '='; + break; + } + strValue = os.str(); + } + return strValue; + }; + static const char basis_64[]; +}; + +const char FIBase64ValueImpl::basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +std::shared_ptr<FIBase64Value> FIBase64Value::create(std::vector<uint8_t> &&value) { + return std::make_shared<FIBase64ValueImpl>(std::move(value)); +} + +struct FIShortValueImpl: public FIShortValue { + mutable std::string strValue; + mutable bool strValueValid; + inline FIShortValueImpl(std::vector<int16_t> &&value_): strValueValid(false) { value = std::move(value_); } + virtual const std::string &toString() const /*override*/ { + if (!strValueValid) { + strValueValid = true; + std::ostringstream os; + int n = 0; + std::for_each(value.begin(), value.end(), [&](int16_t s) { if (++n > 1) os << ' '; os << s; }); + strValue = os.str(); + } + return strValue; + } +}; + +std::shared_ptr<FIShortValue> FIShortValue::create(std::vector<int16_t> &&value) { + return std::make_shared<FIShortValueImpl>(std::move(value)); +} + +struct FIIntValueImpl: public FIIntValue { + mutable std::string strValue; + mutable bool strValueValid; + inline FIIntValueImpl(std::vector<int32_t> &&value_): strValueValid(false) { value = std::move(value_); } + virtual const std::string &toString() const /*override*/ { + if (!strValueValid) { + strValueValid = true; + std::ostringstream os; + int n = 0; + std::for_each(value.begin(), value.end(), [&](int32_t i) { if (++n > 1) os << ' '; os << i; }); + strValue = os.str(); + } + return strValue; + }; +}; + +std::shared_ptr<FIIntValue> FIIntValue::create(std::vector<int32_t> &&value) { + return std::make_shared<FIIntValueImpl>(std::move(value)); +} + +struct FILongValueImpl: public FILongValue { + mutable std::string strValue; + mutable bool strValueValid; + inline FILongValueImpl(std::vector<int64_t> &&value_): strValueValid(false) { value = std::move(value_); } + virtual const std::string &toString() const /*override*/ { + if (!strValueValid) { + strValueValid = true; + std::ostringstream os; + int n = 0; + std::for_each(value.begin(), value.end(), [&](int64_t l) { if (++n > 1) os << ' '; os << l; }); + strValue = os.str(); + } + return strValue; + }; +}; + +std::shared_ptr<FILongValue> FILongValue::create(std::vector<int64_t> &&value) { + return std::make_shared<FILongValueImpl>(std::move(value)); +} + +struct FIBoolValueImpl: public FIBoolValue { + mutable std::string strValue; + mutable bool strValueValid; + inline FIBoolValueImpl(std::vector<bool> &&value_): strValueValid(false) { value = std::move(value_); } + virtual const std::string &toString() const /*override*/ { + if (!strValueValid) { + strValueValid = true; + std::ostringstream os; + os << std::boolalpha; + int n = 0; + std::for_each(value.begin(), value.end(), [&](bool b) { if (++n > 1) os << ' '; os << b; }); + strValue = os.str(); + } + return strValue; + }; +}; + +std::shared_ptr<FIBoolValue> FIBoolValue::create(std::vector<bool> &&value) { + return std::make_shared<FIBoolValueImpl>(std::move(value)); +} + +struct FIFloatValueImpl: public FIFloatValue { + mutable std::string strValue; + mutable bool strValueValid; + inline FIFloatValueImpl(std::vector<float> &&value_): strValueValid(false) { value = std::move(value_); } + virtual const std::string &toString() const /*override*/ { + if (!strValueValid) { + strValueValid = true; + std::ostringstream os; + int n = 0; + std::for_each(value.begin(), value.end(), [&](float f) { if (++n > 1) os << ' '; os << f; }); + strValue = os.str(); + } + return strValue; + } +}; + +std::shared_ptr<FIFloatValue> FIFloatValue::create(std::vector<float> &&value) { + return std::make_shared<FIFloatValueImpl>(std::move(value)); +} + +struct FIDoubleValueImpl: public FIDoubleValue { + mutable std::string strValue; + mutable bool strValueValid; + inline FIDoubleValueImpl(std::vector<double> &&value_): strValueValid(false) { value = std::move(value_); } + virtual const std::string &toString() const /*override*/ { + if (!strValueValid) { + strValueValid = true; + std::ostringstream os; + int n = 0; + std::for_each(value.begin(), value.end(), [&](double d) { if (++n > 1) os << ' '; os << d; }); + strValue = os.str(); + } + return strValue; + } +}; + +std::shared_ptr<FIDoubleValue> FIDoubleValue::create(std::vector<double> &&value) { + return std::make_shared<FIDoubleValueImpl>(std::move(value)); +} + +struct FIUUIDValueImpl: public FIUUIDValue { + mutable std::string strValue; + mutable bool strValueValid; + inline FIUUIDValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); } + virtual const std::string &toString() const /*override*/ { + if (!strValueValid) { + strValueValid = true; + std::ostringstream os; + os << std::hex << std::uppercase << std::setfill('0'); + std::vector<uint8_t>::size_type valueSize = value.size(); + for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) { + switch (i & 15) { + case 0: + if (i > 0) { + os << ' '; + } + os << std::setw(2) << static_cast<int>(value[i]); + break; + case 4: + case 6: + case 8: + case 10: + os << '-'; + // intentionally fall through! + case 1: + case 2: + case 3: + case 5: + case 7: + case 9: + case 11: + case 12: + case 13: + case 14: + case 15: + os << std::setw(2) << static_cast<int>(value[i]); + break; + } + } + strValue = os.str(); + } + return strValue; + }; +}; + +std::shared_ptr<FIUUIDValue> FIUUIDValue::create(std::vector<uint8_t> &&value) { + return std::make_shared<FIUUIDValueImpl>(std::move(value)); +} + +struct FICDATAValueImpl: public FICDATAValue { + inline FICDATAValueImpl(std::string &&value_) { value = std::move(value_); } + virtual const std::string &toString() const /*override*/ { return value; } +}; + +std::shared_ptr<FICDATAValue> FICDATAValue::create(std::string &&value) { + return std::make_shared<FICDATAValueImpl>(std::move(value)); +} + +struct FIHexDecoder: public FIDecoder { + virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { + return FIHexValue::create(std::vector<uint8_t>(data, data + len)); + } +}; + +struct FIBase64Decoder: public FIDecoder { + virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { + return FIBase64Value::create(std::vector<uint8_t>(data, data + len)); + } +}; + +struct FIShortDecoder: public FIDecoder { + virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { + if (len & 1) { + throw DeadlyImportError(parseErrorMessage); + } + std::vector<int16_t> value; + size_t numShorts = len / 2; + value.reserve(numShorts); + for (size_t i = 0; i < numShorts; ++i) { + int16_t v = (data[0] << 8) | data[1]; + value.push_back(v); + data += 2; + } + return FIShortValue::create(std::move(value)); + } +}; + +struct FIIntDecoder: public FIDecoder { + virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { + if (len & 3) { + throw DeadlyImportError(parseErrorMessage); + } + std::vector<int32_t> value; + size_t numInts = len / 4; + value.reserve(numInts); + for (size_t i = 0; i < numInts; ++i) { + int32_t v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + value.push_back(v); + data += 4; + } + return FIIntValue::create(std::move(value)); + } +}; + +struct FILongDecoder: public FIDecoder { + virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { + if (len & 7) { + throw DeadlyImportError(parseErrorMessage); + } + std::vector<int64_t> value; + size_t numLongs = len / 8; + value.reserve(numLongs); + for (size_t i = 0; i < numLongs; ++i) { + int64_t b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7]; + int64_t v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; + value.push_back(v); + data += 8; + } + return FILongValue::create(std::move(value)); + } +}; + +struct FIBoolDecoder: public FIDecoder { + virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { + if (len < 1) { + throw DeadlyImportError(parseErrorMessage); + } + std::vector<bool> value; + uint8_t b = *data++; + size_t unusedBits = b >> 4; + size_t numBools = (len * 8) - 4 - unusedBits; + value.reserve(numBools); + uint8_t mask = 1 << 3; + for (size_t i = 0; i < numBools; ++i) { + if (!mask) { + mask = 1 << 7; + b = *data++; + } + value.push_back((b & mask) != 0); + } + return FIBoolValue::create(std::move(value)); + } +}; + +struct FIFloatDecoder: public FIDecoder { + virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { + if (len & 3) { + throw DeadlyImportError(parseErrorMessage); + } + std::vector<float> value; + size_t numFloats = len / 4; + value.reserve(numFloats); + for (size_t i = 0; i < numFloats; ++i) { + int v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + float f; + memcpy(&f, &v, 4); + value.push_back(f); + data += 4; + } + return FIFloatValue::create(std::move(value)); + } +}; + +struct FIDoubleDecoder: public FIDecoder { + virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { + if (len & 7) { + throw DeadlyImportError(parseErrorMessage); + } + std::vector<double> value; + size_t numDoubles = len / 8; + value.reserve(numDoubles); + for (size_t i = 0; i < numDoubles; ++i) { + long long b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7]; + long long v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; + double f; + memcpy(&f, &v, 8); + value.push_back(f); + data += 8; + } + return FIDoubleValue::create(std::move(value)); + } +}; + +struct FIUUIDDecoder: public FIDecoder { + virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { + if (len & 15) { + throw DeadlyImportError(parseErrorMessage); + } + return FIUUIDValue::create(std::vector<uint8_t>(data, data + len)); + } +}; + +struct FICDATADecoder: public FIDecoder { + virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ { + return FICDATAValue::create(parseUTF8String(data, len)); + } +}; + +class CFIReaderImpl: public FIReader { +public: + + CFIReaderImpl(std::unique_ptr<uint8_t[]> data_, size_t size): + data(std::move(data_)), dataP(data.get()), dataEnd(data.get() + size), currentNodeType(irr::io::EXN_NONE), + emptyElement(false), headerPending(true), terminatorPending(false) + {} + + virtual ~CFIReaderImpl() {} + + virtual bool read() /*override*/ { + if (headerPending) { + headerPending = false; + parseHeader(); + } + if (terminatorPending) { + terminatorPending = false; + if (elementStack.empty()) { + return false; + } + else { + nodeName = elementStack.top(); + elementStack.pop(); + currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END; + return true; + } + } + if (dataP >= dataEnd) { + return false; + } + uint8_t b = *dataP; + if (b < 0x80) { // Element (C.2.11.2, C.3.7.2) + // C.3 + parseElement(); + return true; + } + else if (b < 0xc0) { // Characters (C.3.7.5) + // C.7 + auto chars = parseNonIdentifyingStringOrIndex3(vocabulary.charactersTable); + nodeName = chars->toString(); + currentNodeType = irr::io::EXN_TEXT; + return true; + } + else if (b < 0xe0) { + if ((b & 0xfc) == 0xc4) { // DTD (C.2.11.5) + // C.9 + ++dataP; + if (b & 0x02) { + /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); + } + if (b & 0x01) { + /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); + } + elementStack.push(EmptyString); + currentNodeType = irr::io::EXN_UNKNOWN; + return true; + } + else if ((b & 0xfc) == 0xc8) { // Unexpanded entity reference (C.3.7.4) + // C.6 + ++dataP; + /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); + if (b & 0x02) { + /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); + } + if (b & 0x01) { + /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); + } + currentNodeType = irr::io::EXN_UNKNOWN; + return true; + } + } + else if (b < 0xf0) { + if (b == 0xe1) { // Processing instruction (C.2.11.3, C.3.7.3) + // C.5 + ++dataP; + /*const std::string &target =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + /*std::shared_ptr<const FIValue> data =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable); + currentNodeType = irr::io::EXN_UNKNOWN; + return true; + } + else if (b == 0xe2) { // Comment (C.2.11.4, C.3.7.6) + // C.8 + ++dataP; + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + std::shared_ptr<const FIValue> comment = parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable); + nodeName = comment->toString(); + currentNodeType = irr::io::EXN_COMMENT; + return true; + } + } + else { // Terminator (C.2.12, C.3.8) + ++dataP; + if (b == 0xff) { + terminatorPending = true; + } + if (elementStack.empty()) { + return false; + } + else { + nodeName = elementStack.top(); + elementStack.pop(); + currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END; + return true; + } + } + throw DeadlyImportError(parseErrorMessage); + } + + virtual irr::io::EXML_NODE getNodeType() const /*override*/ { + return currentNodeType; + } + + virtual int getAttributeCount() const /*override*/ { + return static_cast<int>(attributes.size()); + } + + virtual const char* getAttributeName(int idx) const /*override*/ { + if (idx < 0 || idx >= (int)attributes.size()) { + return nullptr; + } + return attributes[idx].name.c_str(); + } + + virtual const char* getAttributeValue(int idx) const /*override*/ { + if (idx < 0 || idx >= (int)attributes.size()) { + return nullptr; + } + return attributes[idx].value->toString().c_str(); + } + + virtual const char* getAttributeValue(const char* name) const /*override*/ { + const Attribute* attr = getAttributeByName(name); + if (!attr) { + return nullptr; + } + return attr->value->toString().c_str(); + } + + virtual const char* getAttributeValueSafe(const char* name) const /*override*/ { + const Attribute* attr = getAttributeByName(name); + if (!attr) { + return EmptyString.c_str(); + } + return attr->value->toString().c_str(); + } + + virtual int getAttributeValueAsInt(const char* name) const /*override*/ { + const Attribute* attr = getAttributeByName(name); + if (!attr) { + return 0; + } + std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attr->value); + if (intValue) { + return intValue->value.size() == 1 ? intValue->value.front() : 0; + } + return atoi(attr->value->toString().c_str()); + } + + virtual int getAttributeValueAsInt(int idx) const /*override*/ { + if (idx < 0 || idx >= (int)attributes.size()) { + return 0; + } + std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attributes[idx].value); + if (intValue) { + return intValue->value.size() == 1 ? intValue->value.front() : 0; + } + return atoi(attributes[idx].value->toString().c_str()); + } + + virtual float getAttributeValueAsFloat(const char* name) const /*override*/ { + const Attribute* attr = getAttributeByName(name); + if (!attr) { + return 0; + } + std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attr->value); + if (floatValue) { + return floatValue->value.size() == 1 ? floatValue->value.front() : 0; + } + + return fast_atof(attr->value->toString().c_str()); + } + + virtual float getAttributeValueAsFloat(int idx) const /*override*/ { + if (idx < 0 || idx >= (int)attributes.size()) { + return 0; + } + std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attributes[idx].value); + if (floatValue) { + return floatValue->value.size() == 1 ? floatValue->value.front() : 0; + } + return fast_atof(attributes[idx].value->toString().c_str()); + } + + virtual const char* getNodeName() const /*override*/ { + return nodeName.c_str(); + } + + virtual const char* getNodeData() const /*override*/ { + return nodeName.c_str(); + } + + virtual bool isEmptyElement() const /*override*/ { + return emptyElement; + } + + virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ { + return irr::io::ETF_UTF8; + } + + virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ { + return irr::io::ETF_UTF8; + } + + virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int idx) const /*override*/ { + if (idx < 0 || idx >= (int)attributes.size()) { + return nullptr; + } + return attributes[idx].value; + } + + virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* name) const /*override*/ { + const Attribute* attr = getAttributeByName(name); + if (!attr) { + return nullptr; + } + return attr->value; + } + + virtual void registerDecoder(const std::string &algorithmUri, std::unique_ptr<FIDecoder> decoder) /*override*/ { + decoderMap[algorithmUri] = std::move(decoder); + } + + virtual void registerVocabulary(const std::string &vocabularyUri, const FIVocabulary *vocabulary) /*override*/ { + vocabularyMap[vocabularyUri] = vocabulary; + } + +private: + + struct QName { + std::string prefix; + std::string uri; + std::string name; + inline QName() {} + inline QName(const FIQName &qname): prefix(qname.prefix ? qname.prefix : ""), uri(qname.uri ? qname.uri : ""), name(qname.name) {} + }; + + struct Attribute { + QName qname; + std::string name; + std::shared_ptr<const FIValue> value; + }; + + struct Vocabulary { + std::vector<std::string> restrictedAlphabetTable; + std::vector<std::string> encodingAlgorithmTable; + std::vector<std::string> prefixTable; + std::vector<std::string> namespaceNameTable; + std::vector<std::string> localNameTable; + std::vector<std::string> otherNCNameTable; + std::vector<std::string> otherURITable; + std::vector<std::shared_ptr<const FIValue>> attributeValueTable; + std::vector<std::shared_ptr<const FIValue>> charactersTable; + std::vector<std::shared_ptr<const FIValue>> otherStringTable; + std::vector<QName> elementNameTable; + std::vector<QName> attributeNameTable; + Vocabulary() { + prefixTable.push_back("xml"); + namespaceNameTable.push_back("http://www.w3.org/XML/1998/namespace"); + } + }; + + const Attribute* getAttributeByName(const char* name) const { + if (!name) { + return 0; + } + std::string n = name; + for (int i=0; i<(int)attributes.size(); ++i) { + if (attributes[i].name == n) { + return &attributes[i]; + } + } + return 0; + } + + size_t parseInt2() { // C.25 + uint8_t b = *dataP++; + if (!(b & 0x40)) { // x0...... (C.25.2) + return b & 0x3f; + } + else if ((b & 0x60) == 0x40) { // x10..... ........ (C.25.3) + if (dataEnd - dataP > 0) { + return (((b & 0x1f) << 8) | *dataP++) + 0x40; + } + } + else if ((b & 0x70) == 0x60) { // x110.... ........ ........ (C.25.4) + if (dataEnd - dataP > 1) { + size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x2040; + dataP += 2; + return result; + } + } + throw DeadlyImportError(parseErrorMessage); + } + + size_t parseInt3() { // C.27 + uint8_t b = *dataP++; + if (!(b & 0x20)) { // xx0..... (C.27.2) + return b & 0x1f; + } + else if ((b & 0x38) == 0x20) { // xx100... ........ (C.27.3) + if (dataEnd - dataP > 0) { + return (((b & 0x07) << 8) | *dataP++) + 0x20; + } + } + else if ((b & 0x38) == 0x28) { // xx101... ........ ........ (C.27.4) + if (dataEnd - dataP > 1) { + size_t result = (((b & 0x07) << 16) | (dataP[0] << 8) | dataP[1]) + 0x820; + dataP += 2; + return result; + } + } + else if ((b & 0x3f) == 0x30) { // xx110000 0000.... ........ ........ (C.27.5) + if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) { + size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x80820; + dataP += 3; + return result; + } + } + throw DeadlyImportError(parseErrorMessage); + } + + size_t parseInt4() { // C.28 + uint8_t b = *dataP++; + if (!(b & 0x10)) { // xxx0.... (C.28.2) + return b & 0x0f; + } + else if ((b & 0x1c) == 0x10) { // xxx100.. ........ (C.28.3) + if (dataEnd - dataP > 0) { + return (((b & 0x03) << 8) | *dataP++) + 0x10; + } + } + else if ((b & 0x1c) == 0x14) { // xxx101.. ........ ........ (C.28.4) + if (dataEnd - dataP > 1) { + size_t result = (((b & 0x03) << 16) | (dataP[0] << 8) | dataP[1]) + 0x410; + dataP += 2; + return result; + } + } + else if ((b & 0x1f) == 0x18) { // xxx11000 0000.... ........ ........ (C.28.5) + if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) { + size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x40410; + dataP += 3; + return result; + } + } + throw DeadlyImportError(parseErrorMessage); + } + + size_t parseSequenceLen() { // C.21 + if (dataEnd - dataP > 0) { + uint8_t b = *dataP++; + if (b < 0x80) { // 0....... (C.21.2) + return b; + } + else if ((b & 0xf0) == 0x80) { // 1000.... ........ ........ (C.21.3) + if (dataEnd - dataP > 1) { + size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x80; + dataP += 2; + return result; + } + } + } + throw DeadlyImportError(parseErrorMessage); + } + + std::string parseNonEmptyOctetString2() { // C.22 + // Parse the length of the string + uint8_t b = *dataP++ & 0x7f; + size_t len; + if (!(b & 0x40)) { // x0...... (C.22.3.1) + len = b + 1; + } + else if (b == 0x40) { // x1000000 ........ (C.22.3.2) + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + len = *dataP++ + 0x41; + } + else if (b == 0x60) { // x1100000 ........ ........ ........ ........ (C.22.3.3) + if (dataEnd - dataP < 4) { + throw DeadlyImportError(parseErrorMessage); + } + len = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x141; + dataP += 4; + } + else { + throw DeadlyImportError(parseErrorMessage); + } + + // Parse the string (C.22.4) + if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) { + throw DeadlyImportError(parseErrorMessage); + } + std::string s = parseUTF8String(dataP, len); + dataP += len; + + return s; + } + + size_t parseNonEmptyOctetString5Length() { // C.23 + // Parse the length of the string + size_t b = *dataP++ & 0x0f; + if (!(b & 0x08)) { // xxxx0... (C.23.3.1) + return b + 1; + } + else if (b == 0x08) { // xxxx1000 ........ (C.23.3.2) + if (dataEnd - dataP > 0) { + return *dataP++ + 0x09; + } + } + else if (b == 0x0c) { // xxxx1100 ........ ........ ........ ........ (C.23.3.3) + if (dataEnd - dataP > 3) { + size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x109; + dataP += 4; + return result; + } + } + throw DeadlyImportError(parseErrorMessage); + } + + size_t parseNonEmptyOctetString7Length() { // C.24 + // Parse the length of the string + size_t b = *dataP++ & 0x03; + if (!(b & 0x02)) { // xxxxxx0. (C.24.3.1) + return b + 1; + } + else if (b == 0x02) { // xxxxxx10 ........ (C.24.3.2) + if (dataEnd - dataP > 0) { + return *dataP++ + 0x3; + } + } + else if (b == 0x03) { // xxxxxx11 ........ ........ ........ ........ (C.24.3.3) + if (dataEnd - dataP > 3) { + size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x103; + dataP += 4; + return result; + } + } + throw DeadlyImportError(parseErrorMessage); + } + + std::shared_ptr<const FIValue> parseEncodedData(size_t index, size_t len) { + if (index < 32) { + FIDecoder *decoder = defaultDecoder[index]; + if (!decoder) { + throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index)); + } + return decoder->decode(dataP, len); + } + else { + if (index - 32 >= vocabulary.encodingAlgorithmTable.size()) { + throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index)); + } + std::string uri = vocabulary.encodingAlgorithmTable[index - 32]; + auto it = decoderMap.find(uri); + if (it == decoderMap.end()) { + throw DeadlyImportError("Unsupported encoding algorithm " + uri); + } + else { + return it->second->decode(dataP, len); + } + } + } + + std::shared_ptr<const FIValue> parseRestrictedAlphabet(size_t index, size_t len) { + std::string alphabet; + if (index < 16) { + switch (index) { + case 0: // numeric + alphabet = "0123456789-+.e "; + break; + case 1: // date and time + alphabet = "0123456789-:TZ "; + break; + default: + throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index)); + } + } + else { + if (index - 16 >= vocabulary.restrictedAlphabetTable.size()) { + throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index)); + } + alphabet = vocabulary.restrictedAlphabetTable[index - 16]; + } + std::vector<uint32_t> alphabetUTF32; + utf8::utf8to32(alphabet.begin(), alphabet.end(), back_inserter(alphabetUTF32)); + std::string::size_type alphabetLength = alphabetUTF32.size(); + if (alphabetLength < 2) { + throw DeadlyImportError("Invalid restricted alphabet length " + to_string(alphabetLength)); + } + std::string::size_type bitsPerCharacter = 1; + while ((1ull << bitsPerCharacter) <= alphabetLength) { + ++bitsPerCharacter; + } + size_t bitsAvail = 0; + uint8_t mask = (1 << bitsPerCharacter) - 1; + uint32_t bits = 0; + std::string s; + for (size_t i = 0; i < len; ++i) { + bits = (bits << 8) | dataP[i]; + bitsAvail += 8; + while (bitsAvail >= bitsPerCharacter) { + bitsAvail -= bitsPerCharacter; + size_t charIndex = (bits >> bitsAvail) & mask; + if (charIndex < alphabetLength) { + s.push_back(alphabetUTF32[charIndex]); + } + else if (charIndex != mask) { + throw DeadlyImportError(parseErrorMessage); + } + } + } + return FIStringValue::create(std::move(s)); + } + + std::shared_ptr<const FIValue> parseEncodedCharacterString3() { // C.19 + std::shared_ptr<const FIValue> result; + size_t len; + uint8_t b = *dataP; + if (b & 0x20) { + ++dataP; + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + size_t index = ((b & 0x0f) << 4) | ((*dataP & 0xf0) >> 4); // C.29 + len = parseNonEmptyOctetString5Length(); + if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) { + throw DeadlyImportError(parseErrorMessage); + } + if (b & 0x10) { + // encoding algorithm (C.19.3.4) + result = parseEncodedData(index, len); + } + else { + // Restricted alphabet (C.19.3.3) + result = parseRestrictedAlphabet(index, len); + } + } + else { + len = parseNonEmptyOctetString5Length(); + if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) { + throw DeadlyImportError(parseErrorMessage); + } + if (b & 0x10) { + // UTF-16 (C.19.3.2) + if (len & 1) { + throw DeadlyImportError(parseErrorMessage); + } + result = FIStringValue::create(parseUTF16String(dataP, len)); + } + else { + // UTF-8 (C.19.3.1) + result = FIStringValue::create(parseUTF8String(dataP, len)); + } + } + dataP += len; + return result; + } + + std::shared_ptr<const FIValue> parseEncodedCharacterString5() { // C.20 + std::shared_ptr<const FIValue> result; + size_t len; + uint8_t b = *dataP; + if (b & 0x08) { + ++dataP; + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + size_t index = ((b & 0x03) << 6) | ((*dataP & 0xfc) >> 2); /* C.29 */ + len = parseNonEmptyOctetString7Length(); + if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) { + throw DeadlyImportError(parseErrorMessage); + } + if (b & 0x04) { + // encoding algorithm (C.20.3.4) + result = parseEncodedData(index, len); + } + else { + // Restricted alphabet (C.20.3.3) + result = parseRestrictedAlphabet(index, len); + } + } + else { + len = parseNonEmptyOctetString7Length(); + if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) { + throw DeadlyImportError(parseErrorMessage); + } + if (b & 0x04) { + // UTF-16 (C.20.3.2) + if (len & 1) { + throw DeadlyImportError(parseErrorMessage); + } + result = FIStringValue::create(parseUTF16String(dataP, len)); + } + else { + // UTF-8 (C.20.3.1) + result = FIStringValue::create(parseUTF8String(dataP, len)); + } + } + dataP += len; + return result; + } + + const std::string &parseIdentifyingStringOrIndex(std::vector<std::string> &stringTable) { // C.13 + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + uint8_t b = *dataP; + if (b & 0x80) { + // We have an index (C.13.4) + size_t index = parseInt2(); + if (index >= stringTable.size()) { + throw DeadlyImportError(parseErrorMessage); + } + return stringTable[index]; + } + else { + // We have a string (C.13.3) + stringTable.push_back(parseNonEmptyOctetString2()); + return stringTable.back(); + } + } + + QName parseNameSurrogate() { // C.16 + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + uint8_t b = *dataP++; + if (b & 0xfc) { // Padding '000000' C.2.5.5 + throw DeadlyImportError(parseErrorMessage); + } + QName result; + size_t index; + if (b & 0x02) { // prefix (C.16.3) + if ((dataEnd - dataP < 1) || (*dataP & 0x80)) { + throw DeadlyImportError(parseErrorMessage); + } + index = parseInt2(); + if (index >= vocabulary.prefixTable.size()) { + throw DeadlyImportError(parseErrorMessage); + } + result.prefix = vocabulary.prefixTable[index]; + } + if (b & 0x01) { // namespace-name (C.16.4) + if ((dataEnd - dataP < 1) || (*dataP & 0x80)) { + throw DeadlyImportError(parseErrorMessage); + } + index = parseInt2(); + if (index >= vocabulary.namespaceNameTable.size()) { + throw DeadlyImportError(parseErrorMessage); + } + result.uri = vocabulary.namespaceNameTable[index]; + } + // local-name + if ((dataEnd - dataP < 1) || (*dataP & 0x80)) { + throw DeadlyImportError(parseErrorMessage); + } + index = parseInt2(); + if (index >= vocabulary.localNameTable.size()) { + throw DeadlyImportError(parseErrorMessage); + } + result.name = vocabulary.localNameTable[index]; + return result; + } + + const QName &parseQualifiedNameOrIndex2(std::vector<QName> &qNameTable) { // C.17 + uint8_t b = *dataP; + if ((b & 0x7c) == 0x78) { // x11110.. + // We have a literal (C.17.3) + ++dataP; + QName result; + // prefix (C.17.3.1) + result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string(); + // namespace-name (C.17.3.1) + result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string(); + // local-name + result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable); + qNameTable.push_back(result); + return qNameTable.back(); + } + else { + // We have an index (C.17.4) + size_t index = parseInt2(); + if (index >= qNameTable.size()) { + throw DeadlyImportError(parseErrorMessage); + } + return qNameTable[index]; + } + } + + const QName &parseQualifiedNameOrIndex3(std::vector<QName> &qNameTable) { // C.18 + uint8_t b = *dataP; + if ((b & 0x3c) == 0x3c) { // xx1111.. + // We have a literal (C.18.3) + ++dataP; + QName result; + // prefix (C.18.3.1) + result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string(); + // namespace-name (C.18.3.1) + result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string(); + // local-name + result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable); + qNameTable.push_back(result); + return qNameTable.back(); + } + else { + // We have an index (C.18.4) + size_t index = parseInt3(); + if (index >= qNameTable.size()) { + throw DeadlyImportError(parseErrorMessage); + } + return qNameTable[index]; + } + } + + std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex1(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.14 + uint8_t b = *dataP; + if (b == 0xff) { // C.26.2 + // empty string + ++dataP; + return EmptyFIString; + } + else if (b & 0x80) { // C.14.4 + // We have an index + size_t index = parseInt2(); + if (index >= valueTable.size()) { + throw DeadlyImportError(parseErrorMessage); + } + return valueTable[index]; + } + else { // C.14.3 + // We have a literal + std::shared_ptr<const FIValue> result = parseEncodedCharacterString3(); + if (b & 0x40) { // C.14.3.1 + valueTable.push_back(result); + } + return result; + } + } + + std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex3(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.15 + uint8_t b = *dataP; + if (b & 0x20) { // C.15.4 + // We have an index + size_t index = parseInt4(); + if (index >= valueTable.size()) { + throw DeadlyImportError(parseErrorMessage); + } + return valueTable[index]; + } + else { // C.15.3 + // We have a literal + std::shared_ptr<const FIValue> result = parseEncodedCharacterString5(); + if (b & 0x10) { // C.15.3.1 + valueTable.push_back(result); + } + return result; + } + } + + void parseElement() { + // C.3 + + attributes.clear(); + + uint8_t b = *dataP; + bool hasAttributes = (b & 0x40) != 0; // C.3.3 + if ((b & 0x3f) == 0x38) { // C.3.4.1 + // Parse namespaces + ++dataP; + for (;;) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + b = *dataP++; + if (b == 0xf0) { // C.3.4.3 + break; + } + if ((b & 0xfc) != 0xcc) { // C.3.4.2 + throw DeadlyImportError(parseErrorMessage); + } + // C.12 + Attribute attr; + attr.qname.prefix = "xmlns"; + attr.qname.name = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string(); + attr.qname.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string(); + attr.name = attr.qname.name.empty() ? "xmlns" : "xmlns:" + attr.qname.name; + attr.value = FIStringValue::create(std::string(attr.qname.uri)); + attributes.push_back(attr); + } + if ((dataEnd - dataP < 1) || (*dataP & 0xc0)) { + throw DeadlyImportError(parseErrorMessage); + } + } + + // Parse Element name (C.3.5) + const QName &elemName = parseQualifiedNameOrIndex3(vocabulary.elementNameTable); + nodeName = elemName.prefix.empty() ? elemName.name : elemName.prefix + ':' + elemName.name; + + if (hasAttributes) { + for (;;) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + b = *dataP; + if (b < 0x80) { // C.3.6.1 + // C.4 + Attribute attr; + attr.qname = parseQualifiedNameOrIndex2(vocabulary.attributeNameTable); + attr.name = attr.qname.prefix.empty() ? attr.qname.name : attr.qname.prefix + ':' + attr.qname.name; + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + attr.value = parseNonIdentifyingStringOrIndex1(vocabulary.attributeValueTable); + attributes.push_back(attr); + } + else { + if ((b & 0xf0) != 0xf0) { // C.3.6.2 + throw DeadlyImportError(parseErrorMessage); + } + emptyElement = b == 0xff; // C.3.6.2, C.3.8 + ++dataP; + break; + } + } + } + else { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + b = *dataP; + switch (b) { + case 0xff: + terminatorPending = true; + // Intentionally fall through + case 0xf0: + emptyElement = true; + ++dataP; + break; + default: + emptyElement = false; + } + } + if (!emptyElement) { + elementStack.push(nodeName); + } + + currentNodeType = irr::io::EXN_ELEMENT; + } + + void parseHeader() { + // Parse header (C.1.3) + size_t magicSize = parseMagic(dataP, dataEnd); + if (!magicSize) { + throw DeadlyImportError(parseErrorMessage); + } + dataP += magicSize; + // C.2.3 + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + uint8_t b = *dataP++; + if (b & 0x40) { + // Parse additional data (C.2.4) + size_t len = parseSequenceLen(); + for (size_t i = 0; i < len; ++i) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + /*std::string id =*/ parseNonEmptyOctetString2(); + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + /*std::string data =*/ parseNonEmptyOctetString2(); + } + } + if (b & 0x20) { + // Parse initial vocabulary (C.2.5) + if (dataEnd - dataP < 2) { + throw DeadlyImportError(parseErrorMessage); + } + uint16_t b1 = (dataP[0] << 8) | dataP[1]; + dataP += 2; + if (b1 & 0x1000) { + // External vocabulary (C.2.5.2) + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + std::string uri = parseNonEmptyOctetString2(); + auto it = vocabularyMap.find(uri); + if (it == vocabularyMap.end()) { + throw DeadlyImportError("Unknown vocabulary " + uri); + } + const FIVocabulary *externalVocabulary = it->second; + if (externalVocabulary->restrictedAlphabetTable) { + std::copy(externalVocabulary->restrictedAlphabetTable, externalVocabulary->restrictedAlphabetTable + externalVocabulary->restrictedAlphabetTableSize, std::back_inserter(vocabulary.restrictedAlphabetTable)); + } + if (externalVocabulary->encodingAlgorithmTable) { + std::copy(externalVocabulary->encodingAlgorithmTable, externalVocabulary->encodingAlgorithmTable + externalVocabulary->encodingAlgorithmTableSize, std::back_inserter(vocabulary.encodingAlgorithmTable)); + } + if (externalVocabulary->prefixTable) { + std::copy(externalVocabulary->prefixTable, externalVocabulary->prefixTable + externalVocabulary->prefixTableSize, std::back_inserter(vocabulary.prefixTable)); + } + if (externalVocabulary->namespaceNameTable) { + std::copy(externalVocabulary->namespaceNameTable, externalVocabulary->namespaceNameTable + externalVocabulary->namespaceNameTableSize, std::back_inserter(vocabulary.namespaceNameTable)); + } + if (externalVocabulary->localNameTable) { + std::copy(externalVocabulary->localNameTable, externalVocabulary->localNameTable + externalVocabulary->localNameTableSize, std::back_inserter(vocabulary.localNameTable)); + } + if (externalVocabulary->otherNCNameTable) { + std::copy(externalVocabulary->otherNCNameTable, externalVocabulary->otherNCNameTable + externalVocabulary->otherNCNameTableSize, std::back_inserter(vocabulary.otherNCNameTable)); + } + if (externalVocabulary->otherURITable) { + std::copy(externalVocabulary->otherURITable, externalVocabulary->otherURITable + externalVocabulary->otherURITableSize, std::back_inserter(vocabulary.otherURITable)); + } + if (externalVocabulary->attributeValueTable) { + std::copy(externalVocabulary->attributeValueTable, externalVocabulary->attributeValueTable + externalVocabulary->attributeValueTableSize, std::back_inserter(vocabulary.attributeValueTable)); + } + if (externalVocabulary->charactersTable) { + std::copy(externalVocabulary->charactersTable, externalVocabulary->charactersTable + externalVocabulary->charactersTableSize, std::back_inserter(vocabulary.charactersTable)); + } + if (externalVocabulary->otherStringTable) { + std::copy(externalVocabulary->otherStringTable, externalVocabulary->otherStringTable + externalVocabulary->otherStringTableSize, std::back_inserter(vocabulary.otherStringTable)); + } + if (externalVocabulary->elementNameTable) { + std::copy(externalVocabulary->elementNameTable, externalVocabulary->elementNameTable + externalVocabulary->elementNameTableSize, std::back_inserter(vocabulary.elementNameTable)); + } + if (externalVocabulary->attributeNameTable) { + std::copy(externalVocabulary->attributeNameTable, externalVocabulary->attributeNameTable + externalVocabulary->attributeNameTableSize, std::back_inserter(vocabulary.attributeNameTable)); + } + } + if (b1 & 0x0800) { + // Parse restricted alphabets (C.2.5.3) + for (size_t len = parseSequenceLen(); len > 0; --len) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + vocabulary.restrictedAlphabetTable.push_back(parseNonEmptyOctetString2()); + } + } + if (b1 & 0x0400) { + // Parse encoding algorithms (C.2.5.3) + for (size_t len = parseSequenceLen(); len > 0; --len) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + vocabulary.encodingAlgorithmTable.push_back(parseNonEmptyOctetString2()); + } + } + if (b1 & 0x0200) { + // Parse prefixes (C.2.5.3) + for (size_t len = parseSequenceLen(); len > 0; --len) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + vocabulary.prefixTable.push_back(parseNonEmptyOctetString2()); + } + } + if (b1 & 0x0100) { + // Parse namespace names (C.2.5.3) + for (size_t len = parseSequenceLen(); len > 0; --len) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + vocabulary.namespaceNameTable.push_back(parseNonEmptyOctetString2()); + } + } + if (b1 & 0x0080) { + // Parse local names (C.2.5.3) + for (size_t len = parseSequenceLen(); len > 0; --len) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + vocabulary.localNameTable.push_back(parseNonEmptyOctetString2()); + } + } + if (b1 & 0x0040) { + // Parse other ncnames (C.2.5.3) + for (size_t len = parseSequenceLen(); len > 0; --len) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + vocabulary.otherNCNameTable.push_back(parseNonEmptyOctetString2()); + } + } + if (b1 & 0x0020) { + // Parse other uris (C.2.5.3) + for (size_t len = parseSequenceLen(); len > 0; --len) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + vocabulary.otherURITable.push_back(parseNonEmptyOctetString2()); + } + } + if (b1 & 0x0010) { + // Parse attribute values (C.2.5.4) + for (size_t len = parseSequenceLen(); len > 0; --len) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + vocabulary.attributeValueTable.push_back(parseEncodedCharacterString3()); + } + } + if (b1 & 0x0008) { + // Parse content character chunks (C.2.5.4) + for (size_t len = parseSequenceLen(); len > 0; --len) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + vocabulary.charactersTable.push_back(parseEncodedCharacterString3()); + } + } + if (b1 & 0x0004) { + // Parse other strings (C.2.5.4) + for (size_t len = parseSequenceLen(); len > 0; --len) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + vocabulary.otherStringTable.push_back(parseEncodedCharacterString3()); + } + } + if (b1 & 0x0002) { + // Parse element name surrogates (C.2.5.5) + for (size_t len = parseSequenceLen(); len > 0; --len) { + vocabulary.elementNameTable.push_back(parseNameSurrogate()); + } + } + if (b1 & 0x0001) { + // Parse attribute name surrogates (C.2.5.5) + for (size_t len = parseSequenceLen(); len > 0; --len) { + vocabulary.attributeNameTable.push_back(parseNameSurrogate()); + } + } + } + if (b & 0x10) { + // Parse notations (C.2.6) + for (;;) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + uint8_t b1 = *dataP++; + if (b1 == 0xf0) { + break; + } + if ((b1 & 0xfc) != 0xc0) { + throw DeadlyImportError(parseErrorMessage); + } + /* C.11 */ + /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); + if (b1 & 0x02) { + /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); + } + if (b1 & 0x01) { + /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); + } + } + } + if (b & 0x08) { + // Parse unparsed entities (C.2.7) + for (;;) { + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + uint8_t b1 = *dataP++; + if (b1 == 0xf0) { + break; + } + if ((b1 & 0xfe) != 0xd0) { + throw DeadlyImportError(parseErrorMessage); + } + /* C.10 */ + /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); + /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); + if (b1 & 0x01) { + /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable); + } + /*const std::string ¬ationName =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable); + } + } + if (b & 0x04) { + // Parse character encoding scheme (C.2.8) + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + /*std::string characterEncodingScheme =*/ parseNonEmptyOctetString2(); + } + if (b & 0x02) { + // Parse standalone flag (C.2.9) + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + uint8_t b1 = *dataP++; + if (b1 & 0xfe) { + throw DeadlyImportError(parseErrorMessage); + } + //bool standalone = b1 & 0x01; + } + if (b & 0x01) { + // Parse version (C.2.10) + if (dataEnd - dataP < 1) { + throw DeadlyImportError(parseErrorMessage); + } + /*std::shared_ptr<const FIValue> version =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable); + } + } + + std::unique_ptr<uint8_t[]> data; + uint8_t *dataP, *dataEnd; + irr::io::EXML_NODE currentNodeType; + bool emptyElement; + bool headerPending; + bool terminatorPending; + Vocabulary vocabulary; + std::vector<Attribute> attributes; + std::stack<std::string> elementStack; + std::string nodeName; + std::map<std::string, std::unique_ptr<FIDecoder>> decoderMap; + std::map<std::string, const FIVocabulary*> vocabularyMap; + + static const std::string EmptyString; + static std::shared_ptr<const FIValue> EmptyFIString; + + static FIHexDecoder hexDecoder; + static FIBase64Decoder base64Decoder; + static FIShortDecoder shortDecoder; + static FIIntDecoder intDecoder; + static FILongDecoder longDecoder; + static FIBoolDecoder boolDecoder; + static FIFloatDecoder floatDecoder; + static FIDoubleDecoder doubleDecoder; + static FIUUIDDecoder uuidDecoder; + static FICDATADecoder cdataDecoder; + static FIDecoder *defaultDecoder[32]; +}; + +const std::string CFIReaderImpl::EmptyString; +std::shared_ptr<const FIValue> CFIReaderImpl::EmptyFIString = FIStringValue::create(std::string()); + +FIHexDecoder CFIReaderImpl::hexDecoder; +FIBase64Decoder CFIReaderImpl::base64Decoder; +FIShortDecoder CFIReaderImpl::shortDecoder; +FIIntDecoder CFIReaderImpl::intDecoder; +FILongDecoder CFIReaderImpl::longDecoder; +FIBoolDecoder CFIReaderImpl::boolDecoder; +FIFloatDecoder CFIReaderImpl::floatDecoder; +FIDoubleDecoder CFIReaderImpl::doubleDecoder; +FIUUIDDecoder CFIReaderImpl::uuidDecoder; +FICDATADecoder CFIReaderImpl::cdataDecoder; + +FIDecoder *CFIReaderImpl::defaultDecoder[32] = { + &hexDecoder, + &base64Decoder, + &shortDecoder, + &intDecoder, + &longDecoder, + &boolDecoder, + &floatDecoder, + &doubleDecoder, + &uuidDecoder, + &cdataDecoder +}; + +class CXMLReaderImpl : public FIReader +{ +public: + + //! Constructor + CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader_) + : reader(std::move(reader_)) + {} + + virtual ~CXMLReaderImpl() {} + + virtual bool read() /*override*/ { + return reader->read(); + } + + virtual irr::io::EXML_NODE getNodeType() const /*override*/ { + return reader->getNodeType(); + } + + virtual int getAttributeCount() const /*override*/ { + return reader->getAttributeCount(); + } + + virtual const char* getAttributeName(int idx) const /*override*/ { + return reader->getAttributeName(idx); + } + + virtual const char* getAttributeValue(int idx) const /*override*/ { + return reader->getAttributeValue(idx); + } + + virtual const char* getAttributeValue(const char* name) const /*override*/ { + return reader->getAttributeValue(name); + } + + virtual const char* getAttributeValueSafe(const char* name) const /*override*/ { + return reader->getAttributeValueSafe(name); + } + + virtual int getAttributeValueAsInt(const char* name) const /*override*/ { + return reader->getAttributeValueAsInt(name); + } + + virtual int getAttributeValueAsInt(int idx) const /*override*/ { + return reader->getAttributeValueAsInt(idx); + } + + virtual float getAttributeValueAsFloat(const char* name) const /*override*/ { + return reader->getAttributeValueAsFloat(name); + } + + virtual float getAttributeValueAsFloat(int idx) const /*override*/ { + return reader->getAttributeValueAsFloat(idx); + } + + virtual const char* getNodeName() const /*override*/ { + return reader->getNodeName(); + } + + virtual const char* getNodeData() const /*override*/ { + return reader->getNodeData(); + } + + virtual bool isEmptyElement() const /*override*/ { + return reader->isEmptyElement(); + } + + virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ { + return reader->getSourceFormat(); + } + + virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ { + return reader->getParserFormat(); + } + + virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int /*idx*/) const /*override*/ { + return nullptr; + } + + virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* /*name*/) const /*override*/ { + return nullptr; + } + + virtual void registerDecoder(const std::string & /*algorithmUri*/, std::unique_ptr<FIDecoder> /*decoder*/) /*override*/ {} + + + virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary * /*vocabulary*/) /*override*/ {} + +private: + + std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader; +}; + +static std::unique_ptr<uint8_t[]> readFile(IOStream *stream, size_t &size, bool &isFI) { + size = stream->FileSize(); + std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[size]); + if (stream->Read(data.get(), size, 1) != 1) { + size = 0; + data.reset(); + } + isFI = parseMagic(data.get(), data.get() + size) > 0; + return data; +} + +std::unique_ptr<FIReader> FIReader::create(IOStream *stream) +{ + size_t size; + bool isFI; + auto data = readFile(stream, size, isFI); + if (isFI) { + return std::unique_ptr<FIReader>(new CFIReaderImpl(std::move(data), size)); + } + else { + auto memios = std::unique_ptr<MemoryIOStream>(new MemoryIOStream(data.release(), size, true)); + auto callback = std::unique_ptr<CIrrXML_IOStreamReader>(new CIrrXML_IOStreamReader(memios.get())); + return std::unique_ptr<FIReader>(new CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>>(createIrrXMLReader(callback.get())))); + } +} + +}// namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/thirdparty/assimp/code/FileLogStream.h b/thirdparty/assimp/code/FileLogStream.h new file mode 100644 index 0000000000..740c503192 --- /dev/null +++ b/thirdparty/assimp/code/FileLogStream.h @@ -0,0 +1,107 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/** @file FileLofStream.h +*/ +#ifndef ASSIMP_FILELOGSTREAM_H_INC +#define ASSIMP_FILELOGSTREAM_H_INC + +#include <assimp/LogStream.hpp> +#include <assimp/IOStream.hpp> +#include <assimp/DefaultIOSystem.h> + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** @class FileLogStream + * @brief Logstream to write into a file. + */ +class FileLogStream : + public LogStream +{ +public: + FileLogStream( const char* file, IOSystem* io = NULL ); + ~FileLogStream(); + void write( const char* message ); + +private: + IOStream *m_pStream; +}; + +// ---------------------------------------------------------------------------------- +// Constructor +inline FileLogStream::FileLogStream( const char* file, IOSystem* io ) : + m_pStream(NULL) +{ + if ( !file || 0 == *file ) + return; + + // If no IOSystem is specified: take a default one + if (!io) + { + DefaultIOSystem FileSystem; + m_pStream = FileSystem.Open( file, "wt"); + } + else m_pStream = io->Open( file, "wt" ); +} + +// ---------------------------------------------------------------------------------- +// Destructor +inline FileLogStream::~FileLogStream() +{ + // The virtual d'tor should destroy the underlying file + delete m_pStream; +} + +// ---------------------------------------------------------------------------------- +// Write method +inline void FileLogStream::write( const char* message ) +{ + if (m_pStream != NULL) + { + m_pStream->Write(message, sizeof(char), ::strlen(message)); + m_pStream->Flush(); + } +} + +// ---------------------------------------------------------------------------------- +} // !Namespace Assimp + +#endif // !! ASSIMP_FILELOGSTREAM_H_INC diff --git a/thirdparty/assimp/code/FileSystemFilter.h b/thirdparty/assimp/code/FileSystemFilter.h new file mode 100644 index 0000000000..9923cdbdd3 --- /dev/null +++ b/thirdparty/assimp/code/FileSystemFilter.h @@ -0,0 +1,345 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2008, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FileSystemFilter.h + * Implements a filter system to filter calls to Exists() and Open() + * in order to improve the success rate of file opening ... + */ +#pragma once +#ifndef AI_FILESYSTEMFILTER_H_INC +#define AI_FILESYSTEMFILTER_H_INC + +#include <assimp/IOSystem.hpp> +#include <assimp/DefaultLogger.hpp> +#include <assimp/fast_atof.h> +#include <assimp/ParsingUtils.h> + +namespace Assimp { + +inline bool IsHex(char s) { + return (s>='0' && s<='9') || (s>='a' && s<='f') || (s>='A' && s<='F'); +} + +// --------------------------------------------------------------------------- +/** File system filter + */ +class FileSystemFilter : public IOSystem +{ +public: + /** Constructor. */ + FileSystemFilter(const std::string& file, IOSystem* old) + : mWrapped (old) + , mSrc_file(file) + , mSep(mWrapped->getOsSeparator()) { + ai_assert(nullptr != mWrapped); + + // Determine base directory + mBase = mSrc_file; + std::string::size_type ss2; + if (std::string::npos != (ss2 = mBase.find_last_of("\\/"))) { + mBase.erase(ss2,mBase.length()-ss2); + } else { + mBase = ""; + } + + // make sure the directory is terminated properly + char s; + + if ( mBase.empty() ) { + mBase = "."; + mBase += getOsSeparator(); + } else if ((s = *(mBase.end()-1)) != '\\' && s != '/') { + mBase += getOsSeparator(); + } + + DefaultLogger::get()->info("Import root directory is \'" + mBase + "\'"); + } + + /** Destructor. */ + ~FileSystemFilter() { + // empty + } + + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists( const char* pFile) const { + ai_assert( nullptr != mWrapped ); + + std::string tmp = pFile; + + // Currently this IOSystem is also used to open THE ONE FILE. + if (tmp != mSrc_file) { + BuildPath(tmp); + Cleanup(tmp); + } + + return mWrapped->Exists(tmp); + } + + // ------------------------------------------------------------------- + /** Returns the directory separator. */ + char getOsSeparator() const { + return mSep; + } + + // ------------------------------------------------------------------- + /** Open a new file with a given path. */ + IOStream* Open( const char* pFile, const char* pMode = "rb") { + ai_assert( nullptr != mWrapped ); + if ( nullptr == pFile || nullptr == pMode ) { + return nullptr; + } + + ai_assert( nullptr != pFile ); + ai_assert( nullptr != pMode ); + + // First try the unchanged path + IOStream* s = mWrapped->Open(pFile,pMode); + + if (nullptr == s) { + std::string tmp = pFile; + + // Try to convert between absolute and relative paths + BuildPath(tmp); + s = mWrapped->Open(tmp,pMode); + + if (nullptr == s) { + // Finally, look for typical issues with paths + // and try to correct them. This is our last + // resort. + tmp = pFile; + Cleanup(tmp); + BuildPath(tmp); + s = mWrapped->Open(tmp,pMode); + } + } + + return s; + } + + // ------------------------------------------------------------------- + /** Closes the given file and releases all resources associated with it. */ + void Close( IOStream* pFile) { + ai_assert( nullptr != mWrapped ); + return mWrapped->Close(pFile); + } + + // ------------------------------------------------------------------- + /** Compare two paths */ + bool ComparePaths (const char* one, const char* second) const { + ai_assert( nullptr != mWrapped ); + return mWrapped->ComparePaths (one,second); + } + + // ------------------------------------------------------------------- + /** Pushes a new directory onto the directory stack. */ + bool PushDirectory(const std::string &path ) { + ai_assert( nullptr != mWrapped ); + return mWrapped->PushDirectory(path); + } + + // ------------------------------------------------------------------- + /** Returns the top directory from the stack. */ + const std::string &CurrentDirectory() const { + ai_assert( nullptr != mWrapped ); + return mWrapped->CurrentDirectory(); + } + + // ------------------------------------------------------------------- + /** Returns the number of directories stored on the stack. */ + size_t StackSize() const { + ai_assert( nullptr != mWrapped ); + return mWrapped->StackSize(); + } + + // ------------------------------------------------------------------- + /** Pops the top directory from the stack. */ + bool PopDirectory() { + ai_assert( nullptr != mWrapped ); + return mWrapped->PopDirectory(); + } + + // ------------------------------------------------------------------- + /** Creates an new directory at the given path. */ + bool CreateDirectory(const std::string &path) { + ai_assert( nullptr != mWrapped ); + return mWrapped->CreateDirectory(path); + } + + // ------------------------------------------------------------------- + /** Will change the current directory to the given path. */ + bool ChangeDirectory(const std::string &path) { + ai_assert( nullptr != mWrapped ); + return mWrapped->ChangeDirectory(path); + } + + // ------------------------------------------------------------------- + /** Delete file. */ + bool DeleteFile(const std::string &file) { + ai_assert( nullptr != mWrapped ); + return mWrapped->DeleteFile(file); + } + +private: + // ------------------------------------------------------------------- + /** Build a valid path from a given relative or absolute path. + */ + void BuildPath (std::string& in) const { + ai_assert( nullptr != mWrapped ); + // if we can already access the file, great. + if (in.length() < 3 || mWrapped->Exists(in)) { + return; + } + + // Determine whether this is a relative path (Windows-specific - most assets are packaged on Windows). + if (in[1] != ':') { + + // append base path and try + const std::string tmp = mBase + in; + if (mWrapped->Exists(tmp)) { + in = tmp; + return; + } + } + + // Chop of the file name and look in the model directory, if + // this fails try all sub paths of the given path, i.e. + // if the given path is foo/bar/something.lwo, try + // <base>/something.lwo + // <base>/bar/something.lwo + // <base>/foo/bar/something.lwo + std::string::size_type pos = in.rfind('/'); + if (std::string::npos == pos) { + pos = in.rfind('\\'); + } + + if (std::string::npos != pos) { + std::string tmp; + std::string::size_type last_dirsep = std::string::npos; + + while(true) { + tmp = mBase; + tmp += mSep; + + std::string::size_type dirsep = in.rfind('/', last_dirsep); + if (std::string::npos == dirsep) { + dirsep = in.rfind('\\', last_dirsep); + } + + if (std::string::npos == dirsep || dirsep == 0) { + // we did try this already. + break; + } + + last_dirsep = dirsep-1; + + tmp += in.substr(dirsep+1, in.length()-pos); + if (mWrapped->Exists(tmp)) { + in = tmp; + return; + } + } + } + + // hopefully the underlying file system has another few tricks to access this file ... + } + + // ------------------------------------------------------------------- + /** Cleanup the given path + */ + void Cleanup (std::string& in) const { + if(in.empty()) { + return; + } + + // Remove a very common issue when we're parsing file names: spaces at the + // beginning of the path. + char last = 0; + std::string::iterator it = in.begin(); + while (IsSpaceOrNewLine( *it ))++it; + if (it != in.begin()) { + in.erase(in.begin(),it+1); + } + + const char separator = getOsSeparator(); + for (it = in.begin(); it != in.end(); ++it) { + // Exclude :// and \\, which remain untouched. + // https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632 + if ( !strncmp(&*it, "://", 3 )) { + it += 3; + continue; + } + if (it == in.begin() && !strncmp(&*it, "\\\\", 2)) { + it += 2; + continue; + } + + // Cleanup path delimiters + if (*it == '/' || (*it) == '\\') { + *it = separator; + + // And we're removing double delimiters, frequent issue with + // incorrectly composited paths ... + if (last == *it) { + it = in.erase(it); + --it; + } + } else if (*it == '%' && in.end() - it > 2) { + // Hex sequence in URIs + if( IsHex((&*it)[0]) && IsHex((&*it)[1]) ) { + *it = HexOctetToDecimal(&*it); + it = in.erase(it+1,it+2); + --it; + } + } + + last = *it; + } + } + +private: + IOSystem *mWrapped; + std::string mSrc_file, mBase; + char mSep; +}; + +} //!ns Assimp + +#endif //AI_DEFAULTIOSYSTEM_H_INC diff --git a/thirdparty/assimp/code/FindDegenerates.cpp b/thirdparty/assimp/code/FindDegenerates.cpp new file mode 100644 index 0000000000..365f5d7447 --- /dev/null +++ b/thirdparty/assimp/code/FindDegenerates.cpp @@ -0,0 +1,300 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file FindDegenerates.cpp + * @brief Implementation of the FindDegenerates post-process step. +*/ + + + +// internal headers +#include "ProcessHelper.h" +#include "FindDegenerates.h" +#include <assimp/Exceptional.h> + +using namespace Assimp; + +//remove mesh at position 'index' from the scene +static void removeMesh(aiScene* pScene, unsigned const index); +//correct node indices to meshes and remove references to deleted mesh +static void updateSceneGraph(aiNode* pNode, unsigned const index); + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FindDegeneratesProcess::FindDegeneratesProcess() +: mConfigRemoveDegenerates( false ) +, mConfigCheckAreaOfTriangle( false ){ + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FindDegeneratesProcess::~FindDegeneratesProcess() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const { + return 0 != (pFlags & aiProcess_FindDegenerates); +} + +// ------------------------------------------------------------------------------------------------ +// Setup import configuration +void FindDegeneratesProcess::SetupProperties(const Importer* pImp) { + // Get the current value of AI_CONFIG_PP_FD_REMOVE + mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0)); + mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) ); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FindDegeneratesProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin"); + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + { + //Do not process point cloud, ExecuteOnMesh works only with faces data + if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) { + removeMesh(pScene, i); + --i; //the current i is removed, do not skip the next one + } + } + ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished"); +} + +static void removeMesh(aiScene* pScene, unsigned const index) { + //we start at index and copy the pointers one position forward + //save the mesh pointer to delete it later + auto delete_me = pScene->mMeshes[index]; + for (unsigned i = index; i < pScene->mNumMeshes - 1; ++i) { + pScene->mMeshes[i] = pScene->mMeshes[i+1]; + } + pScene->mMeshes[pScene->mNumMeshes - 1] = nullptr; + --(pScene->mNumMeshes); + delete delete_me; + + //removing a mesh also requires updating all references to it in the scene graph + updateSceneGraph(pScene->mRootNode, index); +} + +static void updateSceneGraph(aiNode* pNode, unsigned const index) { + for (unsigned i = 0; i < pNode->mNumMeshes; ++i) { + if (pNode->mMeshes[i] > index) { + --(pNode->mMeshes[i]); + continue; + } + if (pNode->mMeshes[i] == index) { + for (unsigned j = i; j < pNode->mNumMeshes -1; ++j) { + pNode->mMeshes[j] = pNode->mMeshes[j+1]; + } + --(pNode->mNumMeshes); + --i; + continue; + } + } + //recurse to all children + for (unsigned i = 0; i < pNode->mNumChildren; ++i) { + updateSceneGraph(pNode->mChildren[i], index); + } +} + +static ai_real heron( ai_real a, ai_real b, ai_real c ) { + ai_real s = (a + b + c) / 2; + ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 ); + return area; +} + +static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) { + const ai_real lx = ( vB.x - vA.x ); + const ai_real ly = ( vB.y - vA.y ); + const ai_real lz = ( vB.z - vA.z ); + ai_real a = lx*lx + ly*ly + lz*lz; + ai_real d = pow( a, (ai_real)0.5 ); + + return d; +} + +static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) { + ai_real area = 0; + + aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] ); + aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] ); + aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] ); + + ai_real a( distance3D( vA, vB ) ); + ai_real b( distance3D( vB, vC ) ); + ai_real c( distance3D( vC, vA ) ); + area = heron( a, b, c ); + + return area; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported mesh +bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { + mesh->mPrimitiveTypes = 0; + + std::vector<bool> remove_me; + if (mConfigRemoveDegenerates) { + remove_me.resize( mesh->mNumFaces, false ); + } + + unsigned int deg = 0, limit; + for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) { + aiFace& face = mesh->mFaces[a]; + bool first = true; + + // check whether the face contains degenerated entries + for (unsigned int i = 0; i < face.mNumIndices; ++i) { + // Polygons with more than 4 points are allowed to have double points, that is + // simulating polygons with holes just with concave polygons. However, + // double points may not come directly after another. + limit = face.mNumIndices; + if (face.mNumIndices > 4) { + limit = std::min( limit, i+2 ); + } + + for (unsigned int t = i+1; t < limit; ++t) { + if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) { + // we have found a matching vertex position + // remove the corresponding index from the array + --face.mNumIndices; + --limit; + for (unsigned int m = t; m < face.mNumIndices; ++m) { + face.mIndices[ m ] = face.mIndices[ m+1 ]; + } + --t; + + // NOTE: we set the removed vertex index to an unique value + // to make sure the developer gets notified when his + // application attempts to access this data. + face.mIndices[ face.mNumIndices ] = 0xdeadbeef; + + if(first) { + ++deg; + first = false; + } + + if ( mConfigRemoveDegenerates ) { + remove_me[ a ] = true; + goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby! + } + } + } + + if ( mConfigCheckAreaOfTriangle ) { + if ( face.mNumIndices == 3 ) { + ai_real area = calculateAreaOfTriangle( face, mesh ); + if ( area < 1e-6 ) { + if ( mConfigRemoveDegenerates ) { + remove_me[ a ] = true; + goto evil_jump_outside; + } + + // todo: check for index which is corrupt. + } + } + } + } + + // We need to update the primitive flags array of the mesh. + switch (face.mNumIndices) + { + case 1u: + mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 2u: + mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 3u: + mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + }; +evil_jump_outside: + continue; + } + + // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import + if (mConfigRemoveDegenerates && deg) { + unsigned int n = 0; + for (unsigned int a = 0; a < mesh->mNumFaces; ++a) + { + aiFace& face_src = mesh->mFaces[a]; + if (!remove_me[a]) { + aiFace& face_dest = mesh->mFaces[n++]; + + // Do a manual copy, keep the index array + face_dest.mNumIndices = face_src.mNumIndices; + face_dest.mIndices = face_src.mIndices; + + if (&face_src != &face_dest) { + // clear source + face_src.mNumIndices = 0; + face_src.mIndices = nullptr; + } + } + else { + // Otherwise delete it if we don't need this face + delete[] face_src.mIndices; + face_src.mIndices = nullptr; + face_src.mNumIndices = 0; + } + } + // Just leave the rest of the array unreferenced, we don't care for now + mesh->mNumFaces = n; + if (!mesh->mNumFaces) { + //The whole mesh consists of degenerated faces + //signal upward, that this mesh should be deleted. + ASSIMP_LOG_DEBUG("FindDegeneratesProcess removed a mesh full of degenerated primitives"); + return true; + } + } + + if (deg && !DefaultLogger::isNullLogger()) { + ASSIMP_LOG_WARN_F( "Found ", deg, " degenerated primitives"); + } + return false; +} diff --git a/thirdparty/assimp/code/FindDegenerates.h b/thirdparty/assimp/code/FindDegenerates.h new file mode 100644 index 0000000000..880f5f16a2 --- /dev/null +++ b/thirdparty/assimp/code/FindDegenerates.h @@ -0,0 +1,129 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to search all meshes for + degenerated faces */ +#ifndef AI_FINDDEGENERATESPROCESS_H_INC +#define AI_FINDDEGENERATESPROCESS_H_INC + +#include "BaseProcess.h" +#include <assimp/mesh.h> + +class FindDegeneratesProcessTest; +namespace Assimp { + + +// --------------------------------------------------------------------------- +/** FindDegeneratesProcess: Searches a mesh for degenerated triangles. +*/ +class ASSIMP_API FindDegeneratesProcess : public BaseProcess { +public: + FindDegeneratesProcess(); + ~FindDegeneratesProcess(); + + // ------------------------------------------------------------------- + // Check whether step is active + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + // Execute step on a given scene + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + // Setup import settings + void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + // Execute step on a given mesh + ///@returns true if the current mesh should be deleted, false otherwise + bool ExecuteOnMesh( aiMesh* mesh); + + // ------------------------------------------------------------------- + /// @brief Enable the instant removal of degenerated primitives + /// @param enabled true for enabled. + void EnableInstantRemoval(bool enabled); + + // ------------------------------------------------------------------- + /// @brief Check whether instant removal is currently enabled + /// @return The instant removal state. + bool IsInstantRemoval() const; + + // ------------------------------------------------------------------- + /// @brief Enable the area check for triangles. + /// @param enabled true for enabled. + void EnableAreaCheck( bool enabled ); + + // ------------------------------------------------------------------- + /// @brief Check whether the area check is enabled. + /// @return The area check state. + bool isAreaCheckEnabled() const; + +private: + //! Configuration option: remove degenerates faces immediately + bool mConfigRemoveDegenerates; + //! Configuration option: check for area + bool mConfigCheckAreaOfTriangle; +}; + +inline +void FindDegeneratesProcess::EnableInstantRemoval(bool enabled) { + mConfigRemoveDegenerates = enabled; +} + +inline +bool FindDegeneratesProcess::IsInstantRemoval() const { + return mConfigRemoveDegenerates; +} + +inline +void FindDegeneratesProcess::EnableAreaCheck( bool enabled ) { + mConfigCheckAreaOfTriangle = enabled; +} + +inline +bool FindDegeneratesProcess::isAreaCheckEnabled() const { + return mConfigCheckAreaOfTriangle; +} + +} // Namespace Assimp + +#endif // !! AI_FINDDEGENERATESPROCESS_H_INC diff --git a/thirdparty/assimp/code/FindInstancesProcess.cpp b/thirdparty/assimp/code/FindInstancesProcess.cpp new file mode 100644 index 0000000000..be1138116e --- /dev/null +++ b/thirdparty/assimp/code/FindInstancesProcess.cpp @@ -0,0 +1,277 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file FindInstancesProcess.cpp + * @brief Implementation of the aiProcess_FindInstances postprocessing step +*/ + + +#include "FindInstancesProcess.h" +#include <memory> +#include <stdio.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FindInstancesProcess::FindInstancesProcess() +: configSpeedFlag (false) +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FindInstancesProcess::~FindInstancesProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FindInstancesProcess::IsActive( unsigned int pFlags) const +{ + // FindInstances makes absolutely no sense together with PreTransformVertices + // fixme: spawn error message somewhere else? + return 0 != (pFlags & aiProcess_FindInstances) && 0 == (pFlags & aiProcess_PreTransformVertices); +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties for the step +void FindInstancesProcess::SetupProperties(const Importer* pImp) +{ + // AI_CONFIG_FAVOUR_SPEED + configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0)); +} + +// ------------------------------------------------------------------------------------------------ +// Compare the bones of two meshes +bool CompareBones(const aiMesh* orig, const aiMesh* inst) +{ + for (unsigned int i = 0; i < orig->mNumBones;++i) { + aiBone* aha = orig->mBones[i]; + aiBone* oha = inst->mBones[i]; + + if (aha->mNumWeights != oha->mNumWeights || + aha->mOffsetMatrix != oha->mOffsetMatrix) { + return false; + } + + // compare weight per weight --- + for (unsigned int n = 0; n < aha->mNumWeights;++n) { + if (aha->mWeights[n].mVertexId != oha->mWeights[n].mVertexId || + (aha->mWeights[n].mWeight - oha->mWeights[n].mWeight) < 10e-3f) { + return false; + } + } + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Update mesh indices in the node graph +void UpdateMeshIndices(aiNode* node, unsigned int* lookup) +{ + for (unsigned int n = 0; n < node->mNumMeshes;++n) + node->mMeshes[n] = lookup[node->mMeshes[n]]; + + for (unsigned int n = 0; n < node->mNumChildren;++n) + UpdateMeshIndices(node->mChildren[n],lookup); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FindInstancesProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("FindInstancesProcess begin"); + if (pScene->mNumMeshes) { + + // use a pseudo hash for all meshes in the scene to quickly find + // the ones which are possibly equal. This step is executed early + // in the pipeline, so we could, depending on the file format, + // have several thousand small meshes. That's too much for a brute + // everyone-against-everyone check involving up to 10 comparisons + // each. + std::unique_ptr<uint64_t[]> hashes (new uint64_t[pScene->mNumMeshes]); + std::unique_ptr<unsigned int[]> remapping (new unsigned int[pScene->mNumMeshes]); + + unsigned int numMeshesOut = 0; + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + + aiMesh* inst = pScene->mMeshes[i]; + hashes[i] = GetMeshHash(inst); + + for (int a = i-1; a >= 0; --a) { + if (hashes[i] == hashes[a]) + { + aiMesh* orig = pScene->mMeshes[a]; + if (!orig) + continue; + + // check for hash collision .. we needn't check + // the vertex format, it *must* match due to the + // (brilliant) construction of the hash + if (orig->mNumBones != inst->mNumBones || + orig->mNumFaces != inst->mNumFaces || + orig->mNumVertices != inst->mNumVertices || + orig->mMaterialIndex != inst->mMaterialIndex || + orig->mPrimitiveTypes != inst->mPrimitiveTypes) + continue; + + // up to now the meshes are equal. find an appropriate + // epsilon to compare position differences against + float epsilon = ComputePositionEpsilon(inst); + epsilon *= epsilon; + + // now compare vertex positions, normals, + // tangents and bitangents using this epsilon. + if (orig->HasPositions()) { + if(!CompareArrays(orig->mVertices,inst->mVertices,orig->mNumVertices,epsilon)) + continue; + } + if (orig->HasNormals()) { + if(!CompareArrays(orig->mNormals,inst->mNormals,orig->mNumVertices,epsilon)) + continue; + } + if (orig->HasTangentsAndBitangents()) { + if (!CompareArrays(orig->mTangents,inst->mTangents,orig->mNumVertices,epsilon) || + !CompareArrays(orig->mBitangents,inst->mBitangents,orig->mNumVertices,epsilon)) + continue; + } + + // use a constant epsilon for colors and UV coordinates + static const float uvEpsilon = 10e-4f; + { + unsigned int j, end = orig->GetNumUVChannels(); + for(j = 0; j < end; ++j) { + if (!orig->mTextureCoords[j]) { + continue; + } + if(!CompareArrays(orig->mTextureCoords[j],inst->mTextureCoords[j],orig->mNumVertices,uvEpsilon)) { + break; + } + } + if (j != end) { + continue; + } + } + { + unsigned int j, end = orig->GetNumColorChannels(); + for(j = 0; j < end; ++j) { + if (!orig->mColors[j]) { + continue; + } + if(!CompareArrays(orig->mColors[j],inst->mColors[j],orig->mNumVertices,uvEpsilon)) { + break; + } + } + if (j != end) { + continue; + } + } + + // These two checks are actually quite expensive and almost *never* required. + // Almost. That's why they're still here. But there's no reason to do them + // in speed-targeted imports. + if (!configSpeedFlag) { + + // It seems to be strange, but we really need to check whether the + // bones are identical too. Although it's extremely unprobable + // that they're not if control reaches here, we need to deal + // with unprobable cases, too. It could still be that there are + // equal shapes which are deformed differently. + if (!CompareBones(orig,inst)) + continue; + + // For completeness ... compare even the index buffers for equality + // face order & winding order doesn't care. Input data is in verbose format. + std::unique_ptr<unsigned int[]> ftbl_orig(new unsigned int[orig->mNumVertices]); + std::unique_ptr<unsigned int[]> ftbl_inst(new unsigned int[orig->mNumVertices]); + + for (unsigned int tt = 0; tt < orig->mNumFaces;++tt) { + aiFace& f = orig->mFaces[tt]; + for (unsigned int nn = 0; nn < f.mNumIndices;++nn) + ftbl_orig[f.mIndices[nn]] = tt; + + aiFace& f2 = inst->mFaces[tt]; + for (unsigned int nn = 0; nn < f2.mNumIndices;++nn) + ftbl_inst[f2.mIndices[nn]] = tt; + } + if (0 != ::memcmp(ftbl_inst.get(),ftbl_orig.get(),orig->mNumVertices*sizeof(unsigned int))) + continue; + } + + // We're still here. Or in other words: 'inst' is an instance of 'orig'. + // Place a marker in our list that we can easily update mesh indices. + remapping[i] = remapping[a]; + + // Delete the instanced mesh, we don't need it anymore + delete inst; + pScene->mMeshes[i] = NULL; + break; + } + } + + // If we didn't find a match for the current mesh: keep it + if (pScene->mMeshes[i]) { + remapping[i] = numMeshesOut++; + } + } + ai_assert(0 != numMeshesOut); + if (numMeshesOut != pScene->mNumMeshes) { + + // Collapse the meshes array by removing all NULL entries + for (unsigned int real = 0, i = 0; real < numMeshesOut; ++i) { + if (pScene->mMeshes[i]) + pScene->mMeshes[real++] = pScene->mMeshes[i]; + } + + // And update the node graph with our nice lookup table + UpdateMeshIndices(pScene->mRootNode,remapping.get()); + + // write to log + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_INFO_F( "FindInstancesProcess finished. Found ", (pScene->mNumMeshes - numMeshesOut), " instances" ); + } + pScene->mNumMeshes = numMeshesOut; + } else { + ASSIMP_LOG_DEBUG("FindInstancesProcess finished. No instanced meshes found"); + } + } +} diff --git a/thirdparty/assimp/code/FindInstancesProcess.h b/thirdparty/assimp/code/FindInstancesProcess.h new file mode 100644 index 0000000000..ab4a371c71 --- /dev/null +++ b/thirdparty/assimp/code/FindInstancesProcess.h @@ -0,0 +1,137 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FindInstancesProcess.h + * @brief Declares the aiProcess_FindInstances post-process step + */ +#ifndef AI_FINDINSTANCES_H_INC +#define AI_FINDINSTANCES_H_INC + +#include "BaseProcess.h" +#include "ProcessHelper.h" + +class FindInstancesProcessTest; +namespace Assimp { + +// ------------------------------------------------------------------------------- +/** @brief Get a pseudo(!)-hash representing a mesh. + * + * The hash is built from number of vertices, faces, primitive types, + * .... but *not* from the real mesh data. The funcction is not a perfect hash. + * @param in Input mesh + * @return Hash. + */ +inline +uint64_t GetMeshHash(aiMesh* in) { + ai_assert(nullptr != in); + + // ... get an unique value representing the vertex format of the mesh + const unsigned int fhash = GetMeshVFormatUnique(in); + + // and bake it with number of vertices/faces/bones/matidx/ptypes + return ((uint64_t)fhash << 32u) | (( + (in->mNumBones << 16u) ^ (in->mNumVertices) ^ + (in->mNumFaces<<4u) ^ (in->mMaterialIndex<<15) ^ + (in->mPrimitiveTypes<<28)) & 0xffffffff ); +} + +// ------------------------------------------------------------------------------- +/** @brief Perform a component-wise comparison of two arrays + * + * @param first First array + * @param second Second array + * @param size Size of both arrays + * @param e Epsilon + * @return true if the arrays are identical + */ +inline +bool CompareArrays(const aiVector3D* first, const aiVector3D* second, + unsigned int size, float e) { + for (const aiVector3D* end = first+size; first != end; ++first,++second) { + if ( (*first - *second).SquareLength() >= e) + return false; + } + return true; +} + +// and the same for colors ... +inline bool CompareArrays(const aiColor4D* first, const aiColor4D* second, + unsigned int size, float e) +{ + for (const aiColor4D* end = first+size; first != end; ++first,++second) { + if ( GetColorDifference(*first,*second) >= e) + return false; + } + return true; +} + +// --------------------------------------------------------------------------- +/** @brief A post-processing steps to search for instanced meshes +*/ +class FindInstancesProcess : public BaseProcess +{ +public: + + FindInstancesProcess(); + ~FindInstancesProcess(); + +public: + // ------------------------------------------------------------------- + // Check whether step is active in given flags combination + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + // Execute step on a given scene + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + // Setup properties prior to executing the process + void SetupProperties(const Importer* pImp); + +private: + + bool configSpeedFlag; + +}; // ! end class FindInstancesProcess +} // ! end namespace Assimp + +#endif // !! AI_FINDINSTANCES_H_INC diff --git a/thirdparty/assimp/code/FindInvalidDataProcess.cpp b/thirdparty/assimp/code/FindInvalidDataProcess.cpp new file mode 100644 index 0000000000..433f042448 --- /dev/null +++ b/thirdparty/assimp/code/FindInvalidDataProcess.cpp @@ -0,0 +1,424 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to search an importer's output + for data that is obviously invalid */ + + + +#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS + +// internal headers +#include "FindInvalidDataProcess.h" +#include "ProcessHelper.h" + +#include <assimp/Macros.h> +#include <assimp/Exceptional.h> +#include <assimp/qnan.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FindInvalidDataProcess::FindInvalidDataProcess() +: configEpsilon(0.0) +, mIgnoreTexCoods( false ){ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FindInvalidDataProcess::~FindInvalidDataProcess() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FindInvalidDataProcess::IsActive( unsigned int pFlags) const { + return 0 != (pFlags & aiProcess_FindInvalidData); +} + +// ------------------------------------------------------------------------------------------------ +// Setup import configuration +void FindInvalidDataProcess::SetupProperties(const Importer* pImp) { + // Get the current value of AI_CONFIG_PP_FID_ANIM_ACCURACY + configEpsilon = (0 != pImp->GetPropertyFloat(AI_CONFIG_PP_FID_ANIM_ACCURACY,0.f)); + mIgnoreTexCoods = pImp->GetPropertyBool(AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS, false); +} + +// ------------------------------------------------------------------------------------------------ +// Update mesh references in the node graph +void UpdateMeshReferences(aiNode* node, const std::vector<unsigned int>& meshMapping) { + if (node->mNumMeshes) { + unsigned int out = 0; + for (unsigned int a = 0; a < node->mNumMeshes;++a) { + + unsigned int ref = node->mMeshes[a]; + if (UINT_MAX != (ref = meshMapping[ref])) { + node->mMeshes[out++] = ref; + } + } + // just let the members that are unused, that's much cheaper + // than a full array realloc'n'copy party ... + if(!(node->mNumMeshes = out)) { + + delete[] node->mMeshes; + node->mMeshes = NULL; + } + } + // recursively update all children + for (unsigned int i = 0; i < node->mNumChildren;++i) { + UpdateMeshReferences(node->mChildren[i],meshMapping); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FindInvalidDataProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("FindInvalidDataProcess begin"); + + bool out = false; + std::vector<unsigned int> meshMapping(pScene->mNumMeshes); + unsigned int real = 0; + + // Process meshes + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { + + int result; + if ((result = ProcessMesh( pScene->mMeshes[a]))) { + out = true; + + if (2 == result) { + // remove this mesh + delete pScene->mMeshes[a]; + AI_DEBUG_INVALIDATE_PTR(pScene->mMeshes[a]); + + meshMapping[a] = UINT_MAX; + continue; + } + } + pScene->mMeshes[real] = pScene->mMeshes[a]; + meshMapping[a] = real++; + } + + // Process animations + for (unsigned int a = 0; a < pScene->mNumAnimations;++a) { + ProcessAnimation( pScene->mAnimations[a]); + } + + + if (out) { + if ( real != pScene->mNumMeshes) { + if (!real) { + throw DeadlyImportError("No meshes remaining"); + } + + // we need to remove some meshes. + // therefore we'll also need to remove all references + // to them from the scenegraph + UpdateMeshReferences(pScene->mRootNode,meshMapping); + pScene->mNumMeshes = real; + } + + ASSIMP_LOG_INFO("FindInvalidDataProcess finished. Found issues ..."); + } else { + ASSIMP_LOG_DEBUG("FindInvalidDataProcess finished. Everything seems to be OK."); + } +} + +// ------------------------------------------------------------------------------------------------ +template <typename T> +inline +const char* ValidateArrayContents(const T* /*arr*/, unsigned int /*size*/, + const std::vector<bool>& /*dirtyMask*/, bool /*mayBeIdentical = false*/, bool /*mayBeZero = true*/) +{ + return nullptr; +} + +// ------------------------------------------------------------------------------------------------ +template <> +inline +const char* ValidateArrayContents<aiVector3D>(const aiVector3D* arr, unsigned int size, + const std::vector<bool>& dirtyMask, bool mayBeIdentical , bool mayBeZero ) { + bool b = false; + unsigned int cnt = 0; + for (unsigned int i = 0; i < size;++i) { + + if (dirtyMask.size() && dirtyMask[i]) { + continue; + } + ++cnt; + + const aiVector3D& v = arr[i]; + if (is_special_float(v.x) || is_special_float(v.y) || is_special_float(v.z)) { + return "INF/NAN was found in a vector component"; + } + if (!mayBeZero && !v.x && !v.y && !v.z ) { + return "Found zero-length vector"; + } + if (i && v != arr[i-1])b = true; + } + if (cnt > 1 && !b && !mayBeIdentical) { + return "All vectors are identical"; + } + return nullptr; +} + +// ------------------------------------------------------------------------------------------------ +template <typename T> +inline +bool ProcessArray(T*& in, unsigned int num,const char* name, + const std::vector<bool>& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true) { + const char* err = ValidateArrayContents(in,num,dirtyMask,mayBeIdentical,mayBeZero); + if (err) { + ASSIMP_LOG_ERROR_F( "FindInvalidDataProcess fails on mesh ", name, ": ", err); + delete[] in; + in = NULL; + return true; + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +template <typename T> +AI_FORCE_INLINE bool EpsilonCompare(const T& n, const T& s, ai_real epsilon); + +// ------------------------------------------------------------------------------------------------ +AI_FORCE_INLINE bool EpsilonCompare(ai_real n, ai_real s, ai_real epsilon) { + return std::fabs(n-s)>epsilon; +} + +// ------------------------------------------------------------------------------------------------ +template <> +bool EpsilonCompare<aiVectorKey>(const aiVectorKey& n, const aiVectorKey& s, ai_real epsilon) { + return + EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) && + EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) && + EpsilonCompare(n.mValue.z,s.mValue.z,epsilon); +} + +// ------------------------------------------------------------------------------------------------ +template <> +bool EpsilonCompare<aiQuatKey>(const aiQuatKey& n, const aiQuatKey& s, ai_real epsilon) { + return + EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) && + EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) && + EpsilonCompare(n.mValue.z,s.mValue.z,epsilon) && + EpsilonCompare(n.mValue.w,s.mValue.w,epsilon); +} + +// ------------------------------------------------------------------------------------------------ +template <typename T> +inline +bool AllIdentical(T* in, unsigned int num, ai_real epsilon) { + if (num <= 1) { + return true; + } + + if (fabs(epsilon) > 0.f) { + for (unsigned int i = 0; i < num-1;++i) { + if (!EpsilonCompare(in[i],in[i+1],epsilon)) { + return false; + } + } + } else { + for (unsigned int i = 0; i < num-1;++i) { + if (in[i] != in[i+1]) { + return false; + } + } + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Search an animation for invalid content +void FindInvalidDataProcess::ProcessAnimation (aiAnimation* anim) { + // Process all animation channels + for ( unsigned int a = 0; a < anim->mNumChannels; ++a ) { + ProcessAnimationChannel( anim->mChannels[a]); + } +} + +// ------------------------------------------------------------------------------------------------ +void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) { + ai_assert( nullptr != anim ); + if (anim->mNumPositionKeys == 0 && anim->mNumRotationKeys == 0 && anim->mNumScalingKeys == 0) { + ai_assert_entry(); + return; + } + + // Check whether all values in a tracks are identical - in this case + // we can remove al keys except one. + // POSITIONS + int i = 0; + if (anim->mNumPositionKeys > 1 && AllIdentical(anim->mPositionKeys,anim->mNumPositionKeys,configEpsilon)) { + aiVectorKey v = anim->mPositionKeys[0]; + + // Reallocate ... we need just ONE element, it makes no sense to reuse the array + delete[] anim->mPositionKeys; + anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys = 1]; + anim->mPositionKeys[0] = v; + i = 1; + } + + // ROTATIONS + if (anim->mNumRotationKeys > 1 && AllIdentical(anim->mRotationKeys,anim->mNumRotationKeys,configEpsilon)) { + aiQuatKey v = anim->mRotationKeys[0]; + + // Reallocate ... we need just ONE element, it makes no sense to reuse the array + delete[] anim->mRotationKeys; + anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys = 1]; + anim->mRotationKeys[0] = v; + i = 1; + } + + // SCALINGS + if (anim->mNumScalingKeys > 1 && AllIdentical(anim->mScalingKeys,anim->mNumScalingKeys,configEpsilon)) { + aiVectorKey v = anim->mScalingKeys[0]; + + // Reallocate ... we need just ONE element, it makes no sense to reuse the array + delete[] anim->mScalingKeys; + anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys = 1]; + anim->mScalingKeys[0] = v; + i = 1; + } + if ( 1 == i ) { + ASSIMP_LOG_WARN("Simplified dummy tracks with just one key"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Search a mesh for invalid contents +int FindInvalidDataProcess::ProcessMesh(aiMesh* pMesh) +{ + bool ret = false; + std::vector<bool> dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces != 0); + + // Ignore elements that are not referenced by vertices. + // (they are, for example, caused by the FindDegenerates step) + for (unsigned int m = 0; m < pMesh->mNumFaces; ++m) { + const aiFace& f = pMesh->mFaces[m]; + + for (unsigned int i = 0; i < f.mNumIndices; ++i) { + dirtyMask[f.mIndices[i]] = false; + } + } + + // Process vertex positions + if (pMesh->mVertices && ProcessArray(pMesh->mVertices, pMesh->mNumVertices, "positions", dirtyMask)) { + ASSIMP_LOG_ERROR("Deleting mesh: Unable to continue without vertex positions"); + + return 2; + } + + // process texture coordinates + if (!mIgnoreTexCoods) { + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i]; ++i) { + if (ProcessArray(pMesh->mTextureCoords[i], pMesh->mNumVertices, "uvcoords", dirtyMask)) { + pMesh->mNumUVComponents[i] = 0; + + // delete all subsequent texture coordinate sets. + for (unsigned int a = i + 1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { + delete[] pMesh->mTextureCoords[a]; + pMesh->mTextureCoords[a] = NULL; + pMesh->mNumUVComponents[a] = 0; + } + + ret = true; + } + } + } + + // -- we don't validate vertex colors, it's difficult to say whether + // they are invalid or not. + + // Normals and tangents are undefined for point and line faces. + if (pMesh->mNormals || pMesh->mTangents) { + + if (aiPrimitiveType_POINT & pMesh->mPrimitiveTypes || + aiPrimitiveType_LINE & pMesh->mPrimitiveTypes) + { + if (aiPrimitiveType_TRIANGLE & pMesh->mPrimitiveTypes || + aiPrimitiveType_POLYGON & pMesh->mPrimitiveTypes) + { + // We need to update the lookup-table + for (unsigned int m = 0; m < pMesh->mNumFaces;++m) { + const aiFace& f = pMesh->mFaces[ m ]; + + if (f.mNumIndices < 3) { + dirtyMask[f.mIndices[0]] = true; + if (f.mNumIndices == 2) { + dirtyMask[f.mIndices[1]] = true; + } + } + } + } + // Normals, tangents and bitangents are undefined for + // the whole mesh (and should not even be there) + else { + return ret; + } + } + + // Process mesh normals + if (pMesh->mNormals && ProcessArray(pMesh->mNormals,pMesh->mNumVertices, + "normals",dirtyMask,true,false)) + ret = true; + + // Process mesh tangents + if (pMesh->mTangents && ProcessArray(pMesh->mTangents,pMesh->mNumVertices,"tangents",dirtyMask)) { + delete[] pMesh->mBitangents; pMesh->mBitangents = NULL; + ret = true; + } + + // Process mesh bitangents + if (pMesh->mBitangents && ProcessArray(pMesh->mBitangents,pMesh->mNumVertices,"bitangents",dirtyMask)) { + delete[] pMesh->mTangents; pMesh->mTangents = NULL; + ret = true; + } + } + return ret ? 1 : 0; +} + +#endif // !! ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS diff --git a/thirdparty/assimp/code/FindInvalidDataProcess.h b/thirdparty/assimp/code/FindInvalidDataProcess.h new file mode 100644 index 0000000000..8504fb7b1f --- /dev/null +++ b/thirdparty/assimp/code/FindInvalidDataProcess.h @@ -0,0 +1,105 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to search an importer's output + * for data that is obviously invalid + */ +#ifndef AI_FINDINVALIDDATA_H_INC +#define AI_FINDINVALIDDATA_H_INC + +#include "BaseProcess.h" +#include <assimp/types.h> +#include <assimp/anim.h> + +struct aiMesh; + +class FindInvalidDataProcessTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** The FindInvalidData post-processing step. It searches the mesh data + * for parts that are obviously invalid and removes them. + * + * Originally this was a workaround for some models written by Blender + * which have zero normal vectors. */ +class ASSIMP_API FindInvalidDataProcess : public BaseProcess { +public: + FindInvalidDataProcess(); + ~FindInvalidDataProcess(); + + // ------------------------------------------------------------------- + // + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + // Setup import settings + void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + // Run the step + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + /** Executes the post-processing step on the given mesh + * @param pMesh The mesh to process. + * @return 0 - nothing, 1 - removed sth, 2 - please delete me */ + int ProcessMesh( aiMesh* pMesh); + + // ------------------------------------------------------------------- + /** Executes the post-processing step on the given animation + * @param anim The animation to process. */ + void ProcessAnimation (aiAnimation* anim); + + // ------------------------------------------------------------------- + /** Executes the post-processing step on the given anim channel + * @param anim The animation channel to process.*/ + void ProcessAnimationChannel (aiNodeAnim* anim); + +private: + ai_real configEpsilon; + bool mIgnoreTexCoods; +}; + +} // end of namespace Assimp + +#endif // AI_AI_FINDINVALIDDATA_H_INC diff --git a/thirdparty/assimp/code/FixNormalsStep.cpp b/thirdparty/assimp/code/FixNormalsStep.cpp new file mode 100644 index 0000000000..bbbe6899b4 --- /dev/null +++ b/thirdparty/assimp/code/FixNormalsStep.cpp @@ -0,0 +1,184 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to invert + * all normals in meshes with infacing normals. + */ + +// internal headers +#include "FixNormalsStep.h" +#include <assimp/StringUtils.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/postprocess.h> +#include <assimp/scene.h> +#include <stdio.h> + + +using namespace Assimp; + + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +FixInfacingNormalsProcess::FixInfacingNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +FixInfacingNormalsProcess::~FixInfacingNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool FixInfacingNormalsProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_FixInfacingNormals) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void FixInfacingNormalsProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess begin"); + + bool bHas( false ); + for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { + if (ProcessMesh(pScene->mMeshes[a], a)) { + bHas = true; + } + } + + if (bHas) { + ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess finished. Found issues."); + } else { + ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess finished. No changes to the scene."); + } +} + +// ------------------------------------------------------------------------------------------------ +// Apply the step to the mesh +bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index) +{ + ai_assert(nullptr != pcMesh); + + // Nothing to do if there are no model normals + if (!pcMesh->HasNormals()) { + return false; + } + + // Compute the bounding box of both the model vertices + normals and + // the unmodified model vertices. Then check whether the first BB + // is smaller than the second. In this case we can assume that the + // normals need to be flipped, although there are a few special cases .. + // convex, concave, planar models ... + + aiVector3D vMin0 (1e10f,1e10f,1e10f); + aiVector3D vMin1 (1e10f,1e10f,1e10f); + aiVector3D vMax0 (-1e10f,-1e10f,-1e10f); + aiVector3D vMax1 (-1e10f,-1e10f,-1e10f); + + for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) + { + vMin1.x = std::min(vMin1.x,pcMesh->mVertices[i].x); + vMin1.y = std::min(vMin1.y,pcMesh->mVertices[i].y); + vMin1.z = std::min(vMin1.z,pcMesh->mVertices[i].z); + + vMax1.x = std::max(vMax1.x,pcMesh->mVertices[i].x); + vMax1.y = std::max(vMax1.y,pcMesh->mVertices[i].y); + vMax1.z = std::max(vMax1.z,pcMesh->mVertices[i].z); + + const aiVector3D vWithNormal = pcMesh->mVertices[i] + pcMesh->mNormals[i]; + + vMin0.x = std::min(vMin0.x,vWithNormal.x); + vMin0.y = std::min(vMin0.y,vWithNormal.y); + vMin0.z = std::min(vMin0.z,vWithNormal.z); + + vMax0.x = std::max(vMax0.x,vWithNormal.x); + vMax0.y = std::max(vMax0.y,vWithNormal.y); + vMax0.z = std::max(vMax0.z,vWithNormal.z); + } + + const float fDelta0_x = (vMax0.x - vMin0.x); + const float fDelta0_y = (vMax0.y - vMin0.y); + const float fDelta0_z = (vMax0.z - vMin0.z); + + const float fDelta1_x = (vMax1.x - vMin1.x); + const float fDelta1_y = (vMax1.y - vMin1.y); + const float fDelta1_z = (vMax1.z - vMin1.z); + + // Check whether the boxes are overlapping + if ((fDelta0_x > 0.0f) != (fDelta1_x > 0.0f))return false; + if ((fDelta0_y > 0.0f) != (fDelta1_y > 0.0f))return false; + if ((fDelta0_z > 0.0f) != (fDelta1_z > 0.0f))return false; + + // Check whether this is a planar surface + const float fDelta1_yz = fDelta1_y * fDelta1_z; + + if (fDelta1_x < 0.05f * std::sqrt( fDelta1_yz ))return false; + if (fDelta1_y < 0.05f * std::sqrt( fDelta1_z * fDelta1_x ))return false; + if (fDelta1_z < 0.05f * std::sqrt( fDelta1_y * fDelta1_x ))return false; + + // now compare the volumes of the bounding boxes + if (std::fabs(fDelta0_x * fDelta0_y * fDelta0_z) < std::fabs(fDelta1_x * fDelta1_yz)) { + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_INFO_F("Mesh ", index, ": Normals are facing inwards (or the mesh is planar)", index); + } + + // Invert normals + for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) + pcMesh->mNormals[i] *= -1.0f; + + // ... and flip faces + for (unsigned int i = 0; i < pcMesh->mNumFaces;++i) + { + aiFace& face = pcMesh->mFaces[i]; + for( unsigned int b = 0; b < face.mNumIndices / 2; b++) + std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]); + } + return true; + } + return false; +} diff --git a/thirdparty/assimp/code/FixNormalsStep.h b/thirdparty/assimp/code/FixNormalsStep.h new file mode 100644 index 0000000000..6be27faef6 --- /dev/null +++ b/thirdparty/assimp/code/FixNormalsStep.h @@ -0,0 +1,91 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + + +/** @file Defines a post processing step to fix infacing normals */ +#ifndef AI_FIXNORMALSPROCESS_H_INC +#define AI_FIXNORMALSPROCESS_H_INC + +#include "BaseProcess.h" + +struct aiMesh; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The FixInfacingNormalsProcess tries to determine whether the normal + * vectors of an object are facing inwards. In this case they will be + * flipped. + */ +class FixInfacingNormalsProcess : public BaseProcess { +public: + FixInfacingNormalsProcess(); + ~FixInfacingNormalsProcess(); + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + +protected: + + // ------------------------------------------------------------------- + /** Executes the step on the given mesh + * @param pMesh The mesh to process. + */ + bool ProcessMesh( aiMesh* pMesh, unsigned int index); +}; + +} // end of namespace Assimp + +#endif // AI_FIXNORMALSPROCESS_H_INC diff --git a/thirdparty/assimp/code/GenFaceNormalsProcess.cpp b/thirdparty/assimp/code/GenFaceNormalsProcess.cpp new file mode 100644 index 0000000000..028334dec7 --- /dev/null +++ b/thirdparty/assimp/code/GenFaceNormalsProcess.cpp @@ -0,0 +1,146 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to generate face +* normals for all imported faces. +*/ + + +#include "GenFaceNormalsProcess.h" +#include <assimp/postprocess.h> +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/Exceptional.h> +#include <assimp/qnan.h> + + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +GenFaceNormalsProcess::GenFaceNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +GenFaceNormalsProcess::~GenFaceNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const { + force_ = (pFlags & aiProcess_ForceGenNormals) != 0; + return (pFlags & aiProcess_GenNormals) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void GenFaceNormalsProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("GenFaceNormalsProcess begin"); + + if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { + throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); + } + + bool bHas = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { + if(this->GenMeshFaceNormals( pScene->mMeshes[a])) { + bHas = true; + } + } + if (bHas) { + ASSIMP_LOG_INFO("GenFaceNormalsProcess finished. " + "Face normals have been calculated"); + } else { + ASSIMP_LOG_DEBUG("GenFaceNormalsProcess finished. " + "Normals are already there"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh) +{ + if (NULL != pMesh->mNormals) { + if (force_) delete[] pMesh->mNormals; + else return false; + } + + // If the mesh consists of lines and/or points but not of + // triangles or higher-order polygons the normal vectors + // are undefined. + if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) { + ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes"); + return false; + } + + // allocate an array to hold the output normals + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + const float qnan = get_qnan(); + + // iterate through all faces and compute per-face normals but store them per-vertex. + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { + const aiFace& face = pMesh->mFaces[a]; + if (face.mNumIndices < 3) { + // either a point or a line -> no well-defined normal vector + for (unsigned int i = 0;i < face.mNumIndices;++i) { + pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan); + } + continue; + } + + const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]]; + const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]]; + const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]]; + const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe(); + + for (unsigned int i = 0;i < face.mNumIndices;++i) { + pMesh->mNormals[face.mIndices[i]] = vNor; + } + } + return true; +} diff --git a/thirdparty/assimp/code/GenFaceNormalsProcess.h b/thirdparty/assimp/code/GenFaceNormalsProcess.h new file mode 100644 index 0000000000..c80ec9fddc --- /dev/null +++ b/thirdparty/assimp/code/GenFaceNormalsProcess.h @@ -0,0 +1,87 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to compute face normals for all loaded faces*/ +#ifndef AI_GENFACENORMALPROCESS_H_INC +#define AI_GENFACENORMALPROCESS_H_INC + +#include "BaseProcess.h" +#include <assimp/mesh.h> + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The GenFaceNormalsProcess computes face normals for all faces of all meshes +*/ +class ASSIMP_API_WINONLY GenFaceNormalsProcess : public BaseProcess +{ +public: + + GenFaceNormalsProcess(); + ~GenFaceNormalsProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + +private: + bool GenMeshFaceNormals(aiMesh* pcMesh); + mutable bool force_ = false; +}; + +} // end of namespace Assimp + +#endif // !!AI_GENFACENORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/GenVertexNormalsProcess.cpp b/thirdparty/assimp/code/GenVertexNormalsProcess.cpp new file mode 100644 index 0000000000..3f6c2f86bd --- /dev/null +++ b/thirdparty/assimp/code/GenVertexNormalsProcess.cpp @@ -0,0 +1,239 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to generate face +* normals for all imported faces. +*/ + + + +// internal headers +#include "GenVertexNormalsProcess.h" +#include "ProcessHelper.h" +#include <assimp/Exceptional.h> +#include <assimp/qnan.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +GenVertexNormalsProcess::GenVertexNormalsProcess() +: configMaxAngle( AI_DEG_TO_RAD( 175.f ) ) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +GenVertexNormalsProcess::~GenVertexNormalsProcess() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const +{ + force_ = (pFlags & aiProcess_ForceGenNormals) != 0; + return (pFlags & aiProcess_GenSmoothNormals) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void GenVertexNormalsProcess::SetupProperties(const Importer* pImp) +{ + // Get the current value of the AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE property + configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,(ai_real)175.0); + configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle,(ai_real)175.0),(ai_real)0.0)); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void GenVertexNormalsProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("GenVertexNormalsProcess begin"); + + if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { + throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here"); + } + + bool bHas = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) { + if(GenMeshVertexNormals( pScene->mMeshes[a],a)) + bHas = true; + } + + if (bHas) { + ASSIMP_LOG_INFO("GenVertexNormalsProcess finished. " + "Vertex normals have been calculated"); + } else { + ASSIMP_LOG_DEBUG("GenVertexNormalsProcess finished. " + "Normals are already there"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int meshIndex) +{ + if (NULL != pMesh->mNormals) { + if (force_) delete[] pMesh->mNormals; + else return false; + } + + // If the mesh consists of lines and/or points but not of + // triangles or higher-order polygons the normal vectors + // are undefined. + if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) + { + ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes"); + return false; + } + + // Allocate the array to hold the output normals + const float qnan = std::numeric_limits<ai_real>::quiet_NaN(); + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + + // Compute per-face normals but store them per-vertex + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + const aiFace& face = pMesh->mFaces[a]; + if (face.mNumIndices < 3) + { + // either a point or a line -> no normal vector + for (unsigned int i = 0;i < face.mNumIndices;++i) { + pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan); + } + + continue; + } + + const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]]; + const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]]; + const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]]; + const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe(); + + for (unsigned int i = 0;i < face.mNumIndices;++i) { + pMesh->mNormals[face.mIndices[i]] = vNor; + } + } + + // Set up a SpatialSort to quickly find all vertices close to a given position + // check whether we can reuse the SpatialSort of a previous step. + SpatialSort* vertexFinder = NULL; + SpatialSort _vertexFinder; + ai_real posEpsilon = ai_real( 1e-5 ); + if (shared) { + std::vector<std::pair<SpatialSort,ai_real> >* avf; + shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); + if (avf) + { + std::pair<SpatialSort,ai_real>& blubb = avf->operator [] (meshIndex); + vertexFinder = &blubb.first; + posEpsilon = blubb.second; + } + } + if (!vertexFinder) { + _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); + vertexFinder = &_vertexFinder; + posEpsilon = ComputePositionEpsilon(pMesh); + } + std::vector<unsigned int> verticesFound; + aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices]; + + if (configMaxAngle >= AI_DEG_TO_RAD( 175.f )) { + // There is no angle limit. Thus all vertices with positions close + // to each other will receive the same vertex normal. This allows us + // to optimize the whole algorithm a little bit ... + std::vector<bool> abHad(pMesh->mNumVertices,false); + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { + if (abHad[i]) { + continue; + } + + // Get all vertices that share this one ... + vertexFinder->FindPositions( pMesh->mVertices[i], posEpsilon, verticesFound); + + aiVector3D pcNor; + for (unsigned int a = 0; a < verticesFound.size(); ++a) { + const aiVector3D& v = pMesh->mNormals[verticesFound[a]]; + if (is_not_qnan(v.x))pcNor += v; + } + pcNor.NormalizeSafe(); + + // Write the smoothed normal back to all affected normals + for (unsigned int a = 0; a < verticesFound.size(); ++a) + { + unsigned int vidx = verticesFound[a]; + pcNew[vidx] = pcNor; + abHad[vidx] = true; + } + } + } + // Slower code path if a smooth angle is set. There are many ways to achieve + // the effect, this one is the most straightforward one. + else { + const ai_real fLimit = std::cos(configMaxAngle); + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { + // Get all vertices that share this one ... + vertexFinder->FindPositions( pMesh->mVertices[i] , posEpsilon, verticesFound); + + aiVector3D vr = pMesh->mNormals[i]; + + aiVector3D pcNor; + for (unsigned int a = 0; a < verticesFound.size(); ++a) { + aiVector3D v = pMesh->mNormals[verticesFound[a]]; + + // Check whether the angle between the two normals is not too large. + // Skip the angle check on our own normal to avoid false negatives + // (v*v is not guaranteed to be 1.0 for all unit vectors v) + if (is_not_qnan(v.x) && (verticesFound[a] == i || (v * vr >= fLimit))) + pcNor += v; + } + pcNew[i] = pcNor.NormalizeSafe(); + } + } + + delete[] pMesh->mNormals; + pMesh->mNormals = pcNew; + + return true; +} diff --git a/thirdparty/assimp/code/GenVertexNormalsProcess.h b/thirdparty/assimp/code/GenVertexNormalsProcess.h new file mode 100644 index 0000000000..9142ad26f5 --- /dev/null +++ b/thirdparty/assimp/code/GenVertexNormalsProcess.h @@ -0,0 +1,115 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to compute vertex normals + for all loaded vertizes */ +#ifndef AI_GENVERTEXNORMALPROCESS_H_INC +#define AI_GENVERTEXNORMALPROCESS_H_INC + +#include "BaseProcess.h" +#include <assimp/mesh.h> + +class GenNormalsTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** The GenFaceNormalsProcess computes vertex normals for all vertizes +*/ +class ASSIMP_API GenVertexNormalsProcess : public BaseProcess +{ +public: + + GenVertexNormalsProcess(); + ~GenVertexNormalsProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. + * A bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + + // setter for configMaxAngle + inline void SetMaxSmoothAngle(ai_real f) + { + configMaxAngle =f; + } + +public: + + // ------------------------------------------------------------------- + /** Computes normals for a specific mesh + * @param pcMesh Mesh + * @param meshIndex Index of the mesh + * @return true if vertex normals have been computed + */ + bool GenMeshVertexNormals (aiMesh* pcMesh, unsigned int meshIndex); + +private: + + /** Configuration option: maximum smoothing angle, in radians*/ + ai_real configMaxAngle; + mutable bool force_ = false; +}; + +} // end of namespace Assimp + +#endif // !!AI_GENVERTEXNORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/Importer.cpp b/thirdparty/assimp/code/Importer.cpp new file mode 100644 index 0000000000..65b16471cc --- /dev/null +++ b/thirdparty/assimp/code/Importer.cpp @@ -0,0 +1,1171 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Importer.cpp + * @brief Implementation of the CPP-API class #Importer + */ + +#include <assimp/version.h> +#include <assimp/config.h> +#include <assimp/importerdesc.h> + +// ------------------------------------------------------------------------------------------------ +/* Uncomment this line to prevent Assimp from catching unknown exceptions. + * + * Note that any Exception except DeadlyImportError may lead to + * undefined behaviour -> loaders could remain in an unusable state and + * further imports with the same Importer instance could fail/crash/burn ... + */ +// ------------------------------------------------------------------------------------------------ +#ifndef ASSIMP_BUILD_DEBUG +# define ASSIMP_CATCH_GLOBAL_EXCEPTIONS +#endif + +// ------------------------------------------------------------------------------------------------ +// Internal headers +// ------------------------------------------------------------------------------------------------ +#include "Importer.h" +#include <assimp/BaseImporter.h> +#include "BaseProcess.h" + +#include "DefaultProgressHandler.h" +#include <assimp/GenericProperty.h> +#include "ProcessHelper.h" +#include "ScenePreprocessor.h" +#include "ScenePrivate.h" +#include <assimp/MemoryIOWrapper.h> +#include <assimp/Profiler.h> +#include <assimp/TinyFormatter.h> +#include <assimp/Exceptional.h> +#include <assimp/Profiler.h> +#include <set> +#include <memory> +#include <cctype> + +#include <assimp/DefaultIOStream.h> +#include <assimp/DefaultIOSystem.h> + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS +# include "ValidateDataStructure.h" +#endif + +using namespace Assimp::Profiling; +using namespace Assimp::Formatter; + +namespace Assimp { + // ImporterRegistry.cpp + void GetImporterInstanceList(std::vector< BaseImporter* >& out); + void DeleteImporterInstanceList(std::vector< BaseImporter* >& out); + + // PostStepRegistry.cpp + void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); +} + +using namespace Assimp; +using namespace Assimp::Intern; + +// ------------------------------------------------------------------------------------------------ +// Intern::AllocateFromAssimpHeap serves as abstract base class. It overrides +// new and delete (and their array counterparts) of public API classes (e.g. Logger) to +// utilize our DLL heap. +// See http://www.gotw.ca/publications/mill15.htm +// ------------------------------------------------------------------------------------------------ +void* AllocateFromAssimpHeap::operator new ( size_t num_bytes) { + return ::operator new(num_bytes); +} + +void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothrow_t& ) throw() { + try { + return AllocateFromAssimpHeap::operator new( num_bytes ); + } + catch( ... ) { + return NULL; + } +} + +void AllocateFromAssimpHeap::operator delete ( void* data) { + return ::operator delete(data); +} + +void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes) { + return ::operator new[](num_bytes); +} + +void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() { + try { + return AllocateFromAssimpHeap::operator new[]( num_bytes ); + } + catch( ... ) { + return NULL; + } +} + +void AllocateFromAssimpHeap::operator delete[] ( void* data) { + return ::operator delete[](data); +} + +// ------------------------------------------------------------------------------------------------ +// Importer constructor. +Importer::Importer() + : pimpl( new ImporterPimpl ) { + pimpl->mScene = NULL; + pimpl->mErrorString = ""; + + // Allocate a default IO handler + pimpl->mIOHandler = new DefaultIOSystem; + pimpl->mIsDefaultHandler = true; + pimpl->bExtraVerbose = false; // disable extra verbose mode by default + + pimpl->mProgressHandler = new DefaultProgressHandler(); + pimpl->mIsDefaultProgressHandler = true; + + GetImporterInstanceList(pimpl->mImporter); + GetPostProcessingStepInstanceList(pimpl->mPostProcessingSteps); + + // Allocate a SharedPostProcessInfo object and store pointers to it in all post-process steps in the list. + pimpl->mPPShared = new SharedPostProcessInfo(); + for (std::vector<BaseProcess*>::iterator it = pimpl->mPostProcessingSteps.begin(); + it != pimpl->mPostProcessingSteps.end(); + ++it) { + + (*it)->SetSharedData(pimpl->mPPShared); + } +} + +// ------------------------------------------------------------------------------------------------ +// Destructor of Importer +Importer::~Importer() +{ + // Delete all import plugins + DeleteImporterInstanceList(pimpl->mImporter); + + // Delete all post-processing plug-ins + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) + delete pimpl->mPostProcessingSteps[a]; + + // Delete the assigned IO and progress handler + delete pimpl->mIOHandler; + delete pimpl->mProgressHandler; + + // Kill imported scene. Destructor's should do that recursively + delete pimpl->mScene; + + // Delete shared post-processing data + delete pimpl->mPPShared; + + // and finally the pimpl itself + delete pimpl; +} + +// ------------------------------------------------------------------------------------------------ +// Register a custom post-processing step +aiReturn Importer::RegisterPPStep(BaseProcess* pImp) +{ + ai_assert(NULL != pImp); + ASSIMP_BEGIN_EXCEPTION_REGION(); + + pimpl->mPostProcessingSteps.push_back(pImp); + ASSIMP_LOG_INFO("Registering custom post-processing step"); + + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Register a custom loader plugin +aiReturn Importer::RegisterLoader(BaseImporter* pImp) +{ + ai_assert(NULL != pImp); + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // -------------------------------------------------------------------- + // Check whether we would have two loaders for the same file extension + // This is absolutely OK, but we should warn the developer of the new + // loader that his code will probably never be called if the first + // loader is a bit too lazy in his file checking. + // -------------------------------------------------------------------- + std::set<std::string> st; + std::string baked; + pImp->GetExtensionList(st); + + for(std::set<std::string>::const_iterator it = st.begin(); it != st.end(); ++it) { + +#ifdef ASSIMP_BUILD_DEBUG + if (IsExtensionSupported(*it)) { + ASSIMP_LOG_WARN_F("The file extension ", *it, " is already in use"); + } +#endif + baked += *it; + } + + // add the loader + pimpl->mImporter.push_back(pImp); + ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked); + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Unregister a custom loader plugin +aiReturn Importer::UnregisterLoader(BaseImporter* pImp) +{ + if(!pImp) { + // unregistering a NULL importer is no problem for us ... really! + return AI_SUCCESS; + } + + ASSIMP_BEGIN_EXCEPTION_REGION(); + std::vector<BaseImporter*>::iterator it = std::find(pimpl->mImporter.begin(), + pimpl->mImporter.end(),pImp); + + if (it != pimpl->mImporter.end()) { + pimpl->mImporter.erase(it); + ASSIMP_LOG_INFO("Unregistering custom importer: "); + return AI_SUCCESS; + } + ASSIMP_LOG_WARN("Unable to remove custom importer: I can't find you ..."); + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +// Unregister a custom loader plugin +aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) +{ + if(!pImp) { + // unregistering a NULL ppstep is no problem for us ... really! + return AI_SUCCESS; + } + + ASSIMP_BEGIN_EXCEPTION_REGION(); + std::vector<BaseProcess*>::iterator it = std::find(pimpl->mPostProcessingSteps.begin(), + pimpl->mPostProcessingSteps.end(),pImp); + + if (it != pimpl->mPostProcessingSteps.end()) { + pimpl->mPostProcessingSteps.erase(it); + ASSIMP_LOG_INFO("Unregistering custom post-processing step"); + return AI_SUCCESS; + } + ASSIMP_LOG_WARN("Unable to remove custom post-processing step: I can't find you .."); + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +// Supplies a custom IO handler to the importer to open and access files. +void Importer::SetIOHandler( IOSystem* pIOHandler) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + // If the new handler is zero, allocate a default IO implementation. + if (!pIOHandler) + { + // Release pointer in the possession of the caller + pimpl->mIOHandler = new DefaultIOSystem(); + pimpl->mIsDefaultHandler = true; + } + // Otherwise register the custom handler + else if (pimpl->mIOHandler != pIOHandler) + { + delete pimpl->mIOHandler; + pimpl->mIOHandler = pIOHandler; + pimpl->mIsDefaultHandler = false; + } + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the currently set IO handler +IOSystem* Importer::GetIOHandler() const { + return pimpl->mIOHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Check whether a custom IO handler is currently set +bool Importer::IsDefaultIOHandler() const { + return pimpl->mIsDefaultHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Supplies a custom progress handler to get regular callbacks during importing +void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + // If the new handler is zero, allocate a default implementation. + if (!pHandler) + { + // Release pointer in the possession of the caller + pimpl->mProgressHandler = new DefaultProgressHandler(); + pimpl->mIsDefaultProgressHandler = true; + } + // Otherwise register the custom handler + else if (pimpl->mProgressHandler != pHandler) + { + delete pimpl->mProgressHandler; + pimpl->mProgressHandler = pHandler; + pimpl->mIsDefaultProgressHandler = false; + } + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the currently set progress handler +ProgressHandler* Importer::GetProgressHandler() const { + return pimpl->mProgressHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Check whether a custom progress handler is currently set +bool Importer::IsDefaultProgressHandler() const { + return pimpl->mIsDefaultProgressHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Validate post process step flags +bool _ValidateFlags(unsigned int pFlags) +{ + if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) { + ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible"); + return false; + } + if (pFlags & aiProcess_OptimizeGraph && pFlags & aiProcess_PreTransformVertices) { + ASSIMP_LOG_ERROR("#aiProcess_OptimizeGraph and #aiProcess_PreTransformVertices are incompatible"); + return false; + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Free the current scene +void Importer::FreeScene( ) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + + delete pimpl->mScene; + pimpl->mScene = NULL; + + pimpl->mErrorString = ""; + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the current error string, if any +const char* Importer::GetErrorString() const +{ + /* Must remain valid as long as ReadFile() or FreeFile() are not called */ + return pimpl->mErrorString.c_str(); +} + +// ------------------------------------------------------------------------------------------------ +// Enable extra-verbose mode +void Importer::SetExtraVerbose(bool bDo) +{ + pimpl->bExtraVerbose = bDo; +} + +// ------------------------------------------------------------------------------------------------ +// Get the current scene +const aiScene* Importer::GetScene() const +{ + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +// Orphan the current scene and return it. +aiScene* Importer::GetOrphanedScene() +{ + aiScene* s = pimpl->mScene; + + ASSIMP_BEGIN_EXCEPTION_REGION(); + pimpl->mScene = NULL; + + pimpl->mErrorString = ""; /* reset error string */ + ASSIMP_END_EXCEPTION_REGION(aiScene*); + return s; +} + +// ------------------------------------------------------------------------------------------------ +// Validate post-processing flags +bool Importer::ValidateFlags(unsigned int pFlags) const +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + // run basic checks for mutually exclusive flags + if(!_ValidateFlags(pFlags)) { + return false; + } + + // ValidateDS does not anymore occur in the pp list, it plays an awesome extra role ... +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + if (pFlags & aiProcess_ValidateDataStructure) { + return false; + } +#endif + pFlags &= ~aiProcess_ValidateDataStructure; + + // Now iterate through all bits which are set in the flags and check whether we find at least + // one pp plugin which handles it. + for (unsigned int mask = 1; mask < (1u << (sizeof(unsigned int)*8-1));mask <<= 1) { + + if (pFlags & mask) { + + bool have = false; + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + if (pimpl->mPostProcessingSteps[a]-> IsActive(mask) ) { + + have = true; + break; + } + } + if (!have) { + return false; + } + } + } + ASSIMP_END_EXCEPTION_REGION(bool); + return true; +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, + size_t pLength, + unsigned int pFlags, + const char* pHint /*= ""*/) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + if (!pHint) { + pHint = ""; + } + + if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) { + pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()"; + return NULL; + } + + // prevent deletion of the previous IOHandler + IOSystem* io = pimpl->mIOHandler; + pimpl->mIOHandler = NULL; + + SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io)); + + // read the file and recover the previous IOSystem + static const size_t BufSize(Importer::MaxLenHint + 28); + char fbuff[BufSize]; + ai_snprintf(fbuff, BufSize, "%s.%s",AI_MEMORYIO_MAGIC_FILENAME,pHint); + + ReadFile(fbuff,pFlags); + SetIOHandler(io); + + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +void WriteLogOpening(const std::string& file) +{ + ASSIMP_LOG_INFO_F("Load ", file); + + // print a full version dump. This is nice because we don't + // need to ask the authors of incoming bug reports for + // the library version they're using - a log dump is + // sufficient. + const unsigned int flags( aiGetCompileFlags() ); + std::stringstream stream; + stream << "Assimp " << aiGetVersionMajor() << "." << aiGetVersionMinor() << "." << aiGetVersionRevision() << " " +#if defined(ASSIMP_BUILD_ARCHITECTURE) + << ASSIMP_BUILD_ARCHITECTURE +#elif defined(_M_IX86) || defined(__x86_32__) || defined(__i386__) + << "x86" +#elif defined(_M_X64) || defined(__x86_64__) + << "amd64" +#elif defined(_M_IA64) || defined(__ia64__) + << "itanium" +#elif defined(__ppc__) || defined(__powerpc__) + << "ppc32" +#elif defined(__powerpc64__) + << "ppc64" +#elif defined(__arm__) + << "arm" +#else + << "<unknown architecture>" +#endif + << " " +#if defined(ASSIMP_BUILD_COMPILER) + << ( ASSIMP_BUILD_COMPILER ) +#elif defined(_MSC_VER) + << "msvc" +#elif defined(__GNUC__) + << "gcc" +#else + << "<unknown compiler>" +#endif + +#ifdef ASSIMP_BUILD_DEBUG + << " debug" +#endif + + << (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "") + << (flags & ASSIMP_CFLAGS_SHARED ? " shared" : "") + << (flags & ASSIMP_CFLAGS_SINGLETHREADED ? " singlethreaded" : ""); + + ASSIMP_LOG_DEBUG(stream.str()); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the given file and returns its contents if successful. +const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + const std::string pFile(_pFile); + + // ---------------------------------------------------------------------- + // Put a large try block around everything to catch all std::exception's + // that might be thrown by STL containers or by new(). + // ImportErrorException's are throw by ourselves and caught elsewhere. + //----------------------------------------------------------------------- + + WriteLogOpening(pFile); + +#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS + try +#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS + { + // Check whether this Importer instance has already loaded + // a scene. In this case we need to delete the old one + if (pimpl->mScene) { + + ASSIMP_LOG_DEBUG("(Deleting previous scene)"); + FreeScene(); + } + + // First check if the file is accessible at all + if( !pimpl->mIOHandler->Exists( pFile)) { + + pimpl->mErrorString = "Unable to open file \"" + pFile + "\"."; + ASSIMP_LOG_ERROR(pimpl->mErrorString); + return NULL; + } + + std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); + if (profiler) { + profiler->BeginRegion("total"); + } + + // Find an worker class which can handle the file + BaseImporter* imp = NULL; + for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { + + if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) { + imp = pimpl->mImporter[a]; + break; + } + } + + if (!imp) { + // not so bad yet ... try format auto detection. + const std::string::size_type s = pFile.find_last_of('.'); + if (s != std::string::npos) { + ASSIMP_LOG_INFO("File extension not known, trying signature-based detection"); + for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { + if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) { + imp = pimpl->mImporter[a]; + break; + } + } + } + // Put a proper error message if no suitable importer was found + if( !imp) { + pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\"."; + ASSIMP_LOG_ERROR(pimpl->mErrorString); + return NULL; + } + } + + // Get file size for progress handler + IOStream * fileIO = pimpl->mIOHandler->Open( pFile ); + uint32_t fileSize = 0; + if (fileIO) + { + fileSize = static_cast<uint32_t>(fileIO->FileSize()); + pimpl->mIOHandler->Close( fileIO ); + } + + // Dispatch the reading to the worker class for this format + const aiImporterDesc *desc( imp->GetInfo() ); + std::string ext( "unknown" ); + if ( NULL != desc ) { + ext = desc->mName; + } + ASSIMP_LOG_INFO("Found a matching importer for this file format: " + ext + "." ); + pimpl->mProgressHandler->UpdateFileRead( 0, fileSize ); + + if (profiler) { + profiler->BeginRegion("import"); + } + + pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler); + pimpl->mProgressHandler->UpdateFileRead( fileSize, fileSize ); + + if (profiler) { + profiler->EndRegion("import"); + } + + SetPropertyString("sourceFilePath", pFile); + + // If successful, apply all active post processing steps to the imported data + if( pimpl->mScene) { + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called. + if (pFlags & aiProcess_ValidateDataStructure) + { + ValidateDSProcess ds; + ds.ExecuteOnScene (this); + if (!pimpl->mScene) { + return NULL; + } + } +#endif // no validation + + // Preprocess the scene and prepare it for post-processing + if (profiler) { + profiler->BeginRegion("preprocess"); + } + + ScenePreprocessor pre(pimpl->mScene); + pre.ProcessScene(); + + if (profiler) { + profiler->EndRegion("preprocess"); + } + + // Ensure that the validation process won't be called twice + ApplyPostProcessing(pFlags & (~aiProcess_ValidateDataStructure)); + } + // if failed, extract the error string + else if( !pimpl->mScene) { + pimpl->mErrorString = imp->GetErrorText(); + } + + // clear any data allocated by post-process steps + pimpl->mPPShared->Clean(); + + if (profiler) { + profiler->EndRegion("total"); + } + } +#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS + catch (std::exception &e) + { +#if (defined _MSC_VER) && (defined _CPPRTTI) + // if we have RTTI get the full name of the exception that occurred + pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what(); +#else + pimpl->mErrorString = std::string("std::exception: ") + e.what(); +#endif + + ASSIMP_LOG_ERROR(pimpl->mErrorString); + delete pimpl->mScene; pimpl->mScene = NULL; + } +#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS + + // either successful or failure - the pointer expresses it anyways + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return pimpl->mScene; +} + + +// ------------------------------------------------------------------------------------------------ +// Apply post-processing to the currently bound scene +const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + // Return immediately if no scene is active + if (!pimpl->mScene) { + return NULL; + } + + // If no flags are given, return the current scene with no further action + if (!pFlags) { + return pimpl->mScene; + } + + // In debug builds: run basic flag validation + ai_assert(_ValidateFlags(pFlags)); + ASSIMP_LOG_INFO("Entering post processing pipeline"); + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + // The ValidateDS process plays an exceptional role. It isn't contained in the global + // list of post-processing steps, so we need to call it manually. + if (pFlags & aiProcess_ValidateDataStructure) + { + ValidateDSProcess ds; + ds.ExecuteOnScene (this); + if (!pimpl->mScene) { + return NULL; + } + } +#endif // no validation +#ifdef ASSIMP_BUILD_DEBUG + if (pimpl->bExtraVerbose) + { +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + ASSIMP_LOG_ERROR("Verbose Import is not available due to build settings"); +#endif // no validation + pFlags |= aiProcess_ValidateDataStructure; + } +#else + if (pimpl->bExtraVerbose) { + ASSIMP_LOG_WARN("Not a debug build, ignoring extra verbose setting"); + } +#endif // ! DEBUG + + std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); + for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { + + BaseProcess* process = pimpl->mPostProcessingSteps[a]; + pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) ); + if( process->IsActive( pFlags)) { + + if (profiler) { + profiler->BeginRegion("postprocess"); + } + + process->ExecuteOnScene ( this ); + + if (profiler) { + profiler->EndRegion("postprocess"); + } + } + if( !pimpl->mScene) { + break; + } +#ifdef ASSIMP_BUILD_DEBUG + +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + continue; +#endif // no validation + + // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step + if (pimpl->bExtraVerbose) { + ASSIMP_LOG_DEBUG("Verbose Import: re-validating data structures"); + + ValidateDSProcess ds; + ds.ExecuteOnScene (this); + if( !pimpl->mScene) { + ASSIMP_LOG_ERROR("Verbose Import: failed to re-validate data structures"); + break; + } + } +#endif // ! DEBUG + } + pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()), + static_cast<int>(pimpl->mPostProcessingSteps.size()) ); + + // update private scene flags + if( pimpl->mScene ) + ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags; + + // clear any data allocated by post-process steps + pimpl->mPPShared->Clean(); + ASSIMP_LOG_INFO("Leaving post processing pipeline"); + + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) { + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // Return immediately if no scene is active + if ( NULL == pimpl->mScene ) { + return NULL; + } + + // If no flags are given, return the current scene with no further action + if ( NULL == rootProcess ) { + return pimpl->mScene; + } + + // In debug builds: run basic flag validation + ASSIMP_LOG_INFO( "Entering customized post processing pipeline" ); + +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + // The ValidateDS process plays an exceptional role. It isn't contained in the global + // list of post-processing steps, so we need to call it manually. + if ( requestValidation ) + { + ValidateDSProcess ds; + ds.ExecuteOnScene( this ); + if ( !pimpl->mScene ) { + return NULL; + } + } +#endif // no validation +#ifdef ASSIMP_BUILD_DEBUG + if ( pimpl->bExtraVerbose ) + { +#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS + ASSIMP_LOG_ERROR( "Verbose Import is not available due to build settings" ); +#endif // no validation + } +#else + if ( pimpl->bExtraVerbose ) { + ASSIMP_LOG_WARN( "Not a debug build, ignoring extra verbose setting" ); + } +#endif // ! DEBUG + + std::unique_ptr<Profiler> profiler( GetPropertyInteger( AI_CONFIG_GLOB_MEASURE_TIME, 0 ) ? new Profiler() : NULL ); + + if ( profiler ) { + profiler->BeginRegion( "postprocess" ); + } + + rootProcess->ExecuteOnScene( this ); + + if ( profiler ) { + profiler->EndRegion( "postprocess" ); + } + + // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step + if ( pimpl->bExtraVerbose || requestValidation ) { + ASSIMP_LOG_DEBUG( "Verbose Import: revalidating data structures" ); + + ValidateDSProcess ds; + ds.ExecuteOnScene( this ); + if ( !pimpl->mScene ) { + ASSIMP_LOG_ERROR( "Verbose Import: failed to revalidate data structures" ); + } + } + + // clear any data allocated by post-process steps + pimpl->mPPShared->Clean(); + ASSIMP_LOG_INFO( "Leaving customized post processing pipeline" ); + + ASSIMP_END_EXCEPTION_REGION( const aiScene* ); + + return pimpl->mScene; +} + +// ------------------------------------------------------------------------------------------------ +// Helper function to check whether an extension is supported by ASSIMP +bool Importer::IsExtensionSupported(const char* szExtension) const +{ + return nullptr != GetImporter(szExtension); +} + +// ------------------------------------------------------------------------------------------------ +size_t Importer::GetImporterCount() const +{ + return pimpl->mImporter.size(); +} + +// ------------------------------------------------------------------------------------------------ +const aiImporterDesc* Importer::GetImporterInfo(size_t index) const +{ + if (index >= pimpl->mImporter.size()) { + return NULL; + } + return pimpl->mImporter[index]->GetInfo(); +} + + +// ------------------------------------------------------------------------------------------------ +BaseImporter* Importer::GetImporter (size_t index) const +{ + if (index >= pimpl->mImporter.size()) { + return NULL; + } + return pimpl->mImporter[index]; +} + +// ------------------------------------------------------------------------------------------------ +// Find a loader plugin for a given file extension +BaseImporter* Importer::GetImporter (const char* szExtension) const +{ + return GetImporter(GetImporterIndex(szExtension)); +} + +// ------------------------------------------------------------------------------------------------ +// Find a loader plugin for a given file extension +size_t Importer::GetImporterIndex (const char* szExtension) const { + ai_assert(nullptr != szExtension); + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // skip over wildcard and dot characters at string head -- + for ( ; *szExtension == '*' || *szExtension == '.'; ++szExtension ); + + std::string ext(szExtension); + if (ext.empty()) { + return static_cast<size_t>(-1); + } + std::transform( ext.begin(), ext.end(), ext.begin(), ToLower<char> ); + + std::set<std::string> str; + for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { + str.clear(); + + (*i)->GetExtensionList(str); + for (std::set<std::string>::const_iterator it = str.begin(); it != str.end(); ++it) { + if (ext == *it) { + return std::distance(static_cast< std::vector<BaseImporter*>::const_iterator >(pimpl->mImporter.begin()), i); + } + } + } + ASSIMP_END_EXCEPTION_REGION(size_t); + return static_cast<size_t>(-1); +} + +// ------------------------------------------------------------------------------------------------ +// Helper function to build a list of all file extensions supported by ASSIMP +void Importer::GetExtensionList(aiString& szOut) const +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + std::set<std::string> str; + for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { + (*i)->GetExtensionList(str); + } + + // List can be empty + if( !str.empty() ) { + for (std::set<std::string>::const_iterator it = str.begin();; ) { + szOut.Append("*."); + szOut.Append((*it).c_str()); + + if (++it == str.end()) { + break; + } + szOut.Append(";"); + } + } + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyInteger(const char* szName, int iValue) +{ + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) +{ + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyString(const char* szName, const std::string& value) +{ + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Set a configuration property +bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) +{ + bool existing; + ASSIMP_BEGIN_EXCEPTION_REGION(); + existing = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value); + ASSIMP_END_EXCEPTION_REGION(bool); + return existing; +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +int Importer::GetPropertyInteger(const char* szName, + int iErrorReturn /*= 0xffffffff*/) const +{ + return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +ai_real Importer::GetPropertyFloat(const char* szName, + ai_real iErrorReturn /*= 10e10*/) const +{ + return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +const std::string Importer::GetPropertyString(const char* szName, + const std::string& iErrorReturn /*= ""*/) const +{ + return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get a configuration property +const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, + const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const +{ + return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +// Get the memory requirements of a single node +inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) +{ + iScene += sizeof(aiNode); + iScene += sizeof(unsigned int) * pcNode->mNumMeshes; + iScene += sizeof(void*) * pcNode->mNumChildren; + + for (unsigned int i = 0; i < pcNode->mNumChildren;++i) { + AddNodeWeight(iScene,pcNode->mChildren[i]); + } +} + +// ------------------------------------------------------------------------------------------------ +// Get the memory requirements of the scene +void Importer::GetMemoryRequirements(aiMemoryInfo& in) const +{ + in = aiMemoryInfo(); + aiScene* mScene = pimpl->mScene; + + // return if we have no scene loaded + if (!pimpl->mScene) + return; + + + in.total = sizeof(aiScene); + + // add all meshes + for (unsigned int i = 0; i < mScene->mNumMeshes;++i) + { + in.meshes += sizeof(aiMesh); + if (mScene->mMeshes[i]->HasPositions()) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; + } + + if (mScene->mMeshes[i]->HasNormals()) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; + } + + if (mScene->mMeshes[i]->HasTangentsAndBitangents()) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices * 2; + } + + for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) { + if (mScene->mMeshes[i]->HasVertexColors(a)) { + in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices; + } + else break; + } + for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) { + if (mScene->mMeshes[i]->HasTextureCoords(a)) { + in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; + } + else break; + } + if (mScene->mMeshes[i]->HasBones()) { + in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones; + for (unsigned int p = 0; p < mScene->mMeshes[i]->mNumBones;++p) { + in.meshes += sizeof(aiBone); + in.meshes += mScene->mMeshes[i]->mBones[p]->mNumWeights * sizeof(aiVertexWeight); + } + } + in.meshes += (sizeof(aiFace) + 3 * sizeof(unsigned int))*mScene->mMeshes[i]->mNumFaces; + } + in.total += in.meshes; + + // add all embedded textures + for (unsigned int i = 0; i < mScene->mNumTextures;++i) { + const aiTexture* pc = mScene->mTextures[i]; + in.textures += sizeof(aiTexture); + if (pc->mHeight) { + in.textures += 4 * pc->mHeight * pc->mWidth; + } + else in.textures += pc->mWidth; + } + in.total += in.textures; + + // add all animations + for (unsigned int i = 0; i < mScene->mNumAnimations;++i) { + const aiAnimation* pc = mScene->mAnimations[i]; + in.animations += sizeof(aiAnimation); + + // add all bone anims + for (unsigned int a = 0; a < pc->mNumChannels; ++a) { + const aiNodeAnim* pc2 = pc->mChannels[i]; + in.animations += sizeof(aiNodeAnim); + in.animations += pc2->mNumPositionKeys * sizeof(aiVectorKey); + in.animations += pc2->mNumScalingKeys * sizeof(aiVectorKey); + in.animations += pc2->mNumRotationKeys * sizeof(aiQuatKey); + } + } + in.total += in.animations; + + // add all cameras and all lights + in.total += in.cameras = sizeof(aiCamera) * mScene->mNumCameras; + in.total += in.lights = sizeof(aiLight) * mScene->mNumLights; + + // add all nodes + AddNodeWeight(in.nodes,mScene->mRootNode); + in.total += in.nodes; + + // add all materials + for (unsigned int i = 0; i < mScene->mNumMaterials;++i) { + const aiMaterial* pc = mScene->mMaterials[i]; + in.materials += sizeof(aiMaterial); + in.materials += pc->mNumAllocated * sizeof(void*); + + for (unsigned int a = 0; a < pc->mNumProperties;++a) { + in.materials += pc->mProperties[a]->mDataLength; + } + } + in.total += in.materials; +} diff --git a/thirdparty/assimp/code/Importer.h b/thirdparty/assimp/code/Importer.h new file mode 100644 index 0000000000..a439d99c2f --- /dev/null +++ b/thirdparty/assimp/code/Importer.h @@ -0,0 +1,247 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Importer.h mostly internal stuff for use by #Assimp::Importer */ +#pragma once +#ifndef INCLUDED_AI_IMPORTER_H +#define INCLUDED_AI_IMPORTER_H + +#include <map> +#include <vector> +#include <string> +#include <assimp/matrix4x4.h> + +struct aiScene; + +namespace Assimp { + class ProgressHandler; + class IOSystem; + class BaseImporter; + class BaseProcess; + class SharedPostProcessInfo; + + +//! @cond never +// --------------------------------------------------------------------------- +/** @brief Internal PIMPL implementation for Assimp::Importer + * + * Using this idiom here allows us to drop the dependency from + * std::vector and std::map in the public headers. Furthermore we are dropping + * any STL interface problems caused by mismatching STL settings. All + * size calculation are now done by us, not the app heap. */ +class ImporterPimpl { +public: + // Data type to store the key hash + typedef unsigned int KeyType; + + // typedefs for our four configuration maps. + // We don't need more, so there is no need for a generic solution + typedef std::map<KeyType, int> IntPropertyMap; + typedef std::map<KeyType, ai_real> FloatPropertyMap; + typedef std::map<KeyType, std::string> StringPropertyMap; + typedef std::map<KeyType, aiMatrix4x4> MatrixPropertyMap; + + /** IO handler to use for all file accesses. */ + IOSystem* mIOHandler; + bool mIsDefaultHandler; + + /** Progress handler for feedback. */ + ProgressHandler* mProgressHandler; + bool mIsDefaultProgressHandler; + + /** Format-specific importer worker objects - one for each format we can read.*/ + std::vector< BaseImporter* > mImporter; + + /** Post processing steps we can apply at the imported data. */ + std::vector< BaseProcess* > mPostProcessingSteps; + + /** The imported data, if ReadFile() was successful, NULL otherwise. */ + aiScene* mScene; + + /** The error description, if there was one. */ + std::string mErrorString; + + /** List of integer properties */ + IntPropertyMap mIntProperties; + + /** List of floating-point properties */ + FloatPropertyMap mFloatProperties; + + /** List of string properties */ + StringPropertyMap mStringProperties; + + /** List of Matrix properties */ + MatrixPropertyMap mMatrixProperties; + + /** Used for testing - extra verbose mode causes the ValidateDataStructure-Step + * to be executed before and after every single post-process step */ + bool bExtraVerbose; + + /** Used by post-process steps to share data */ + SharedPostProcessInfo* mPPShared; + + /// The default class constructor. + ImporterPimpl() AI_NO_EXCEPT; +}; + +inline +ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT +: mIOHandler( nullptr ) +, mIsDefaultHandler( false ) +, mProgressHandler( nullptr ) +, mIsDefaultProgressHandler( false ) +, mImporter() +, mPostProcessingSteps() +, mScene( nullptr ) +, mErrorString() +, mIntProperties() +, mFloatProperties() +, mStringProperties() +, mMatrixProperties() +, bExtraVerbose( false ) +, mPPShared( nullptr ) { + // empty +} +//! @endcond + + +struct BatchData; + +// --------------------------------------------------------------------------- +/** FOR IMPORTER PLUGINS ONLY: A helper class to the pleasure of importers + * that need to load many external meshes recursively. + * + * The class uses several threads to load these meshes (or at least it + * could, this has not yet been implemented at the moment). + * + * @note The class may not be used by more than one thread*/ +class ASSIMP_API BatchLoader +{ + // friend of Importer + +public: + //! @cond never + // ------------------------------------------------------------------- + /** Wraps a full list of configuration properties for an importer. + * Properties can be set using SetGenericProperty */ + struct PropertyMap + { + ImporterPimpl::IntPropertyMap ints; + ImporterPimpl::FloatPropertyMap floats; + ImporterPimpl::StringPropertyMap strings; + ImporterPimpl::MatrixPropertyMap matrices; + + bool operator == (const PropertyMap& prop) const { + // fixme: really isocpp? gcc complains + return ints == prop.ints && floats == prop.floats && strings == prop.strings && matrices == prop.matrices; + } + + bool empty () const { + return ints.empty() && floats.empty() && strings.empty() && matrices.empty(); + } + }; + //! @endcond + +public: + // ------------------------------------------------------------------- + /** Construct a batch loader from a given IO system to be used + * to access external files + */ + explicit BatchLoader(IOSystem* pIO, bool validate = false ); + + // ------------------------------------------------------------------- + /** The class destructor. + */ + ~BatchLoader(); + + // ------------------------------------------------------------------- + /** Sets the validation step. True for enable validation during postprocess. + * @param enable True for validation. + */ + void setValidation( bool enabled ); + + // ------------------------------------------------------------------- + /** Returns the current validation step. + * @return The current validation step. + */ + bool getValidation() const; + + // ------------------------------------------------------------------- + /** Add a new file to the list of files to be loaded. + * @param file File to be loaded + * @param steps Post-processing steps to be executed on the file + * @param map Optional configuration properties + * @return 'Load request channel' - an unique ID that can later + * be used to access the imported file data. + * @see GetImport */ + unsigned int AddLoadRequest ( + const std::string& file, + unsigned int steps = 0, + const PropertyMap* map = NULL + ); + + // ------------------------------------------------------------------- + /** Get an imported scene. + * This polls the import from the internal request list. + * If an import is requested several times, this function + * can be called several times, too. + * + * @param which LRWC returned by AddLoadRequest(). + * @return NULL if there is no scene with this file name + * in the queue of the scene hasn't been loaded yet. */ + aiScene* GetImport( + unsigned int which + ); + + // ------------------------------------------------------------------- + /** Waits until all scenes have been loaded. This returns + * immediately if no scenes are queued.*/ + void LoadAll(); + +private: + // No need to have that in the public API ... + BatchData *m_data; +}; + +} // Namespace Assimp + +#endif // INCLUDED_AI_IMPORTER_H diff --git a/thirdparty/assimp/code/ImporterRegistry.cpp b/thirdparty/assimp/code/ImporterRegistry.cpp new file mode 100644 index 0000000000..747815fa6f --- /dev/null +++ b/thirdparty/assimp/code/ImporterRegistry.cpp @@ -0,0 +1,371 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file ImporterRegistry.cpp + +Central registry for all importers available. Do not edit this file +directly (unless you are adding new loaders), instead use the +corresponding preprocessor flag to selectively disable formats. +*/ + +#include <vector> +#include <assimp/BaseImporter.h> + +// ------------------------------------------------------------------------------------------------ +// Importers +// (include_new_importers_here) +// ------------------------------------------------------------------------------------------------ +#ifndef ASSIMP_BUILD_NO_X_IMPORTER +# include "XFileImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER +# include "AMFImporter.hpp" +#endif +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER +# include "3DSLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER +# include "MD3Loader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER +# include "MDLLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER +# include "MD2Loader.h" +#endif +#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER +# include "PlyLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER +# include "ASELoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER +# include "ObjFileImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER +# include "HMPLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER +# include "SMDLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER +# include "MDCLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER +# include "MD5Loader.h" +#endif +#ifndef ASSIMP_BUILD_NO_STL_IMPORTER +# include "STLLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER +# include "LWOLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER +# include "DXFLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER +# include "NFFLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER +# include "RawLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER +# include "SIBImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER +# include "OFFLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_AC_IMPORTER +# include "ACLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER +# include "BVHLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER +# include "IRRMeshLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER +# include "IRRLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER +# include "Q3DLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER +# include "B3DImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER +# include "ColladaLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER +# include "TerragenLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER +# include "CSMLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_3D_IMPORTER +# include "UnrealLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER +# include "LWSLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER +# include "OgreImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER +# include "OpenGEXImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER +# include "MS3DLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_COB_IMPORTER +# include "COBLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER +# include "BlenderLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER +# include "Q3BSPFileImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER +# include "NDOLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER +# include "Importer/IFC/IFCLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER +# include "XGLLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER +# include "FBXImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER +# include "AssbinLoader.h" +#endif +#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER +# include "glTFImporter.h" +# include "glTF2Importer.h" +#endif +#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER +# include "C4DImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER +# include "D3MFImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER +# include "X3DImporter.hpp" +#endif +#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER +# include "MMDImporter.h" +#endif +#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER +# include "Importer/StepFile/StepFileImporter.h" +#endif + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +void GetImporterInstanceList(std::vector< BaseImporter* >& out) +{ + // ---------------------------------------------------------------------------- + // Add an instance of each worker class here + // (register_new_importers_here) + // ---------------------------------------------------------------------------- + out.reserve(64); +#if (!defined ASSIMP_BUILD_NO_X_IMPORTER) + out.push_back( new XFileImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OBJ_IMPORTER) + out.push_back( new ObjFileImporter()); +#endif +#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER + out.push_back( new AMFImporter() ); +#endif +#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER) + out.push_back( new Discreet3DSImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER) + out.push_back( new MD3Importer()); +#endif +#if (!defined ASSIMP_BUILD_NO_MD2_IMPORTER) + out.push_back( new MD2Importer()); +#endif +#if (!defined ASSIMP_BUILD_NO_PLY_IMPORTER) + out.push_back( new PLYImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MDL_IMPORTER) + out.push_back( new MDLImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_ASE_IMPORTER) + #if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER) + out.push_back( new ASEImporter()); +# endif +#endif +#if (!defined ASSIMP_BUILD_NO_HMP_IMPORTER) + out.push_back( new HMPImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_SMD_IMPORTER) + out.push_back( new SMDImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MDC_IMPORTER) + out.push_back( new MDCImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_MD5_IMPORTER) + out.push_back( new MD5Importer()); +#endif +#if (!defined ASSIMP_BUILD_NO_STL_IMPORTER) + out.push_back( new STLImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER) + out.push_back( new LWOImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_DXF_IMPORTER) + out.push_back( new DXFImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_NFF_IMPORTER) + out.push_back( new NFFImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_RAW_IMPORTER) + out.push_back( new RAWImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_SIB_IMPORTER) + out.push_back( new SIBImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OFF_IMPORTER) + out.push_back( new OFFImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_AC_IMPORTER) + out.push_back( new AC3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_BVH_IMPORTER) + out.push_back( new BVHLoader()); +#endif +#if (!defined ASSIMP_BUILD_NO_IRRMESH_IMPORTER) + out.push_back( new IRRMeshImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_IRR_IMPORTER) + out.push_back( new IRRImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_Q3D_IMPORTER) + out.push_back( new Q3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_B3D_IMPORTER) + out.push_back( new B3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_COLLADA_IMPORTER) + out.push_back( new ColladaLoader()); +#endif +#if (!defined ASSIMP_BUILD_NO_TERRAGEN_IMPORTER) + out.push_back( new TerragenImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_CSM_IMPORTER) + out.push_back( new CSMImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_3D_IMPORTER) + out.push_back( new UnrealImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_LWS_IMPORTER) + out.push_back( new LWSImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OGRE_IMPORTER) + out.push_back( new Ogre::OgreImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_OPENGEX_IMPORTER ) + out.push_back( new OpenGEX::OpenGEXImporter() ); +#endif +#if (!defined ASSIMP_BUILD_NO_MS3D_IMPORTER) + out.push_back( new MS3DImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_COB_IMPORTER) + out.push_back( new COBImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER) + out.push_back( new BlenderImporter()); +#endif +#if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER) + out.push_back( new Q3BSPFileImporter() ); +#endif +#if (!defined ASSIMP_BUILD_NO_NDO_IMPORTER) + out.push_back( new NDOImporter() ); +#endif +#if (!defined ASSIMP_BUILD_NO_IFC_IMPORTER) + out.push_back( new IFCImporter() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_XGL_IMPORTER ) + out.push_back( new XGLImporter() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_FBX_IMPORTER ) + out.push_back( new FBXImporter() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER ) + out.push_back( new AssbinImporter() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_GLTF_IMPORTER ) + out.push_back( new glTFImporter() ); + out.push_back( new glTF2Importer() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_C4D_IMPORTER ) + out.push_back( new C4DImporter() ); +#endif +#if ( !defined ASSIMP_BUILD_NO_3MF_IMPORTER ) + out.push_back( new D3MFImporter() ); +#endif +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + out.push_back( new X3DImporter() ); +#endif +#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER + out.push_back( new MMDImporter() ); +#endif +#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER + out.push_back(new StepFile::StepFileImporter()); +#endif +} + +/** will delete all registered importers. */ +void DeleteImporterInstanceList(std::vector< BaseImporter* >& deleteList){ + for(size_t i= 0; i<deleteList.size();++i){ + delete deleteList[i]; + deleteList[i]=NULL; + }//for +} + +} // namespace Assimp diff --git a/thirdparty/assimp/code/ImproveCacheLocality.cpp b/thirdparty/assimp/code/ImproveCacheLocality.cpp new file mode 100644 index 0000000000..ace9d95ff8 --- /dev/null +++ b/thirdparty/assimp/code/ImproveCacheLocality.cpp @@ -0,0 +1,386 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to improve the cache locality of a mesh. + * <br> + * The algorithm is roughly basing on this paper: + * http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf + * .. although overdraw rduction isn't implemented yet ... + */ + + + +// internal headers +#include "ImproveCacheLocality.h" +#include "VertexTriangleAdjacency.h" +#include <assimp/StringUtils.h> +#include <assimp/postprocess.h> +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> +#include <stdio.h> +#include <stack> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() { + configCacheDepth = PP_ICL_PTCACHE_SIZE; +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +ImproveCacheLocalityProcess::~ImproveCacheLocalityProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool ImproveCacheLocalityProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_ImproveCacheLocality) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration +void ImproveCacheLocalityProcess::SetupProperties(const Importer* pImp) +{ + // AI_CONFIG_PP_ICL_PTCACHE_SIZE controls the target cache size for the optimizer + configCacheDepth = pImp->GetPropertyInteger(AI_CONFIG_PP_ICL_PTCACHE_SIZE,PP_ICL_PTCACHE_SIZE); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void ImproveCacheLocalityProcess::Execute( aiScene* pScene) +{ + if (!pScene->mNumMeshes) { + ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess skipped; there are no meshes"); + return; + } + + ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess begin"); + + float out = 0.f; + unsigned int numf = 0, numm = 0; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++){ + const float res = ProcessMesh( pScene->mMeshes[a],a); + if (res) { + numf += pScene->mMeshes[a]->mNumFaces; + out += res; + ++numm; + } + } + if (!DefaultLogger::isNullLogger()) { + if (numf > 0) { + ASSIMP_LOG_INFO_F("Cache relevant are ", numm, " meshes (", numf, " faces). Average output ACMR is ", out / numf); + } + ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess finished. "); + } +} + +// ------------------------------------------------------------------------------------------------ +// Improves the cache coherency of a specific mesh +float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum) +{ + // TODO: rewrite this to use std::vector or boost::shared_array + ai_assert(NULL != pMesh); + + // Check whether the input data is valid + // - there must be vertices and faces + // - all faces must be triangulated or we can't operate on them + if (!pMesh->HasFaces() || !pMesh->HasPositions()) + return 0.f; + + if (pMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) { + ASSIMP_LOG_ERROR("This algorithm works on triangle meshes only"); + return 0.f; + } + + if(pMesh->mNumVertices <= configCacheDepth) { + return 0.f; + } + + float fACMR = 3.f; + const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces; + + // Input ACMR is for logging purposes only + if (!DefaultLogger::isNullLogger()) { + + unsigned int* piFIFOStack = new unsigned int[configCacheDepth]; + memset(piFIFOStack,0xff,configCacheDepth*sizeof(unsigned int)); + unsigned int* piCur = piFIFOStack; + const unsigned int* const piCurEnd = piFIFOStack + configCacheDepth; + + // count the number of cache misses + unsigned int iCacheMisses = 0; + for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace) { + + for (unsigned int qq = 0; qq < 3;++qq) { + bool bInCache = false; + + for (unsigned int* pp = piFIFOStack;pp < piCurEnd;++pp) { + if (*pp == pcFace->mIndices[qq]) { + // the vertex is in cache + bInCache = true; + break; + } + } + if (!bInCache) { + ++iCacheMisses; + if (piCurEnd == piCur) { + piCur = piFIFOStack; + } + *piCur++ = pcFace->mIndices[qq]; + } + } + } + delete[] piFIFOStack; + fACMR = (float)iCacheMisses / pMesh->mNumFaces; + if (3.0 == fACMR) { + char szBuff[128]; // should be sufficiently large in every case + + // the JoinIdenticalVertices process has not been executed on this + // mesh, otherwise this value would normally be at least minimally + // smaller than 3.0 ... + ai_snprintf(szBuff,128,"Mesh %u: Not suitable for vcache optimization",meshNum); + ASSIMP_LOG_WARN(szBuff); + return 0.f; + } + } + + // first we need to build a vertex-triangle adjacency list + VertexTriangleAdjacency adj(pMesh->mFaces,pMesh->mNumFaces, pMesh->mNumVertices,true); + + // build a list to store per-vertex caching time stamps + unsigned int* const piCachingStamps = new unsigned int[pMesh->mNumVertices]; + memset(piCachingStamps,0x0,pMesh->mNumVertices*sizeof(unsigned int)); + + // allocate an empty output index buffer. We store the output indices in one large array. + // Since the number of triangles won't change the input faces can be reused. This is how + // we save thousands of redundant mini allocations for aiFace::mIndices + const unsigned int iIdxCnt = pMesh->mNumFaces*3; + unsigned int* const piIBOutput = new unsigned int[iIdxCnt]; + unsigned int* piCSIter = piIBOutput; + + // allocate the flag array to hold the information + // whether a face has already been emitted or not + std::vector<bool> abEmitted(pMesh->mNumFaces,false); + + // dead-end vertex index stack + std::stack<unsigned int, std::vector<unsigned int> > sDeadEndVStack; + + // create a copy of the piNumTriPtr buffer + unsigned int* const piNumTriPtr = adj.mLiveTriangles; + const std::vector<unsigned int> piNumTriPtrNoModify(piNumTriPtr, piNumTriPtr + pMesh->mNumVertices); + + // get the largest number of referenced triangles and allocate the "candidate buffer" + unsigned int iMaxRefTris = 0; { + const unsigned int* piCur = adj.mLiveTriangles; + const unsigned int* const piCurEnd = adj.mLiveTriangles+pMesh->mNumVertices; + for (;piCur != piCurEnd;++piCur) { + iMaxRefTris = std::max(iMaxRefTris,*piCur); + } + } + ai_assert(iMaxRefTris > 0); + unsigned int* piCandidates = new unsigned int[iMaxRefTris*3]; + unsigned int iCacheMisses = 0; + + // ................................................................................... + /** PSEUDOCODE for the algorithm + + A = Build-Adjacency(I) Vertex-triangle adjacency + L = Get-Triangle-Counts(A) Per-vertex live triangle counts + C = Zero(Vertex-Count(I)) Per-vertex caching time stamps + D = Empty-Stack() Dead-end vertex stack + E = False(Triangle-Count(I)) Per triangle emitted flag + O = Empty-Index-Buffer() Empty output buffer + f = 0 Arbitrary starting vertex + s = k+1, i = 1 Time stamp and cursor + while f >= 0 For all valid fanning vertices + N = Empty-Set() 1-ring of next candidates + for each Triangle t in Neighbors(A, f) + if !Emitted(E,t) + for each Vertex v in t + Append(O,v) Output vertex + Push(D,v) Add to dead-end stack + Insert(N,v) Register as candidate + L[v] = L[v]-1 Decrease live triangle count + if s-C[v] > k If not in cache + C[v] = s Set time stamp + s = s+1 Increment time stamp + E[t] = true Flag triangle as emitted + Select next fanning vertex + f = Get-Next-Vertex(I,i,k,N,C,s,L,D) + return O + */ + // ................................................................................... + + int ivdx = 0; + int ics = 1; + int iStampCnt = configCacheDepth+1; + while (ivdx >= 0) { + + unsigned int icnt = piNumTriPtrNoModify[ivdx]; + unsigned int* piList = adj.GetAdjacentTriangles(ivdx); + unsigned int* piCurCandidate = piCandidates; + + // get all triangles in the neighborhood + for (unsigned int tri = 0; tri < icnt;++tri) { + + // if they have not yet been emitted, add them to the output IB + const unsigned int fidx = *piList++; + if (!abEmitted[fidx]) { + + // so iterate through all vertices of the current triangle + const aiFace* pcFace = &pMesh->mFaces[ fidx ]; + unsigned nind = pcFace->mNumIndices; + for (unsigned ind = 0; ind < nind; ind++) { + unsigned dp = pcFace->mIndices[ind]; + + // the current vertex won't have any free triangles after this step + if (ivdx != (int)dp) { + // append the vertex to the dead-end stack + sDeadEndVStack.push(dp); + + // register as candidate for the next step + *piCurCandidate++ = dp; + + // decrease the per-vertex triangle counts + piNumTriPtr[dp]--; + } + + // append the vertex to the output index buffer + *piCSIter++ = dp; + + // if the vertex is not yet in cache, set its cache count + if (iStampCnt-piCachingStamps[dp] > configCacheDepth) { + piCachingStamps[dp] = iStampCnt++; + ++iCacheMisses; + } + } + // flag triangle as emitted + abEmitted[fidx] = true; + } + } + + // the vertex has now no living adjacent triangles anymore + piNumTriPtr[ivdx] = 0; + + // get next fanning vertex + ivdx = -1; + int max_priority = -1; + for (unsigned int* piCur = piCandidates;piCur != piCurCandidate;++piCur) { + const unsigned int dp = *piCur; + + // must have live triangles + if (piNumTriPtr[dp] > 0) { + int priority = 0; + + // will the vertex be in cache, even after fanning occurs? + unsigned int tmp; + if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= configCacheDepth) { + priority = tmp; + } + + // keep best candidate + if (priority > max_priority) { + max_priority = priority; + ivdx = dp; + } + } + } + // did we reach a dead end? + if (-1 == ivdx) { + // need to get a non-local vertex for which we have a good chance that it is still + // in the cache ... + while (!sDeadEndVStack.empty()) { + unsigned int iCachedIdx = sDeadEndVStack.top(); + sDeadEndVStack.pop(); + if (piNumTriPtr[ iCachedIdx ] > 0) { + ivdx = iCachedIdx; + break; + } + } + + if (-1 == ivdx) { + // well, there isn't such a vertex. Simply get the next vertex in input order and + // hope it is not too bad ... + while (ics < (int)pMesh->mNumVertices) { + ++ics; + if (piNumTriPtr[ics] > 0) { + ivdx = ics; + break; + } + } + } + } + } + float fACMR2 = 0.0f; + if (!DefaultLogger::isNullLogger()) { + fACMR2 = (float)iCacheMisses / pMesh->mNumFaces; + + // very intense verbose logging ... prepare for much text if there are many meshes + if ( DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) { + ASSIMP_LOG_DEBUG_F("Mesh %u | ACMR in: ", meshNum, " out: ", fACMR, " | ~", fACMR2, ((fACMR - fACMR2) / fACMR) * 100.f); + } + + fACMR2 *= pMesh->mNumFaces; + } + // sort the output index buffer back to the input array + piCSIter = piIBOutput; + for (aiFace* pcFace = pMesh->mFaces; pcFace != pcEnd;++pcFace) { + unsigned nind = pcFace->mNumIndices; + unsigned * ind = pcFace->mIndices; + if (nind > 0) ind[0] = *piCSIter++; + if (nind > 1) ind[1] = *piCSIter++; + if (nind > 2) ind[2] = *piCSIter++; + } + + // delete temporary storage + delete[] piCachingStamps; + delete[] piIBOutput; + delete[] piCandidates; + + return fACMR2; +} diff --git a/thirdparty/assimp/code/ImproveCacheLocality.h b/thirdparty/assimp/code/ImproveCacheLocality.h new file mode 100644 index 0000000000..1b29ee0d6e --- /dev/null +++ b/thirdparty/assimp/code/ImproveCacheLocality.h @@ -0,0 +1,100 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to reorder faces for + better cache locality*/ +#ifndef AI_IMPROVECACHELOCALITY_H_INC +#define AI_IMPROVECACHELOCALITY_H_INC + +#include "BaseProcess.h" +#include <assimp/types.h> + +struct aiMesh; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The ImproveCacheLocalityProcess reorders all faces for improved vertex + * cache locality. It tries to arrange all faces to fans and to render + * faces which share vertices directly one after the other. + * + * @note This step expects triagulated input data. + */ +class ImproveCacheLocalityProcess : public BaseProcess +{ +public: + + ImproveCacheLocalityProcess(); + ~ImproveCacheLocalityProcess(); + +public: + + // ------------------------------------------------------------------- + // Check whether the pp step is active + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + // Executes the pp step on a given scene + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + // Configures the pp step + void SetupProperties(const Importer* pImp); + +protected: + // ------------------------------------------------------------------- + /** Executes the postprocessing step on the given mesh + * @param pMesh The mesh to process. + * @param meshNum Index of the mesh to process + */ + float ProcessMesh( aiMesh* pMesh, unsigned int meshNum); + +private: + //! Configuration parameter: specifies the size of the cache to + //! optimize the vertex data for. + unsigned int configCacheDepth; +}; + +} // end of namespace Assimp + +#endif // AI_IMPROVECACHELOCALITY_H_INC diff --git a/thirdparty/assimp/code/JoinVerticesProcess.cpp b/thirdparty/assimp/code/JoinVerticesProcess.cpp new file mode 100644 index 0000000000..914ec05b46 --- /dev/null +++ b/thirdparty/assimp/code/JoinVerticesProcess.cpp @@ -0,0 +1,463 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to join identical vertices + * for all imported meshes + */ + + +#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS + +#include "JoinVerticesProcess.h" +#include "ProcessHelper.h" +#include <assimp/Vertex.h> +#include <assimp/TinyFormatter.h> +#include <stdio.h> +#include <unordered_set> + +using namespace Assimp; +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +JoinVerticesProcess::JoinVerticesProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +JoinVerticesProcess::~JoinVerticesProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool JoinVerticesProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_JoinIdenticalVertices) != 0; +} +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void JoinVerticesProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("JoinVerticesProcess begin"); + + // get the total number of vertices BEFORE the step is executed + int iNumOldVertices = 0; + if (!DefaultLogger::isNullLogger()) { + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { + iNumOldVertices += pScene->mMeshes[a]->mNumVertices; + } + } + + // execute the step + int iNumVertices = 0; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + iNumVertices += ProcessMesh( pScene->mMeshes[a],a); + + // if logging is active, print detailed statistics + if (!DefaultLogger::isNullLogger()) { + if (iNumOldVertices == iNumVertices) { + ASSIMP_LOG_DEBUG("JoinVerticesProcess finished "); + } else { + ASSIMP_LOG_INFO_F("JoinVerticesProcess finished | Verts in: ", iNumOldVertices, + " out: ", iNumVertices, " | ~", + ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f ); + } + } + + pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; +} + +namespace { + +bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex) +{ + // A little helper to find locally close vertices faster. + // Try to reuse the lookup table from the last step. + const static float epsilon = 1e-5f; + // Squared because we check against squared length of the vector difference + static const float squareEpsilon = epsilon * epsilon; + + // Square compare is useful for animeshes vertices compare + if ((lhs.position - rhs.position).SquareLength() > squareEpsilon) { + return false; + } + + // We just test the other attributes even if they're not present in the mesh. + // In this case they're initialized to 0 so the comparison succeeds. + // By this method the non-present attributes are effectively ignored in the comparison. + if ((lhs.normal - rhs.normal).SquareLength() > squareEpsilon) { + return false; + } + + if ((lhs.texcoords[0] - rhs.texcoords[0]).SquareLength() > squareEpsilon) { + return false; + } + + if ((lhs.tangent - rhs.tangent).SquareLength() > squareEpsilon) { + return false; + } + + if ((lhs.bitangent - rhs.bitangent).SquareLength() > squareEpsilon) { + return false; + } + + // Usually we won't have vertex colors or multiple UVs, so we can skip from here + // Actually this increases runtime performance slightly, at least if branch + // prediction is on our side. + if (complex) { + for (int i = 0; i < 8; i++) { + if (i > 0 && (lhs.texcoords[i] - rhs.texcoords[i]).SquareLength() > squareEpsilon) { + return false; + } + if (GetColorDifference(lhs.colors[i], rhs.colors[i]) > squareEpsilon) { + return false; + } + } + } + return true; +} + +template<class XMesh> +void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) { + // replace vertex data with the unique data sets + pMesh->mNumVertices = (unsigned int)uniqueVertices.size(); + + // ---------------------------------------------------------------------------- + // NOTE - we're *not* calling Vertex::SortBack() because it would check for + // presence of every single vertex component once PER VERTEX. And our CPU + // dislikes branches, even if they're easily predictable. + // ---------------------------------------------------------------------------- + + // Position, if present (check made for aiAnimMesh) + if (pMesh->mVertices) + { + delete [] pMesh->mVertices; + pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; + for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { + pMesh->mVertices[a] = uniqueVertices[a].position; + } + } + + // Normals, if present + if (pMesh->mNormals) + { + delete [] pMesh->mNormals; + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { + pMesh->mNormals[a] = uniqueVertices[a].normal; + } + } + // Tangents, if present + if (pMesh->mTangents) + { + delete [] pMesh->mTangents; + pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; + for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { + pMesh->mTangents[a] = uniqueVertices[a].tangent; + } + } + // Bitangents as well + if (pMesh->mBitangents) + { + delete [] pMesh->mBitangents; + pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; + for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { + pMesh->mBitangents[a] = uniqueVertices[a].bitangent; + } + } + // Vertex colors + for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) + { + delete [] pMesh->mColors[a]; + pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices]; + for( unsigned int b = 0; b < pMesh->mNumVertices; b++) { + pMesh->mColors[a][b] = uniqueVertices[b].colors[a]; + } + } + // Texture coords + for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) + { + delete [] pMesh->mTextureCoords[a]; + pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices]; + for (unsigned int b = 0; b < pMesh->mNumVertices; b++) { + pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a]; + } + } +} +} // namespace + +// ------------------------------------------------------------------------------------------------ +// Unites identical vertices in the given mesh +int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) +{ + static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8"); + static_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8"); + + // Return early if we don't have any positions + if (!pMesh->HasPositions() || !pMesh->HasFaces()) { + return 0; + } + + // We should care only about used vertices, not all of them + // (this can happen due to original file vertices buffer being used by + // multiple meshes) + std::unordered_set<unsigned int> usedVertexIndices; + usedVertexIndices.reserve(pMesh->mNumVertices); + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + aiFace& face = pMesh->mFaces[a]; + for( unsigned int b = 0; b < face.mNumIndices; b++) { + usedVertexIndices.insert(face.mIndices[b]); + } + } + + // We'll never have more vertices afterwards. + std::vector<Vertex> uniqueVertices; + uniqueVertices.reserve( pMesh->mNumVertices); + + // For each vertex the index of the vertex it was replaced by. + // Since the maximal number of vertices is 2^31-1, the most significand bit can be used to mark + // whether a new vertex was created for the index (true) or if it was replaced by an existing + // unique vertex (false). This saves an additional std::vector<bool> and greatly enhances + // branching performance. + static_assert(AI_MAX_VERTICES == 0x7fffffff, "AI_MAX_VERTICES == 0x7fffffff"); + std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff); + + // float posEpsilonSqr; + SpatialSort* vertexFinder = NULL; + SpatialSort _vertexFinder; + + typedef std::pair<SpatialSort,float> SpatPair; + if (shared) { + std::vector<SpatPair >* avf; + shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); + if (avf) { + SpatPair& blubb = (*avf)[meshIndex]; + vertexFinder = &blubb.first; + // posEpsilonSqr = blubb.second; + } + } + if (!vertexFinder) { + // bad, need to compute it. + _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); + vertexFinder = &_vertexFinder; + // posEpsilonSqr = ComputePositionEpsilon(pMesh); + } + + // Again, better waste some bytes than a realloc ... + std::vector<unsigned int> verticesFound; + verticesFound.reserve(10); + + // Run an optimized code path if we don't have multiple UVs or vertex colors. + // This should yield false in more than 99% of all imports ... + const bool complex = ( pMesh->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1); + const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0; + + // We'll never have more vertices afterwards. + std::vector<std::vector<Vertex>> uniqueAnimatedVertices; + if (hasAnimMeshes) { + uniqueAnimatedVertices.resize(pMesh->mNumAnimMeshes); + for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { + uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices); + } + } + + // Now check each vertex if it brings something new to the table + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { + if (usedVertexIndices.find(a) == usedVertexIndices.end()) { + continue; + } + + // collect the vertex data + Vertex v(pMesh,a); + + // collect all vertices that are close enough to the given position + vertexFinder->FindIdenticalPositions( v.position, verticesFound); + unsigned int matchIndex = 0xffffffff; + + // check all unique vertices close to the position if this vertex is already present among them + for( unsigned int b = 0; b < verticesFound.size(); b++) { + const unsigned int vidx = verticesFound[b]; + const unsigned int uidx = replaceIndex[ vidx]; + if( uidx & 0x80000000) + continue; + + const Vertex& uv = uniqueVertices[ uidx]; + + if (!areVerticesEqual(v, uv, complex)) { + continue; + } + + if (hasAnimMeshes) { + // If given vertex is animated, then it has to be preserver 1 to 1 (base mesh and animated mesh require same topology) + // NOTE: not doing this totaly breaks anim meshes as they don't have their own faces (they use pMesh->mFaces) + bool breaksAnimMesh = false; + for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { + const Vertex& animatedUV = uniqueAnimatedVertices[animMeshIndex][ uidx]; + Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a); + if (!areVerticesEqual(aniMeshVertex, animatedUV, complex)) { + breaksAnimMesh = true; + break; + } + } + if (breaksAnimMesh) { + continue; + } + } + + // we're still here -> this vertex perfectly matches our given vertex + matchIndex = uidx; + break; + } + + // found a replacement vertex among the uniques? + if( matchIndex != 0xffffffff) + { + // store where to found the matching unique vertex + replaceIndex[a] = matchIndex | 0x80000000; + } + else + { + // no unique vertex matches it up to now -> so add it + replaceIndex[a] = (unsigned int)uniqueVertices.size(); + uniqueVertices.push_back( v); + if (hasAnimMeshes) { + for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { + Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a); + uniqueAnimatedVertices[animMeshIndex].push_back(aniMeshVertex); + } + } + } + } + + if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) { + ASSIMP_LOG_DEBUG_F( + "Mesh ",meshIndex, + " (", + (pMesh->mName.length ? pMesh->mName.data : "unnamed"), + ") | Verts in: ",pMesh->mNumVertices, + " out: ", + uniqueVertices.size(), + " | ~", + ((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f, + "%" + ); + } + + updateXMeshVertices(pMesh, uniqueVertices); + if (hasAnimMeshes) { + for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { + updateXMeshVertices(pMesh->mAnimMeshes[animMeshIndex], uniqueAnimatedVertices[animMeshIndex]); + } + } + + // adjust the indices in all faces + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + aiFace& face = pMesh->mFaces[a]; + for( unsigned int b = 0; b < face.mNumIndices; b++) { + face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000; + } + } + + // adjust bone vertex weights. + for( int a = 0; a < (int)pMesh->mNumBones; a++) { + aiBone* bone = pMesh->mBones[a]; + std::vector<aiVertexWeight> newWeights; + newWeights.reserve( bone->mNumWeights); + + if ( NULL != bone->mWeights ) { + for ( unsigned int b = 0; b < bone->mNumWeights; b++ ) { + const aiVertexWeight& ow = bone->mWeights[ b ]; + // if the vertex is a unique one, translate it + if ( !( replaceIndex[ ow.mVertexId ] & 0x80000000 ) ) { + aiVertexWeight nw; + nw.mVertexId = replaceIndex[ ow.mVertexId ]; + nw.mWeight = ow.mWeight; + newWeights.push_back( nw ); + } + } + } else { + ASSIMP_LOG_ERROR( "X-Export: aiBone shall contain weights, but pointer to them is NULL." ); + } + + if (newWeights.size() > 0) { + // kill the old and replace them with the translated weights + delete [] bone->mWeights; + bone->mNumWeights = (unsigned int)newWeights.size(); + + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight)); + } + else { + + /* NOTE: + * + * In the algorithm above we're assuming that there are no vertices + * with a different bone weight setup at the same position. That wouldn't + * make sense, but it is not absolutely impossible. SkeletonMeshBuilder + * for example generates such input data if two skeleton points + * share the same position. Again this doesn't make sense but is + * reality for some model formats (MD5 for example uses these special + * nodes as attachment tags for its weapons). + * + * Then it is possible that a bone has no weights anymore .... as a quick + * workaround, we're just removing these bones. If they're animated, + * model geometry might be modified but at least there's no risk of a crash. + */ + delete bone; + --pMesh->mNumBones; + for (unsigned int n = a; n < pMesh->mNumBones; ++n) { + pMesh->mBones[n] = pMesh->mBones[n+1]; + } + + --a; + ASSIMP_LOG_WARN("Removing bone -> no weights remaining"); + } + } + return pMesh->mNumVertices; +} + +#endif // !! ASSIMP_BUILD_NO_JOINVERTICES_PROCESS diff --git a/thirdparty/assimp/code/JoinVerticesProcess.h b/thirdparty/assimp/code/JoinVerticesProcess.h new file mode 100644 index 0000000000..66fa362de2 --- /dev/null +++ b/thirdparty/assimp/code/JoinVerticesProcess.h @@ -0,0 +1,99 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to join identical vertices + on all imported meshes.*/ +#ifndef AI_JOINVERTICESPROCESS_H_INC +#define AI_JOINVERTICESPROCESS_H_INC + +#include "BaseProcess.h" +#include <assimp/types.h> + +struct aiMesh; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The JoinVerticesProcess unites identical vertices in all imported meshes. + * By default the importer returns meshes where each face addressed its own + * set of vertices even if that means that identical vertices are stored multiple + * times. The JoinVerticesProcess finds these identical vertices and + * erases all but one of the copies. This usually reduces the number of vertices + * in a mesh by a serious amount and is the standard form to render a mesh. + */ +class ASSIMP_API JoinVerticesProcess : public BaseProcess +{ +public: + JoinVerticesProcess(); + ~JoinVerticesProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + +public: + // ------------------------------------------------------------------- + /** Unites identical vertices in the given mesh. + * @param pMesh The mesh to process. + * @param meshIndex Index of the mesh to process + */ + int ProcessMesh( aiMesh* pMesh, unsigned int meshIndex); + +private: +}; + +} // end of namespace Assimp + +#endif // AI_CALCTANGENTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/LimitBoneWeightsProcess.cpp b/thirdparty/assimp/code/LimitBoneWeightsProcess.cpp new file mode 100644 index 0000000000..d560f19287 --- /dev/null +++ b/thirdparty/assimp/code/LimitBoneWeightsProcess.cpp @@ -0,0 +1,201 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** Implementation of the LimitBoneWeightsProcess post processing step */ + + +#include "LimitBoneWeightsProcess.h" +#include <assimp/StringUtils.h> +#include <assimp/postprocess.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/scene.h> +#include <stdio.h> + +using namespace Assimp; + + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +LimitBoneWeightsProcess::LimitBoneWeightsProcess() +{ + mMaxWeights = AI_LMW_MAX_WEIGHTS; +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +LimitBoneWeightsProcess::~LimitBoneWeightsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_LimitBoneWeights) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void LimitBoneWeightsProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin"); + for (unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { + ProcessMesh(pScene->mMeshes[a]); + } + + ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess end"); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) +{ + // get the current value of the property + this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS); +} + +// ------------------------------------------------------------------------------------------------ +// Unites identical vertices in the given mesh +void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh) +{ + if( !pMesh->HasBones()) + return; + + // collect all bone weights per vertex + typedef std::vector< std::vector< Weight > > WeightsPerVertex; + WeightsPerVertex vertexWeights( pMesh->mNumVertices); + + // collect all weights per vertex + for( unsigned int a = 0; a < pMesh->mNumBones; a++) + { + const aiBone* bone = pMesh->mBones[a]; + for( unsigned int b = 0; b < bone->mNumWeights; b++) + { + const aiVertexWeight& w = bone->mWeights[b]; + vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight)); + } + } + + unsigned int removed = 0, old_bones = pMesh->mNumBones; + + // now cut the weight count if it exceeds the maximum + bool bChanged = false; + for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit) + { + if( vit->size() <= mMaxWeights) + continue; + + bChanged = true; + + // more than the defined maximum -> first sort by weight in descending order. That's + // why we defined the < operator in such a weird way. + std::sort( vit->begin(), vit->end()); + + // now kill everything beyond the maximum count + unsigned int m = static_cast<unsigned int>(vit->size()); + vit->erase( vit->begin() + mMaxWeights, vit->end()); + removed += static_cast<unsigned int>(m-vit->size()); + + // and renormalize the weights + float sum = 0.0f; + for( std::vector<Weight>::const_iterator it = vit->begin(); it != vit->end(); ++it ) { + sum += it->mWeight; + } + if( 0.0f != sum ) { + const float invSum = 1.0f / sum; + for( std::vector<Weight>::iterator it = vit->begin(); it != vit->end(); ++it ) { + it->mWeight *= invSum; + } + } + } + + if (bChanged) { + // rebuild the vertex weight array for all bones + typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone; + WeightsPerBone boneWeights( pMesh->mNumBones); + for( unsigned int a = 0; a < vertexWeights.size(); a++) + { + const std::vector<Weight>& vw = vertexWeights[a]; + for( std::vector<Weight>::const_iterator it = vw.begin(); it != vw.end(); ++it) + boneWeights[it->mBone].push_back( aiVertexWeight( a, it->mWeight)); + } + + // and finally copy the vertex weight list over to the mesh's bones + std::vector<bool> abNoNeed(pMesh->mNumBones,false); + bChanged = false; + + for( unsigned int a = 0; a < pMesh->mNumBones; a++) + { + const std::vector<aiVertexWeight>& bw = boneWeights[a]; + aiBone* bone = pMesh->mBones[a]; + + if ( bw.empty() ) + { + abNoNeed[a] = bChanged = true; + continue; + } + + // copy the weight list. should always be less weights than before, so we don't need a new allocation + ai_assert( bw.size() <= bone->mNumWeights); + bone->mNumWeights = static_cast<unsigned int>( bw.size() ); + ::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight)); + } + + if (bChanged) { + // the number of new bones is smaller than before, so we can reuse the old array + aiBone** ppcCur = pMesh->mBones;aiBone** ppcSrc = ppcCur; + + for (std::vector<bool>::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) { + if (*iter) { + delete *ppcSrc; + --pMesh->mNumBones; + } + else *ppcCur++ = *ppcSrc; + ++ppcSrc; + } + } + + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones ); + } + } +} diff --git a/thirdparty/assimp/code/LimitBoneWeightsProcess.h b/thirdparty/assimp/code/LimitBoneWeightsProcess.h new file mode 100644 index 0000000000..3602fd8edf --- /dev/null +++ b/thirdparty/assimp/code/LimitBoneWeightsProcess.h @@ -0,0 +1,148 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** Defines a post processing step to limit the number of bones affecting a single vertex. */ +#ifndef AI_LIMITBONEWEIGHTSPROCESS_H_INC +#define AI_LIMITBONEWEIGHTSPROCESS_H_INC + +#include "BaseProcess.h" + +struct aiMesh; + +class LimitBoneWeightsTest; + +namespace Assimp +{ + +// NOTE: If you change these limits, don't forget to change the +// corresponding values in all Assimp ports + +// ********************************************************** +// Java: ConfigProperty.java, +// ConfigProperty.DEFAULT_BONE_WEIGHT_LIMIT +// ********************************************************** + +#if (!defined AI_LMW_MAX_WEIGHTS) +# define AI_LMW_MAX_WEIGHTS 0x4 +#endif // !! AI_LMW_MAX_WEIGHTS + +// --------------------------------------------------------------------------- +/** This post processing step limits the number of bones affecting a vertex +* to a certain maximum value. If a vertex is affected by more than that number +* of bones, the bone weight with the least influence on this vertex are removed. +* The other weights on this bone are then renormalized to assure the sum weight +* to be 1. +*/ +class ASSIMP_API LimitBoneWeightsProcess : public BaseProcess +{ +public: + + LimitBoneWeightsProcess(); + ~LimitBoneWeightsProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. + * A bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + +public: + + // ------------------------------------------------------------------- + /** Limits the bone weight count for all vertices in the given mesh. + * @param pMesh The mesh to process. + */ + void ProcessMesh( aiMesh* pMesh); + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + +public: + + // ------------------------------------------------------------------- + /** Describes a bone weight on a vertex */ + struct Weight + { + unsigned int mBone; ///< Index of the bone + float mWeight; ///< Weight of that bone on this vertex + Weight() AI_NO_EXCEPT + : mBone(0) + , mWeight(0.0f) + { } + + Weight( unsigned int pBone, float pWeight) + { + mBone = pBone; + mWeight = pWeight; + } + + /** Comparison operator to sort bone weights by descending weight */ + bool operator < (const Weight& pWeight) const + { + return mWeight > pWeight.mWeight; + } + }; + +public: + /** Maximum number of bones influencing any single vertex. */ + unsigned int mMaxWeights; +}; + +} // end of namespace Assimp + +#endif // AI_LIMITBONEWEIGHTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/MMDCpp14.h b/thirdparty/assimp/code/MMDCpp14.h new file mode 100644 index 0000000000..638b0bfd2f --- /dev/null +++ b/thirdparty/assimp/code/MMDCpp14.h @@ -0,0 +1,83 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#ifndef MMD_CPP14_H +#define MMD_CPP14_H + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <utility> + +namespace mmd { + template<class T> struct _Unique_if { + typedef std::unique_ptr<T> _Single_object; + }; + + template<class T> struct _Unique_if<T[]> { + typedef std::unique_ptr<T[]> _Unknown_bound; + }; + + template<class T, size_t N> struct _Unique_if<T[N]> { + typedef void _Known_bound; + }; + + template<class T, class... Args> + typename _Unique_if<T>::_Single_object + make_unique(Args&&... args) { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); + } + + template<class T> + typename _Unique_if<T>::_Unknown_bound + make_unique(size_t n) { + typedef typename std::remove_extent<T>::type U; + return std::unique_ptr<T>(new U[n]()); + } + + template<class T, class... Args> + typename _Unique_if<T>::_Known_bound + make_unique(Args&&...) = delete; +} + +#endif diff --git a/thirdparty/assimp/code/MMDImporter.cpp b/thirdparty/assimp/code/MMDImporter.cpp new file mode 100644 index 0000000000..84b9e35a6b --- /dev/null +++ b/thirdparty/assimp/code/MMDImporter.cpp @@ -0,0 +1,370 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2016, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER + +#include "MMDImporter.h" +#include "MMDPmdParser.h" +#include "MMDPmxParser.h" +#include "MMDVmdParser.h" +#include "ConvertToLHProcess.h" +#include <assimp/DefaultIOSystem.h> +#include <assimp/Importer.hpp> +#include <assimp/ai_assert.h> +#include <assimp/scene.h> +#include <fstream> +#include <iomanip> +#include <memory> + +static const aiImporterDesc desc = {"MMD Importer", + "", + "", + "surfaces supported?", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "pmx"}; + +namespace Assimp { + +using namespace std; + +// ------------------------------------------------------------------------------------------------ +// Default constructor +MMDImporter::MMDImporter() +: m_Buffer() +, m_strAbsPath("") { + DefaultIOSystem io; + m_strAbsPath = io.getOsSeparator(); +} + +// ------------------------------------------------------------------------------------------------ +// Destructor. +MMDImporter::~MMDImporter() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns true, if file is an pmx file. +bool MMDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, + bool checkSig) const { + if (!checkSig) // Check File Extension + { + return SimpleExtensionCheck(pFile, "pmx"); + } else // Check file Header + { + static const char *pTokens[] = {"PMX "}; + return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 1); + } +} + +// ------------------------------------------------------------------------------------------------ +const aiImporterDesc *MMDImporter::GetInfo() const { return &desc; } + +// ------------------------------------------------------------------------------------------------ +// MMD import implementation +void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene, + IOSystem * /*pIOHandler*/) { + // Read file by istream + std::filebuf fb; + if (!fb.open(file, std::ios::in | std::ios::binary)) { + throw DeadlyImportError("Failed to open file " + file + "."); + } + + std::istream fileStream(&fb); + + // Get the file-size and validate it, throwing an exception when fails + fileStream.seekg(0, fileStream.end); + size_t fileSize = static_cast<size_t>(fileStream.tellg()); + fileStream.seekg(0, fileStream.beg); + + if (fileSize < sizeof(pmx::PmxModel)) { + throw DeadlyImportError(file + " is too small."); + } + + pmx::PmxModel model; + model.Read(&fileStream); + + CreateDataFromImport(&model, pScene); +} + +// ------------------------------------------------------------------------------------------------ +void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel, + aiScene *pScene) { + if (pModel == NULL) { + return; + } + + aiNode *pNode = new aiNode; + if (!pModel->model_name.empty()) { + pNode->mName.Set(pModel->model_name); + } + + pScene->mRootNode = pNode; + + pNode = new aiNode; + pScene->mRootNode->addChildren(1, &pNode); + pNode->mName.Set(string(pModel->model_name) + string("_mesh")); + + // split mesh by materials + pNode->mNumMeshes = pModel->material_count; + pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; + for (unsigned int index = 0; index < pNode->mNumMeshes; index++) { + pNode->mMeshes[index] = index; + } + + pScene->mNumMeshes = pModel->material_count; + pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; + for (unsigned int i = 0, indexStart = 0; i < pScene->mNumMeshes; i++) { + const int indexCount = pModel->materials[i].index_count; + + pScene->mMeshes[i] = CreateMesh(pModel, indexStart, indexCount); + pScene->mMeshes[i]->mName = pModel->materials[i].material_name; + pScene->mMeshes[i]->mMaterialIndex = i; + indexStart += indexCount; + } + + // create node hierarchy for bone position + std::unique_ptr<aiNode *[]> ppNode(new aiNode *[pModel->bone_count]); + for (auto i = 0; i < pModel->bone_count; i++) { + ppNode[i] = new aiNode(pModel->bones[i].bone_name); + } + + for (auto i = 0; i < pModel->bone_count; i++) { + const pmx::PmxBone &bone = pModel->bones[i]; + + if (bone.parent_index < 0) { + pScene->mRootNode->addChildren(1, ppNode.get() + i); + } else { + ppNode[bone.parent_index]->addChildren(1, ppNode.get() + i); + + aiVector3D v3 = aiVector3D( + bone.position[0] - pModel->bones[bone.parent_index].position[0], + bone.position[1] - pModel->bones[bone.parent_index].position[1], + bone.position[2] - pModel->bones[bone.parent_index].position[2]); + aiMatrix4x4::Translation(v3, ppNode[i]->mTransformation); + } + } + + // create materials + pScene->mNumMaterials = pModel->material_count; + pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; + for (unsigned int i = 0; i < pScene->mNumMaterials; i++) { + pScene->mMaterials[i] = CreateMaterial(&pModel->materials[i], pModel); + } + + // Convert everything to OpenGL space + MakeLeftHandedProcess convertProcess; + convertProcess.Execute(pScene); + + FlipUVsProcess uvFlipper; + uvFlipper.Execute(pScene); + + FlipWindingOrderProcess windingFlipper; + windingFlipper.Execute(pScene); +} + +// ------------------------------------------------------------------------------------------------ +aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel, + const int indexStart, const int indexCount) { + aiMesh *pMesh = new aiMesh; + + pMesh->mNumVertices = indexCount; + + pMesh->mNumFaces = indexCount / 3; + pMesh->mFaces = new aiFace[pMesh->mNumFaces]; + + const int numIndices = 3; // triangular face + for (unsigned int index = 0; index < pMesh->mNumFaces; index++) { + pMesh->mFaces[index].mNumIndices = numIndices; + unsigned int *indices = new unsigned int[numIndices]; + indices[0] = numIndices * index; + indices[1] = numIndices * index + 1; + indices[2] = numIndices * index + 2; + pMesh->mFaces[index].mIndices = indices; + } + + pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices]; + pMesh->mNumUVComponents[0] = 2; + + // additional UVs + for (int i = 1; i <= pModel->setting.uv; i++) { + pMesh->mTextureCoords[i] = new aiVector3D[pMesh->mNumVertices]; + pMesh->mNumUVComponents[i] = 4; + } + + map<int, vector<aiVertexWeight>> bone_vertex_map; + + // fill in contents and create bones + for (int index = 0; index < indexCount; index++) { + const pmx::PmxVertex *v = + &pModel->vertices[pModel->indices[indexStart + index]]; + const float *position = v->position; + pMesh->mVertices[index].Set(position[0], position[1], position[2]); + const float *normal = v->normal; + + pMesh->mNormals[index].Set(normal[0], normal[1], normal[2]); + pMesh->mTextureCoords[0][index].x = v->uv[0]; + pMesh->mTextureCoords[0][index].y = v->uv[1]; + + for (int i = 1; i <= pModel->setting.uv; i++) { + // TODO: wrong here? use quaternion transform? + pMesh->mTextureCoords[i][index].x = v->uva[i][0]; + pMesh->mTextureCoords[i][index].y = v->uva[i][1]; + } + + // handle bone map + const auto vsBDEF1_ptr = + dynamic_cast<pmx::PmxVertexSkinningBDEF1 *>(v->skinning.get()); + const auto vsBDEF2_ptr = + dynamic_cast<pmx::PmxVertexSkinningBDEF2 *>(v->skinning.get()); + const auto vsBDEF4_ptr = + dynamic_cast<pmx::PmxVertexSkinningBDEF4 *>(v->skinning.get()); + const auto vsSDEF_ptr = + dynamic_cast<pmx::PmxVertexSkinningSDEF *>(v->skinning.get()); + switch (v->skinning_type) { + case pmx::PmxVertexSkinningType::BDEF1: + bone_vertex_map[vsBDEF1_ptr->bone_index].push_back( + aiVertexWeight(index, 1.0)); + break; + case pmx::PmxVertexSkinningType::BDEF2: + bone_vertex_map[vsBDEF2_ptr->bone_index1].push_back( + aiVertexWeight(index, vsBDEF2_ptr->bone_weight)); + bone_vertex_map[vsBDEF2_ptr->bone_index2].push_back( + aiVertexWeight(index, 1.0f - vsBDEF2_ptr->bone_weight)); + break; + case pmx::PmxVertexSkinningType::BDEF4: + bone_vertex_map[vsBDEF4_ptr->bone_index1].push_back( + aiVertexWeight(index, vsBDEF4_ptr->bone_weight1)); + bone_vertex_map[vsBDEF4_ptr->bone_index2].push_back( + aiVertexWeight(index, vsBDEF4_ptr->bone_weight2)); + bone_vertex_map[vsBDEF4_ptr->bone_index3].push_back( + aiVertexWeight(index, vsBDEF4_ptr->bone_weight3)); + bone_vertex_map[vsBDEF4_ptr->bone_index4].push_back( + aiVertexWeight(index, vsBDEF4_ptr->bone_weight4)); + break; + case pmx::PmxVertexSkinningType::SDEF: // TODO: how to use sdef_c, sdef_r0, + // sdef_r1? + bone_vertex_map[vsSDEF_ptr->bone_index1].push_back( + aiVertexWeight(index, vsSDEF_ptr->bone_weight)); + bone_vertex_map[vsSDEF_ptr->bone_index2].push_back( + aiVertexWeight(index, 1.0f - vsSDEF_ptr->bone_weight)); + break; + case pmx::PmxVertexSkinningType::QDEF: + const auto vsQDEF_ptr = + dynamic_cast<pmx::PmxVertexSkinningQDEF *>(v->skinning.get()); + bone_vertex_map[vsQDEF_ptr->bone_index1].push_back( + aiVertexWeight(index, vsQDEF_ptr->bone_weight1)); + bone_vertex_map[vsQDEF_ptr->bone_index2].push_back( + aiVertexWeight(index, vsQDEF_ptr->bone_weight2)); + bone_vertex_map[vsQDEF_ptr->bone_index3].push_back( + aiVertexWeight(index, vsQDEF_ptr->bone_weight3)); + bone_vertex_map[vsQDEF_ptr->bone_index4].push_back( + aiVertexWeight(index, vsQDEF_ptr->bone_weight4)); + break; + } + } + + // make all bones for each mesh + // assign bone weights to skinned bones (otherwise just initialize) + auto bone_ptr_ptr = new aiBone *[pModel->bone_count]; + pMesh->mNumBones = pModel->bone_count; + pMesh->mBones = bone_ptr_ptr; + for (auto ii = 0; ii < pModel->bone_count; ++ii) { + auto pBone = new aiBone; + const auto &pmxBone = pModel->bones[ii]; + pBone->mName = pmxBone.bone_name; + aiVector3D pos(pmxBone.position[0], pmxBone.position[1], pmxBone.position[2]); + aiMatrix4x4::Translation(-pos, pBone->mOffsetMatrix); + auto it = bone_vertex_map.find(ii); + if (it != bone_vertex_map.end()) { + pBone->mNumWeights = static_cast<unsigned int>(it->second.size()); + pBone->mWeights = new aiVertexWeight[pBone->mNumWeights]; + for (unsigned int j = 0; j < pBone->mNumWeights; j++) { + pBone->mWeights[j] = it->second[j]; + } + } + bone_ptr_ptr[ii] = pBone; + } + + return pMesh; +} + +// ------------------------------------------------------------------------------------------------ +aiMaterial *MMDImporter::CreateMaterial(const pmx::PmxMaterial *pMat, + const pmx::PmxModel *pModel) { + aiMaterial *mat = new aiMaterial(); + aiString name(pMat->material_english_name); + mat->AddProperty(&name, AI_MATKEY_NAME); + + aiColor3D diffuse(pMat->diffuse[0], pMat->diffuse[1], pMat->diffuse[2]); + mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + aiColor3D specular(pMat->specular[0], pMat->specular[1], pMat->specular[2]); + mat->AddProperty(&specular, 1, AI_MATKEY_COLOR_SPECULAR); + aiColor3D ambient(pMat->ambient[0], pMat->ambient[1], pMat->ambient[2]); + mat->AddProperty(&ambient, 1, AI_MATKEY_COLOR_AMBIENT); + + float opacity = pMat->diffuse[3]; + mat->AddProperty(&opacity, 1, AI_MATKEY_OPACITY); + float shininess = pMat->specularlity; + mat->AddProperty(&shininess, 1, AI_MATKEY_SHININESS_STRENGTH); + + if(pMat->diffuse_texture_index >= 0) { + aiString texture_path(pModel->textures[pMat->diffuse_texture_index]); + mat->AddProperty(&texture_path, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); + } + + int mapping_uvwsrc = 0; + mat->AddProperty(&mapping_uvwsrc, 1, + AI_MATKEY_UVWSRC(aiTextureType_DIFFUSE, 0)); + + return mat; +} + +// ------------------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_MMD_IMPORTER diff --git a/thirdparty/assimp/code/MMDImporter.h b/thirdparty/assimp/code/MMDImporter.h new file mode 100644 index 0000000000..4ee94eeb00 --- /dev/null +++ b/thirdparty/assimp/code/MMDImporter.h @@ -0,0 +1,96 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2016, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef MMD_FILE_IMPORTER_H_INC +#define MMD_FILE_IMPORTER_H_INC + +#include <assimp/BaseImporter.h> +#include "MMDPmxParser.h" +#include <assimp/material.h> +#include <vector> + +struct aiMesh; + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +/// \class MMDImporter +/// \brief Imports MMD a pmx/pmd/vmd file +// ------------------------------------------------------------------------------------------------ +class MMDImporter : public BaseImporter { +public: + /// \brief Default constructor + MMDImporter(); + + /// \brief Destructor + ~MMDImporter(); + +public: + /// \brief Returns whether the class can handle the format of the given file. + /// \remark See BaseImporter::CanRead() for details. + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; + +private: + //! \brief Appends the supported extension. + const aiImporterDesc* GetInfo () const; + + //! \brief File import implementation. + void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + //! \brief Create the data from imported content. + void CreateDataFromImport(const pmx::PmxModel* pModel, aiScene* pScene); + + //! \brief Create the mesh + aiMesh* CreateMesh(const pmx::PmxModel* pModel, const int indexStart, const int indexCount); + + //! \brief Create the material + aiMaterial* CreateMaterial(const pmx::PmxMaterial* pMat, const pmx::PmxModel* pModel); + +private: + //! Data buffer + std::vector<char> m_Buffer; + //! Absolute pathname of model in file system + std::string m_strAbsPath; +}; + +// ------------------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif
\ No newline at end of file diff --git a/thirdparty/assimp/code/MMDPmdParser.h b/thirdparty/assimp/code/MMDPmdParser.h new file mode 100644 index 0000000000..d2f2224aa1 --- /dev/null +++ b/thirdparty/assimp/code/MMDPmdParser.h @@ -0,0 +1,597 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include <vector> +#include <string> +#include <memory> +#include <iostream> +#include <fstream> +#include "MMDCpp14.h" + +namespace pmd +{ + class PmdHeader + { + public: + std::string name; + std::string name_english; + std::string comment; + std::string comment_english; + + bool Read(std::ifstream* stream) + { + char buffer[256]; + stream->read(buffer, 20); + name = std::string(buffer); + stream->read(buffer, 256); + comment = std::string(buffer); + return true; + } + + bool ReadExtension(std::ifstream* stream) + { + char buffer[256]; + stream->read(buffer, 20); + name_english = std::string(buffer); + stream->read(buffer, 256); + comment_english = std::string(buffer); + return true; + } + }; + + class PmdVertex + { + public: + float position[3]; + + float normal[3]; + + float uv[2]; + + uint16_t bone_index[2]; + + uint8_t bone_weight; + + bool edge_invisible; + + bool Read(std::ifstream* stream) + { + stream->read((char*) position, sizeof(float) * 3); + stream->read((char*) normal, sizeof(float) * 3); + stream->read((char*) uv, sizeof(float) * 2); + stream->read((char*) bone_index, sizeof(uint16_t) * 2); + stream->read((char*) &bone_weight, sizeof(uint8_t)); + stream->read((char*) &edge_invisible, sizeof(uint8_t)); + return true; + } + }; + + class PmdMaterial + { + public: + float diffuse[4]; + float power; + float specular[3]; + float ambient[3]; + uint8_t toon_index; + uint8_t edge_flag; + uint32_t index_count; + std::string texture_filename; + std::string sphere_filename; + + bool Read(std::ifstream* stream) + { + char buffer[20]; + stream->read((char*) &diffuse, sizeof(float) * 4); + stream->read((char*) &power, sizeof(float)); + stream->read((char*) &specular, sizeof(float) * 3); + stream->read((char*) &ambient, sizeof(float) * 3); + stream->read((char*) &toon_index, sizeof(uint8_t)); + stream->read((char*) &edge_flag, sizeof(uint8_t)); + stream->read((char*) &index_count, sizeof(uint32_t)); + stream->read((char*) &buffer, sizeof(char) * 20); + char* pstar = strchr(buffer, '*'); + if (NULL == pstar) + { + texture_filename = std::string(buffer); + sphere_filename.clear(); + } + else { + *pstar = 0; + texture_filename = std::string(buffer); + sphere_filename = std::string(pstar+1); + } + return true; + } + }; + + enum class BoneType : uint8_t + { + Rotation, + RotationAndMove, + IkEffector, + Unknown, + IkEffectable, + RotationEffectable, + IkTarget, + Invisible, + Twist, + RotationMovement + }; + + class PmdBone + { + public: + std::string name; + std::string name_english; + uint16_t parent_bone_index; + uint16_t tail_pos_bone_index; + BoneType bone_type; + uint16_t ik_parent_bone_index; + float bone_head_pos[3]; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name = std::string(buffer); + stream->read((char*) &parent_bone_index, sizeof(uint16_t)); + stream->read((char*) &tail_pos_bone_index, sizeof(uint16_t)); + stream->read((char*) &bone_type, sizeof(uint8_t)); + stream->read((char*) &ik_parent_bone_index, sizeof(uint16_t)); + stream->read((char*) &bone_head_pos, sizeof(float) * 3); + } + + void ReadExpantion(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name_english = std::string(buffer); + } + }; + + class PmdIk + { + public: + uint16_t ik_bone_index; + uint16_t target_bone_index; + uint16_t interations; + float angle_limit; + std::vector<uint16_t> ik_child_bone_index; + + void Read(std::istream *stream) + { + stream->read((char *) &ik_bone_index, sizeof(uint16_t)); + stream->read((char *) &target_bone_index, sizeof(uint16_t)); + uint8_t ik_chain_length; + stream->read((char*) &ik_chain_length, sizeof(uint8_t)); + stream->read((char *) &interations, sizeof(uint16_t)); + stream->read((char *) &angle_limit, sizeof(float)); + ik_child_bone_index.resize(ik_chain_length); + for (int i = 0; i < ik_chain_length; i++) + { + stream->read((char *) &ik_child_bone_index[i], sizeof(uint16_t)); + } + } + }; + + class PmdFaceVertex + { + public: + int vertex_index; + float position[3]; + + void Read(std::istream *stream) + { + stream->read((char *) &vertex_index, sizeof(int)); + stream->read((char *) position, sizeof(float) * 3); + } + }; + + enum class FaceCategory : uint8_t + { + Base, + Eyebrow, + Eye, + Mouth, + Other + }; + + class PmdFace + { + public: + std::string name; + FaceCategory type; + std::vector<PmdFaceVertex> vertices; + std::string name_english; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name = std::string(buffer); + int vertex_count; + stream->read((char*) &vertex_count, sizeof(int)); + stream->read((char*) &type, sizeof(uint8_t)); + vertices.resize(vertex_count); + for (int i = 0; i < vertex_count; i++) + { + vertices[i].Read(stream); + } + } + + void ReadExpantion(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name_english = std::string(buffer); + } + }; + + class PmdBoneDispName + { + public: + std::string bone_disp_name; + std::string bone_disp_name_english; + + void Read(std::istream *stream) + { + char buffer[50]; + stream->read(buffer, 50); + bone_disp_name = std::string(buffer); + bone_disp_name_english.clear(); + } + void ReadExpantion(std::istream *stream) + { + char buffer[50]; + stream->read(buffer, 50); + bone_disp_name_english = std::string(buffer); + } + }; + + class PmdBoneDisp + { + public: + uint16_t bone_index; + uint8_t bone_disp_index; + + void Read(std::istream *stream) + { + stream->read((char*) &bone_index, sizeof(uint16_t)); + stream->read((char*) &bone_disp_index, sizeof(uint8_t)); + } + }; + + enum class RigidBodyShape : uint8_t + { + Sphere = 0, + Box = 1, + Cpusel = 2 + }; + + enum class RigidBodyType : uint8_t + { + BoneConnected = 0, + Physics = 1, + ConnectedPhysics = 2 + }; + + class PmdRigidBody + { + public: + std::string name; + uint16_t related_bone_index; + uint8_t group_index; + uint16_t mask; + RigidBodyShape shape; + float size[3]; + float position[3]; + float orientation[3]; + float weight; + float linear_damping; + float anglar_damping; + float restitution; + float friction; + RigidBodyType rigid_type; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, sizeof(char) * 20); + name = (std::string(buffer)); + stream->read((char*) &related_bone_index, sizeof(uint16_t)); + stream->read((char*) &group_index, sizeof(uint8_t)); + stream->read((char*) &mask, sizeof(uint16_t)); + stream->read((char*) &shape, sizeof(uint8_t)); + stream->read((char*) size, sizeof(float) * 3); + stream->read((char*) position, sizeof(float) * 3); + stream->read((char*) orientation, sizeof(float) * 3); + stream->read((char*) &weight, sizeof(float)); + stream->read((char*) &linear_damping, sizeof(float)); + stream->read((char*) &anglar_damping, sizeof(float)); + stream->read((char*) &restitution, sizeof(float)); + stream->read((char*) &friction, sizeof(float)); + stream->read((char*) &rigid_type, sizeof(char)); + } + }; + + class PmdConstraint + { + public: + std::string name; + uint32_t rigid_body_index_a; + uint32_t rigid_body_index_b; + float position[3]; + float orientation[3]; + float linear_lower_limit[3]; + float linear_upper_limit[3]; + float angular_lower_limit[3]; + float angular_upper_limit[3]; + float linear_stiffness[3]; + float angular_stiffness[3]; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read(buffer, 20); + name = std::string(buffer); + stream->read((char *) &rigid_body_index_a, sizeof(uint32_t)); + stream->read((char *) &rigid_body_index_b, sizeof(uint32_t)); + stream->read((char *) position, sizeof(float) * 3); + stream->read((char *) orientation, sizeof(float) * 3); + stream->read((char *) linear_lower_limit, sizeof(float) * 3); + stream->read((char *) linear_upper_limit, sizeof(float) * 3); + stream->read((char *) angular_lower_limit, sizeof(float) * 3); + stream->read((char *) angular_upper_limit, sizeof(float) * 3); + stream->read((char *) linear_stiffness, sizeof(float) * 3); + stream->read((char *) angular_stiffness, sizeof(float) * 3); + } + }; + + class PmdModel + { + public: + float version; + PmdHeader header; + std::vector<PmdVertex> vertices; + std::vector<uint16_t> indices; + std::vector<PmdMaterial> materials; + std::vector<PmdBone> bones; + std::vector<PmdIk> iks; + std::vector<PmdFace> faces; + std::vector<uint16_t> faces_indices; + std::vector<PmdBoneDispName> bone_disp_name; + std::vector<PmdBoneDisp> bone_disp; + std::vector<std::string> toon_filenames; + std::vector<PmdRigidBody> rigid_bodies; + std::vector<PmdConstraint> constraints; + + static std::unique_ptr<PmdModel> LoadFromFile(const char *filename) + { + std::ifstream stream(filename, std::ios::binary); + if (stream.fail()) + { + std::cerr << "could not open \"" << filename << "\"" << std::endl; + return nullptr; + } + auto result = LoadFromStream(&stream); + stream.close(); + return result; + } + + static std::unique_ptr<PmdModel> LoadFromStream(std::ifstream *stream) + { + auto result = mmd::make_unique<PmdModel>(); + char buffer[100]; + + // magic + char magic[3]; + stream->read(magic, 3); + if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd') + { + std::cerr << "invalid file" << std::endl; + return nullptr; + } + + // version + stream->read((char*) &(result->version), sizeof(float)); + if (result ->version != 1.0f) + { + std::cerr << "invalid version" << std::endl; + return nullptr; + } + + // header + result->header.Read(stream); + + // vertices + uint32_t vertex_num; + stream->read((char*) &vertex_num, sizeof(uint32_t)); + result->vertices.resize(vertex_num); + for (uint32_t i = 0; i < vertex_num; i++) + { + result->vertices[i].Read(stream); + } + + // indices + uint32_t index_num; + stream->read((char*) &index_num, sizeof(uint32_t)); + result->indices.resize(index_num); + for (uint32_t i = 0; i < index_num; i++) + { + stream->read((char*) &result->indices[i], sizeof(uint16_t)); + } + + // materials + uint32_t material_num; + stream->read((char*) &material_num, sizeof(uint32_t)); + result->materials.resize(material_num); + for (uint32_t i = 0; i < material_num; i++) + { + result->materials[i].Read(stream); + } + + // bones + uint16_t bone_num; + stream->read((char*) &bone_num, sizeof(uint16_t)); + result->bones.resize(bone_num); + for (uint32_t i = 0; i < bone_num; i++) + { + result->bones[i].Read(stream); + } + + // iks + uint16_t ik_num; + stream->read((char*) &ik_num, sizeof(uint16_t)); + result->iks.resize(ik_num); + for (uint32_t i = 0; i < ik_num; i++) + { + result->iks[i].Read(stream); + } + + // faces + uint16_t face_num; + stream->read((char*) &face_num, sizeof(uint16_t)); + result->faces.resize(face_num); + for (uint32_t i = 0; i < face_num; i++) + { + result->faces[i].Read(stream); + } + + // face frames + uint8_t face_frame_num; + stream->read((char*) &face_frame_num, sizeof(uint8_t)); + result->faces_indices.resize(face_frame_num); + for (uint32_t i = 0; i < face_frame_num; i++) + { + stream->read((char*) &result->faces_indices[i], sizeof(uint16_t)); + } + + // bone names + uint8_t bone_disp_num; + stream->read((char*) &bone_disp_num, sizeof(uint8_t)); + result->bone_disp_name.resize(bone_disp_num); + for (uint32_t i = 0; i < bone_disp_num; i++) + { + result->bone_disp_name[i].Read(stream); + } + + // bone frame + uint32_t bone_frame_num; + stream->read((char*) &bone_frame_num, sizeof(uint32_t)); + result->bone_disp.resize(bone_frame_num); + for (uint32_t i = 0; i < bone_frame_num; i++) + { + result->bone_disp[i].Read(stream); + } + + // english name + bool english; + stream->read((char*) &english, sizeof(char)); + if (english) + { + result->header.ReadExtension(stream); + for (uint32_t i = 0; i < bone_num; i++) + { + result->bones[i].ReadExpantion(stream); + } + for (uint32_t i = 0; i < face_num; i++) + { + if (result->faces[i].type == pmd::FaceCategory::Base) + { + continue; + } + result->faces[i].ReadExpantion(stream); + } + for (uint32_t i = 0; i < result->bone_disp_name.size(); i++) + { + result->bone_disp_name[i].ReadExpantion(stream); + } + } + + // toon textures + if (stream->peek() == std::ios::traits_type::eof()) + { + result->toon_filenames.clear(); + } + else { + result->toon_filenames.resize(10); + for (uint32_t i = 0; i < 10; i++) + { + stream->read(buffer, 100); + result->toon_filenames[i] = std::string(buffer); + } + } + + // physics + if (stream->peek() == std::ios::traits_type::eof()) + { + result->rigid_bodies.clear(); + result->constraints.clear(); + } + else { + uint32_t rigid_body_num; + stream->read((char*) &rigid_body_num, sizeof(uint32_t)); + result->rigid_bodies.resize(rigid_body_num); + for (uint32_t i = 0; i < rigid_body_num; i++) + { + result->rigid_bodies[i].Read(stream); + } + uint32_t constraint_num; + stream->read((char*) &constraint_num, sizeof(uint32_t)); + result->constraints.resize(constraint_num); + for (uint32_t i = 0; i < constraint_num; i++) + { + result->constraints[i].Read(stream); + } + } + + if (stream->peek() != std::ios::traits_type::eof()) + { + std::cerr << "there is unknown data" << std::endl; + } + + return result; + } + }; +} diff --git a/thirdparty/assimp/code/MMDPmxParser.cpp b/thirdparty/assimp/code/MMDPmxParser.cpp new file mode 100644 index 0000000000..7425ceac22 --- /dev/null +++ b/thirdparty/assimp/code/MMDPmxParser.cpp @@ -0,0 +1,604 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#include <utility> +#include "MMDPmxParser.h" +#include <assimp/StringUtils.h> +#include "../contrib/utf8cpp/source/utf8.h" +#include <assimp/Exceptional.h> + +namespace pmx +{ + int ReadIndex(std::istream *stream, int size) + { + switch (size) + { + case 1: + uint8_t tmp8; + stream->read((char*) &tmp8, sizeof(uint8_t)); + if (255 == tmp8) + { + return -1; + } + else { + return (int) tmp8; + } + case 2: + uint16_t tmp16; + stream->read((char*) &tmp16, sizeof(uint16_t)); + if (65535 == tmp16) + { + return -1; + } + else { + return (int) tmp16; + } + case 4: + int tmp32; + stream->read((char*) &tmp32, sizeof(int)); + return tmp32; + default: + return -1; + } + } + + std::string ReadString(std::istream *stream, uint8_t encoding) + { + int size; + stream->read((char*) &size, sizeof(int)); + std::vector<char> buffer; + if (size == 0) + { + return std::string(""); + } + buffer.reserve(size); + stream->read((char*) buffer.data(), size); + if (encoding == 0) + { + // UTF16 to UTF8 + const uint16_t* sourceStart = (uint16_t*)buffer.data(); + const unsigned int targetSize = size * 3; // enough to encode + char *targetStart = new char[targetSize]; + std::memset(targetStart, 0, targetSize * sizeof(char)); + + utf8::utf16to8( sourceStart, sourceStart + size/2, targetStart ); + + std::string result(targetStart); + delete [] targetStart; + return result; + } + else + { + // the name is already UTF8 + return std::string((const char*)buffer.data(), size); + } + } + + void PmxSetting::Read(std::istream *stream) + { + uint8_t count; + stream->read((char*) &count, sizeof(uint8_t)); + if (count < 8) + { + throw DeadlyImportError("MMD: invalid size"); + } + stream->read((char*) &encoding, sizeof(uint8_t)); + stream->read((char*) &uv, sizeof(uint8_t)); + stream->read((char*) &vertex_index_size, sizeof(uint8_t)); + stream->read((char*) &texture_index_size, sizeof(uint8_t)); + stream->read((char*) &material_index_size, sizeof(uint8_t)); + stream->read((char*) &bone_index_size, sizeof(uint8_t)); + stream->read((char*) &morph_index_size, sizeof(uint8_t)); + stream->read((char*) &rigidbody_index_size, sizeof(uint8_t)); + uint8_t temp; + for (int i = 8; i < count; i++) + { + stream->read((char*)&temp, sizeof(uint8_t)); + } + } + + void PmxVertexSkinningBDEF1::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index = ReadIndex(stream, setting->bone_index_size); + } + + void PmxVertexSkinningBDEF2::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight, sizeof(float)); + } + + void PmxVertexSkinningBDEF4::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + this->bone_index3 = ReadIndex(stream, setting->bone_index_size); + this->bone_index4 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight1, sizeof(float)); + stream->read((char*) &this->bone_weight2, sizeof(float)); + stream->read((char*) &this->bone_weight3, sizeof(float)); + stream->read((char*) &this->bone_weight4, sizeof(float)); + } + + void PmxVertexSkinningSDEF::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight, sizeof(float)); + stream->read((char*) this->sdef_c, sizeof(float) * 3); + stream->read((char*) this->sdef_r0, sizeof(float) * 3); + stream->read((char*) this->sdef_r1, sizeof(float) * 3); + } + + void PmxVertexSkinningQDEF::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index1 = ReadIndex(stream, setting->bone_index_size); + this->bone_index2 = ReadIndex(stream, setting->bone_index_size); + this->bone_index3 = ReadIndex(stream, setting->bone_index_size); + this->bone_index4 = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->bone_weight1, sizeof(float)); + stream->read((char*) &this->bone_weight2, sizeof(float)); + stream->read((char*) &this->bone_weight3, sizeof(float)); + stream->read((char*) &this->bone_weight4, sizeof(float)); + } + + void PmxVertex::Read(std::istream *stream, PmxSetting *setting) + { + stream->read((char*) this->position, sizeof(float) * 3); + stream->read((char*) this->normal, sizeof(float) * 3); + stream->read((char*) this->uv, sizeof(float) * 2); + for (int i = 0; i < setting->uv; ++i) + { + stream->read((char*) this->uva[i], sizeof(float) * 4); + } + stream->read((char*) &this->skinning_type, sizeof(PmxVertexSkinningType)); + switch (this->skinning_type) + { + case PmxVertexSkinningType::BDEF1: + this->skinning = mmd::make_unique<PmxVertexSkinningBDEF1>(); + break; + case PmxVertexSkinningType::BDEF2: + this->skinning = mmd::make_unique<PmxVertexSkinningBDEF2>(); + break; + case PmxVertexSkinningType::BDEF4: + this->skinning = mmd::make_unique<PmxVertexSkinningBDEF4>(); + break; + case PmxVertexSkinningType::SDEF: + this->skinning = mmd::make_unique<PmxVertexSkinningSDEF>(); + break; + case PmxVertexSkinningType::QDEF: + this->skinning = mmd::make_unique<PmxVertexSkinningQDEF>(); + break; + default: + throw "invalid skinning type"; + } + this->skinning->Read(stream, setting); + stream->read((char*) &this->edge, sizeof(float)); + } + + void PmxMaterial::Read(std::istream *stream, PmxSetting *setting) + { + this->material_name = ReadString(stream, setting->encoding); + this->material_english_name = ReadString(stream, setting->encoding); + stream->read((char*) this->diffuse, sizeof(float) * 4); + stream->read((char*) this->specular, sizeof(float) * 3); + stream->read((char*) &this->specularlity, sizeof(float)); + stream->read((char*) this->ambient, sizeof(float) * 3); + stream->read((char*) &this->flag, sizeof(uint8_t)); + stream->read((char*) this->edge_color, sizeof(float) * 4); + stream->read((char*) &this->edge_size, sizeof(float)); + this->diffuse_texture_index = ReadIndex(stream, setting->texture_index_size); + this->sphere_texture_index = ReadIndex(stream, setting->texture_index_size); + stream->read((char*) &this->sphere_op_mode, sizeof(uint8_t)); + stream->read((char*) &this->common_toon_flag, sizeof(uint8_t)); + if (this->common_toon_flag) + { + stream->read((char*) &this->toon_texture_index, sizeof(uint8_t)); + } + else { + this->toon_texture_index = ReadIndex(stream, setting->texture_index_size); + } + this->memo = ReadString(stream, setting->encoding); + stream->read((char*) &this->index_count, sizeof(int)); + } + + void PmxIkLink::Read(std::istream *stream, PmxSetting *setting) + { + this->link_target = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->angle_lock, sizeof(uint8_t)); + if (angle_lock == 1) + { + stream->read((char*) this->max_radian, sizeof(float) * 3); + stream->read((char*) this->min_radian, sizeof(float) * 3); + } + } + + void PmxBone::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_name = ReadString(stream, setting->encoding); + this->bone_english_name = ReadString(stream, setting->encoding); + stream->read((char*) this->position, sizeof(float) * 3); + this->parent_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->level, sizeof(int)); + stream->read((char*) &this->bone_flag, sizeof(uint16_t)); + if (this->bone_flag & 0x0001) { + this->target_index = ReadIndex(stream, setting->bone_index_size); + } + else { + stream->read((char*)this->offset, sizeof(float) * 3); + } + if (this->bone_flag & (0x0100 | 0x0200)) { + this->grant_parent_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->grant_weight, sizeof(float)); + } + if (this->bone_flag & 0x0400) { + stream->read((char*)this->lock_axis_orientation, sizeof(float) * 3); + } + if (this->bone_flag & 0x0800) { + stream->read((char*)this->local_axis_x_orientation, sizeof(float) * 3); + stream->read((char*)this->local_axis_y_orientation, sizeof(float) * 3); + } + if (this->bone_flag & 0x2000) { + stream->read((char*) &this->key, sizeof(int)); + } + if (this->bone_flag & 0x0020) { + this->ik_target_bone_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &ik_loop, sizeof(int)); + stream->read((char*) &ik_loop_angle_limit, sizeof(float)); + stream->read((char*) &ik_link_count, sizeof(int)); + this->ik_links = mmd::make_unique<PmxIkLink []>(ik_link_count); + for (int i = 0; i < ik_link_count; i++) { + ik_links[i].Read(stream, setting); + } + } + } + + void PmxMorphVertexOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->vertex_index = ReadIndex(stream, setting->vertex_index_size); + stream->read((char*)this->position_offset, sizeof(float) * 3); + } + + void PmxMorphUVOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->vertex_index = ReadIndex(stream, setting->vertex_index_size); + stream->read((char*)this->uv_offset, sizeof(float) * 4); + } + + void PmxMorphBoneOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->bone_index = ReadIndex(stream, setting->bone_index_size); + stream->read((char*)this->translation, sizeof(float) * 3); + stream->read((char*)this->rotation, sizeof(float) * 4); + } + + void PmxMorphMaterialOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->material_index = ReadIndex(stream, setting->material_index_size); + stream->read((char*) &this->offset_operation, sizeof(uint8_t)); + stream->read((char*)this->diffuse, sizeof(float) * 4); + stream->read((char*)this->specular, sizeof(float) * 3); + stream->read((char*) &this->specularity, sizeof(float)); + stream->read((char*)this->ambient, sizeof(float) * 3); + stream->read((char*)this->edge_color, sizeof(float) * 4); + stream->read((char*) &this->edge_size, sizeof(float)); + stream->read((char*)this->texture_argb, sizeof(float) * 4); + stream->read((char*)this->sphere_texture_argb, sizeof(float) * 4); + stream->read((char*)this->toon_texture_argb, sizeof(float) * 4); + } + + void PmxMorphGroupOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->morph_index = ReadIndex(stream, setting->morph_index_size); + stream->read((char*) &this->morph_weight, sizeof(float)); + } + + void PmxMorphFlipOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->morph_index = ReadIndex(stream, setting->morph_index_size); + stream->read((char*) &this->morph_value, sizeof(float)); + } + + void PmxMorphImplusOffset::Read(std::istream *stream, PmxSetting *setting) + { + this->rigid_body_index = ReadIndex(stream, setting->rigidbody_index_size); + stream->read((char*) &this->is_local, sizeof(uint8_t)); + stream->read((char*)this->velocity, sizeof(float) * 3); + stream->read((char*)this->angular_torque, sizeof(float) * 3); + } + + void PmxMorph::Read(std::istream *stream, PmxSetting *setting) + { + this->morph_name = ReadString(stream, setting->encoding); + this->morph_english_name = ReadString(stream, setting->encoding); + stream->read((char*) &category, sizeof(MorphCategory)); + stream->read((char*) &morph_type, sizeof(MorphType)); + stream->read((char*) &this->offset_count, sizeof(int)); + switch (this->morph_type) + { + case MorphType::Group: + group_offsets = mmd::make_unique<PmxMorphGroupOffset []>(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + group_offsets[i].Read(stream, setting); + } + break; + case MorphType::Vertex: + vertex_offsets = mmd::make_unique<PmxMorphVertexOffset []>(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + vertex_offsets[i].Read(stream, setting); + } + break; + case MorphType::Bone: + bone_offsets = mmd::make_unique<PmxMorphBoneOffset []>(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + bone_offsets[i].Read(stream, setting); + } + break; + case MorphType::Matrial: + material_offsets = mmd::make_unique<PmxMorphMaterialOffset []>(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + material_offsets[i].Read(stream, setting); + } + break; + case MorphType::UV: + case MorphType::AdditionalUV1: + case MorphType::AdditionalUV2: + case MorphType::AdditionalUV3: + case MorphType::AdditionalUV4: + uv_offsets = mmd::make_unique<PmxMorphUVOffset []>(this->offset_count); + for (int i = 0; i < offset_count; i++) + { + uv_offsets[i].Read(stream, setting); + } + break; + default: + throw DeadlyImportError("MMD: unknown morth type"); + } + } + + void PmxFrameElement::Read(std::istream *stream, PmxSetting *setting) + { + stream->read((char*) &this->element_target, sizeof(uint8_t)); + if (this->element_target == 0x00) + { + this->index = ReadIndex(stream, setting->bone_index_size); + } + else { + this->index = ReadIndex(stream, setting->morph_index_size); + } + } + + void PmxFrame::Read(std::istream *stream, PmxSetting *setting) + { + this->frame_name = ReadString(stream, setting->encoding); + this->frame_english_name = ReadString(stream, setting->encoding); + stream->read((char*) &this->frame_flag, sizeof(uint8_t)); + stream->read((char*) &this->element_count, sizeof(int)); + this->elements = mmd::make_unique<PmxFrameElement []>(this->element_count); + for (int i = 0; i < this->element_count; i++) + { + this->elements[i].Read(stream, setting); + } + } + + void PmxRigidBody::Read(std::istream *stream, PmxSetting *setting) + { + this->girid_body_name = ReadString(stream, setting->encoding); + this->girid_body_english_name = ReadString(stream, setting->encoding); + this->target_bone = ReadIndex(stream, setting->bone_index_size); + stream->read((char*) &this->group, sizeof(uint8_t)); + stream->read((char*) &this->mask, sizeof(uint16_t)); + stream->read((char*) &this->shape, sizeof(uint8_t)); + stream->read((char*) this->size, sizeof(float) * 3); + stream->read((char*) this->position, sizeof(float) * 3); + stream->read((char*) this->orientation, sizeof(float) * 3); + stream->read((char*) &this->mass, sizeof(float)); + stream->read((char*) &this->move_attenuation, sizeof(float)); + stream->read((char*) &this->rotation_attenuation, sizeof(float)); + stream->read((char*) &this->repulsion, sizeof(float)); + stream->read((char*) &this->friction, sizeof(float)); + stream->read((char*) &this->physics_calc_type, sizeof(uint8_t)); + } + + void PmxJointParam::Read(std::istream *stream, PmxSetting *setting) + { + this->rigid_body1 = ReadIndex(stream, setting->rigidbody_index_size); + this->rigid_body2 = ReadIndex(stream, setting->rigidbody_index_size); + stream->read((char*) this->position, sizeof(float) * 3); + stream->read((char*) this->orientaiton, sizeof(float) * 3); + stream->read((char*) this->move_limitation_min, sizeof(float) * 3); + stream->read((char*) this->move_limitation_max, sizeof(float) * 3); + stream->read((char*) this->rotation_limitation_min, sizeof(float) * 3); + stream->read((char*) this->rotation_limitation_max, sizeof(float) * 3); + stream->read((char*) this->spring_move_coefficient, sizeof(float) * 3); + stream->read((char*) this->spring_rotation_coefficient, sizeof(float) * 3); + } + + void PmxJoint::Read(std::istream *stream, PmxSetting *setting) + { + this->joint_name = ReadString(stream, setting->encoding); + this->joint_english_name = ReadString(stream, setting->encoding); + stream->read((char*) &this->joint_type, sizeof(uint8_t)); + this->param.Read(stream, setting); + } + + void PmxAncherRigidBody::Read(std::istream *stream, PmxSetting *setting) + { + this->related_rigid_body = ReadIndex(stream, setting->rigidbody_index_size); + this->related_vertex = ReadIndex(stream, setting->vertex_index_size); + stream->read((char*) &this->is_near, sizeof(uint8_t)); + } + + void PmxSoftBody::Read(std::istream * /*stream*/, PmxSetting * /*setting*/) + { + std::cerr << "Not Implemented Exception" << std::endl; + throw DeadlyImportError("MMD: Not Implemented Exception"); + } + + void PmxModel::Init() + { + this->version = 0.0f; + this->model_name.clear(); + this->model_english_name.clear(); + this->model_comment.clear(); + this->model_english_comment.clear(); + this->vertex_count = 0; + this->vertices = nullptr; + this->index_count = 0; + this->indices = nullptr; + this->texture_count = 0; + this->textures = nullptr; + this->material_count = 0; + this->materials = nullptr; + this->bone_count = 0; + this->bones = nullptr; + this->morph_count = 0; + this->morphs = nullptr; + this->frame_count = 0; + this->frames = nullptr; + this->rigid_body_count = 0; + this->rigid_bodies = nullptr; + this->joint_count = 0; + this->joints = nullptr; + this->soft_body_count = 0; + this->soft_bodies = nullptr; + } + + void PmxModel::Read(std::istream *stream) + { + char magic[4]; + stream->read((char*) magic, sizeof(char) * 4); + if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20) + { + std::cerr << "invalid magic number." << std::endl; + throw DeadlyImportError("MMD: invalid magic number."); + } + stream->read((char*) &version, sizeof(float)); + if (version != 2.0f && version != 2.1f) + { + std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl; + throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but " + to_string(version)); + } + this->setting.Read(stream); + + this->model_name = ReadString(stream, setting.encoding); + this->model_english_name = ReadString(stream, setting.encoding); + this->model_comment = ReadString(stream, setting.encoding); + this->model_english_comment = ReadString(stream, setting.encoding); + + // read vertices + stream->read((char*) &vertex_count, sizeof(int)); + this->vertices = mmd::make_unique<PmxVertex []>(vertex_count); + for (int i = 0; i < vertex_count; i++) + { + vertices[i].Read(stream, &setting); + } + + // read indices + stream->read((char*) &index_count, sizeof(int)); + this->indices = mmd::make_unique<int []>(index_count); + for (int i = 0; i < index_count; i++) + { + this->indices[i] = ReadIndex(stream, setting.vertex_index_size); + } + + // read texture names + stream->read((char*) &texture_count, sizeof(int)); + this->textures = mmd::make_unique<std::string []>(texture_count); + for (int i = 0; i < texture_count; i++) + { + this->textures[i] = ReadString(stream, setting.encoding); + } + + // read materials + stream->read((char*) &material_count, sizeof(int)); + this->materials = mmd::make_unique<PmxMaterial []>(material_count); + for (int i = 0; i < material_count; i++) + { + this->materials[i].Read(stream, &setting); + } + + // read bones + stream->read((char*) &this->bone_count, sizeof(int)); + this->bones = mmd::make_unique<PmxBone []>(this->bone_count); + for (int i = 0; i < this->bone_count; i++) + { + this->bones[i].Read(stream, &setting); + } + + // read morphs + stream->read((char*) &this->morph_count, sizeof(int)); + this->morphs = mmd::make_unique<PmxMorph []>(this->morph_count); + for (int i = 0; i < this->morph_count; i++) + { + this->morphs[i].Read(stream, &setting); + } + + // read display frames + stream->read((char*) &this->frame_count, sizeof(int)); + this->frames = mmd::make_unique<PmxFrame []>(this->frame_count); + for (int i = 0; i < this->frame_count; i++) + { + this->frames[i].Read(stream, &setting); + } + + // read rigid bodies + stream->read((char*) &this->rigid_body_count, sizeof(int)); + this->rigid_bodies = mmd::make_unique<PmxRigidBody []>(this->rigid_body_count); + for (int i = 0; i < this->rigid_body_count; i++) + { + this->rigid_bodies[i].Read(stream, &setting); + } + + // read joints + stream->read((char*) &this->joint_count, sizeof(int)); + this->joints = mmd::make_unique<PmxJoint []>(this->joint_count); + for (int i = 0; i < this->joint_count; i++) + { + this->joints[i].Read(stream, &setting); + } + } +} diff --git a/thirdparty/assimp/code/MMDPmxParser.h b/thirdparty/assimp/code/MMDPmxParser.h new file mode 100644 index 0000000000..cf523a1298 --- /dev/null +++ b/thirdparty/assimp/code/MMDPmxParser.h @@ -0,0 +1,782 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include <vector> +#include <string> +#include <iostream> +#include <fstream> +#include <memory> +#include "MMDCpp14.h" + +namespace pmx +{ + class PmxSetting + { + public: + PmxSetting() + : encoding(0) + , uv(0) + , vertex_index_size(0) + , texture_index_size(0) + , material_index_size(0) + , bone_index_size(0) + , morph_index_size(0) + , rigidbody_index_size(0) + {} + + uint8_t encoding; + uint8_t uv; + uint8_t vertex_index_size; + uint8_t texture_index_size; + uint8_t material_index_size; + uint8_t bone_index_size; + uint8_t morph_index_size; + uint8_t rigidbody_index_size; + void Read(std::istream *stream); + }; + + enum class PmxVertexSkinningType : uint8_t + { + BDEF1 = 0, + BDEF2 = 1, + BDEF4 = 2, + SDEF = 3, + QDEF = 4, + }; + + class PmxVertexSkinning + { + public: + virtual void Read(std::istream *stream, PmxSetting *setting) = 0; + virtual ~PmxVertexSkinning() {} + }; + + class PmxVertexSkinningBDEF1 : public PmxVertexSkinning + { + public: + PmxVertexSkinningBDEF1() + : bone_index(0) + {} + + int bone_index; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningBDEF2 : public PmxVertexSkinning + { + public: + PmxVertexSkinningBDEF2() + : bone_index1(0) + , bone_index2(0) + , bone_weight(0.0f) + {} + + int bone_index1; + int bone_index2; + float bone_weight; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningBDEF4 : public PmxVertexSkinning + { + public: + PmxVertexSkinningBDEF4() + : bone_index1(0) + , bone_index2(0) + , bone_index3(0) + , bone_index4(0) + , bone_weight1(0.0f) + , bone_weight2(0.0f) + , bone_weight3(0.0f) + , bone_weight4(0.0f) + {} + + int bone_index1; + int bone_index2; + int bone_index3; + int bone_index4; + float bone_weight1; + float bone_weight2; + float bone_weight3; + float bone_weight4; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningSDEF : public PmxVertexSkinning + { + public: + PmxVertexSkinningSDEF() + : bone_index1(0) + , bone_index2(0) + , bone_weight(0.0f) + { + for (int i = 0; i < 3; ++i) { + sdef_c[i] = 0.0f; + sdef_r0[i] = 0.0f; + sdef_r1[i] = 0.0f; + } + } + + int bone_index1; + int bone_index2; + float bone_weight; + float sdef_c[3]; + float sdef_r0[3]; + float sdef_r1[3]; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertexSkinningQDEF : public PmxVertexSkinning + { + public: + PmxVertexSkinningQDEF() + : bone_index1(0) + , bone_index2(0) + , bone_index3(0) + , bone_index4(0) + , bone_weight1(0.0f) + , bone_weight2(0.0f) + , bone_weight3(0.0f) + , bone_weight4(0.0f) + {} + + int bone_index1; + int bone_index2; + int bone_index3; + int bone_index4; + float bone_weight1; + float bone_weight2; + float bone_weight3; + float bone_weight4; + void Read(std::istream *stresam, PmxSetting *setting); + }; + + class PmxVertex + { + public: + PmxVertex() + : edge(0.0f) + { + uv[0] = uv[1] = 0.0f; + for (int i = 0; i < 3; ++i) { + position[i] = 0.0f; + normal[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + for (int k = 0; k < 4; ++k) { + uva[i][k] = 0.0f; + } + } + } + + float position[3]; + float normal[3]; + float uv[2]; + float uva[4][4]; + PmxVertexSkinningType skinning_type; + std::unique_ptr<PmxVertexSkinning> skinning; + float edge; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxMaterial + { + public: + PmxMaterial() + : specularlity(0.0f) + , flag(0) + , edge_size(0.0f) + , diffuse_texture_index(0) + , sphere_texture_index(0) + , sphere_op_mode(0) + , common_toon_flag(0) + , toon_texture_index(0) + , index_count(0) + { + for (int i = 0; i < 3; ++i) { + specular[i] = 0.0f; + ambient[i] = 0.0f; + edge_color[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + diffuse[i] = 0.0f; + } + } + + std::string material_name; + std::string material_english_name; + float diffuse[4]; + float specular[3]; + float specularlity; + float ambient[3]; + uint8_t flag; + float edge_color[4]; + float edge_size; + int diffuse_texture_index; + int sphere_texture_index; + uint8_t sphere_op_mode; + uint8_t common_toon_flag; + int toon_texture_index; + std::string memo; + int index_count; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxIkLink + { + public: + PmxIkLink() + : link_target(0) + , angle_lock(0) + { + for (int i = 0; i < 3; ++i) { + max_radian[i] = 0.0f; + min_radian[i] = 0.0f; + } + } + + int link_target; + uint8_t angle_lock; + float max_radian[3]; + float min_radian[3]; + void Read(std::istream *stream, PmxSetting *settingn); + }; + + class PmxBone + { + public: + PmxBone() + : parent_index(0) + , level(0) + , bone_flag(0) + , target_index(0) + , grant_parent_index(0) + , grant_weight(0.0f) + , key(0) + , ik_target_bone_index(0) + , ik_loop(0) + , ik_loop_angle_limit(0.0f) + , ik_link_count(0) + { + for (int i = 0; i < 3; ++i) { + position[i] = 0.0f; + offset[i] = 0.0f; + lock_axis_orientation[i] = 0.0f; + local_axis_x_orientation[i] = 0.0f; + local_axis_y_orientation[i] = 0.0f; + } + } + + std::string bone_name; + std::string bone_english_name; + float position[3]; + int parent_index; + int level; + uint16_t bone_flag; + float offset[3]; + int target_index; + int grant_parent_index; + float grant_weight; + float lock_axis_orientation[3]; + float local_axis_x_orientation[3]; + float local_axis_y_orientation[3]; + int key; + int ik_target_bone_index; + int ik_loop; + float ik_loop_angle_limit; + int ik_link_count; + std::unique_ptr<PmxIkLink []> ik_links; + void Read(std::istream *stream, PmxSetting *setting); + }; + + enum class MorphType : uint8_t + { + Group = 0, + Vertex = 1, + Bone = 2, + UV = 3, + AdditionalUV1 = 4, + AdditionalUV2 = 5, + AdditionalUV3 = 6, + AdditionalUV4 = 7, + Matrial = 8, + Flip = 9, + Implus = 10, + }; + + enum class MorphCategory : uint8_t + { + ReservedCategory = 0, + Eyebrow = 1, + Eye = 2, + Mouth = 3, + Other = 4, + }; + + class PmxMorphOffset + { + public: + void virtual Read(std::istream *stream, PmxSetting *setting) = 0; + }; + + class PmxMorphVertexOffset : public PmxMorphOffset + { + public: + PmxMorphVertexOffset() + : vertex_index(0) + { + for (int i = 0; i < 3; ++i) { + position_offset[i] = 0.0f; + } + } + int vertex_index; + float position_offset[3]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphUVOffset : public PmxMorphOffset + { + public: + PmxMorphUVOffset() + : vertex_index(0) + { + for (int i = 0; i < 4; ++i) { + uv_offset[i] = 0.0f; + } + } + int vertex_index; + float uv_offset[4]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphBoneOffset : public PmxMorphOffset + { + public: + PmxMorphBoneOffset() + : bone_index(0) + { + for (int i = 0; i < 3; ++i) { + translation[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + rotation[i] = 0.0f; + } + } + int bone_index; + float translation[3]; + float rotation[4]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphMaterialOffset : public PmxMorphOffset + { + public: + PmxMorphMaterialOffset() + : specularity(0.0f) + , edge_size(0.0f) + { + for (int i = 0; i < 3; ++i) { + specular[i] = 0.0f; + ambient[i] = 0.0f; + } + for (int i = 0; i < 4; ++i) { + diffuse[i] = 0.0f; + edge_color[i] = 0.0f; + texture_argb[i] = 0.0f; + sphere_texture_argb[i] = 0.0f; + toon_texture_argb[i] = 0.0f; + } + } + int material_index; + uint8_t offset_operation; + float diffuse[4]; + float specular[3]; + float specularity; + float ambient[3]; + float edge_color[4]; + float edge_size; + float texture_argb[4]; + float sphere_texture_argb[4]; + float toon_texture_argb[4]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphGroupOffset : public PmxMorphOffset + { + public: + PmxMorphGroupOffset() + : morph_index(0) + , morph_weight(0.0f) + {} + int morph_index; + float morph_weight; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphFlipOffset : public PmxMorphOffset + { + public: + PmxMorphFlipOffset() + : morph_index(0) + , morph_value(0.0f) + {} + int morph_index; + float morph_value; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorphImplusOffset : public PmxMorphOffset + { + public: + PmxMorphImplusOffset() + : rigid_body_index(0) + , is_local(0) + { + for (int i = 0; i < 3; ++i) { + velocity[i] = 0.0f; + angular_torque[i] = 0.0f; + } + } + int rigid_body_index; + uint8_t is_local; + float velocity[3]; + float angular_torque[3]; + void Read(std::istream *stream, PmxSetting *setting); //override; + }; + + class PmxMorph + { + public: + PmxMorph() + : offset_count(0) + { + } + std::string morph_name; + std::string morph_english_name; + MorphCategory category; + MorphType morph_type; + int offset_count; + std::unique_ptr<PmxMorphVertexOffset []> vertex_offsets; + std::unique_ptr<PmxMorphUVOffset []> uv_offsets; + std::unique_ptr<PmxMorphBoneOffset []> bone_offsets; + std::unique_ptr<PmxMorphMaterialOffset []> material_offsets; + std::unique_ptr<PmxMorphGroupOffset []> group_offsets; + std::unique_ptr<PmxMorphFlipOffset []> flip_offsets; + std::unique_ptr<PmxMorphImplusOffset []> implus_offsets; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxFrameElement + { + public: + PmxFrameElement() + : element_target(0) + , index(0) + { + } + uint8_t element_target; + int index; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxFrame + { + public: + PmxFrame() + : frame_flag(0) + , element_count(0) + { + } + std::string frame_name; + std::string frame_english_name; + uint8_t frame_flag; + int element_count; + std::unique_ptr<PmxFrameElement []> elements; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxRigidBody + { + public: + PmxRigidBody() + : target_bone(0) + , group(0) + , mask(0) + , shape(0) + , mass(0.0f) + , move_attenuation(0.0f) + , rotation_attenuation(0.0f) + , repulsion(0.0f) + , friction(0.0f) + , physics_calc_type(0) + { + for (int i = 0; i < 3; ++i) { + size[i] = 0.0f; + position[i] = 0.0f; + orientation[i] = 0.0f; + } + } + std::string girid_body_name; + std::string girid_body_english_name; + int target_bone; + uint8_t group; + uint16_t mask; + uint8_t shape; + float size[3]; + float position[3]; + float orientation[3]; + float mass; + float move_attenuation; + float rotation_attenuation; + float repulsion; + float friction; + uint8_t physics_calc_type; + void Read(std::istream *stream, PmxSetting *setting); + }; + + enum class PmxJointType : uint8_t + { + Generic6DofSpring = 0, + Generic6Dof = 1, + Point2Point = 2, + ConeTwist = 3, + Slider = 5, + Hinge = 6 + }; + + class PmxJointParam + { + public: + PmxJointParam() + : rigid_body1(0) + , rigid_body2(0) + { + for (int i = 0; i < 3; ++i) { + position[i] = 0.0f; + orientaiton[i] = 0.0f; + move_limitation_min[i] = 0.0f; + move_limitation_max[i] = 0.0f; + rotation_limitation_min[i] = 0.0f; + rotation_limitation_max[i] = 0.0f; + spring_move_coefficient[i] = 0.0f; + spring_rotation_coefficient[i] = 0.0f; + } + } + int rigid_body1; + int rigid_body2; + float position[3]; + float orientaiton[3]; + float move_limitation_min[3]; + float move_limitation_max[3]; + float rotation_limitation_min[3]; + float rotation_limitation_max[3]; + float spring_move_coefficient[3]; + float spring_rotation_coefficient[3]; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxJoint + { + public: + std::string joint_name; + std::string joint_english_name; + PmxJointType joint_type; + PmxJointParam param; + void Read(std::istream *stream, PmxSetting *setting); + }; + + enum PmxSoftBodyFlag : uint8_t + { + BLink = 0x01, + Cluster = 0x02, + Link = 0x04 + }; + + class PmxAncherRigidBody + { + public: + PmxAncherRigidBody() + : related_rigid_body(0) + , related_vertex(0) + , is_near(false) + {} + int related_rigid_body; + int related_vertex; + bool is_near; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxSoftBody + { + public: + PmxSoftBody() + : shape(0) + , target_material(0) + , group(0) + , mask(0) + , blink_distance(0) + , cluster_count(0) + , mass(0.0) + , collisioni_margin(0.0) + , aero_model(0) + , VCF(0.0f) + , DP(0.0f) + , DG(0.0f) + , LF(0.0f) + , PR(0.0f) + , VC(0.0f) + , DF(0.0f) + , MT(0.0f) + , CHR(0.0f) + , KHR(0.0f) + , SHR(0.0f) + , AHR(0.0f) + , SRHR_CL(0.0f) + , SKHR_CL(0.0f) + , SSHR_CL(0.0f) + , SR_SPLT_CL(0.0f) + , SK_SPLT_CL(0.0f) + , SS_SPLT_CL(0.0f) + , V_IT(0) + , P_IT(0) + , D_IT(0) + , C_IT(0) + , LST(0.0f) + , AST(0.0f) + , VST(0.0f) + , anchor_count(0) + , pin_vertex_count(0) + {} + std::string soft_body_name; + std::string soft_body_english_name; + uint8_t shape; + int target_material; + uint8_t group; + uint16_t mask; + PmxSoftBodyFlag flag; + int blink_distance; + int cluster_count; + float mass; + float collisioni_margin; + int aero_model; + float VCF; + float DP; + float DG; + float LF; + float PR; + float VC; + float DF; + float MT; + float CHR; + float KHR; + float SHR; + float AHR; + float SRHR_CL; + float SKHR_CL; + float SSHR_CL; + float SR_SPLT_CL; + float SK_SPLT_CL; + float SS_SPLT_CL; + int V_IT; + int P_IT; + int D_IT; + int C_IT; + float LST; + float AST; + float VST; + int anchor_count; + std::unique_ptr<PmxAncherRigidBody []> anchers; + int pin_vertex_count; + std::unique_ptr<int []> pin_vertices; + void Read(std::istream *stream, PmxSetting *setting); + }; + + class PmxModel + { + public: + PmxModel() + : version(0.0f) + , vertex_count(0) + , index_count(0) + , texture_count(0) + , material_count(0) + , bone_count(0) + , morph_count(0) + , frame_count(0) + , rigid_body_count(0) + , joint_count(0) + , soft_body_count(0) + {} + + float version; + PmxSetting setting; + std::string model_name; + std::string model_english_name; + std::string model_comment; + std::string model_english_comment; + int vertex_count; + std::unique_ptr<PmxVertex []> vertices; + int index_count; + std::unique_ptr<int []> indices; + int texture_count; + std::unique_ptr< std::string []> textures; + int material_count; + std::unique_ptr<PmxMaterial []> materials; + int bone_count; + std::unique_ptr<PmxBone []> bones; + int morph_count; + std::unique_ptr<PmxMorph []> morphs; + int frame_count; + std::unique_ptr<PmxFrame [] > frames; + int rigid_body_count; + std::unique_ptr<PmxRigidBody []> rigid_bodies; + int joint_count; + std::unique_ptr<PmxJoint []> joints; + int soft_body_count; + std::unique_ptr<PmxSoftBody []> soft_bodies; + void Init(); + void Read(std::istream *stream); + //static std::unique_ptr<PmxModel> ReadFromFile(const char *filename); + //static std::unique_ptr<PmxModel> ReadFromStream(std::istream *stream); + }; +} diff --git a/thirdparty/assimp/code/MMDVmdParser.h b/thirdparty/assimp/code/MMDVmdParser.h new file mode 100644 index 0000000000..947c3a2422 --- /dev/null +++ b/thirdparty/assimp/code/MMDVmdParser.h @@ -0,0 +1,376 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include <vector> +#include <string> +#include <memory> +#include <iostream> +#include <fstream> +#include <ostream> +#include "MMDCpp14.h" + +namespace vmd +{ + class VmdBoneFrame + { + public: + std::string name; + int frame; + float position[3]; + float orientation[4]; + char interpolation[4][4][4]; + + void Read(std::istream* stream) + { + char buffer[15]; + stream->read((char*) buffer, sizeof(char)*15); + name = std::string(buffer); + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) position, sizeof(float)*3); + stream->read((char*) orientation, sizeof(float)*4); + stream->read((char*) interpolation, sizeof(char) * 4 * 4 * 4); + } + + void Write(std::ostream* stream) + { + stream->write((char*)name.c_str(), sizeof(char) * 15); + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)position, sizeof(float) * 3); + stream->write((char*)orientation, sizeof(float) * 4); + stream->write((char*)interpolation, sizeof(char) * 4 * 4 * 4); + } + }; + + class VmdFaceFrame + { + public: + std::string face_name; + float weight; + uint32_t frame; + + void Read(std::istream* stream) + { + char buffer[15]; + stream->read((char*) &buffer, sizeof(char) * 15); + face_name = std::string(buffer); + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) &weight, sizeof(float)); + } + + void Write(std::ostream* stream) + { + stream->write((char*)face_name.c_str(), sizeof(char) * 15); + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)&weight, sizeof(float)); + } + }; + + class VmdCameraFrame + { + public: + int frame; + float distance; + float position[3]; + float orientation[3]; + char interpolation[6][4]; + float angle; + char unknown[3]; + + void Read(std::istream *stream) + { + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) &distance, sizeof(float)); + stream->read((char*) position, sizeof(float) * 3); + stream->read((char*) orientation, sizeof(float) * 3); + stream->read((char*) interpolation, sizeof(char) * 24); + stream->read((char*) &angle, sizeof(float)); + stream->read((char*) unknown, sizeof(char) * 3); + } + + void Write(std::ostream *stream) + { + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)&distance, sizeof(float)); + stream->write((char*)position, sizeof(float) * 3); + stream->write((char*)orientation, sizeof(float) * 3); + stream->write((char*)interpolation, sizeof(char) * 24); + stream->write((char*)&angle, sizeof(float)); + stream->write((char*)unknown, sizeof(char) * 3); + } + }; + + class VmdLightFrame + { + public: + int frame; + float color[3]; + float position[3]; + + void Read(std::istream* stream) + { + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) color, sizeof(float) * 3); + stream->read((char*) position, sizeof(float) * 3); + } + + void Write(std::ostream* stream) + { + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)color, sizeof(float) * 3); + stream->write((char*)position, sizeof(float) * 3); + } + }; + + class VmdIkEnable + { + public: + std::string ik_name; + bool enable; + }; + + class VmdIkFrame + { + public: + int frame; + bool display; + std::vector<VmdIkEnable> ik_enable; + + void Read(std::istream *stream) + { + char buffer[20]; + stream->read((char*) &frame, sizeof(int)); + stream->read((char*) &display, sizeof(uint8_t)); + int ik_count; + stream->read((char*) &ik_count, sizeof(int)); + ik_enable.resize(ik_count); + for (int i = 0; i < ik_count; i++) + { + stream->read(buffer, 20); + ik_enable[i].ik_name = std::string(buffer); + stream->read((char*) &ik_enable[i].enable, sizeof(uint8_t)); + } + } + + void Write(std::ostream *stream) + { + stream->write((char*)&frame, sizeof(int)); + stream->write((char*)&display, sizeof(uint8_t)); + int ik_count = static_cast<int>(ik_enable.size()); + stream->write((char*)&ik_count, sizeof(int)); + for (int i = 0; i < ik_count; i++) + { + const VmdIkEnable& ik_enable = this->ik_enable.at(i); + stream->write(ik_enable.ik_name.c_str(), 20); + stream->write((char*)&ik_enable.enable, sizeof(uint8_t)); + } + } + }; + + class VmdMotion + { + public: + std::string model_name; + int version; + std::vector<VmdBoneFrame> bone_frames; + std::vector<VmdFaceFrame> face_frames; + std::vector<VmdCameraFrame> camera_frames; + std::vector<VmdLightFrame> light_frames; + std::vector<VmdIkFrame> ik_frames; + + static std::unique_ptr<VmdMotion> LoadFromFile(char const *filename) + { + std::ifstream stream(filename, std::ios::binary); + auto result = LoadFromStream(&stream); + stream.close(); + return result; + } + + static std::unique_ptr<VmdMotion> LoadFromStream(std::ifstream *stream) + { + + char buffer[30]; + auto result = mmd::make_unique<VmdMotion>(); + + // magic and version + stream->read((char*) buffer, 30); + if (strncmp(buffer, "Vocaloid Motion Data", 20)) + { + std::cerr << "invalid vmd file." << std::endl; + return nullptr; + } + result->version = std::atoi(buffer + 20); + + // name + stream->read(buffer, 20); + result->model_name = std::string(buffer); + + // bone frames + int bone_frame_num; + stream->read((char*) &bone_frame_num, sizeof(int)); + result->bone_frames.resize(bone_frame_num); + for (int i = 0; i < bone_frame_num; i++) + { + result->bone_frames[i].Read(stream); + } + + // face frames + int face_frame_num; + stream->read((char*) &face_frame_num, sizeof(int)); + result->face_frames.resize(face_frame_num); + for (int i = 0; i < face_frame_num; i++) + { + result->face_frames[i].Read(stream); + } + + // camera frames + int camera_frame_num; + stream->read((char*) &camera_frame_num, sizeof(int)); + result->camera_frames.resize(camera_frame_num); + for (int i = 0; i < camera_frame_num; i++) + { + result->camera_frames[i].Read(stream); + } + + // light frames + int light_frame_num; + stream->read((char*) &light_frame_num, sizeof(int)); + result->light_frames.resize(light_frame_num); + for (int i = 0; i < light_frame_num; i++) + { + result->light_frames[i].Read(stream); + } + + // unknown2 + stream->read(buffer, 4); + + // ik frames + if (stream->peek() != std::ios::traits_type::eof()) + { + int ik_num; + stream->read((char*) &ik_num, sizeof(int)); + result->ik_frames.resize(ik_num); + for (int i = 0; i < ik_num; i++) + { + result->ik_frames[i].Read(stream); + } + } + + if (stream->peek() != std::ios::traits_type::eof()) + { + std::cerr << "vmd stream has unknown data." << std::endl; + } + + return result; + } + + bool SaveToFile(const std::u16string& /*filename*/) + { + // TODO: How to adapt u16string to string? + /* + std::ofstream stream(filename.c_str(), std::ios::binary); + auto result = SaveToStream(&stream); + stream.close(); + return result; + */ + return false; + } + + bool SaveToStream(std::ofstream *stream) + { + std::string magic = "Vocaloid Motion Data 0002\0"; + magic.resize(30); + + // magic and version + stream->write(magic.c_str(), 30); + + // name + stream->write(model_name.c_str(), 20); + + // bone frames + const int bone_frame_num = static_cast<int>(bone_frames.size()); + stream->write(reinterpret_cast<const char*>(&bone_frame_num), sizeof(int)); + for (int i = 0; i < bone_frame_num; i++) + { + bone_frames[i].Write(stream); + } + + // face frames + const int face_frame_num = static_cast<int>(face_frames.size()); + stream->write(reinterpret_cast<const char*>(&face_frame_num), sizeof(int)); + for (int i = 0; i < face_frame_num; i++) + { + face_frames[i].Write(stream); + } + + // camera frames + const int camera_frame_num = static_cast<int>(camera_frames.size()); + stream->write(reinterpret_cast<const char*>(&camera_frame_num), sizeof(int)); + for (int i = 0; i < camera_frame_num; i++) + { + camera_frames[i].Write(stream); + } + + // light frames + const int light_frame_num = static_cast<int>(light_frames.size()); + stream->write(reinterpret_cast<const char*>(&light_frame_num), sizeof(int)); + for (int i = 0; i < light_frame_num; i++) + { + light_frames[i].Write(stream); + } + + // self shadow datas + const int self_shadow_num = 0; + stream->write(reinterpret_cast<const char*>(&self_shadow_num), sizeof(int)); + + // ik frames + const int ik_num = static_cast<int>(ik_frames.size()); + stream->write(reinterpret_cast<const char*>(&ik_num), sizeof(int)); + for (int i = 0; i < ik_num; i++) + { + ik_frames[i].Write(stream); + } + + return true; + } + }; +} diff --git a/thirdparty/assimp/code/MakeVerboseFormat.cpp b/thirdparty/assimp/code/MakeVerboseFormat.cpp new file mode 100644 index 0000000000..50ff5ed93d --- /dev/null +++ b/thirdparty/assimp/code/MakeVerboseFormat.cpp @@ -0,0 +1,226 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file Implementation of the post processing step "MakeVerboseFormat" +*/ + + +#include "MakeVerboseFormat.h" +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +MakeVerboseFormatProcess::MakeVerboseFormatProcess() +{ + // nothing to do here +} +// ------------------------------------------------------------------------------------------------ +MakeVerboseFormatProcess::~MakeVerboseFormatProcess() +{ + // nothing to do here +} +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void MakeVerboseFormatProcess::Execute( aiScene* pScene) +{ + ai_assert(NULL != pScene); + ASSIMP_LOG_DEBUG("MakeVerboseFormatProcess begin"); + + bool bHas = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + { + if( MakeVerboseFormat( pScene->mMeshes[a])) + bHas = true; + } + if (bHas) { + ASSIMP_LOG_INFO("MakeVerboseFormatProcess finished. There was much work to do ..."); + } else { + ASSIMP_LOG_DEBUG("MakeVerboseFormatProcess. There was nothing to do."); + } + + pScene->mFlags &= ~AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh) +{ + ai_assert(NULL != pcMesh); + + unsigned int iOldNumVertices = pcMesh->mNumVertices; + const unsigned int iNumVerts = pcMesh->mNumFaces*3; + + aiVector3D* pvPositions = new aiVector3D[ iNumVerts ]; + + aiVector3D* pvNormals = NULL; + if (pcMesh->HasNormals()) + { + pvNormals = new aiVector3D[iNumVerts]; + } + aiVector3D* pvTangents = NULL, *pvBitangents = NULL; + if (pcMesh->HasTangentsAndBitangents()) + { + pvTangents = new aiVector3D[iNumVerts]; + pvBitangents = new aiVector3D[iNumVerts]; + } + + aiVector3D* apvTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS] = {0}; + aiColor4D* apvColorSets[AI_MAX_NUMBER_OF_COLOR_SETS] = {0}; + + unsigned int p = 0; + while (pcMesh->HasTextureCoords(p)) + apvTextureCoords[p++] = new aiVector3D[iNumVerts]; + + p = 0; + while (pcMesh->HasVertexColors(p)) + apvColorSets[p++] = new aiColor4D[iNumVerts]; + + // allocate enough memory to hold output bones and vertex weights ... + std::vector<aiVertexWeight>* newWeights = new std::vector<aiVertexWeight>[pcMesh->mNumBones]; + for (unsigned int i = 0;i < pcMesh->mNumBones;++i) { + newWeights[i].reserve(pcMesh->mBones[i]->mNumWeights*3); + } + + // iterate through all faces and build a clean list + unsigned int iIndex = 0; + for (unsigned int a = 0; a< pcMesh->mNumFaces;++a) + { + aiFace* pcFace = &pcMesh->mFaces[a]; + for (unsigned int q = 0; q < pcFace->mNumIndices;++q,++iIndex) + { + // need to build a clean list of bones, too + for (unsigned int i = 0;i < pcMesh->mNumBones;++i) + { + for (unsigned int a = 0; a < pcMesh->mBones[i]->mNumWeights;a++) + { + const aiVertexWeight& w = pcMesh->mBones[i]->mWeights[a]; + if(pcFace->mIndices[q] == w.mVertexId) + { + aiVertexWeight wNew; + wNew.mVertexId = iIndex; + wNew.mWeight = w.mWeight; + newWeights[i].push_back(wNew); + } + } + } + + pvPositions[iIndex] = pcMesh->mVertices[pcFace->mIndices[q]]; + + if (pcMesh->HasNormals()) + { + pvNormals[iIndex] = pcMesh->mNormals[pcFace->mIndices[q]]; + } + if (pcMesh->HasTangentsAndBitangents()) + { + pvTangents[iIndex] = pcMesh->mTangents[pcFace->mIndices[q]]; + pvBitangents[iIndex] = pcMesh->mBitangents[pcFace->mIndices[q]]; + } + + unsigned int p = 0; + while (pcMesh->HasTextureCoords(p)) + { + apvTextureCoords[p][iIndex] = pcMesh->mTextureCoords[p][pcFace->mIndices[q]]; + ++p; + } + p = 0; + while (pcMesh->HasVertexColors(p)) + { + apvColorSets[p][iIndex] = pcMesh->mColors[p][pcFace->mIndices[q]]; + ++p; + } + pcFace->mIndices[q] = iIndex; + } + } + + + + // build output vertex weights + for (unsigned int i = 0;i < pcMesh->mNumBones;++i) + { + delete [] pcMesh->mBones[i]->mWeights; + if (!newWeights[i].empty()) { + pcMesh->mBones[i]->mWeights = new aiVertexWeight[newWeights[i].size()]; + aiVertexWeight *weightToCopy = &( newWeights[i][0] ); + memcpy(pcMesh->mBones[i]->mWeights, weightToCopy, + sizeof(aiVertexWeight) * newWeights[i].size()); + } else { + pcMesh->mBones[i]->mWeights = NULL; + } + } + delete[] newWeights; + + // delete the old members + delete[] pcMesh->mVertices; + pcMesh->mVertices = pvPositions; + + p = 0; + while (pcMesh->HasTextureCoords(p)) + { + delete[] pcMesh->mTextureCoords[p]; + pcMesh->mTextureCoords[p] = apvTextureCoords[p]; + ++p; + } + p = 0; + while (pcMesh->HasVertexColors(p)) + { + delete[] pcMesh->mColors[p]; + pcMesh->mColors[p] = apvColorSets[p]; + ++p; + } + pcMesh->mNumVertices = iNumVerts; + + if (pcMesh->HasNormals()) + { + delete[] pcMesh->mNormals; + pcMesh->mNormals = pvNormals; + } + if (pcMesh->HasTangentsAndBitangents()) + { + delete[] pcMesh->mTangents; + pcMesh->mTangents = pvTangents; + delete[] pcMesh->mBitangents; + pcMesh->mBitangents = pvBitangents; + } + return (pcMesh->mNumVertices != iOldNumVertices); +} diff --git a/thirdparty/assimp/code/MakeVerboseFormat.h b/thirdparty/assimp/code/MakeVerboseFormat.h new file mode 100644 index 0000000000..d12db63ae1 --- /dev/null +++ b/thirdparty/assimp/code/MakeVerboseFormat.h @@ -0,0 +1,105 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to bring a given scene + into the verbose format that is expected by most postprocess steps. + This is the inverse of the "JoinIdenticalVertices" step. */ +#ifndef AI_MAKEVERBOSEFORMAT_H_INC +#define AI_MAKEVERBOSEFORMAT_H_INC + +#include "BaseProcess.h" +struct aiMesh; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** MakeVerboseFormatProcess: Class to convert an asset to the verbose + * format which is expected by most postprocess steps. + * + * This is the inverse of what the "JoinIdenticalVertices" step is doing. + * This step has no official flag (since it wouldn't make sense to run it + * during import). It is intended for applications intending to modify the + * returned aiScene. After this step has been executed, they can execute + * other postprocess steps on the data. The code might also be useful to + * quickly adapt code that doesn't result in a verbose representation of + * the scene data. + * The step has been added because it was required by the viewer, however + * it has been moved to the main library since others might find it + * useful, too. */ +class ASSIMP_API_WINONLY MakeVerboseFormatProcess : public BaseProcess +{ +public: + + + MakeVerboseFormatProcess(); + ~MakeVerboseFormatProcess(); + +public: + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not */ + bool IsActive( unsigned int /*pFlags*/ ) const + { + // NOTE: There is no direct flag that corresponds to + // this postprocess step. + return false; + } + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. */ + void Execute( aiScene* pScene); + + +private: + + //! Apply the postprocess step to a given submesh + bool MakeVerboseFormat (aiMesh* pcMesh); +}; + +} // end of namespace Assimp + +#endif // !!AI_KILLNORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/MaterialSystem.cpp b/thirdparty/assimp/code/MaterialSystem.cpp new file mode 100644 index 0000000000..03d5a18a34 --- /dev/null +++ b/thirdparty/assimp/code/MaterialSystem.cpp @@ -0,0 +1,647 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file MaterialSystem.cpp + * @brief Implementation of the material system of the library + */ + +#include <assimp/Hash.h> +#include <assimp/fast_atof.h> +#include <assimp/ParsingUtils.h> +#include "MaterialSystem.h" +#include <assimp/types.h> +#include <assimp/material.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/Macros.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Get a specific property from a material +aiReturn aiGetMaterialProperty(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + const aiMaterialProperty** pPropOut) +{ + ai_assert( pMat != NULL ); + ai_assert( pKey != NULL ); + ai_assert( pPropOut != NULL ); + + /* Just search for a property with exactly this name .. + * could be improved by hashing, but it's possibly + * no worth the effort (we're bound to C structures, + * thus std::map or derivates are not applicable. */ + for ( unsigned int i = 0; i < pMat->mNumProperties; ++i ) { + aiMaterialProperty* prop = pMat->mProperties[i]; + + if (prop /* just for safety ... */ + && 0 == strcmp( prop->mKey.data, pKey ) + && (UINT_MAX == type || prop->mSemantic == type) /* UINT_MAX is a wild-card, but this is undocumented :-) */ + && (UINT_MAX == index || prop->mIndex == index)) + { + *pPropOut = pMat->mProperties[i]; + return AI_SUCCESS; + } + } + *pPropOut = NULL; + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +// Get an array of floating-point values from the material. +aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + ai_real* pOut, + unsigned int* pMax) +{ + ai_assert( pOut != NULL ); + ai_assert( pMat != NULL ); + + const aiMaterialProperty* prop; + aiGetMaterialProperty(pMat,pKey,type,index, (const aiMaterialProperty**) &prop); + if (!prop) { + return AI_FAILURE; + } + + // data is given in floats, convert to ai_real + unsigned int iWrite = 0; + if( aiPTI_Float == prop->mType || aiPTI_Buffer == prop->mType) { + iWrite = prop->mDataLength / sizeof(float); + if (pMax) { + iWrite = std::min(*pMax,iWrite); ; + } + for (unsigned int a = 0; a < iWrite;++a) { + pOut[a] = static_cast<ai_real> ( reinterpret_cast<float*>(prop->mData)[a] ); + } + if (pMax) { + *pMax = iWrite; + } + } + // data is given in doubles, convert to float + else if( aiPTI_Double == prop->mType) { + iWrite = prop->mDataLength / sizeof(double); + if (pMax) { + iWrite = std::min(*pMax,iWrite); ; + } + for (unsigned int a = 0; a < iWrite;++a) { + pOut[a] = static_cast<ai_real> ( reinterpret_cast<double*>(prop->mData)[a] ); + } + if (pMax) { + *pMax = iWrite; + } + } + // data is given in ints, convert to float + else if( aiPTI_Integer == prop->mType) { + iWrite = prop->mDataLength / sizeof(int32_t); + if (pMax) { + iWrite = std::min(*pMax,iWrite); ; + } + for (unsigned int a = 0; a < iWrite;++a) { + pOut[a] = static_cast<ai_real> ( reinterpret_cast<int32_t*>(prop->mData)[a] ); + } + if (pMax) { + *pMax = iWrite; + } + } + // a string ... read floats separated by spaces + else { + if (pMax) { + iWrite = *pMax; + } + // strings are zero-terminated with a 32 bit length prefix, so this is safe + const char *cur = prop->mData + 4; + ai_assert( prop->mDataLength >= 5 ); + ai_assert( !prop->mData[ prop->mDataLength - 1 ] ); + for ( unsigned int a = 0; ;++a) { + cur = fast_atoreal_move<ai_real>(cur,pOut[a]); + if ( a==iWrite-1 ) { + break; + } + if ( !IsSpace(*cur) ) { + ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + + " is a string; failed to parse a float array out of it."); + return AI_FAILURE; + } + } + + if (pMax) { + *pMax = iWrite; + } + } + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Get an array if integers from the material +aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + int* pOut, + unsigned int* pMax) +{ + ai_assert( pOut != NULL ); + ai_assert( pMat != NULL ); + + const aiMaterialProperty* prop; + aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**) &prop); + if (!prop) { + return AI_FAILURE; + } + + // data is given in ints, simply copy it + unsigned int iWrite = 0; + if( aiPTI_Integer == prop->mType || aiPTI_Buffer == prop->mType) { + iWrite = std::max(static_cast<unsigned int>(prop->mDataLength / sizeof(int32_t)), 1u); + if (pMax) { + iWrite = std::min(*pMax,iWrite); + } + if (1 == prop->mDataLength) { + // bool type, 1 byte + *pOut = static_cast<int>(*prop->mData); + } + else { + for (unsigned int a = 0; a < iWrite;++a) { + pOut[a] = static_cast<int>(reinterpret_cast<int32_t*>(prop->mData)[a]); + } + } + if (pMax) { + *pMax = iWrite; + } + } + // data is given in floats convert to int + else if( aiPTI_Float == prop->mType) { + iWrite = prop->mDataLength / sizeof(float); + if (pMax) { + iWrite = std::min(*pMax,iWrite); ; + } + for (unsigned int a = 0; a < iWrite;++a) { + pOut[a] = static_cast<int>(reinterpret_cast<float*>(prop->mData)[a]); + } + if (pMax) { + *pMax = iWrite; + } + } + // it is a string ... no way to read something out of this + else { + if (pMax) { + iWrite = *pMax; + } + // strings are zero-terminated with a 32 bit length prefix, so this is safe + const char *cur = prop->mData+4; + ai_assert( prop->mDataLength >= 5 ); + ai_assert( !prop->mData[ prop->mDataLength - 1 ] ); + for (unsigned int a = 0; ;++a) { + pOut[a] = strtol10(cur,&cur); + if(a==iWrite-1) { + break; + } + if(!IsSpace(*cur)) { + ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + + " is a string; failed to parse an integer array out of it."); + return AI_FAILURE; + } + } + + if (pMax) { + *pMax = iWrite; + } + } + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Get a color (3 or 4 floats) from the material +aiReturn aiGetMaterialColor(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + aiColor4D* pOut) +{ + unsigned int iMax = 4; + const aiReturn eRet = aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax); + + // if no alpha channel is defined: set it to 1.0 + if (3 == iMax) { + pOut->a = 1.0; + } + + return eRet; +} + +// ------------------------------------------------------------------------------------------------ +// Get a aiUVTransform (4 floats) from the material +aiReturn aiGetMaterialUVTransform(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + aiUVTransform* pOut) +{ + unsigned int iMax = 4; + return aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax); +} + +// ------------------------------------------------------------------------------------------------ +// Get a string from the material +aiReturn aiGetMaterialString(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + aiString* pOut) +{ + ai_assert (pOut != NULL); + + const aiMaterialProperty* prop; + aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**)&prop); + if (!prop) { + return AI_FAILURE; + } + + if( aiPTI_String == prop->mType) { + ai_assert(prop->mDataLength>=5); + + // The string is stored as 32 but length prefix followed by zero-terminated UTF8 data + pOut->length = static_cast<unsigned int>(*reinterpret_cast<uint32_t*>(prop->mData)); + + ai_assert( pOut->length+1+4==prop->mDataLength ); + ai_assert( !prop->mData[ prop->mDataLength - 1 ] ); + memcpy(pOut->data,prop->mData+4,pOut->length+1); + } + else { + // TODO - implement lexical cast as well + ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + + " was found, but is no string" ); + return AI_FAILURE; + } + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +// Get the number of textures on a particular texture stack +unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial* pMat, + C_ENUM aiTextureType type) +{ + ai_assert (pMat != NULL); + + // Textures are always stored with ascending indices (ValidateDS provides a check, so we don't need to do it again) + unsigned int max = 0; + for (unsigned int i = 0; i < pMat->mNumProperties;++i) { + aiMaterialProperty* prop = pMat->mProperties[i]; + + if ( prop /* just a sanity check ... */ + && 0 == strcmp( prop->mKey.data, _AI_MATKEY_TEXTURE_BASE ) + && prop->mSemantic == type) { + + max = std::max(max,prop->mIndex+1); + } + } + return max; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat, + aiTextureType type, + unsigned int index, + C_STRUCT aiString* path, + aiTextureMapping* _mapping /*= NULL*/, + unsigned int* uvindex /*= NULL*/, + ai_real* blend /*= NULL*/, + aiTextureOp* op /*= NULL*/, + aiTextureMapMode* mapmode /*= NULL*/, + unsigned int* flags /*= NULL*/ + ) +{ + ai_assert( NULL != mat ); + ai_assert( NULL != path ); + + // Get the path to the texture + if (AI_SUCCESS != aiGetMaterialString(mat,AI_MATKEY_TEXTURE(type,index),path)) { + return AI_FAILURE; + } + + // Determine mapping type + int mapping_ = static_cast<int>(aiTextureMapping_UV); + aiGetMaterialInteger(mat,AI_MATKEY_MAPPING(type,index), &mapping_); + aiTextureMapping mapping = static_cast<aiTextureMapping>(mapping_); + if (_mapping) + *_mapping = mapping; + + // Get UV index + if (aiTextureMapping_UV == mapping && uvindex) { + aiGetMaterialInteger(mat,AI_MATKEY_UVWSRC(type,index),(int*)uvindex); + } + // Get blend factor + if (blend) { + aiGetMaterialFloat(mat,AI_MATKEY_TEXBLEND(type,index),blend); + } + // Get texture operation + if (op){ + aiGetMaterialInteger(mat,AI_MATKEY_TEXOP(type,index),(int*)op); + } + // Get texture mapping modes + if (mapmode) { + aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_U(type,index),(int*)&mapmode[0]); + aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_V(type,index),(int*)&mapmode[1]); + } + // Get texture flags + if (flags){ + aiGetMaterialInteger(mat,AI_MATKEY_TEXFLAGS(type,index),(int*)flags); + } + + return AI_SUCCESS; +} + + +static const unsigned int DefaultNumAllocated = 5; + +// ------------------------------------------------------------------------------------------------ +// Construction. Actually the one and only way to get an aiMaterial instance +aiMaterial::aiMaterial() +: mProperties( nullptr ) +, mNumProperties( 0 ) +, mNumAllocated( DefaultNumAllocated ) { + // Allocate 5 entries by default + mProperties = new aiMaterialProperty*[ DefaultNumAllocated ]; +} + +// ------------------------------------------------------------------------------------------------ +aiMaterial::~aiMaterial() +{ + Clear(); + + delete[] mProperties; +} + +// ------------------------------------------------------------------------------------------------ +aiString aiMaterial::GetName() { + aiString name; + Get(AI_MATKEY_NAME, name); + + return name; +} + +// ------------------------------------------------------------------------------------------------ +void aiMaterial::Clear() +{ + for ( unsigned int i = 0; i < mNumProperties; ++i ) { + // delete this entry + delete mProperties[ i ]; + AI_DEBUG_INVALIDATE_PTR(mProperties[i]); + } + mNumProperties = 0; + + // The array remains allocated, we just invalidated its contents +} + +// ------------------------------------------------------------------------------------------------ +aiReturn aiMaterial::RemoveProperty ( const char* pKey,unsigned int type, unsigned int index ) +{ + ai_assert( nullptr != pKey ); + + for (unsigned int i = 0; i < mNumProperties;++i) { + aiMaterialProperty* prop = mProperties[i]; + + if (prop && !strcmp( prop->mKey.data, pKey ) && + prop->mSemantic == type && prop->mIndex == index) + { + // Delete this entry + delete mProperties[i]; + + // collapse the array behind --. + --mNumProperties; + for (unsigned int a = i; a < mNumProperties;++a) { + mProperties[a] = mProperties[a+1]; + } + return AI_SUCCESS; + } + } + + return AI_FAILURE; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn aiMaterial::AddBinaryProperty (const void* pInput, + unsigned int pSizeInBytes, + const char* pKey, + unsigned int type, + unsigned int index, + aiPropertyTypeInfo pType + ) +{ + ai_assert( pInput != NULL ); + ai_assert( pKey != NULL ); + ai_assert( 0 != pSizeInBytes ); + + if ( 0 == pSizeInBytes ) { + + } + + // first search the list whether there is already an entry with this key + unsigned int iOutIndex( UINT_MAX ); + for ( unsigned int i = 0; i < mNumProperties; ++i ) { + aiMaterialProperty *prop( mProperties[ i ] ); + + if (prop /* just for safety */ && !strcmp( prop->mKey.data, pKey ) && + prop->mSemantic == type && prop->mIndex == index){ + + delete mProperties[i]; + iOutIndex = i; + } + } + + // Allocate a new material property + aiMaterialProperty* pcNew = new aiMaterialProperty(); + + // .. and fill it + pcNew->mType = pType; + pcNew->mSemantic = type; + pcNew->mIndex = index; + + pcNew->mDataLength = pSizeInBytes; + pcNew->mData = new char[pSizeInBytes]; + memcpy (pcNew->mData,pInput,pSizeInBytes); + + pcNew->mKey.length = ::strlen(pKey); + ai_assert ( MAXLEN > pcNew->mKey.length); + strcpy( pcNew->mKey.data, pKey ); + + if (UINT_MAX != iOutIndex) { + mProperties[iOutIndex] = pcNew; + return AI_SUCCESS; + } + + // resize the array ... double the storage allocated + if (mNumProperties == mNumAllocated) { + const unsigned int iOld = mNumAllocated; + mNumAllocated *= 2; + + aiMaterialProperty** ppTemp; + try { + ppTemp = new aiMaterialProperty*[mNumAllocated]; + } catch (std::bad_alloc&) { + delete pcNew; + return AI_OUTOFMEMORY; + } + + // just copy all items over; then replace the old array + memcpy (ppTemp,mProperties,iOld * sizeof(void*)); + + delete[] mProperties; + mProperties = ppTemp; + } + // push back ... + mProperties[mNumProperties++] = pcNew; + + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +aiReturn aiMaterial::AddProperty (const aiString* pInput, + const char* pKey, + unsigned int type, + unsigned int index) +{ + // We don't want to add the whole buffer .. write a 32 bit length + // prefix followed by the zero-terminated UTF8 string. + // (HACK) I don't want to break the ABI now, but we definitely + // ought to change aiString::mLength to uint32_t one day. + if (sizeof(size_t) == 8) { + aiString copy = *pInput; + uint32_t* s = reinterpret_cast<uint32_t*>(©.length); + s[1] = static_cast<uint32_t>(pInput->length); + + return AddBinaryProperty(s+1, + static_cast<unsigned int>(pInput->length+1+4), + pKey, + type, + index, + aiPTI_String); + } + ai_assert(sizeof(size_t)==4); + return AddBinaryProperty(pInput, + static_cast<unsigned int>(pInput->length+1+4), + pKey, + type, + index, + aiPTI_String); +} + +// ------------------------------------------------------------------------------------------------ +uint32_t Assimp::ComputeMaterialHash(const aiMaterial* mat, bool includeMatName /*= false*/) +{ + uint32_t hash = 1503; // magic start value, chosen to be my birthday :-) + for ( unsigned int i = 0; i < mat->mNumProperties; ++i ) { + aiMaterialProperty* prop; + + // Exclude all properties whose first character is '?' from the hash + // See doc for aiMaterialProperty. + if ((prop = mat->mProperties[i]) && (includeMatName || prop->mKey.data[0] != '?')) { + + hash = SuperFastHash(prop->mKey.data,(unsigned int)prop->mKey.length,hash); + hash = SuperFastHash(prop->mData,prop->mDataLength,hash); + + // Combine the semantic and the index with the hash + hash = SuperFastHash((const char*)&prop->mSemantic,sizeof(unsigned int),hash); + hash = SuperFastHash((const char*)&prop->mIndex,sizeof(unsigned int),hash); + } + } + return hash; +} + +// ------------------------------------------------------------------------------------------------ +void aiMaterial::CopyPropertyList(aiMaterial* pcDest, + const aiMaterial* pcSrc + ) +{ + ai_assert(NULL != pcDest); + ai_assert(NULL != pcSrc); + + unsigned int iOldNum = pcDest->mNumProperties; + pcDest->mNumAllocated += pcSrc->mNumAllocated; + pcDest->mNumProperties += pcSrc->mNumProperties; + + aiMaterialProperty** pcOld = pcDest->mProperties; + pcDest->mProperties = new aiMaterialProperty*[pcDest->mNumAllocated]; + + if (iOldNum && pcOld) { + for (unsigned int i = 0; i < iOldNum;++i) { + pcDest->mProperties[i] = pcOld[i]; + } + } + + if ( pcOld ) { + delete[] pcOld; + } + + for (unsigned int i = iOldNum; i< pcDest->mNumProperties;++i) { + aiMaterialProperty* propSrc = pcSrc->mProperties[i]; + + // search whether we have already a property with this name -> if yes, overwrite it + aiMaterialProperty* prop; + for ( unsigned int q = 0; q < iOldNum; ++q ) { + prop = pcDest->mProperties[q]; + if (prop /* just for safety */ && prop->mKey == propSrc->mKey && prop->mSemantic == propSrc->mSemantic + && prop->mIndex == propSrc->mIndex) { + delete prop; + + // collapse the whole array ... + memmove(&pcDest->mProperties[q],&pcDest->mProperties[q+1],i-q); + i--; + pcDest->mNumProperties--; + } + } + + // Allocate the output property and copy the source property + prop = pcDest->mProperties[i] = new aiMaterialProperty(); + prop->mKey = propSrc->mKey; + prop->mDataLength = propSrc->mDataLength; + prop->mType = propSrc->mType; + prop->mSemantic = propSrc->mSemantic; + prop->mIndex = propSrc->mIndex; + + prop->mData = new char[propSrc->mDataLength]; + memcpy(prop->mData,propSrc->mData,prop->mDataLength); + } +} diff --git a/thirdparty/assimp/code/MaterialSystem.h b/thirdparty/assimp/code/MaterialSystem.h new file mode 100644 index 0000000000..67d53578cb --- /dev/null +++ b/thirdparty/assimp/code/MaterialSystem.h @@ -0,0 +1,72 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file MaterialSystem.h + * Now that #MaterialHelper is gone, this file only contains some + * internal material utility functions. + */ +#ifndef AI_MATERIALSYSTEM_H_INC +#define AI_MATERIALSYSTEM_H_INC + +#include <stdint.h> + +struct aiMaterial; + +namespace Assimp { + +// ------------------------------------------------------------------------------ +/** Computes a hash (hopefully unique) from all material properties + * The hash value reflects the current property state, so if you add any + * property and call this method again, the resulting hash value will be + * different. The hash is not persistent across different builds and platforms. + * + * @param includeMatName Set to 'true' to take all properties with + * '?' as initial character in their name into account. + * Currently #AI_MATKEY_NAME is the only example. + * @return 32 Bit jash value for the material + */ +uint32_t ComputeMaterialHash(const aiMaterial* mat, bool includeMatName = false); + + +} // ! namespace Assimp + +#endif //!! AI_MATERIALSYSTEM_H_INC diff --git a/thirdparty/assimp/code/OptimizeGraph.cpp b/thirdparty/assimp/code/OptimizeGraph.cpp new file mode 100644 index 0000000000..add9ab79e1 --- /dev/null +++ b/thirdparty/assimp/code/OptimizeGraph.cpp @@ -0,0 +1,353 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file OptimizeGraph.cpp + * @brief Implementation of the aiProcess_OptimizGraph step + */ + + +#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS + +#include "OptimizeGraph.h" +#include "ProcessHelper.h" +#include <assimp/SceneCombiner.h> +#include <assimp/Exceptional.h> +#include <stdio.h> + +using namespace Assimp; + +#define AI_RESERVED_NODE_NAME "$Reserved_And_Evil" + +/* AI_OG_USE_HASHING enables the use of hashing to speed-up std::set lookups. + * The unhashed variant should be faster, except for *very* large data sets + */ +#ifdef AI_OG_USE_HASHING + // Use our standard hashing function to compute the hash +# define AI_OG_GETKEY(str) SuperFastHash(str.data,str.length) +#else + // Otherwise hope that std::string will utilize a static buffer + // for shorter node names. This would avoid endless heap copying. +# define AI_OG_GETKEY(str) std::string(str.data) +#endif + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +OptimizeGraphProcess::OptimizeGraphProcess() +: mScene() +, nodes_in() +, nodes_out() +, count_merged() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +OptimizeGraphProcess::~OptimizeGraphProcess() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const { + return (0 != (pFlags & aiProcess_OptimizeGraph)); +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties for the post-processing step +void OptimizeGraphProcess::SetupProperties(const Importer* pImp) { + // Get value of AI_CONFIG_PP_OG_EXCLUDE_LIST + std::string tmp = pImp->GetPropertyString(AI_CONFIG_PP_OG_EXCLUDE_LIST,""); + AddLockedNodeList(tmp); +} + +// ------------------------------------------------------------------------------------------------ +// Collect new children +void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& nodes) { + nodes_in += nd->mNumChildren; + + // Process children + std::list<aiNode*> child_nodes; + for (unsigned int i = 0; i < nd->mNumChildren; ++i) { + CollectNewChildren(nd->mChildren[i],child_nodes); + nd->mChildren[i] = nullptr; + } + + // Check whether we need this node; if not we can replace it by our own children (warn, danger of incest). + if (locked.find(AI_OG_GETKEY(nd->mName)) == locked.end() ) { + for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) { + + if (locked.find(AI_OG_GETKEY((*it)->mName)) == locked.end()) { + (*it)->mTransformation = nd->mTransformation * (*it)->mTransformation; + nodes.push_back(*it); + + it = child_nodes.erase(it); + continue; + } + ++it; + } + + if (nd->mNumMeshes || !child_nodes.empty()) { + nodes.push_back(nd); + } else { + delete nd; /* bye, node */ + return; + } + } else { + + // Retain our current position in the hierarchy + nodes.push_back(nd); + + // Now check for possible optimizations in our list of child nodes. join as many as possible + aiNode* join_master = NULL; + aiMatrix4x4 inv; + + const LockedSetType::const_iterator end = locked.end(); + + std::list<aiNode*> join; + for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) { + aiNode* child = *it; + if (child->mNumChildren == 0 && locked.find(AI_OG_GETKEY(child->mName)) == end) { + + // There may be no instanced meshes + unsigned int n = 0; + for (; n < child->mNumMeshes;++n) { + if (meshes[child->mMeshes[n]] > 1) { + break; + } + } + if (n == child->mNumMeshes) { + if (!join_master) { + join_master = child; + inv = join_master->mTransformation; + inv.Inverse(); + } else { + child->mTransformation = inv * child->mTransformation ; + + join.push_back(child); + it = child_nodes.erase(it); + continue; + } + } + } + ++it; + } + if (join_master && !join.empty()) { + join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i",count_merged++); + + unsigned int out_meshes = 0; + for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) { + out_meshes += (*it)->mNumMeshes; + } + + // copy all mesh references in one array + if (out_meshes) { + unsigned int* meshes = new unsigned int[out_meshes+join_master->mNumMeshes], *tmp = meshes; + for (unsigned int n = 0; n < join_master->mNumMeshes;++n) { + *tmp++ = join_master->mMeshes[n]; + } + + for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) { + for (unsigned int n = 0; n < (*it)->mNumMeshes; ++n) { + + *tmp = (*it)->mMeshes[n]; + aiMesh* mesh = mScene->mMeshes[*tmp++]; + + // manually move the mesh into the right coordinate system + const aiMatrix3x3 IT = aiMatrix3x3( (*it)->mTransformation ).Inverse().Transpose(); + for (unsigned int a = 0; a < mesh->mNumVertices; ++a) { + + mesh->mVertices[a] *= (*it)->mTransformation; + + if (mesh->HasNormals()) + mesh->mNormals[a] *= IT; + + if (mesh->HasTangentsAndBitangents()) { + mesh->mTangents[a] *= IT; + mesh->mBitangents[a] *= IT; + } + } + } + delete *it; // bye, node + } + delete[] join_master->mMeshes; + join_master->mMeshes = meshes; + join_master->mNumMeshes += out_meshes; + } + } + } + // reassign children if something changed + if (child_nodes.empty() || child_nodes.size() > nd->mNumChildren) { + + delete[] nd->mChildren; + + if (!child_nodes.empty()) { + nd->mChildren = new aiNode*[child_nodes.size()]; + } + else nd->mChildren = nullptr; + } + + nd->mNumChildren = static_cast<unsigned int>(child_nodes.size()); + + if (nd->mChildren) { + aiNode** tmp = nd->mChildren; + for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) { + aiNode* node = *tmp++ = *it; + node->mParent = nd; + } + } + + nodes_out += static_cast<unsigned int>(child_nodes.size()); +} + +// ------------------------------------------------------------------------------------------------ +// Execute the post-processing step on the given scene +void OptimizeGraphProcess::Execute( aiScene* pScene) { + ASSIMP_LOG_DEBUG("OptimizeGraphProcess begin"); + nodes_in = nodes_out = count_merged = 0; + mScene = pScene; + + meshes.resize(pScene->mNumMeshes,0); + FindInstancedMeshes(pScene->mRootNode); + + // build a blacklist of identifiers. If the name of a node matches one of these, we won't touch it + locked.clear(); + for (std::list<std::string>::const_iterator it = locked_nodes.begin(); it != locked_nodes.end(); ++it) { +#ifdef AI_OG_USE_HASHING + locked.insert(SuperFastHash((*it).c_str())); +#else + locked.insert(*it); +#endif + } + + for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) { + for (unsigned int a = 0; a < pScene->mAnimations[i]->mNumChannels; ++a) { + aiNodeAnim* anim = pScene->mAnimations[i]->mChannels[a]; + locked.insert(AI_OG_GETKEY(anim->mNodeName)); + } + } + + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + for (unsigned int a = 0; a < pScene->mMeshes[i]->mNumBones; ++a) { + + aiBone* bone = pScene->mMeshes[i]->mBones[a]; + locked.insert(AI_OG_GETKEY(bone->mName)); + + // HACK: Meshes referencing bones may not be transformed; we need to look them. + // The easiest way to do this is to increase their reference counters ... + meshes[i] += 2; + } + } + + for (unsigned int i = 0; i < pScene->mNumCameras; ++i) { + aiCamera* cam = pScene->mCameras[i]; + locked.insert(AI_OG_GETKEY(cam->mName)); + } + + for (unsigned int i = 0; i < pScene->mNumLights; ++i) { + aiLight* lgh = pScene->mLights[i]; + locked.insert(AI_OG_GETKEY(lgh->mName)); + } + + // Insert a dummy master node and make it read-only + aiNode* dummy_root = new aiNode(AI_RESERVED_NODE_NAME); + locked.insert(AI_OG_GETKEY(dummy_root->mName)); + + const aiString prev = pScene->mRootNode->mName; + pScene->mRootNode->mParent = dummy_root; + + dummy_root->mChildren = new aiNode*[dummy_root->mNumChildren = 1]; + dummy_root->mChildren[0] = pScene->mRootNode; + + // Do our recursive processing of scenegraph nodes. For each node collect + // a fully new list of children and allow their children to place themselves + // on the same hierarchy layer as their parents. + std::list<aiNode*> nodes; + CollectNewChildren (dummy_root,nodes); + + ai_assert(nodes.size() == 1); + + if (dummy_root->mNumChildren == 0) { + pScene->mRootNode = NULL; + throw DeadlyImportError("After optimizing the scene graph, no data remains"); + } + + if (dummy_root->mNumChildren > 1) { + pScene->mRootNode = dummy_root; + + // Keep the dummy node but assign the name of the old root node to it + pScene->mRootNode->mName = prev; + } + else { + + // Remove the dummy root node again. + pScene->mRootNode = dummy_root->mChildren[0]; + + dummy_root->mChildren[0] = NULL; + delete dummy_root; + } + + pScene->mRootNode->mParent = NULL; + if (!DefaultLogger::isNullLogger()) { + if ( nodes_in != nodes_out) { + ASSIMP_LOG_INFO_F("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out); + } else { + ASSIMP_LOG_DEBUG("OptimizeGraphProcess finished"); + } + } + meshes.clear(); + locked.clear(); +} + +// ------------------------------------------------------------------------------------------------ +// Build a LUT of all instanced meshes +void OptimizeGraphProcess::FindInstancedMeshes (aiNode* pNode) +{ + for (unsigned int i = 0; i < pNode->mNumMeshes;++i) { + ++meshes[pNode->mMeshes[i]]; + } + + for (unsigned int i = 0; i < pNode->mNumChildren; ++i) + FindInstancedMeshes(pNode->mChildren[i]); +} + +#endif // !! ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS diff --git a/thirdparty/assimp/code/OptimizeGraph.h b/thirdparty/assimp/code/OptimizeGraph.h new file mode 100644 index 0000000000..e5bbed7679 --- /dev/null +++ b/thirdparty/assimp/code/OptimizeGraph.h @@ -0,0 +1,145 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file OptimizeGraph.h + * @brief Declares a post processing step to optimize the scenegraph + */ +#ifndef AI_OPTIMIZEGRAPHPROCESS_H_INC +#define AI_OPTIMIZEGRAPHPROCESS_H_INC + +#include "BaseProcess.h" +#include "ProcessHelper.h" +#include <assimp/types.h> +#include <set> + +struct aiMesh; +class OptimizeGraphProcessTest; +namespace Assimp { + +// ----------------------------------------------------------------------------- +/** @brief Postprocessing step to optimize the scenegraph + * + * The implementation tries to merge nodes, even if they use different + * transformations. Animations are preserved. + * + * @see aiProcess_OptimizeGraph for a detailed description of the + * algorithm being applied. + */ +class OptimizeGraphProcess : public BaseProcess +{ +public: + + OptimizeGraphProcess(); + ~OptimizeGraphProcess(); + +public: + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + void SetupProperties(const Importer* pImp); + + + // ------------------------------------------------------------------- + /** @brief Add a list of node names to be locked and not modified. + * @param in List of nodes. See #AI_CONFIG_PP_OG_EXCLUDE_LIST for + * format explanations. + */ + inline void AddLockedNodeList(std::string& in) + { + ConvertListToStrings (in,locked_nodes); + } + + // ------------------------------------------------------------------- + /** @brief Add another node to be locked and not modified. + * @param name Name to be locked + */ + inline void AddLockedNode(std::string& name) + { + locked_nodes.push_back(name); + } + + // ------------------------------------------------------------------- + /** @brief Remove a node from the list of locked nodes. + * @param name Name to be unlocked + */ + inline void RemoveLockedNode(std::string& name) + { + locked_nodes.remove(name); + } + +protected: + + void CollectNewChildren(aiNode* nd, std::list<aiNode*>& nodes); + void FindInstancedMeshes (aiNode* pNode); + +private: + +#ifdef AI_OG_USE_HASHING + typedef std::set<unsigned int> LockedSetType; +#else + typedef std::set<std::string> LockedSetType; +#endif + + + //! Scene we're working with + aiScene* mScene; + + //! List of locked names. Stored is the hash of the name + LockedSetType locked; + + //! List of nodes to be locked in addition to those with animations, lights or cameras assigned. + std::list<std::string> locked_nodes; + + //! Node counters for logging purposes + unsigned int nodes_in,nodes_out, count_merged; + + //! Reference counters for meshes + std::vector<unsigned int> meshes; +}; + +} // end of namespace Assimp + +#endif // AI_OPTIMIZEGRAPHPROCESS_H_INC diff --git a/thirdparty/assimp/code/OptimizeMeshes.cpp b/thirdparty/assimp/code/OptimizeMeshes.cpp new file mode 100644 index 0000000000..3f6765f6ca --- /dev/null +++ b/thirdparty/assimp/code/OptimizeMeshes.cpp @@ -0,0 +1,256 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file OptimizeMeshes.cpp + * @brief Implementation of the aiProcess_OptimizeMeshes step + */ + + +#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS + + +#include "OptimizeMeshes.h" +#include "ProcessHelper.h" +#include <assimp/SceneCombiner.h> +#include <assimp/Exceptional.h> + +using namespace Assimp; + +static const unsigned int NotSet = 0xffffffff; +static const unsigned int DeadBeef = 0xdeadbeef; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +OptimizeMeshesProcess::OptimizeMeshesProcess() + : mScene() + , pts(false) + , max_verts( NotSet ) + , max_faces( NotSet ) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +OptimizeMeshesProcess::~OptimizeMeshesProcess() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool OptimizeMeshesProcess::IsActive( unsigned int pFlags) const +{ + // Our behaviour needs to be different if the SortByPType or SplitLargeMeshes + // steps are active. Thus we need to query their flags here and store the + // information, although we're breaking const-correctness. + // That's a serious design flaw, consider redesign. + if( 0 != (pFlags & aiProcess_OptimizeMeshes) ) { + pts = (0 != (pFlags & aiProcess_SortByPType)); + max_verts = ( 0 != ( pFlags & aiProcess_SplitLargeMeshes ) ) ? DeadBeef : max_verts; + return true; + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties for the post-processing step +void OptimizeMeshesProcess::SetupProperties(const Importer* pImp) +{ + if( max_verts == DeadBeef /* magic hack */ ) { + max_faces = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); + max_verts = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); + } +} + +// ------------------------------------------------------------------------------------------------ +// Execute step +void OptimizeMeshesProcess::Execute( aiScene* pScene) +{ + const unsigned int num_old = pScene->mNumMeshes; + if (num_old <= 1) { + ASSIMP_LOG_DEBUG("Skipping OptimizeMeshesProcess"); + return; + } + + ASSIMP_LOG_DEBUG("OptimizeMeshesProcess begin"); + mScene = pScene; + + // need to clear persistent members from previous runs + merge_list.resize( 0 ); + output.resize( 0 ); + + // ensure we have the right sizes + merge_list.reserve(pScene->mNumMeshes); + output.reserve(pScene->mNumMeshes); + + // Prepare lookup tables + meshes.resize(pScene->mNumMeshes); + FindInstancedMeshes(pScene->mRootNode); + if( max_verts == DeadBeef ) /* undo the magic hack */ + max_verts = NotSet; + + // ... instanced meshes are immediately processed and added to the output list + for (unsigned int i = 0, n = 0; i < pScene->mNumMeshes;++i) { + meshes[i].vertex_format = GetMeshVFormatUnique(pScene->mMeshes[i]); + + if (meshes[i].instance_cnt > 1 && meshes[i].output_id == NotSet ) { + meshes[i].output_id = n++; + output.push_back(mScene->mMeshes[i]); + } + } + + // and process all nodes in the scenegraph recursively + ProcessNode(pScene->mRootNode); + if (!output.size()) { + throw DeadlyImportError("OptimizeMeshes: No meshes remaining; there's definitely something wrong"); + } + + meshes.resize( 0 ); + ai_assert(output.size() <= num_old); + + mScene->mNumMeshes = static_cast<unsigned int>(output.size()); + std::copy(output.begin(),output.end(),mScene->mMeshes); + + if (output.size() != num_old) { + ASSIMP_LOG_DEBUG_F("OptimizeMeshesProcess finished. Input meshes: ", num_old, ", Output meshes: ", pScene->mNumMeshes); + } else { + ASSIMP_LOG_DEBUG( "OptimizeMeshesProcess finished" ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Process meshes for a single node +void OptimizeMeshesProcess::ProcessNode( aiNode* pNode) +{ + for (unsigned int i = 0; i < pNode->mNumMeshes;++i) { + unsigned int& im = pNode->mMeshes[i]; + + if (meshes[im].instance_cnt > 1) { + im = meshes[im].output_id; + } + else { + merge_list.resize( 0 ); + unsigned int verts = 0, faces = 0; + + // Find meshes to merge with us + for (unsigned int a = i+1; a < pNode->mNumMeshes;++a) { + unsigned int am = pNode->mMeshes[a]; + if (meshes[am].instance_cnt == 1 && CanJoin(im,am,verts,faces)) { + + merge_list.push_back(mScene->mMeshes[am]); + verts += mScene->mMeshes[am]->mNumVertices; + faces += mScene->mMeshes[am]->mNumFaces; + + pNode->mMeshes[a] = pNode->mMeshes[pNode->mNumMeshes - 1]; + --pNode->mNumMeshes; + --a; + } + } + + // and merge all meshes which we found, replace the old ones + if (!merge_list.empty()) { + merge_list.push_back(mScene->mMeshes[im]); + + aiMesh* out; + SceneCombiner::MergeMeshes(&out,0,merge_list.begin(),merge_list.end()); + output.push_back(out); + } else { + output.push_back(mScene->mMeshes[im]); + } + im = static_cast<unsigned int>(output.size()-1); + } + } + + + for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) { + ProcessNode( pNode->mChildren[ i ] ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Check whether two meshes can be joined +bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned int verts, unsigned int faces ) +{ + if (meshes[a].vertex_format != meshes[b].vertex_format) + return false; + + aiMesh* ma = mScene->mMeshes[a], *mb = mScene->mMeshes[b]; + + if ((NotSet != max_verts && verts+mb->mNumVertices > max_verts) || + (NotSet != max_faces && faces+mb->mNumFaces > max_faces)) { + return false; + } + + // Never merge unskinned meshes with skinned meshes + if (ma->mMaterialIndex != mb->mMaterialIndex || ma->HasBones() != mb->HasBones()) + return false; + + // Never merge meshes with different kinds of primitives if SortByPType did already + // do its work. We would destroy everything again ... + if (pts && ma->mPrimitiveTypes != mb->mPrimitiveTypes) + return false; + + // If both meshes are skinned, check whether we have many bones defined in both meshes. + // If yes, we can join them. + if (ma->HasBones()) { + // TODO + return false; + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Build a LUT of all instanced meshes +void OptimizeMeshesProcess::FindInstancedMeshes (aiNode* pNode) +{ + for( unsigned int i = 0; i < pNode->mNumMeshes; ++i ) { + ++meshes[ pNode->mMeshes[ i ] ].instance_cnt; + } + + for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) { + FindInstancedMeshes( pNode->mChildren[ i ] ); + } +} + +// ------------------------------------------------------------------------------------------------ + +#endif // !! ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS diff --git a/thirdparty/assimp/code/OptimizeMeshes.h b/thirdparty/assimp/code/OptimizeMeshes.h new file mode 100644 index 0000000000..9f46f349b4 --- /dev/null +++ b/thirdparty/assimp/code/OptimizeMeshes.h @@ -0,0 +1,186 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file OptimizeMeshes.h + * @brief Declares a post processing step to join meshes, if possible + */ +#ifndef AI_OPTIMIZEMESHESPROCESS_H_INC +#define AI_OPTIMIZEMESHESPROCESS_H_INC + +#include "BaseProcess.h" +#include <assimp/types.h> +#include <vector> + +struct aiMesh; +struct aiNode; +class OptimizeMeshesProcessTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** @brief Postprocessing step to optimize mesh usage + * + * The implementation looks for meshes that could be joined and joins them. + * Usually this will reduce the number of drawcalls. + * + * @note Instanced meshes are currently not processed. + */ +class OptimizeMeshesProcess : public BaseProcess +{ +public: + /// @brief The class constructor. + OptimizeMeshesProcess(); + + /// @brief The class destcructor, + ~OptimizeMeshesProcess(); + + + /** @brief Internal utility to store additional mesh info + */ + struct MeshInfo { + MeshInfo() AI_NO_EXCEPT + : instance_cnt(0) + , vertex_format(0) + , output_id(0xffffffff) { + // empty + } + + //! Number of times this mesh is referenced + unsigned int instance_cnt; + + //! Vertex format id + unsigned int vertex_format; + + //! Output ID + unsigned int output_id; + }; + +public: + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + void SetupProperties(const Importer* pImp); + + + // ------------------------------------------------------------------- + /** @brief Specify whether you want meshes with different + * primitive types to be merged as well. + * + * IsActive() sets this property automatically to true if the + * aiProcess_SortByPType flag is found. + */ + void EnablePrimitiveTypeSorting(bool enable) { + pts = enable; + } + + // Getter + bool IsPrimitiveTypeSortingEnabled () const { + return pts; + } + + + // ------------------------------------------------------------------- + /** @brief Specify a maximum size of a single output mesh. + * + * If a single input mesh already exceeds this limit, it won't + * be split. + * @param verts Maximum number of vertices per mesh + * @param faces Maximum number of faces per mesh + */ + void SetPreferredMeshSizeLimit (unsigned int verts, unsigned int faces) + { + max_verts = verts; + max_faces = faces; + } + + +protected: + + // ------------------------------------------------------------------- + /** @brief Do the actual optimization on all meshes of this node + * @param pNode Node we're working with + */ + void ProcessNode( aiNode* pNode); + + // ------------------------------------------------------------------- + /** @brief Returns true if b can be joined with a + * + * @param verts Number of output verts up to now + * @param faces Number of output faces up to now + */ + bool CanJoin ( unsigned int a, unsigned int b, + unsigned int verts, unsigned int faces ); + + // ------------------------------------------------------------------- + /** @brief Find instanced meshes, for the moment we're excluding + * them from all optimizations + */ + void FindInstancedMeshes (aiNode* pNode); + +private: + + //! Scene we're working with + aiScene* mScene; + + //! Per mesh info + std::vector<MeshInfo> meshes; + + //! Output meshes + std::vector<aiMesh*> output; + + //! @see EnablePrimitiveTypeSorting + mutable bool pts; + + //! @see SetPreferredMeshSizeLimit + mutable unsigned int max_verts,max_faces; + + //! Temporary storage + std::vector<aiMesh*> merge_list; +}; + +} // end of namespace Assimp + +#endif // AI_CALCTANGENTSPROCESS_H_INC diff --git a/thirdparty/assimp/code/PolyTools.h b/thirdparty/assimp/code/PolyTools.h new file mode 100644 index 0000000000..fbbda0e7d1 --- /dev/null +++ b/thirdparty/assimp/code/PolyTools.h @@ -0,0 +1,229 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file PolyTools.h, various utilities for our dealings with arbitrary polygons */ + +#ifndef AI_POLYTOOLS_H_INCLUDED +#define AI_POLYTOOLS_H_INCLUDED + +#include <assimp/material.h> +#include <assimp/ai_assert.h> + +namespace Assimp { + +// ------------------------------------------------------------------------------- +/** Compute the signed area of a triangle. + * The function accepts an unconstrained template parameter for use with + * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ +template <typename T> +inline double GetArea2D(const T& v1, const T& v2, const T& v3) +{ + return 0.5 * (v1.x * ((double)v3.y - v2.y) + v2.x * ((double)v1.y - v3.y) + v3.x * ((double)v2.y - v1.y)); +} + +// ------------------------------------------------------------------------------- +/** Test if a given point p2 is on the left side of the line formed by p0-p1. + * The function accepts an unconstrained template parameter for use with + * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ +template <typename T> +inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2) +{ + return GetArea2D(p0,p2,p1) > 0; +} + +// ------------------------------------------------------------------------------- +/** Test if a given point is inside a given triangle in R2. + * The function accepts an unconstrained template parameter for use with + * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ +template <typename T> +inline bool PointInTriangle2D(const T& p0, const T& p1,const T& p2, const T& pp) +{ + // Point in triangle test using baryzentric coordinates + const aiVector2D v0 = p1 - p0; + const aiVector2D v1 = p2 - p0; + const aiVector2D v2 = pp - p0; + + double dot00 = v0 * v0; + double dot01 = v0 * v1; + double dot02 = v0 * v2; + double dot11 = v1 * v1; + double dot12 = v1 * v2; + + const double invDenom = 1 / (dot00 * dot11 - dot01 * dot01); + dot11 = (dot11 * dot02 - dot01 * dot12) * invDenom; + dot00 = (dot00 * dot12 - dot01 * dot02) * invDenom; + + return (dot11 > 0) && (dot00 > 0) && (dot11 + dot00 < 1); +} + + +// ------------------------------------------------------------------------------- +/** Check whether the winding order of a given polygon is counter-clockwise. + * The function accepts an unconstrained template parameter, but is intended + * to be used only with aiVector2D and aiVector3D (z axis is ignored, only + * x and y are taken into account). + * @note Code taken from http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/applet1.html and translated to C++ + */ +template <typename T> +inline bool IsCCW(T* in, size_t npoints) { + double aa, bb, cc, b, c, theta; + double convex_turn; + double convex_sum = 0; + + ai_assert(npoints >= 3); + + for (size_t i = 0; i < npoints - 2; i++) { + aa = ((in[i+2].x - in[i].x) * (in[i+2].x - in[i].x)) + + ((-in[i+2].y + in[i].y) * (-in[i+2].y + in[i].y)); + + bb = ((in[i+1].x - in[i].x) * (in[i+1].x - in[i].x)) + + ((-in[i+1].y + in[i].y) * (-in[i+1].y + in[i].y)); + + cc = ((in[i+2].x - in[i+1].x) * + (in[i+2].x - in[i+1].x)) + + ((-in[i+2].y + in[i+1].y) * + (-in[i+2].y + in[i+1].y)); + + b = std::sqrt(bb); + c = std::sqrt(cc); + theta = std::acos((bb + cc - aa) / (2 * b * c)); + + if (OnLeftSideOfLine2D(in[i],in[i+2],in[i+1])) { + // if (convex(in[i].x, in[i].y, + // in[i+1].x, in[i+1].y, + // in[i+2].x, in[i+2].y)) { + convex_turn = AI_MATH_PI_F - theta; + convex_sum += convex_turn; + } + else { + convex_sum -= AI_MATH_PI_F - theta; + } + } + aa = ((in[1].x - in[npoints-2].x) * + (in[1].x - in[npoints-2].x)) + + ((-in[1].y + in[npoints-2].y) * + (-in[1].y + in[npoints-2].y)); + + bb = ((in[0].x - in[npoints-2].x) * + (in[0].x - in[npoints-2].x)) + + ((-in[0].y + in[npoints-2].y) * + (-in[0].y + in[npoints-2].y)); + + cc = ((in[1].x - in[0].x) * (in[1].x - in[0].x)) + + ((-in[1].y + in[0].y) * (-in[1].y + in[0].y)); + + b = std::sqrt(bb); + c = std::sqrt(cc); + theta = std::acos((bb + cc - aa) / (2 * b * c)); + + //if (convex(in[npoints-2].x, in[npoints-2].y, + // in[0].x, in[0].y, + // in[1].x, in[1].y)) { + if (OnLeftSideOfLine2D(in[npoints-2],in[1],in[0])) { + convex_turn = AI_MATH_PI_F - theta; + convex_sum += convex_turn; + } + else { + convex_sum -= AI_MATH_PI_F - theta; + } + + return convex_sum >= (2 * AI_MATH_PI_F); +} + + +// ------------------------------------------------------------------------------- +/** Compute the normal of an arbitrary polygon in R3. + * + * The code is based on Newell's formula, that is a polygons normal is the ratio + * of its area when projected onto the three coordinate axes. + * + * @param out Receives the output normal + * @param num Number of input vertices + * @param x X data source. x[ofs_x*n] is the n'th element. + * @param y Y data source. y[ofs_y*n] is the y'th element + * @param z Z data source. z[ofs_z*n] is the z'th element + * + * @note The data arrays must have storage for at least num+2 elements. Using + * this method is much faster than the 'other' NewellNormal() + */ +template <int ofs_x, int ofs_y, int ofs_z, typename TReal> +inline void NewellNormal (aiVector3t<TReal>& out, int num, TReal* x, TReal* y, TReal* z) +{ + // Duplicate the first two vertices at the end + x[(num+0)*ofs_x] = x[0]; + x[(num+1)*ofs_x] = x[ofs_x]; + + y[(num+0)*ofs_y] = y[0]; + y[(num+1)*ofs_y] = y[ofs_y]; + + z[(num+0)*ofs_z] = z[0]; + z[(num+1)*ofs_z] = z[ofs_z]; + + TReal sum_xy = 0.0, sum_yz = 0.0, sum_zx = 0.0; + + TReal *xptr = x +ofs_x, *xlow = x, *xhigh = x + ofs_x*2; + TReal *yptr = y +ofs_y, *ylow = y, *yhigh = y + ofs_y*2; + TReal *zptr = z +ofs_z, *zlow = z, *zhigh = z + ofs_z*2; + + for (int tmp=0; tmp < num; tmp++) { + sum_xy += (*xptr) * ( (*yhigh) - (*ylow) ); + sum_yz += (*yptr) * ( (*zhigh) - (*zlow) ); + sum_zx += (*zptr) * ( (*xhigh) - (*xlow) ); + + xptr += ofs_x; + xlow += ofs_x; + xhigh += ofs_x; + + yptr += ofs_y; + ylow += ofs_y; + yhigh += ofs_y; + + zptr += ofs_z; + zlow += ofs_z; + zhigh += ofs_z; + } + out = aiVector3t<TReal>(sum_yz,sum_zx,sum_xy); +} + +} // ! Assimp + +#endif diff --git a/thirdparty/assimp/code/PostStepRegistry.cpp b/thirdparty/assimp/code/PostStepRegistry.cpp new file mode 100644 index 0000000000..15b4a28843 --- /dev/null +++ b/thirdparty/assimp/code/PostStepRegistry.cpp @@ -0,0 +1,251 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file ImporterRegistry.cpp + +Central registry for all postprocessing steps available. Do not edit this file +directly (unless you are adding new steps), instead use the +corresponding preprocessor flag to selectively disable steps. +*/ + +#include "ProcessHelper.h" + +#ifndef ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS +# include "CalcTangentsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS +# include "JoinVerticesProcess.h" +#endif +#if !(defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS && defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS && defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS) +# include "ConvertToLHProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS +# include "TriangulateProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_DROPFACENORMALS_PROCESS +# include "DropFaceNormalsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS +# include "GenFaceNormalsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS +# include "GenVertexNormalsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_REMOVEVC_PROCESS +# include "RemoveVCProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS +# include "SplitLargeMeshes.h" +#endif +#ifndef ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS +# include "PretransformVertices.h" +#endif +#ifndef ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS +# include "LimitBoneWeightsProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS +# include "ValidateDataStructure.h" +#endif +#ifndef ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS +# include "ImproveCacheLocality.h" +#endif +#ifndef ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS +# include "FixNormalsStep.h" +#endif +#ifndef ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS +# include "RemoveRedundantMaterials.h" +#endif +#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS) +# include "EmbedTexturesProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS +# include "FindInvalidDataProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS +# include "FindDegenerates.h" +#endif +#ifndef ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS +# include "SortByPTypeProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS +# include "ComputeUVMappingProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS +# include "TextureTransform.h" +#endif +#ifndef ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS +# include "FindInstancesProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS +# include "OptimizeMeshes.h" +#endif +#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS +# include "OptimizeGraph.h" +#endif +#ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS +# include "SplitByBoneCountProcess.h" +#endif +#ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS +# include "DeboneProcess.h" +#endif +#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) +# include "ScaleProcess.h" +#endif + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out) +{ + // ---------------------------------------------------------------------------- + // Add an instance of each post processing step here in the order + // of sequence it is executed. Steps that are added here are not + // validated - as RegisterPPStep() does - all dependencies must be given. + // ---------------------------------------------------------------------------- + out.reserve(31); +#if (!defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS) + out.push_back( new MakeLeftHandedProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS) + out.push_back( new FlipUVsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS) + out.push_back( new FlipWindingOrderProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_REMOVEVC_PROCESS) + out.push_back( new RemoveVCProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS) + out.push_back( new RemoveRedundantMatsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS) + out.push_back( new EmbedTexturesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS) + out.push_back( new FindInstancesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS) + out.push_back( new OptimizeGraphProcess()); +#endif +#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS + out.push_back( new ComputeUVMappingProcess()); +#endif +#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS + out.push_back( new TextureTransformStep()); +#endif +#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) + out.push_back( new ScaleProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS) + out.push_back( new PretransformVertices()); +#endif +#if (!defined ASSIMP_BUILD_NO_TRIANGULATE_PROCESS) + out.push_back( new TriangulateProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS) + //find degenerates should run after triangulation (to sort out small + //generated triangles) but before sort by p types (in case there are lines + //and points generated and inserted into a mesh) + out.push_back( new FindDegeneratesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS) + out.push_back( new SortByPTypeProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS) + out.push_back( new FindInvalidDataProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS) + out.push_back( new OptimizeMeshesProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS) + out.push_back( new FixInfacingNormalsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS) + out.push_back( new SplitByBoneCountProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS) + out.push_back( new SplitLargeMeshesProcess_Triangle()); +#endif +#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS) + out.push_back( new DropFaceNormalsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS) + out.push_back( new GenFaceNormalsProcess()); +#endif + // ......................................................................... + // DON'T change the order of these five .. + // XXX this is actually a design weakness that dates back to the time + // when Importer would maintain the postprocessing step list exclusively. + // Now that others access it too, we need a better solution. + out.push_back( new ComputeSpatialSortProcess()); + // ......................................................................... + +#if (!defined ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS) + out.push_back( new GenVertexNormalsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS) + out.push_back( new CalcTangentsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_JOINVERTICES_PROCESS) + out.push_back( new JoinVerticesProcess()); +#endif + + // ......................................................................... + out.push_back( new DestroySpatialSortProcess()); + // ......................................................................... + +#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS) + out.push_back( new SplitLargeMeshesProcess_Vertex()); +#endif +#if (!defined ASSIMP_BUILD_NO_DEBONE_PROCESS) + out.push_back( new DeboneProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS) + out.push_back( new LimitBoneWeightsProcess()); +#endif +#if (!defined ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS) + out.push_back( new ImproveCacheLocalityProcess()); +#endif +} + +} diff --git a/thirdparty/assimp/code/PretransformVertices.cpp b/thirdparty/assimp/code/PretransformVertices.cpp new file mode 100644 index 0000000000..52001a0578 --- /dev/null +++ b/thirdparty/assimp/code/PretransformVertices.cpp @@ -0,0 +1,728 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file PretransformVertices.cpp + * @brief Implementation of the "PretransformVertices" post processing step +*/ + + +#include "PretransformVertices.h" +#include "ProcessHelper.h" +#include <assimp/SceneCombiner.h> +#include <assimp/Exceptional.h> + +using namespace Assimp; + +// some array offsets +#define AI_PTVS_VERTEX 0x0 +#define AI_PTVS_FACE 0x1 + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +PretransformVertices::PretransformVertices() +: configKeepHierarchy (false) +, configNormalize(false) +, configTransform(false) +, configTransformation() +, mConfigPointCloud( false ) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +PretransformVertices::~PretransformVertices() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool PretransformVertices::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_PreTransformVertices) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Setup import configuration +void PretransformVertices::SetupProperties(const Importer* pImp) +{ + // Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE, + // AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION + configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY,0)); + configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE,0)); + configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION,0)); + + configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4()); + + mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); +} + +// ------------------------------------------------------------------------------------------------ +// Count the number of nodes +unsigned int PretransformVertices::CountNodes( aiNode* pcNode ) +{ + unsigned int iRet = 1; + for (unsigned int i = 0;i < pcNode->mNumChildren;++i) + { + iRet += CountNodes(pcNode->mChildren[i]); + } + return iRet; +} + +// ------------------------------------------------------------------------------------------------ +// Get a bitwise combination identifying the vertex format of a mesh +unsigned int PretransformVertices::GetMeshVFormat( aiMesh* pcMesh ) +{ + // the vertex format is stored in aiMesh::mBones for later retrieval. + // there isn't a good reason to compute it a few hundred times + // from scratch. The pointer is unused as animations are lost + // during PretransformVertices. + if (pcMesh->mBones) + return (unsigned int)(uint64_t)pcMesh->mBones; + + + const unsigned int iRet = GetMeshVFormatUnique(pcMesh); + + // store the value for later use + pcMesh->mBones = (aiBone**)(uint64_t)iRet; + return iRet; +} + +// ------------------------------------------------------------------------------------------------ +// Count the number of vertices in the whole scene and a given +// material index +void PretransformVertices::CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, + unsigned int iVFormat, unsigned int* piFaces, unsigned int* piVertices) +{ + for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) + { + aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ]; + if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) + { + *piVertices += pcMesh->mNumVertices; + *piFaces += pcMesh->mNumFaces; + } + } + for (unsigned int i = 0;i < pcNode->mNumChildren;++i) + { + CountVerticesAndFaces(pcScene,pcNode->mChildren[i],iMat, + iVFormat,piFaces,piVertices); + } +} + +// ------------------------------------------------------------------------------------------------ +// Collect vertex/face data +void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, + unsigned int iVFormat, aiMesh* pcMeshOut, + unsigned int aiCurrent[2], unsigned int* num_refs) +{ + // No need to multiply if there's no transformation + const bool identity = pcNode->mTransformation.IsIdentity(); + for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) + { + aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ]; + if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) + { + // Decrement mesh reference counter + unsigned int& num_ref = num_refs[pcNode->mMeshes[i]]; + ai_assert(0 != num_ref); + --num_ref; + // Save the name of the last mesh + if (num_ref==0) + { + pcMeshOut->mName = pcMesh->mName; + } + + if (identity) { + // copy positions without modifying them + ::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mVertices, + pcMesh->mNumVertices * sizeof(aiVector3D)); + + if (iVFormat & 0x2) { + // copy normals without modifying them + ::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mNormals, + pcMesh->mNumVertices * sizeof(aiVector3D)); + } + if (iVFormat & 0x4) + { + // copy tangents without modifying them + ::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mTangents, + pcMesh->mNumVertices * sizeof(aiVector3D)); + // copy bitangents without modifying them + ::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mBitangents, + pcMesh->mNumVertices * sizeof(aiVector3D)); + } + } + else + { + // copy positions, transform them to worldspace + for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { + pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n]; + } + aiMatrix4x4 mWorldIT = pcNode->mTransformation; + mWorldIT.Inverse().Transpose(); + + // TODO: implement Inverse() for aiMatrix3x3 + aiMatrix3x3 m = aiMatrix3x3(mWorldIT); + + if (iVFormat & 0x2) + { + // copy normals, transform them to worldspace + for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { + pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] = + (m * pcMesh->mNormals[n]).Normalize(); + } + } + if (iVFormat & 0x4) + { + // copy tangents and bitangents, transform them to worldspace + for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { + pcMeshOut->mTangents [aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mTangents[n]).Normalize(); + pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mBitangents[n]).Normalize(); + } + } + } + unsigned int p = 0; + while (iVFormat & (0x100 << p)) + { + // copy texture coordinates + memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mTextureCoords[p], + pcMesh->mNumVertices * sizeof(aiVector3D)); + ++p; + } + p = 0; + while (iVFormat & (0x1000000 << p)) + { + // copy vertex colors + memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX], + pcMesh->mColors[p], + pcMesh->mNumVertices * sizeof(aiColor4D)); + ++p; + } + // now we need to copy all faces. since we will delete the source mesh afterwards, + // we don't need to reallocate the array of indices except if this mesh is + // referenced multiple times. + for (unsigned int planck = 0;planck < pcMesh->mNumFaces;++planck) + { + aiFace& f_src = pcMesh->mFaces[planck]; + aiFace& f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck]; + + const unsigned int num_idx = f_src.mNumIndices; + + f_dst.mNumIndices = num_idx; + + unsigned int* pi; + if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */ + pi = f_dst.mIndices = f_src.mIndices; + + // offset all vertex indices + for (unsigned int hahn = 0; hahn < num_idx;++hahn){ + pi[hahn] += aiCurrent[AI_PTVS_VERTEX]; + } + } + else { + pi = f_dst.mIndices = new unsigned int[num_idx]; + + // copy and offset all vertex indices + for (unsigned int hahn = 0; hahn < num_idx;++hahn){ + pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX]; + } + } + + // Update the mPrimitiveTypes member of the mesh + switch (pcMesh->mFaces[planck].mNumIndices) + { + case 0x1: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 0x2: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 0x3: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + }; + } + aiCurrent[AI_PTVS_VERTEX] += pcMesh->mNumVertices; + aiCurrent[AI_PTVS_FACE] += pcMesh->mNumFaces; + } + } + + // append all children of us + for (unsigned int i = 0;i < pcNode->mNumChildren;++i) { + CollectData(pcScene,pcNode->mChildren[i],iMat, + iVFormat,pcMeshOut,aiCurrent,num_refs); + } +} + +// ------------------------------------------------------------------------------------------------ +// Get a list of all vertex formats that occur for a given material index +// The output list contains duplicate elements +void PretransformVertices::GetVFormatList( aiScene* pcScene, unsigned int iMat, + std::list<unsigned int>& aiOut) +{ + for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) + { + aiMesh* pcMesh = pcScene->mMeshes[ i ]; + if (iMat == pcMesh->mMaterialIndex) { + aiOut.push_back(GetMeshVFormat(pcMesh)); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Compute the absolute transformation matrices of each node +void PretransformVertices::ComputeAbsoluteTransform( aiNode* pcNode ) +{ + if (pcNode->mParent) { + pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation; + } + + for (unsigned int i = 0;i < pcNode->mNumChildren;++i) { + ComputeAbsoluteTransform(pcNode->mChildren[i]); + } +} + +// ------------------------------------------------------------------------------------------------ +// Apply the node transformation to a mesh +void PretransformVertices::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat) +{ + // Check whether we need to transform the coordinates at all + if (!mat.IsIdentity()) { + + if (mesh->HasPositions()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mVertices[i] = mat * mesh->mVertices[i]; + } + } + if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) { + aiMatrix4x4 mWorldIT = mat; + mWorldIT.Inverse().Transpose(); + + // TODO: implement Inverse() for aiMatrix3x3 + aiMatrix3x3 m = aiMatrix3x3(mWorldIT); + + if (mesh->HasNormals()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize(); + } + } + if (mesh->HasTangentsAndBitangents()) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize(); + mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize(); + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Simple routine to build meshes in worldspace, no further optimization +void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in, + unsigned int numIn, aiNode* node) +{ + // NOTE: + // aiMesh::mNumBones store original source mesh, or UINT_MAX if not a copy + // aiMesh::mBones store reference to abs. transform we multiplied with + + // process meshes + for (unsigned int i = 0; i < node->mNumMeshes;++i) { + aiMesh* mesh = in[node->mMeshes[i]]; + + // check whether we can operate on this mesh + if (!mesh->mBones || *reinterpret_cast<aiMatrix4x4*>(mesh->mBones) == node->mTransformation) { + // yes, we can. + mesh->mBones = reinterpret_cast<aiBone**> (&node->mTransformation); + mesh->mNumBones = UINT_MAX; + } + else { + + // try to find us in the list of newly created meshes + for (unsigned int n = 0; n < out.size(); ++n) { + aiMesh* ctz = out[n]; + if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4*>(ctz->mBones) == node->mTransformation) { + + // ok, use this one. Update node mesh index + node->mMeshes[i] = numIn + n; + } + } + if (node->mMeshes[i] < numIn) { + // Worst case. Need to operate on a full copy of the mesh + ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms"); + aiMesh* ntz; + + const unsigned int tmp = mesh->mNumBones; // + mesh->mNumBones = 0; + SceneCombiner::Copy(&ntz,mesh); + mesh->mNumBones = tmp; + + ntz->mNumBones = node->mMeshes[i]; + ntz->mBones = reinterpret_cast<aiBone**> (&node->mTransformation); + + out.push_back(ntz); + + node->mMeshes[i] = static_cast<unsigned int>(numIn + out.size() - 1); + } + } + } + + // call children + for (unsigned int i = 0; i < node->mNumChildren;++i) + BuildWCSMeshes(out,in,numIn,node->mChildren[i]); +} + +// ------------------------------------------------------------------------------------------------ +// Reset transformation matrices to identity +void PretransformVertices::MakeIdentityTransform(aiNode* nd) +{ + nd->mTransformation = aiMatrix4x4(); + + // call children + for (unsigned int i = 0; i < nd->mNumChildren;++i) + MakeIdentityTransform(nd->mChildren[i]); +} + +// ------------------------------------------------------------------------------------------------ +// Build reference counters for all meshes +void PretransformVertices::BuildMeshRefCountArray(aiNode* nd, unsigned int * refs) +{ + for (unsigned int i = 0; i< nd->mNumMeshes;++i) + refs[nd->mMeshes[i]]++; + + // call children + for (unsigned int i = 0; i < nd->mNumChildren;++i) + BuildMeshRefCountArray(nd->mChildren[i],refs); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void PretransformVertices::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("PretransformVerticesProcess begin"); + + // Return immediately if we have no meshes + if (!pScene->mNumMeshes) + return; + + const unsigned int iOldMeshes = pScene->mNumMeshes; + const unsigned int iOldAnimationChannels = pScene->mNumAnimations; + const unsigned int iOldNodes = CountNodes(pScene->mRootNode); + + if(configTransform) { + pScene->mRootNode->mTransformation = configTransformation; + } + + // first compute absolute transformation matrices for all nodes + ComputeAbsoluteTransform(pScene->mRootNode); + + // Delete aiMesh::mBones for all meshes. The bones are + // removed during this step and we need the pointer as + // temporary storage + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { + aiMesh* mesh = pScene->mMeshes[i]; + + for (unsigned int a = 0; a < mesh->mNumBones;++a) + delete mesh->mBones[a]; + + delete[] mesh->mBones; + mesh->mBones = NULL; + } + + // now build a list of output meshes + std::vector<aiMesh*> apcOutMeshes; + + // Keep scene hierarchy? It's an easy job in this case ... + // we go on and transform all meshes, if one is referenced by nodes + // with different absolute transformations a depth copy of the mesh + // is required. + if( configKeepHierarchy ) { + + // Hack: store the matrix we're transforming a mesh with in aiMesh::mBones + BuildWCSMeshes(apcOutMeshes,pScene->mMeshes,pScene->mNumMeshes, pScene->mRootNode); + + // ... if new meshes have been generated, append them to the end of the scene + if (apcOutMeshes.size() > 0) { + aiMesh** npp = new aiMesh*[pScene->mNumMeshes + apcOutMeshes.size()]; + + memcpy(npp,pScene->mMeshes,sizeof(aiMesh*)*pScene->mNumMeshes); + memcpy(npp+pScene->mNumMeshes,&apcOutMeshes[0],sizeof(aiMesh*)*apcOutMeshes.size()); + + pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size()); + delete[] pScene->mMeshes; pScene->mMeshes = npp; + } + + // now iterate through all meshes and transform them to worldspace + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + ApplyTransform(pScene->mMeshes[i],*reinterpret_cast<aiMatrix4x4*>( pScene->mMeshes[i]->mBones )); + + // prevent improper destruction + pScene->mMeshes[i]->mBones = NULL; + pScene->mMeshes[i]->mNumBones = 0; + } + } else { + apcOutMeshes.reserve(pScene->mNumMaterials<<1u); + std::list<unsigned int> aiVFormats; + + std::vector<unsigned int> s(pScene->mNumMeshes,0); + BuildMeshRefCountArray(pScene->mRootNode,&s[0]); + + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { + // get the list of all vertex formats for this material + aiVFormats.clear(); + GetVFormatList(pScene,i,aiVFormats); + aiVFormats.sort(); + aiVFormats.unique(); + for (std::list<unsigned int>::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) { + unsigned int iVertices = 0; + unsigned int iFaces = 0; + CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices); + if (0 != iFaces && 0 != iVertices) + { + apcOutMeshes.push_back(new aiMesh()); + aiMesh* pcMesh = apcOutMeshes.back(); + pcMesh->mNumFaces = iFaces; + pcMesh->mNumVertices = iVertices; + pcMesh->mFaces = new aiFace[iFaces]; + pcMesh->mVertices = new aiVector3D[iVertices]; + pcMesh->mMaterialIndex = i; + if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices]; + if ((*j) & 0x4) + { + pcMesh->mTangents = new aiVector3D[iVertices]; + pcMesh->mBitangents = new aiVector3D[iVertices]; + } + iFaces = 0; + while ((*j) & (0x100 << iFaces)) + { + pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices]; + if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3; + else pcMesh->mNumUVComponents[iFaces] = 2; + iFaces++; + } + iFaces = 0; + while ((*j) & (0x1000000 << iFaces)) + pcMesh->mColors[iFaces++] = new aiColor4D[iVertices]; + + // fill the mesh ... + unsigned int aiTemp[2] = {0,0}; + CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp,&s[0]); + } + } + } + + // If no meshes are referenced in the node graph it is possible that we get no output meshes. + if (apcOutMeshes.empty()) { + + throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes"); + } + else + { + // now delete all meshes in the scene and build a new mesh list + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + { + aiMesh* mesh = pScene->mMeshes[i]; + mesh->mNumBones = 0; + mesh->mBones = NULL; + + // we're reusing the face index arrays. avoid destruction + for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { + mesh->mFaces[a].mNumIndices = 0; + mesh->mFaces[a].mIndices = NULL; + } + + delete mesh; + + // Invalidate the contents of the old mesh array. We will most + // likely have less output meshes now, so the last entries of + // the mesh array are not overridden. We set them to NULL to + // make sure the developer gets notified when his application + // attempts to access these fields ... + mesh = NULL; + } + + // It is impossible that we have more output meshes than + // input meshes, so we can easily reuse the old mesh array + pScene->mNumMeshes = (unsigned int)apcOutMeshes.size(); + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { + pScene->mMeshes[i] = apcOutMeshes[i]; + } + } + } + + // remove all animations from the scene + for (unsigned int i = 0; i < pScene->mNumAnimations;++i) + delete pScene->mAnimations[i]; + delete[] pScene->mAnimations; + + pScene->mAnimations = NULL; + pScene->mNumAnimations = 0; + + // --- we need to keep all cameras and lights + for (unsigned int i = 0; i < pScene->mNumCameras;++i) + { + aiCamera* cam = pScene->mCameras[i]; + const aiNode* nd = pScene->mRootNode->FindNode(cam->mName); + ai_assert(NULL != nd); + + // multiply all properties of the camera with the absolute + // transformation of the corresponding node + cam->mPosition = nd->mTransformation * cam->mPosition; + cam->mLookAt = aiMatrix3x3( nd->mTransformation ) * cam->mLookAt; + cam->mUp = aiMatrix3x3( nd->mTransformation ) * cam->mUp; + } + + for (unsigned int i = 0; i < pScene->mNumLights;++i) + { + aiLight* l = pScene->mLights[i]; + const aiNode* nd = pScene->mRootNode->FindNode(l->mName); + ai_assert(NULL != nd); + + // multiply all properties of the camera with the absolute + // transformation of the corresponding node + l->mPosition = nd->mTransformation * l->mPosition; + l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection; + l->mUp = aiMatrix3x3( nd->mTransformation ) * l->mUp; + } + + if( !configKeepHierarchy ) { + + // now delete all nodes in the scene and build a new + // flat node graph with a root node and some level 1 children + aiNode* newRoot = new aiNode(); + newRoot->mName = pScene->mRootNode->mName; + delete pScene->mRootNode; + pScene->mRootNode = newRoot; + + if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras) + { + pScene->mRootNode->mNumMeshes = 1; + pScene->mRootNode->mMeshes = new unsigned int[1]; + pScene->mRootNode->mMeshes[0] = 0; + } + else + { + pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras; + aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren]; + + // generate mesh nodes + for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes) + { + aiNode* pcNode = new aiNode(); + *nodes = pcNode; + pcNode->mParent = pScene->mRootNode; + pcNode->mName = pScene->mMeshes[i]->mName; + + // setup mesh indices + pcNode->mNumMeshes = 1; + pcNode->mMeshes = new unsigned int[1]; + pcNode->mMeshes[0] = i; + } + // generate light nodes + for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes) + { + aiNode* pcNode = new aiNode(); + *nodes = pcNode; + pcNode->mParent = pScene->mRootNode; + pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u",i); + pScene->mLights[i]->mName = pcNode->mName; + } + // generate camera nodes + for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes) + { + aiNode* pcNode = new aiNode(); + *nodes = pcNode; + pcNode->mParent = pScene->mRootNode; + pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"cam_%u",i); + pScene->mCameras[i]->mName = pcNode->mName; + } + } + } + else { + // ... and finally set the transformation matrix of all nodes to identity + MakeIdentityTransform(pScene->mRootNode); + } + + if (configNormalize) { + // compute the boundary of all meshes + aiVector3D min,max; + MinMaxChooser<aiVector3D> ()(min,max); + + for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { + aiMesh* m = pScene->mMeshes[a]; + for (unsigned int i = 0; i < m->mNumVertices;++i) { + min = std::min(m->mVertices[i],min); + max = std::max(m->mVertices[i],max); + } + } + + // find the dominant axis + aiVector3D d = max-min; + const ai_real div = std::max(d.x,std::max(d.y,d.z))*ai_real( 0.5); + + d = min + d * (ai_real)0.5; + for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { + aiMesh* m = pScene->mMeshes[a]; + for (unsigned int i = 0; i < m->mNumVertices;++i) { + m->mVertices[i] = (m->mVertices[i]-d)/div; + } + } + } + + // print statistics + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished"); + + ASSIMP_LOG_INFO_F("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (", + CountNodes(pScene->mRootNode) ," output nodes)" ); + ASSIMP_LOG_INFO_F("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras." ); + ASSIMP_LOG_INFO_F("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")"); + } +} diff --git a/thirdparty/assimp/code/PretransformVertices.h b/thirdparty/assimp/code/PretransformVertices.h new file mode 100644 index 0000000000..b7329af130 --- /dev/null +++ b/thirdparty/assimp/code/PretransformVertices.h @@ -0,0 +1,163 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file PretransformVertices.h + * @brief Defines a post processing step to pretransform all + * vertices in the scenegraph + */ +#ifndef AI_PRETRANSFORMVERTICES_H_INC +#define AI_PRETRANSFORMVERTICES_H_INC + +#include "BaseProcess.h" +#include <assimp/mesh.h> +#include <list> +#include <vector> + +struct aiNode; +class PretransformVerticesTest; +namespace Assimp { + +// --------------------------------------------------------------------------- +/** The PretransformVertices pre-transforms all vertices in the node tree + * and removes the whole graph. The output is a list of meshes, one for + * each material. +*/ +class ASSIMP_API PretransformVertices : public BaseProcess { +public: + PretransformVertices (); + ~PretransformVertices (); + + // ------------------------------------------------------------------- + // Check whether step is active + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + // Execute step on a given scene + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + // Setup import settings + void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + /** @brief Toggle the 'keep hierarchy' option + * @param d hm ... difficult to guess what this means, hu!? + */ + void KeepHierarchy(bool d) { + configKeepHierarchy = d; + } + + // ------------------------------------------------------------------- + /** @brief Check whether 'keep hierarchy' is currently enabled. + * @return ... + */ + bool IsHierarchyKept() const { + return configKeepHierarchy; + } + +private: + // ------------------------------------------------------------------- + // Count the number of nodes + unsigned int CountNodes( aiNode* pcNode ); + + // ------------------------------------------------------------------- + // Get a bitwise combination identifying the vertex format of a mesh + unsigned int GetMeshVFormat(aiMesh* pcMesh); + + // ------------------------------------------------------------------- + // Count the number of vertices in the whole scene and a given + // material index + void CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, + unsigned int iMat, + unsigned int iVFormat, + unsigned int* piFaces, + unsigned int* piVertices); + + // ------------------------------------------------------------------- + // Collect vertex/face data + void CollectData( aiScene* pcScene, aiNode* pcNode, + unsigned int iMat, + unsigned int iVFormat, + aiMesh* pcMeshOut, + unsigned int aiCurrent[2], + unsigned int* num_refs); + + // ------------------------------------------------------------------- + // Get a list of all vertex formats that occur for a given material + // The output list contains duplicate elements + void GetVFormatList( aiScene* pcScene, unsigned int iMat, + std::list<unsigned int>& aiOut); + + // ------------------------------------------------------------------- + // Compute the absolute transformation matrices of each node + void ComputeAbsoluteTransform( aiNode* pcNode ); + + // ------------------------------------------------------------------- + // Simple routine to build meshes in worldspace, no further optimization + void BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in, + unsigned int numIn, aiNode* node); + + // ------------------------------------------------------------------- + // Apply the node transformation to a mesh + void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat); + + // ------------------------------------------------------------------- + // Reset transformation matrices to identity + void MakeIdentityTransform(aiNode* nd); + + // ------------------------------------------------------------------- + // Build reference counters for all meshes + void BuildMeshRefCountArray(aiNode* nd, unsigned int * refs); + + + + //! Configuration option: keep scene hierarchy as long as possible + bool configKeepHierarchy; + bool configNormalize; + bool configTransform; + aiMatrix4x4 configTransformation; + bool mConfigPointCloud; +}; + +} // end of namespace Assimp + +#endif // !!AI_GENFACENORMALPROCESS_H_INC diff --git a/thirdparty/assimp/code/ProcessHelper.cpp b/thirdparty/assimp/code/ProcessHelper.cpp new file mode 100644 index 0000000000..59869fdff7 --- /dev/null +++ b/thirdparty/assimp/code/ProcessHelper.cpp @@ -0,0 +1,443 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/// @file ProcessHelper.cpp +/** Implement shared utility functions for postprocessing steps */ + + +#include "ProcessHelper.h" + + +#include <limits> + +namespace Assimp { + +// ------------------------------------------------------------------------------- +void ConvertListToStrings(const std::string& in, std::list<std::string>& out) +{ + const char* s = in.c_str(); + while (*s) { + SkipSpacesAndLineEnd(&s); + if (*s == '\'') { + const char* base = ++s; + while (*s != '\'') { + ++s; + if (*s == '\0') { + ASSIMP_LOG_ERROR("ConvertListToString: String list is ill-formatted"); + return; + } + } + out.push_back(std::string(base,(size_t)(s-base))); + ++s; + } + else { + out.push_back(GetNextToken(s)); + } + } +} + +// ------------------------------------------------------------------------------- +void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, + const aiMatrix4x4& m) +{ + min = aiVector3D ( ai_real( 10e10 ), ai_real( 10e10 ), ai_real( 10e10 ) ); + max = aiVector3D ( ai_real( -10e10 ), ai_real( -10e10 ), ai_real( -10e10 ) ); + for (unsigned int i = 0;i < mesh->mNumVertices;++i) + { + const aiVector3D v = m * mesh->mVertices[i]; + min = std::min(v,min); + max = std::max(v,max); + } +} + +// ------------------------------------------------------------------------------- +void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max) +{ + ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max); + out = min + (max-min)*(ai_real)0.5; +} + +// ------------------------------------------------------------------------------- +void FindSceneCenter (aiScene* scene, aiVector3D& out, aiVector3D& min, aiVector3D& max) { + if ( NULL == scene ) { + return; + } + + if ( 0 == scene->mNumMeshes ) { + return; + } + FindMeshCenter(scene->mMeshes[0], out, min, max); + for (unsigned int i = 1; i < scene->mNumMeshes; ++i) { + aiVector3D tout, tmin, tmax; + FindMeshCenter(scene->mMeshes[i], tout, tmin, tmax); + if (min[0] > tmin[0]) min[0] = tmin[0]; + if (min[1] > tmin[1]) min[1] = tmin[1]; + if (min[2] > tmin[2]) min[2] = tmin[2]; + if (max[0] < tmax[0]) max[0] = tmax[0]; + if (max[1] < tmax[1]) max[1] = tmax[1]; + if (max[2] < tmax[2]) max[2] = tmax[2]; + } + out = min + (max-min)*(ai_real)0.5; +} + + +// ------------------------------------------------------------------------------- +void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min, + aiVector3D& max, const aiMatrix4x4& m) +{ + FindAABBTransformed(mesh,min,max,m); + out = min + (max-min)*(ai_real)0.5; +} + +// ------------------------------------------------------------------------------- +void FindMeshCenter (aiMesh* mesh, aiVector3D& out) +{ + aiVector3D min,max; + FindMeshCenter(mesh,out,min,max); +} + +// ------------------------------------------------------------------------------- +void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, + const aiMatrix4x4& m) +{ + aiVector3D min,max; + FindMeshCenterTransformed(mesh,out,min,max,m); +} + +// ------------------------------------------------------------------------------- +ai_real ComputePositionEpsilon(const aiMesh* pMesh) +{ + const ai_real epsilon = ai_real( 1e-4 ); + + // calculate the position bounds so we have a reliable epsilon to check position differences against + aiVector3D minVec, maxVec; + ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec); + return (maxVec - minVec).Length() * epsilon; +} + +// ------------------------------------------------------------------------------- +ai_real ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num) +{ + ai_assert( NULL != pMeshes ); + + const ai_real epsilon = ai_real( 1e-4 ); + + // calculate the position bounds so we have a reliable epsilon to check position differences against + aiVector3D minVec, maxVec, mi, ma; + MinMaxChooser<aiVector3D>()(minVec,maxVec); + + for (size_t a = 0; a < num; ++a) { + const aiMesh* pMesh = pMeshes[a]; + ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,mi,ma); + + minVec = std::min(minVec,mi); + maxVec = std::max(maxVec,ma); + } + return (maxVec - minVec).Length() * epsilon; +} + + +// ------------------------------------------------------------------------------- +unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh) +{ + ai_assert(NULL != pcMesh); + + // FIX: the hash may never be 0. Otherwise a comparison against + // nullptr could be successful + unsigned int iRet = 1; + + // normals + if (pcMesh->HasNormals())iRet |= 0x2; + // tangents and bitangents + if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4; + +#ifdef BOOST_STATIC_ASSERT + BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS); + BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS); +#endif + + // texture coordinates + unsigned int p = 0; + while (pcMesh->HasTextureCoords(p)) + { + iRet |= (0x100 << p); + if (3 == pcMesh->mNumUVComponents[p]) + iRet |= (0x10000 << p); + + ++p; + } + // vertex colors + p = 0; + while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++); + return iRet; +} + +// ------------------------------------------------------------------------------- +VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) +{ + if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) { + return NULL; + } + + VertexWeightTable* avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices]; + for (unsigned int i = 0; i < pMesh->mNumBones;++i) { + + aiBone* bone = pMesh->mBones[i]; + for (unsigned int a = 0; a < bone->mNumWeights;++a) { + const aiVertexWeight& weight = bone->mWeights[a]; + avPerVertexWeights[weight.mVertexId].push_back( std::pair<unsigned int,float>(i,weight.mWeight) ); + } + } + return avPerVertexWeights; +} + + +// ------------------------------------------------------------------------------- +const char* TextureTypeToString(aiTextureType in) +{ + switch (in) + { + case aiTextureType_NONE: + return "n/a"; + case aiTextureType_DIFFUSE: + return "Diffuse"; + case aiTextureType_SPECULAR: + return "Specular"; + case aiTextureType_AMBIENT: + return "Ambient"; + case aiTextureType_EMISSIVE: + return "Emissive"; + case aiTextureType_OPACITY: + return "Opacity"; + case aiTextureType_NORMALS: + return "Normals"; + case aiTextureType_HEIGHT: + return "Height"; + case aiTextureType_SHININESS: + return "Shininess"; + case aiTextureType_DISPLACEMENT: + return "Displacement"; + case aiTextureType_LIGHTMAP: + return "Lightmap"; + case aiTextureType_REFLECTION: + return "Reflection"; + case aiTextureType_UNKNOWN: + return "Unknown"; + default: + break; + } + + ai_assert(false); + return "BUG"; +} + +// ------------------------------------------------------------------------------- +const char* MappingTypeToString(aiTextureMapping in) +{ + switch (in) + { + case aiTextureMapping_UV: + return "UV"; + case aiTextureMapping_BOX: + return "Box"; + case aiTextureMapping_SPHERE: + return "Sphere"; + case aiTextureMapping_CYLINDER: + return "Cylinder"; + case aiTextureMapping_PLANE: + return "Plane"; + case aiTextureMapping_OTHER: + return "Other"; + default: + break; + } + + ai_assert(false); + return "BUG"; +} + + +// ------------------------------------------------------------------------------- +aiMesh* MakeSubmesh(const aiMesh *pMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags) +{ + aiMesh *oMesh = new aiMesh(); + std::vector<unsigned int> vMap(pMesh->mNumVertices,UINT_MAX); + + size_t numSubVerts = 0; + size_t numSubFaces = subMeshFaces.size(); + + for(unsigned int i=0;i<numSubFaces;i++) { + const aiFace &f = pMesh->mFaces[subMeshFaces[i]]; + + for(unsigned int j=0;j<f.mNumIndices;j++) { + if(vMap[f.mIndices[j]]==UINT_MAX) { + vMap[f.mIndices[j]] = static_cast<unsigned int>(numSubVerts++); + } + } + } + + oMesh->mName = pMesh->mName; + + oMesh->mMaterialIndex = pMesh->mMaterialIndex; + oMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes; + + // create all the arrays for this mesh if the old mesh contained them + + oMesh->mNumFaces = static_cast<unsigned int>(subMeshFaces.size()); + oMesh->mNumVertices = static_cast<unsigned int>(numSubVerts); + oMesh->mVertices = new aiVector3D[numSubVerts]; + if( pMesh->HasNormals() ) { + oMesh->mNormals = new aiVector3D[numSubVerts]; + } + + if( pMesh->HasTangentsAndBitangents() ) { + oMesh->mTangents = new aiVector3D[numSubVerts]; + oMesh->mBitangents = new aiVector3D[numSubVerts]; + } + + for( size_t a = 0; pMesh->HasTextureCoords(static_cast<unsigned int>(a)) ; ++a ) { + oMesh->mTextureCoords[a] = new aiVector3D[numSubVerts]; + oMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a]; + } + + for( size_t a = 0; pMesh->HasVertexColors( static_cast<unsigned int>(a)); ++a ) { + oMesh->mColors[a] = new aiColor4D[numSubVerts]; + } + + // and copy over the data, generating faces with linear indices along the way + oMesh->mFaces = new aiFace[numSubFaces]; + + for(unsigned int a = 0; a < numSubFaces; ++a ) { + + const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]]; + aiFace& dstFace = oMesh->mFaces[a]; + dstFace.mNumIndices = srcFace.mNumIndices; + dstFace.mIndices = new unsigned int[dstFace.mNumIndices]; + + // accumulate linearly all the vertices of the source face + for( size_t b = 0; b < dstFace.mNumIndices; ++b ) { + dstFace.mIndices[b] = vMap[srcFace.mIndices[b]]; + } + } + + for(unsigned int srcIndex = 0; srcIndex < pMesh->mNumVertices; ++srcIndex ) { + unsigned int nvi = vMap[srcIndex]; + if(nvi==UINT_MAX) { + continue; + } + + oMesh->mVertices[nvi] = pMesh->mVertices[srcIndex]; + if( pMesh->HasNormals() ) { + oMesh->mNormals[nvi] = pMesh->mNormals[srcIndex]; + } + + if( pMesh->HasTangentsAndBitangents() ) { + oMesh->mTangents[nvi] = pMesh->mTangents[srcIndex]; + oMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex]; + } + for( size_t c = 0, cc = pMesh->GetNumUVChannels(); c < cc; ++c ) { + oMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex]; + } + for( size_t c = 0, cc = pMesh->GetNumColorChannels(); c < cc; ++c ) { + oMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex]; + } + } + + if(~subFlags&AI_SUBMESH_FLAGS_SANS_BONES) { + std::vector<unsigned int> subBones(pMesh->mNumBones,0); + + for(unsigned int a=0;a<pMesh->mNumBones;++a) { + const aiBone* bone = pMesh->mBones[a]; + + for(unsigned int b=0;b<bone->mNumWeights;b++) { + unsigned int v = vMap[bone->mWeights[b].mVertexId]; + + if(v!=UINT_MAX) { + subBones[a]++; + } + } + } + + for(unsigned int a=0;a<pMesh->mNumBones;++a) { + if(subBones[a]>0) { + oMesh->mNumBones++; + } + } + + if(oMesh->mNumBones) { + oMesh->mBones = new aiBone*[oMesh->mNumBones](); + unsigned int nbParanoia = oMesh->mNumBones; + + oMesh->mNumBones = 0; //rewind + + for(unsigned int a=0;a<pMesh->mNumBones;++a) { + if(subBones[a]==0) { + continue; + } + aiBone *newBone = new aiBone; + oMesh->mBones[oMesh->mNumBones++] = newBone; + + const aiBone* bone = pMesh->mBones[a]; + + newBone->mName = bone->mName; + newBone->mOffsetMatrix = bone->mOffsetMatrix; + newBone->mWeights = new aiVertexWeight[subBones[a]]; + + for(unsigned int b=0;b<bone->mNumWeights;b++) { + const unsigned int v = vMap[bone->mWeights[b].mVertexId]; + + if(v!=UINT_MAX) { + aiVertexWeight w(v,bone->mWeights[b].mWeight); + newBone->mWeights[newBone->mNumWeights++] = w; + } + } + } + + ai_assert(nbParanoia==oMesh->mNumBones); + (void)nbParanoia; // remove compiler warning on release build + } + } + + return oMesh; +} + +} // namespace Assimp diff --git a/thirdparty/assimp/code/ProcessHelper.h b/thirdparty/assimp/code/ProcessHelper.h new file mode 100644 index 0000000000..c59f3217bf --- /dev/null +++ b/thirdparty/assimp/code/ProcessHelper.h @@ -0,0 +1,386 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_PROCESS_HELPER_H_INCLUDED +#define AI_PROCESS_HELPER_H_INCLUDED + +#include <assimp/postprocess.h> +#include <assimp/anim.h> +#include <assimp/mesh.h> +#include <assimp/material.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/scene.h> + +#include <assimp/SpatialSort.h> +#include "BaseProcess.h" +#include <assimp/ParsingUtils.h> + +#include <list> + +// ------------------------------------------------------------------------------- +// Some extensions to std namespace. Mainly std::min and std::max for all +// flat data types in the aiScene. They're used to quickly determine the +// min/max bounds of data arrays. +#ifdef __cplusplus +namespace std { + + // std::min for aiVector3D + template <typename TReal> + inline ::aiVector3t<TReal> min (const ::aiVector3t<TReal>& a, const ::aiVector3t<TReal>& b) { + return ::aiVector3t<TReal> (min(a.x,b.x),min(a.y,b.y),min(a.z,b.z)); + } + + // std::max for aiVector3t<TReal> + template <typename TReal> + inline ::aiVector3t<TReal> max (const ::aiVector3t<TReal>& a, const ::aiVector3t<TReal>& b) { + return ::aiVector3t<TReal> (max(a.x,b.x),max(a.y,b.y),max(a.z,b.z)); + } + + // std::min for aiVector2t<TReal> + template <typename TReal> + inline ::aiVector2t<TReal> min (const ::aiVector2t<TReal>& a, const ::aiVector2t<TReal>& b) { + return ::aiVector2t<TReal> (min(a.x,b.x),min(a.y,b.y)); + } + + // std::max for aiVector2t<TReal> + template <typename TReal> + inline ::aiVector2t<TReal> max (const ::aiVector2t<TReal>& a, const ::aiVector2t<TReal>& b) { + return ::aiVector2t<TReal> (max(a.x,b.x),max(a.y,b.y)); + } + + // std::min for aiColor4D + template <typename TReal> + inline ::aiColor4t<TReal> min (const ::aiColor4t<TReal>& a, const ::aiColor4t<TReal>& b) { + return ::aiColor4t<TReal> (min(a.r,b.r),min(a.g,b.g),min(a.b,b.b),min(a.a,b.a)); + } + + // std::max for aiColor4D + template <typename TReal> + inline ::aiColor4t<TReal> max (const ::aiColor4t<TReal>& a, const ::aiColor4t<TReal>& b) { + return ::aiColor4t<TReal> (max(a.r,b.r),max(a.g,b.g),max(a.b,b.b),max(a.a,b.a)); + } + + + // std::min for aiQuaterniont<TReal> + template <typename TReal> + inline ::aiQuaterniont<TReal> min (const ::aiQuaterniont<TReal>& a, const ::aiQuaterniont<TReal>& b) { + return ::aiQuaterniont<TReal> (min(a.w,b.w),min(a.x,b.x),min(a.y,b.y),min(a.z,b.z)); + } + + // std::max for aiQuaterniont<TReal> + template <typename TReal> + inline ::aiQuaterniont<TReal> max (const ::aiQuaterniont<TReal>& a, const ::aiQuaterniont<TReal>& b) { + return ::aiQuaterniont<TReal> (max(a.w,b.w),max(a.x,b.x),max(a.y,b.y),max(a.z,b.z)); + } + + + + // std::min for aiVectorKey + inline ::aiVectorKey min (const ::aiVectorKey& a, const ::aiVectorKey& b) { + return ::aiVectorKey (min(a.mTime,b.mTime),min(a.mValue,b.mValue)); + } + + // std::max for aiVectorKey + inline ::aiVectorKey max (const ::aiVectorKey& a, const ::aiVectorKey& b) { + return ::aiVectorKey (max(a.mTime,b.mTime),max(a.mValue,b.mValue)); + } + + // std::min for aiQuatKey + inline ::aiQuatKey min (const ::aiQuatKey& a, const ::aiQuatKey& b) { + return ::aiQuatKey (min(a.mTime,b.mTime),min(a.mValue,b.mValue)); + } + + // std::max for aiQuatKey + inline ::aiQuatKey max (const ::aiQuatKey& a, const ::aiQuatKey& b) { + return ::aiQuatKey (max(a.mTime,b.mTime),max(a.mValue,b.mValue)); + } + + // std::min for aiVertexWeight + inline ::aiVertexWeight min (const ::aiVertexWeight& a, const ::aiVertexWeight& b) { + return ::aiVertexWeight (min(a.mVertexId,b.mVertexId),min(a.mWeight,b.mWeight)); + } + + // std::max for aiVertexWeight + inline ::aiVertexWeight max (const ::aiVertexWeight& a, const ::aiVertexWeight& b) { + return ::aiVertexWeight (max(a.mVertexId,b.mVertexId),max(a.mWeight,b.mWeight)); + } + +} // end namespace std +#endif // !! C++ + +namespace Assimp { + +// ------------------------------------------------------------------------------- +// Start points for ArrayBounds<T> for all supported Ts +template <typename T> +struct MinMaxChooser; + +template <> struct MinMaxChooser<float> { + void operator ()(float& min,float& max) { + max = -1e10f; + min = 1e10f; +}}; +template <> struct MinMaxChooser<double> { + void operator ()(double& min,double& max) { + max = -1e10; + min = 1e10; +}}; +template <> struct MinMaxChooser<unsigned int> { + void operator ()(unsigned int& min,unsigned int& max) { + max = 0; + min = (1u<<(sizeof(unsigned int)*8-1)); +}}; + +template <typename T> struct MinMaxChooser< aiVector3t<T> > { + void operator ()(aiVector3t<T>& min,aiVector3t<T>& max) { + max = aiVector3t<T>(-1e10f,-1e10f,-1e10f); + min = aiVector3t<T>( 1e10f, 1e10f, 1e10f); +}}; +template <typename T> struct MinMaxChooser< aiVector2t<T> > { + void operator ()(aiVector2t<T>& min,aiVector2t<T>& max) { + max = aiVector2t<T>(-1e10f,-1e10f); + min = aiVector2t<T>( 1e10f, 1e10f); + }}; +template <typename T> struct MinMaxChooser< aiColor4t<T> > { + void operator ()(aiColor4t<T>& min,aiColor4t<T>& max) { + max = aiColor4t<T>(-1e10f,-1e10f,-1e10f,-1e10f); + min = aiColor4t<T>( 1e10f, 1e10f, 1e10f, 1e10f); +}}; + +template <typename T> struct MinMaxChooser< aiQuaterniont<T> > { + void operator ()(aiQuaterniont<T>& min,aiQuaterniont<T>& max) { + max = aiQuaterniont<T>(-1e10f,-1e10f,-1e10f,-1e10f); + min = aiQuaterniont<T>( 1e10f, 1e10f, 1e10f, 1e10f); +}}; + +template <> struct MinMaxChooser<aiVectorKey> { + void operator ()(aiVectorKey& min,aiVectorKey& max) { + MinMaxChooser<double>()(min.mTime,max.mTime); + MinMaxChooser<aiVector3D>()(min.mValue,max.mValue); +}}; +template <> struct MinMaxChooser<aiQuatKey> { + void operator ()(aiQuatKey& min,aiQuatKey& max) { + MinMaxChooser<double>()(min.mTime,max.mTime); + MinMaxChooser<aiQuaternion>()(min.mValue,max.mValue); +}}; + +template <> struct MinMaxChooser<aiVertexWeight> { + void operator ()(aiVertexWeight& min,aiVertexWeight& max) { + MinMaxChooser<unsigned int>()(min.mVertexId,max.mVertexId); + MinMaxChooser<float>()(min.mWeight,max.mWeight); +}}; + +// ------------------------------------------------------------------------------- +/** @brief Find the min/max values of an array of Ts + * @param in Input array + * @param size Number of elements to process + * @param[out] min minimum value + * @param[out] max maximum value + */ +template <typename T> +inline void ArrayBounds(const T* in, unsigned int size, T& min, T& max) +{ + MinMaxChooser<T> ()(min,max); + for (unsigned int i = 0; i < size;++i) { + min = std::min(in[i],min); + max = std::max(in[i],max); + } +} + + +// ------------------------------------------------------------------------------- +/** Little helper function to calculate the quadratic difference + * of two colours. + * @param pColor1 First color + * @param pColor2 second color + * @return Quadratic color difference */ +inline ai_real GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pColor2) +{ + const aiColor4D c (pColor1.r - pColor2.r, pColor1.g - pColor2.g, pColor1.b - pColor2.b, pColor1.a - pColor2.a); + return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a; +} + + +// ------------------------------------------------------------------------------- +/** @brief Extract single strings from a list of identifiers + * @param in Input string list. + * @param out Receives a list of clean output strings + * @sdee #AI_CONFIG_PP_OG_EXCLUDE_LIST */ +void ConvertListToStrings(const std::string& in, std::list<std::string>& out); + + +// ------------------------------------------------------------------------------- +/** @brief Compute the AABB of a mesh after applying a given transform + * @param mesh Input mesh + * @param[out] min Receives minimum transformed vertex + * @param[out] max Receives maximum transformed vertex + * @param m Transformation matrix to be applied */ +void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, const aiMatrix4x4& m); + + +// ------------------------------------------------------------------------------- +/** @brief Helper function to determine the 'real' center of a mesh + * + * That is the center of its axis-aligned bounding box. + * @param mesh Input mesh + * @param[out] min Minimum vertex of the mesh + * @param[out] max maximum vertex of the mesh + * @param[out] out Center point */ +void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max); + +// ------------------------------------------------------------------------------- +/** @brief Helper function to determine the 'real' center of a scene + * + * That is the center of its axis-aligned bounding box. + * @param scene Input scene + * @param[out] min Minimum vertex of the scene + * @param[out] max maximum vertex of the scene + * @param[out] out Center point */ +void FindSceneCenter (aiScene* scene, aiVector3D& out, aiVector3D& min, aiVector3D& max); + + +// ------------------------------------------------------------------------------- +// Helper function to determine the 'real' center of a mesh after applying a given transform +void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,aiVector3D& max, const aiMatrix4x4& m); + + +// ------------------------------------------------------------------------------- +// Helper function to determine the 'real' center of a mesh +void FindMeshCenter (aiMesh* mesh, aiVector3D& out); + + +// ------------------------------------------------------------------------------- +// Helper function to determine the 'real' center of a mesh after applying a given transform +void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,const aiMatrix4x4& m); + + +// ------------------------------------------------------------------------------- +// Compute a good epsilon value for position comparisons on a mesh +ai_real ComputePositionEpsilon(const aiMesh* pMesh); + + +// ------------------------------------------------------------------------------- +// Compute a good epsilon value for position comparisons on a array of meshes +ai_real ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num); + + +// ------------------------------------------------------------------------------- +// Compute an unique value for the vertex format of a mesh +unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh); + + +// defs for ComputeVertexBoneWeightTable() +typedef std::pair <unsigned int,float> PerVertexWeight; +typedef std::vector <PerVertexWeight> VertexWeightTable; + +// ------------------------------------------------------------------------------- +// Compute a per-vertex bone weight table +VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh); + + +// ------------------------------------------------------------------------------- +// Get a string for a given aiTextureType +const char* TextureTypeToString(aiTextureType in); + + +// ------------------------------------------------------------------------------- +// Get a string for a given aiTextureMapping +const char* MappingTypeToString(aiTextureMapping in); + + +// flags for MakeSubmesh() +#define AI_SUBMESH_FLAGS_SANS_BONES 0x1 + +// ------------------------------------------------------------------------------- +// Split a mesh given a list of faces to be contained in the sub mesh +aiMesh* MakeSubmesh(const aiMesh *superMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags); + +// ------------------------------------------------------------------------------- +// Utility postprocess step to share the spatial sort tree between +// all steps which use it to speedup its computations. +class ComputeSpatialSortProcess : public BaseProcess +{ + bool IsActive( unsigned int pFlags) const + { + return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace | + aiProcess_GenNormals | aiProcess_JoinIdenticalVertices)); + } + + void Execute( aiScene* pScene) + { + typedef std::pair<SpatialSort, ai_real> _Type; + ASSIMP_LOG_DEBUG("Generate spatially-sorted vertex cache"); + + std::vector<_Type>* p = new std::vector<_Type>(pScene->mNumMeshes); + std::vector<_Type>::iterator it = p->begin(); + + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i, ++it) { + aiMesh* mesh = pScene->mMeshes[i]; + _Type& blubb = *it; + blubb.first.Fill(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D)); + blubb.second = ComputePositionEpsilon(mesh); + } + + shared->AddProperty(AI_SPP_SPATIAL_SORT,p); + } +}; + +// ------------------------------------------------------------------------------- +// ... and the same again to cleanup the whole stuff +class DestroySpatialSortProcess : public BaseProcess +{ + bool IsActive( unsigned int pFlags) const + { + return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace | + aiProcess_GenNormals | aiProcess_JoinIdenticalVertices)); + } + + void Execute( aiScene* /*pScene*/) + { + shared->RemoveProperty(AI_SPP_SPATIAL_SORT); + } +}; + + + +} // ! namespace Assimp +#endif // !! AI_PROCESS_HELPER_H_INCLUDED diff --git a/thirdparty/assimp/code/RawLoader.cpp b/thirdparty/assimp/code/RawLoader.cpp new file mode 100644 index 0000000000..d0da247e47 --- /dev/null +++ b/thirdparty/assimp/code/RawLoader.cpp @@ -0,0 +1,331 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file RawLoader.cpp + * @brief Implementation of the RAW importer class + */ + + +#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER + +// internal headers +#include "RawLoader.h" +#include <assimp/ParsingUtils.h> +#include <assimp/fast_atof.h> +#include <memory> +#include <assimp/IOSystem.hpp> +#include <assimp/DefaultLogger.hpp> +#include <assimp/scene.h> +#include <assimp/importerdesc.h> + +using namespace Assimp; + +static const aiImporterDesc desc = { + "Raw Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "raw" +}; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +RAWImporter::RAWImporter() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +RAWImporter::~RAWImporter() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool RAWImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const +{ + return SimpleExtensionCheck(pFile,"raw"); +} + +// ------------------------------------------------------------------------------------------------ +const aiImporterDesc* RAWImporter::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void RAWImporter::InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) +{ + std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); + + // Check whether we can read from the file + if( file.get() == NULL) { + throw DeadlyImportError( "Failed to open RAW file " + pFile + "."); + } + + // allocate storage and copy the contents of the file to a memory buffer + // (terminate it with zero) + std::vector<char> mBuffer2; + TextFileToBuffer(file.get(),mBuffer2); + const char* buffer = &mBuffer2[0]; + + // list of groups loaded from the file + std::vector< GroupInformation > outGroups(1,GroupInformation("<default>")); + std::vector< GroupInformation >::iterator curGroup = outGroups.begin(); + + // now read all lines + char line[4096]; + while (GetNextLine(buffer,line)) + { + // if the line starts with a non-numeric identifier, it marks + // the beginning of a new group + const char* sz = line;SkipSpaces(&sz); + if (IsLineEnd(*sz))continue; + if (!IsNumeric(*sz)) + { + const char* sz2 = sz; + while (!IsSpaceOrNewLine(*sz2))++sz2; + const unsigned int length = (unsigned int)(sz2-sz); + + // find an existing group with this name + for (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end(); + it != end;++it) + { + if (length == (*it).name.length() && !::strcmp(sz,(*it).name.c_str())) + { + curGroup = it;sz2 = NULL; + break; + } + } + if (sz2) + { + outGroups.push_back(GroupInformation(std::string(sz,length))); + curGroup = outGroups.end()-1; + } + } + else + { + // there can be maximally 12 floats plus an extra texture file name + float data[12]; + unsigned int num; + for (num = 0; num < 12;++num) + { + if(!SkipSpaces(&sz) || !IsNumeric(*sz))break; + sz = fast_atoreal_move<float>(sz,data[num]); + } + if (num != 12 && num != 9) + { + ASSIMP_LOG_ERROR("A line may have either 9 or 12 floats and an optional texture"); + continue; + } + + MeshInformation* output = NULL; + + const char* sz2 = sz; + unsigned int length; + if (!IsLineEnd(*sz)) + { + while (!IsSpaceOrNewLine(*sz2))++sz2; + length = (unsigned int)(sz2-sz); + } + else if (9 == num) + { + sz = "%default%"; + length = 9; + } + else + { + sz = ""; + length = 0; + } + + // search in the list of meshes whether we have one with this texture + for (auto &mesh : (*curGroup).meshes) + { + if (length == mesh.name.length() && (length ? !::strcmp(sz, mesh.name.c_str()) : true)) + { + output = &mesh; + break; + } + } + // if we don't have the mesh, create it + if (!output) + { + (*curGroup).meshes.push_back(MeshInformation(std::string(sz,length))); + output = &((*curGroup).meshes.back()); + } + if (12 == num) + { + aiColor4D v(data[0],data[1],data[2],1.0f); + output->colors.push_back(v); + output->colors.push_back(v); + output->colors.push_back(v); + + output->vertices.push_back(aiVector3D(data[3],data[4],data[5])); + output->vertices.push_back(aiVector3D(data[6],data[7],data[8])); + output->vertices.push_back(aiVector3D(data[9],data[10],data[11])); + } + else + { + output->vertices.push_back(aiVector3D(data[0],data[1],data[2])); + output->vertices.push_back(aiVector3D(data[3],data[4],data[5])); + output->vertices.push_back(aiVector3D(data[6],data[7],data[8])); + } + } + } + + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mName.Set("<RawRoot>"); + + // count the number of valid groups + // (meshes can't be empty) + for (auto & outGroup : outGroups) + { + if (!outGroup.meshes.empty()) + { + ++pScene->mRootNode->mNumChildren; + pScene->mNumMeshes += (unsigned int) outGroup.meshes.size(); + } + } + + if (!pScene->mNumMeshes) + { + throw DeadlyImportError("RAW: No meshes loaded. The file seems to be corrupt or empty."); + } + + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + aiNode** cc; + if (1 == pScene->mRootNode->mNumChildren) + { + cc = &pScene->mRootNode; + pScene->mRootNode->mNumChildren = 0; + } else { + cc = new aiNode*[pScene->mRootNode->mNumChildren]; + memset(cc, 0, sizeof(aiNode*) * pScene->mRootNode->mNumChildren); + pScene->mRootNode->mChildren = cc; + } + + pScene->mNumMaterials = pScene->mNumMeshes; + aiMaterial** mats = pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; + + unsigned int meshIdx = 0; + for (auto & outGroup : outGroups) + { + if (outGroup.meshes.empty())continue; + + aiNode* node; + if (pScene->mRootNode->mNumChildren) + { + node = *cc = new aiNode(); + node->mParent = pScene->mRootNode; + } + else node = *cc; + node->mName.Set(outGroup.name); + + // add all meshes + node->mNumMeshes = (unsigned int) outGroup.meshes.size(); + unsigned int* pi = node->mMeshes = new unsigned int[ node->mNumMeshes ]; + for (std::vector< MeshInformation >::iterator it2 = outGroup.meshes.begin(), + end2 = outGroup.meshes.end(); it2 != end2; ++it2) + { + ai_assert(!(*it2).vertices.empty()); + + // allocate the mesh + *pi++ = meshIdx; + aiMesh* mesh = pScene->mMeshes[meshIdx] = new aiMesh(); + mesh->mMaterialIndex = meshIdx++; + + mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + // allocate storage for the vertex components and copy them + mesh->mNumVertices = (unsigned int)(*it2).vertices.size(); + mesh->mVertices = new aiVector3D[ mesh->mNumVertices ]; + ::memcpy(mesh->mVertices,&(*it2).vertices[0],sizeof(aiVector3D)*mesh->mNumVertices); + + if ((*it2).colors.size()) + { + ai_assert((*it2).colors.size() == mesh->mNumVertices); + + mesh->mColors[0] = new aiColor4D[ mesh->mNumVertices ]; + ::memcpy(mesh->mColors[0],&(*it2).colors[0],sizeof(aiColor4D)*mesh->mNumVertices); + } + + // generate triangles + ai_assert(0 == mesh->mNumVertices % 3); + aiFace* fc = mesh->mFaces = new aiFace[ mesh->mNumFaces = mesh->mNumVertices/3 ]; + aiFace* const fcEnd = fc + mesh->mNumFaces; + unsigned int n = 0; + while (fc != fcEnd) + { + aiFace& f = *fc++; + f.mIndices = new unsigned int[f.mNumIndices = 3]; + for (unsigned int m = 0; m < 3;++m) + f.mIndices[m] = n++; + } + + // generate a material for the mesh + aiMaterial* mat = new aiMaterial(); + + aiColor4D clr(1.0f,1.0f,1.0f,1.0f); + if ("%default%" == (*it2).name) // a gray default material + { + clr.r = clr.g = clr.b = 0.6f; + } + else if ((*it2).name.length() > 0) // a texture + { + aiString s; + s.Set((*it2).name); + mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); + } + mat->AddProperty<aiColor4D>(&clr,1,AI_MATKEY_COLOR_DIFFUSE); + *mats++ = mat; + } + } +} + +#endif // !! ASSIMP_BUILD_NO_RAW_IMPORTER diff --git a/thirdparty/assimp/code/RemoveComments.cpp b/thirdparty/assimp/code/RemoveComments.cpp new file mode 100644 index 0000000000..91700a7699 --- /dev/null +++ b/thirdparty/assimp/code/RemoveComments.cpp @@ -0,0 +1,113 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file RemoveComments.cpp + * @brief Defines the CommentRemover utility class + */ + +#include <assimp/RemoveComments.h> +#include <assimp/ParsingUtils.h> + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +// Remove line comments from a file +void CommentRemover::RemoveLineComments(const char* szComment, + char* szBuffer, char chReplacement /* = ' ' */) +{ + // validate parameters + ai_assert(NULL != szComment && NULL != szBuffer && *szComment); + + const size_t len = strlen(szComment); + while (*szBuffer) { + + // skip over quotes + if (*szBuffer == '\"' || *szBuffer == '\'') + while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\''); + + if (!strncmp(szBuffer,szComment,len)) { + while (!IsLineEnd(*szBuffer)) + *szBuffer++ = chReplacement; + + if (!*szBuffer) { + break; + } + } + ++szBuffer; + } +} + +// ------------------------------------------------------------------------------------------------ +// Remove multi-line comments from a file +void CommentRemover::RemoveMultiLineComments(const char* szCommentStart, + const char* szCommentEnd,char* szBuffer, + char chReplacement) +{ + // validate parameters + ai_assert(NULL != szCommentStart && NULL != szCommentEnd && + NULL != szBuffer && *szCommentStart && *szCommentEnd); + + const size_t len = strlen(szCommentEnd); + const size_t len2 = strlen(szCommentStart); + + while (*szBuffer) { + // skip over quotes + if (*szBuffer == '\"' || *szBuffer == '\'') + while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\''); + + if (!strncmp(szBuffer,szCommentStart,len2)) { + while (*szBuffer) { + if (!::strncmp(szBuffer,szCommentEnd,len)) { + for (unsigned int i = 0; i < len;++i) + *szBuffer++ = chReplacement; + + break; + } + *szBuffer++ = chReplacement; + } + continue; + } + ++szBuffer; + } +} + +} // !! Assimp diff --git a/thirdparty/assimp/code/RemoveRedundantMaterials.cpp b/thirdparty/assimp/code/RemoveRedundantMaterials.cpp new file mode 100644 index 0000000000..632bdca3fe --- /dev/null +++ b/thirdparty/assimp/code/RemoveRedundantMaterials.cpp @@ -0,0 +1,221 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file RemoveRedundantMaterials.cpp + * @brief Implementation of the "RemoveRedundantMaterials" post processing step +*/ + +// internal headers + +#include "RemoveRedundantMaterials.h" +#include <assimp/ParsingUtils.h> +#include "ProcessHelper.h" +#include "MaterialSystem.h" +#include <stdio.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +RemoveRedundantMatsProcess::RemoveRedundantMatsProcess() +: configFixedMaterials() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +RemoveRedundantMatsProcess::~RemoveRedundantMatsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_RemoveRedundantMaterials) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Setup import properties +void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp) +{ + // Get value of AI_CONFIG_PP_RRM_EXCLUDE_LIST + configFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,""); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void RemoveRedundantMatsProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess begin"); + + unsigned int redundantRemoved = 0, unreferencedRemoved = 0; + if (pScene->mNumMaterials) + { + // Find out which materials are referenced by meshes + std::vector<bool> abReferenced(pScene->mNumMaterials,false); + for (unsigned int i = 0;i < pScene->mNumMeshes;++i) + abReferenced[pScene->mMeshes[i]->mMaterialIndex] = true; + + // If a list of materials to be excluded was given, match the list with + // our imported materials and 'salt' all positive matches to ensure that + // we get unique hashes later. + if (configFixedMaterials.length()) { + + std::list<std::string> strings; + ConvertListToStrings(configFixedMaterials,strings); + + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { + aiMaterial* mat = pScene->mMaterials[i]; + + aiString name; + mat->Get(AI_MATKEY_NAME,name); + + if (name.length) { + std::list<std::string>::const_iterator it = std::find(strings.begin(), strings.end(), name.data); + if (it != strings.end()) { + + // Our brilliant 'salt': A single material property with ~ as first + // character to mark it as internal and temporary. + const int dummy = 1; + ((aiMaterial*)mat)->AddProperty(&dummy,1,"~RRM.UniqueMaterial",0,0); + + // Keep this material even if no mesh references it + abReferenced[i] = true; + ASSIMP_LOG_DEBUG_F( "Found positive match in exclusion list: \'", name.data, "\'"); + } + } + } + } + + // TODO: re-implement this algorithm to work in-place + unsigned int *aiMappingTable = new unsigned int[pScene->mNumMaterials]; + for ( unsigned int i=0; i<pScene->mNumMaterials; i++ ) { + aiMappingTable[ i ] = 0; + } + unsigned int iNewNum = 0; + + // Iterate through all materials and calculate a hash for them + // store all hashes in a list and so a quick search whether + // we do already have a specific hash. This allows us to + // determine which materials are identical. + uint32_t *aiHashes = new uint32_t[ pScene->mNumMaterials ];; + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) + { + // No mesh is referencing this material, remove it. + if (!abReferenced[i]) { + ++unreferencedRemoved; + delete pScene->mMaterials[i]; + pScene->mMaterials[i] = nullptr; + continue; + } + + // Check all previously mapped materials for a matching hash. + // On a match we can delete this material and just make it ref to the same index. + uint32_t me = aiHashes[i] = ComputeMaterialHash(pScene->mMaterials[i]); + for (unsigned int a = 0; a < i;++a) + { + if (abReferenced[a] && me == aiHashes[a]) { + ++redundantRemoved; + me = 0; + aiMappingTable[i] = aiMappingTable[a]; + delete pScene->mMaterials[i]; + pScene->mMaterials[i] = nullptr; + break; + } + } + // This is a new material that is referenced, add to the map. + if (me) { + aiMappingTable[i] = iNewNum++; + } + } + // If the new material count differs from the original, + // we need to rebuild the material list and remap mesh material indexes. + if (iNewNum != pScene->mNumMaterials) { + ai_assert(iNewNum > 0); + aiMaterial** ppcMaterials = new aiMaterial*[iNewNum]; + ::memset(ppcMaterials,0,sizeof(void*)*iNewNum); + for (unsigned int p = 0; p < pScene->mNumMaterials;++p) + { + // if the material is not referenced ... remove it + if (!abReferenced[p]) { + continue; + } + + // generate new names for modified materials that had no names + const unsigned int idx = aiMappingTable[p]; + if (ppcMaterials[idx]) { + aiString sz; + if( ppcMaterials[idx]->Get(AI_MATKEY_NAME, sz) != AI_SUCCESS ) { + sz.length = ::ai_snprintf(sz.data,MAXLEN,"JoinedMaterial_#%u",p); + ((aiMaterial*)ppcMaterials[idx])->AddProperty(&sz,AI_MATKEY_NAME); + } + } else { + ppcMaterials[idx] = pScene->mMaterials[p]; + } + } + // update all material indices + for (unsigned int p = 0; p < pScene->mNumMeshes;++p) { + aiMesh* mesh = pScene->mMeshes[p]; + ai_assert( NULL!=mesh ); + mesh->mMaterialIndex = aiMappingTable[mesh->mMaterialIndex]; + } + // delete the old material list + delete[] pScene->mMaterials; + pScene->mMaterials = ppcMaterials; + pScene->mNumMaterials = iNewNum; + } + // delete temporary storage + delete[] aiHashes; + delete[] aiMappingTable; + } + if (redundantRemoved == 0 && unreferencedRemoved == 0) + { + ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess finished "); + } + else + { + ASSIMP_LOG_INFO_F("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", + unreferencedRemoved, " unused materials."); + } +} diff --git a/thirdparty/assimp/code/RemoveRedundantMaterials.h b/thirdparty/assimp/code/RemoveRedundantMaterials.h new file mode 100644 index 0000000000..dbd4d44cc0 --- /dev/null +++ b/thirdparty/assimp/code/RemoveRedundantMaterials.h @@ -0,0 +1,107 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file RemoveRedundantMaterials.h + * @brief Defines a post processing step to remove redundant materials + */ +#ifndef AI_REMOVEREDUNDANTMATERIALS_H_INC +#define AI_REMOVEREDUNDANTMATERIALS_H_INC + +#include "BaseProcess.h" +#include <assimp/mesh.h> + +class RemoveRedundantMatsTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** RemoveRedundantMatsProcess: Post-processing step to remove redundant + * materials from the imported scene. + */ +class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess +{ +public: + /// The default class constructor. + RemoveRedundantMatsProcess(); + + /// The class destructor. + ~RemoveRedundantMatsProcess(); + +public: + // ------------------------------------------------------------------- + // Check whether step is active + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + // Execute step on a given scene + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + // Setup import settings + void SetupProperties(const Importer* pImp); + + + // ------------------------------------------------------------------- + /** @brief Set list of fixed (unmutable) materials + * @param fixed See #AI_CONFIG_PP_RRM_EXCLUDE_LIST + */ + void SetFixedMaterialsString(const std::string& fixed = "") { + configFixedMaterials = fixed; + } + + // ------------------------------------------------------------------- + /** @brief Get list of fixed (unmutable) materials + * @return See #AI_CONFIG_PP_RRM_EXCLUDE_LIST + */ + const std::string& GetFixedMaterialsString() const { + return configFixedMaterials; + } + +private: + + //! Configuration option: list of all fixed materials + std::string configFixedMaterials; +}; + +} // end of namespace Assimp + +#endif // !!AI_REMOVEREDUNDANTMATERIALS_H_INC diff --git a/thirdparty/assimp/code/RemoveVCProcess.cpp b/thirdparty/assimp/code/RemoveVCProcess.cpp new file mode 100644 index 0000000000..99fd47a3aa --- /dev/null +++ b/thirdparty/assimp/code/RemoveVCProcess.cpp @@ -0,0 +1,337 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file Implementation of the post processing step to remove + * any parts of the mesh structure from the imported data. +*/ + + +#include "RemoveVCProcess.h" +#include <assimp/postprocess.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/scene.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +RemoveVCProcess::RemoveVCProcess() : + configDeleteFlags() + , mScene() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +RemoveVCProcess::~RemoveVCProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool RemoveVCProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_RemoveComponent) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Small helper function to delete all elements in a T** aray using delete +template <typename T> +inline void ArrayDelete(T**& in, unsigned int& num) +{ + for (unsigned int i = 0; i < num; ++i) + delete in[i]; + + delete[] in; + in = NULL; + num = 0; +} + +#if 0 +// ------------------------------------------------------------------------------------------------ +// Updates the node graph - removes all nodes which have the "remove" flag set and the +// "don't remove" flag not set. Nodes with meshes are never deleted. +bool UpdateNodeGraph(aiNode* node,std::list<aiNode*>& childsOfParent,bool root) +{ + bool b = false; + + std::list<aiNode*> mine; + for (unsigned int i = 0; i < node->mNumChildren;++i) + { + if(UpdateNodeGraph(node->mChildren[i],mine,false)) + b = true; + } + + // somewhat tricky ... mNumMeshes must be originally 0 and MSB2 may not be set, + // so we can do a simple comparison against MSB here + if (!root && AI_RC_UINT_MSB == node->mNumMeshes ) + { + // this node needs to be removed + if(node->mNumChildren) + { + childsOfParent.insert(childsOfParent.end(),mine.begin(),mine.end()); + + // set all children to NULL to make sure they are not deleted when we delete ourself + for (unsigned int i = 0; i < node->mNumChildren;++i) + node->mChildren[i] = NULL; + } + b = true; + delete node; + } + else + { + AI_RC_UNMASK(node->mNumMeshes); + childsOfParent.push_back(node); + + if (b) + { + // reallocate the array of our children here + node->mNumChildren = (unsigned int)mine.size(); + aiNode** const children = new aiNode*[mine.size()]; + aiNode** ptr = children; + + for (std::list<aiNode*>::iterator it = mine.begin(), end = mine.end(); + it != end; ++it) + { + *ptr++ = *it; + } + delete[] node->mChildren; + node->mChildren = children; + return false; + } + } + return b; +} +#endif + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void RemoveVCProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("RemoveVCProcess begin"); + bool bHas = false; //,bMasked = false; + + mScene = pScene; + + // handle animations + if ( configDeleteFlags & aiComponent_ANIMATIONS) + { + + bHas = true; + ArrayDelete(pScene->mAnimations,pScene->mNumAnimations); + } + + // handle textures + if ( configDeleteFlags & aiComponent_TEXTURES) + { + bHas = true; + ArrayDelete(pScene->mTextures,pScene->mNumTextures); + } + + // handle materials + if ( configDeleteFlags & aiComponent_MATERIALS && pScene->mNumMaterials) + { + bHas = true; + for (unsigned int i = 1;i < pScene->mNumMaterials;++i) + delete pScene->mMaterials[i]; + + pScene->mNumMaterials = 1; + aiMaterial* helper = (aiMaterial*) pScene->mMaterials[0]; + ai_assert(NULL != helper); + helper->Clear(); + + // gray + aiColor3D clr(0.6f,0.6f,0.6f); + helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE); + + // add a small ambient color value + clr = aiColor3D(0.05f,0.05f,0.05f); + helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT); + + aiString s; + s.Set("Dummy_MaterialsRemoved"); + helper->AddProperty(&s,AI_MATKEY_NAME); + } + + // handle light sources + if ( configDeleteFlags & aiComponent_LIGHTS) + { + bHas = true; + ArrayDelete(pScene->mLights,pScene->mNumLights); + } + + // handle camneras + if ( configDeleteFlags & aiComponent_CAMERAS) + { + bHas = true; + ArrayDelete(pScene->mCameras,pScene->mNumCameras); + } + + // handle meshes + if (configDeleteFlags & aiComponent_MESHES) + { + bHas = true; + ArrayDelete(pScene->mMeshes,pScene->mNumMeshes); + } + else + { + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + { + if( ProcessMesh( pScene->mMeshes[a])) + bHas = true; + } + } + + + // now check whether the result is still a full scene + if (!pScene->mNumMeshes || !pScene->mNumMaterials) + { + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + ASSIMP_LOG_DEBUG("Setting AI_SCENE_FLAGS_INCOMPLETE flag"); + + // If we have no meshes anymore we should also clear another flag ... + if (!pScene->mNumMeshes) + pScene->mFlags &= ~AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; + } + + if (bHas) { + ASSIMP_LOG_INFO("RemoveVCProcess finished. Data structure cleanup has been done."); + } else { + ASSIMP_LOG_DEBUG("RemoveVCProcess finished. Nothing to be done ..."); + } +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration properties for the step +void RemoveVCProcess::SetupProperties(const Importer* pImp) +{ + configDeleteFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS,0x0); + if (!configDeleteFlags) + { + ASSIMP_LOG_WARN("RemoveVCProcess: AI_CONFIG_PP_RVC_FLAGS is zero."); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh) +{ + bool ret = false; + + // if all materials have been deleted let the material + // index of the mesh point to the created default material + if ( configDeleteFlags & aiComponent_MATERIALS) + pMesh->mMaterialIndex = 0; + + // handle normals + if (configDeleteFlags & aiComponent_NORMALS && pMesh->mNormals) + { + delete[] pMesh->mNormals; + pMesh->mNormals = NULL; + ret = true; + } + + // handle tangents and bitangents + if (configDeleteFlags & aiComponent_TANGENTS_AND_BITANGENTS && pMesh->mTangents) + { + delete[] pMesh->mTangents; + pMesh->mTangents = NULL; + + delete[] pMesh->mBitangents; + pMesh->mBitangents = NULL; + ret = true; + } + + // handle texture coordinates + bool b = (0 != (configDeleteFlags & aiComponent_TEXCOORDS)); + for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++real) + { + if (!pMesh->mTextureCoords[i])break; + if (configDeleteFlags & aiComponent_TEXCOORDSn(real) || b) + { + delete [] pMesh->mTextureCoords[i]; + pMesh->mTextureCoords[i] = NULL; + ret = true; + + if (!b) + { + // collapse the rest of the array + for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) + pMesh->mTextureCoords[a-1] = pMesh->mTextureCoords[a]; + + pMesh->mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS-1] = NULL; + continue; + } + } + ++i; + } + + // handle vertex colors + b = (0 != (configDeleteFlags & aiComponent_COLORS)); + for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_COLOR_SETS; ++real) + { + if (!pMesh->mColors[i])break; + if (configDeleteFlags & aiComponent_COLORSn(i) || b) + { + delete [] pMesh->mColors[i]; + pMesh->mColors[i] = NULL; + ret = true; + + if (!b) + { + // collapse the rest of the array + for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) + pMesh->mColors[a-1] = pMesh->mColors[a]; + + pMesh->mColors[AI_MAX_NUMBER_OF_COLOR_SETS-1] = NULL; + continue; + } + } + ++i; + } + + // handle bones + if (configDeleteFlags & aiComponent_BONEWEIGHTS && pMesh->mBones) + { + ArrayDelete(pMesh->mBones,pMesh->mNumBones); + ret = true; + } + return ret; +} diff --git a/thirdparty/assimp/code/RemoveVCProcess.h b/thirdparty/assimp/code/RemoveVCProcess.h new file mode 100644 index 0000000000..617d7b9b20 --- /dev/null +++ b/thirdparty/assimp/code/RemoveVCProcess.h @@ -0,0 +1,123 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to remove specific parts of the scene */ +#ifndef AI_REMOVEVCPROCESS_H_INCLUDED +#define AI_REMOVEVCPROCESS_H_INCLUDED + +#include "BaseProcess.h" +#include <assimp/mesh.h> + +class RemoveVCProcessTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** RemoveVCProcess: Class to exclude specific parts of the data structure + * from further processing by removing them, +*/ +class ASSIMP_API RemoveVCProcess : public BaseProcess { +public: + /// The default class constructor. + RemoveVCProcess(); + + /// The class destructor. + ~RemoveVCProcess(); + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + /** Manually setup the configuration flags for the step + * + * @param Bitwise combination of the #aiComponent enumerated values. + */ + void SetDeleteFlags(unsigned int f) + { + configDeleteFlags = f; + } + + // ------------------------------------------------------------------- + /** Query the current configuration. + */ + unsigned int GetDeleteFlags() const + { + return configDeleteFlags; + } + +private: + + bool ProcessMesh (aiMesh* pcMesh); + + /** Configuration flag + */ + unsigned int configDeleteFlags; + + /** The scene we're working with + */ + aiScene* mScene; +}; + +// --------------------------------------------------------------------------- + +} // end of namespace Assimp + +#endif // !!AI_REMOVEVCPROCESS_H_INCLUDED diff --git a/thirdparty/assimp/code/SGSpatialSort.cpp b/thirdparty/assimp/code/SGSpatialSort.cpp new file mode 100644 index 0000000000..120070b0aa --- /dev/null +++ b/thirdparty/assimp/code/SGSpatialSort.cpp @@ -0,0 +1,168 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the helper class to quickly find +vertices close to a given position. Special implementation for +the 3ds loader handling smooth groups correctly */ + +#include <assimp/SGSpatialSort.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +SGSpatialSort::SGSpatialSort() +{ + // define the reference plane. We choose some arbitrary vector away from all basic axises + // in the hope that no model spreads all its vertices along this plane. + mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f); + mPlaneNormal.Normalize(); +} +// ------------------------------------------------------------------------------------------------ +// Destructor +SGSpatialSort::~SGSpatialSort() +{ + // nothing to do here, everything destructs automatically +} +// ------------------------------------------------------------------------------------------------ +void SGSpatialSort::Add(const aiVector3D& vPosition, unsigned int index, + unsigned int smoothingGroup) +{ + // store position by index and distance + float distance = vPosition * mPlaneNormal; + mPositions.push_back( Entry( index, vPosition, + distance, smoothingGroup)); +} +// ------------------------------------------------------------------------------------------------ +void SGSpatialSort::Prepare() +{ + // now sort the array ascending by distance. + std::sort( this->mPositions.begin(), this->mPositions.end()); +} +// ------------------------------------------------------------------------------------------------ +// Returns an iterator for all positions close to the given position. +void SGSpatialSort::FindPositions( const aiVector3D& pPosition, + uint32_t pSG, + float pRadius, + std::vector<unsigned int>& poResults, + bool exactMatch /*= false*/) const +{ + float dist = pPosition * mPlaneNormal; + float minDist = dist - pRadius, maxDist = dist + pRadius; + + // clear the array + poResults.clear(); + + // quick check for positions outside the range + if( mPositions.empty() ) + return; + if( maxDist < mPositions.front().mDistance) + return; + if( minDist > mPositions.back().mDistance) + return; + + // do a binary search for the minimal distance to start the iteration there + unsigned int index = (unsigned int)mPositions.size() / 2; + unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; + while( binaryStepSize > 1) + { + if( mPositions[index].mDistance < minDist) + index += binaryStepSize; + else + index -= binaryStepSize; + + binaryStepSize /= 2; + } + + // depending on the direction of the last step we need to single step a bit back or forth + // to find the actual beginning element of the range + while( index > 0 && mPositions[index].mDistance > minDist) + index--; + while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist) + index++; + + // Mow start iterating from there until the first position lays outside of the distance range. + // Add all positions inside the distance range within the given radius to the result aray + + float squareEpsilon = pRadius * pRadius; + std::vector<Entry>::const_iterator it = mPositions.begin() + index; + std::vector<Entry>::const_iterator end = mPositions.end(); + + if (exactMatch) + { + while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon && it->mSmoothGroups == pSG) + { + poResults.push_back( it->mIndex); + } + ++it; + if( end == it )break; + } + } + else + { + // if the given smoothing group is 0, we'll return all surrounding vertices + if (!pSG) + { + while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon) + poResults.push_back( it->mIndex); + ++it; + if( end == it)break; + } + } + else while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon && + (it->mSmoothGroups & pSG || !it->mSmoothGroups)) + { + poResults.push_back( it->mIndex); + } + ++it; + if( end == it)break; + } + } +} + + diff --git a/thirdparty/assimp/code/ScaleProcess.cpp b/thirdparty/assimp/code/ScaleProcess.cpp new file mode 100644 index 0000000000..6d458c4b11 --- /dev/null +++ b/thirdparty/assimp/code/ScaleProcess.cpp @@ -0,0 +1,103 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS + +#include "ScaleProcess.h" + +#include <assimp/scene.h> +#include <assimp/postprocess.h> + +namespace Assimp { + +ScaleProcess::ScaleProcess() +: BaseProcess() +, mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) { + // empty +} + +ScaleProcess::~ScaleProcess() { + // empty +} + +void ScaleProcess::setScale( ai_real scale ) { + mScale = scale; +} + +ai_real ScaleProcess::getScale() const { + return mScale; +} + +bool ScaleProcess::IsActive( unsigned int pFlags ) const { + return ( pFlags & aiProcess_GlobalScale ) != 0; +} + +void ScaleProcess::SetupProperties( const Importer* pImp ) { + mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0 ); +} + +void ScaleProcess::Execute( aiScene* pScene ) { + if ( nullptr == pScene ) { + return; + } + + if ( nullptr == pScene->mRootNode ) { + return; + } + + traverseNodes( pScene->mRootNode ); +} + +void ScaleProcess::traverseNodes( aiNode *node ) { + applyScaling( node ); +} + +void ScaleProcess::applyScaling( aiNode *currentNode ) { + if ( nullptr != currentNode ) { + currentNode->mTransformation.a1 = currentNode->mTransformation.a1 * mScale; + currentNode->mTransformation.b2 = currentNode->mTransformation.b2 * mScale; + currentNode->mTransformation.c3 = currentNode->mTransformation.c3 * mScale; + } +} + +} // Namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS diff --git a/thirdparty/assimp/code/ScaleProcess.h b/thirdparty/assimp/code/ScaleProcess.h new file mode 100644 index 0000000000..55146ae064 --- /dev/null +++ b/thirdparty/assimp/code/ScaleProcess.h @@ -0,0 +1,88 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include "BaseProcess.h" + +struct aiNode; + +#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT) +# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f +#endif // !! AI_DEBONE_THRESHOLD + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** ScaleProcess: Class to rescale the whole model. +*/ +class ASSIMP_API ScaleProcess : public BaseProcess { +public: + /// The default class constructor. + ScaleProcess(); + + /// The class destructor. + virtual ~ScaleProcess(); + + /// Will set the scale manually. + void setScale( ai_real scale ); + + /// Returns the current scaling value. + ai_real getScale() const; + + /// Overwritten, @see BaseProcess + virtual bool IsActive( unsigned int pFlags ) const; + + /// Overwritten, @see BaseProcess + virtual void SetupProperties( const Importer* pImp ); + + /// Overwritten, @see BaseProcess + virtual void Execute( aiScene* pScene ); + +private: + void traverseNodes( aiNode *currentNode ); + void applyScaling( aiNode *currentNode ); + +private: + ai_real mScale; +}; + +} // Namespace Assimp diff --git a/thirdparty/assimp/code/SceneCombiner.cpp b/thirdparty/assimp/code/SceneCombiner.cpp new file mode 100644 index 0000000000..e445bd7434 --- /dev/null +++ b/thirdparty/assimp/code/SceneCombiner.cpp @@ -0,0 +1,1300 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +// TODO: refactor entire file to get rid of the "flat-copy" first approach +// to copying structures. This easily breaks in the most unintuitive way +// possible as new fields are added to assimp structures. + +// ---------------------------------------------------------------------------- +/** + * @file Implements Assimp::SceneCombiner. This is a smart utility + * class that combines multiple scenes, meshes, ... into one. Currently + * these utilities are used by the IRR and LWS loaders and the + * OptimizeGraph step. + */ +// ---------------------------------------------------------------------------- +#include <assimp/SceneCombiner.h> +#include <assimp/StringUtils.h> +#include <assimp/fast_atof.h> +#include <assimp/metadata.h> +#include <assimp/Hash.h> +#include "time.h" +#include <assimp/DefaultLogger.hpp> +#include <assimp/scene.h> +#include <assimp/mesh.h> +#include <stdio.h> +#include "ScenePrivate.h" + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +// Add a prefix to a string +inline +void PrefixString(aiString& string,const char* prefix, unsigned int len) { + // If the string is already prefixed, we won't prefix it a second time + if (string.length >= 1 && string.data[0] == '$') + return; + + if (len+string.length>=MAXLEN-1) { + ASSIMP_LOG_DEBUG("Can't add an unique prefix because the string is too long"); + ai_assert(false); + return; + } + + // Add the prefix + ::memmove(string.data+len,string.data,string.length+1); + ::memcpy (string.data, prefix, len); + + // And update the string's length + string.length += len; +} + +// ------------------------------------------------------------------------------------------------ +// Add node identifiers to a hashing set +void SceneCombiner::AddNodeHashes(aiNode* node, std::set<unsigned int>& hashes) { + // Add node name to hashing set if it is non-empty - empty nodes are allowed + // and they can't have any anims assigned so its absolutely safe to duplicate them. + if (node->mName.length) { + hashes.insert( SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length)) ); + } + + // Process all children recursively + for (unsigned int i = 0; i < node->mNumChildren;++i) + AddNodeHashes(node->mChildren[i],hashes); +} + +// ------------------------------------------------------------------------------------------------ +// Add a name prefix to all nodes in a hierarchy +void SceneCombiner::AddNodePrefixes(aiNode* node, const char* prefix, unsigned int len) { + ai_assert(NULL != prefix); + PrefixString(node->mName,prefix,len); + + // Process all children recursively + for ( unsigned int i = 0; i < node->mNumChildren; ++i ) { + AddNodePrefixes( node->mChildren[ i ], prefix, len ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Search for matching names +bool SceneCombiner::FindNameMatch(const aiString& name, std::vector<SceneHelper>& input, unsigned int cur) { + const unsigned int hash = SuperFastHash(name.data, static_cast<uint32_t>(name.length)); + + // Check whether we find a positive match in one of the given sets + for (unsigned int i = 0; i < input.size(); ++i) { + if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) { + return true; + } + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Add a name prefix to all nodes in a hierarchy if a hash match is found +void SceneCombiner::AddNodePrefixesChecked(aiNode* node, const char* prefix, unsigned int len, + std::vector<SceneHelper>& input, unsigned int cur) { + ai_assert(NULL != prefix); + const unsigned int hash = SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length)); + + // Check whether we find a positive match in one of the given sets + for (unsigned int i = 0; i < input.size(); ++i) { + if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) { + PrefixString(node->mName,prefix,len); + break; + } + } + + // Process all children recursively + for (unsigned int i = 0; i < node->mNumChildren;++i) + AddNodePrefixesChecked(node->mChildren[i],prefix,len,input,cur); +} + +// ------------------------------------------------------------------------------------------------ +// Add an offset to all mesh indices in a node graph +void SceneCombiner::OffsetNodeMeshIndices (aiNode* node, unsigned int offset) { + for (unsigned int i = 0; i < node->mNumMeshes;++i) + node->mMeshes[i] += offset; + + for ( unsigned int i = 0; i < node->mNumChildren; ++i ) { + OffsetNodeMeshIndices( node->mChildren[ i ], offset ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Merges two scenes. Currently only used by the LWS loader. +void SceneCombiner::MergeScenes(aiScene** _dest,std::vector<aiScene*>& src, unsigned int flags) { + if ( nullptr == _dest ) { + return; + } + + // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it + if (src.empty()) { + if (*_dest) { + (*_dest)->~aiScene(); + SceneCombiner::CopySceneFlat(_dest,src[0]); + } + else *_dest = src[0]; + return; + } + if (*_dest)(*_dest)->~aiScene(); + else *_dest = new aiScene(); + + // Create a dummy scene to serve as master for the others + aiScene* master = new aiScene(); + master->mRootNode = new aiNode(); + master->mRootNode->mName.Set("<MergeRoot>"); + + std::vector<AttachmentInfo> srcList (src.size()); + for (unsigned int i = 0; i < srcList.size();++i) { + srcList[i] = AttachmentInfo(src[i],master->mRootNode); + } + + // 'master' will be deleted afterwards + MergeScenes (_dest, master, srcList, flags); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::AttachToGraph (aiNode* attach, std::vector<NodeAttachmentInfo>& srcList) { + unsigned int cnt; + for ( cnt = 0; cnt < attach->mNumChildren; ++cnt ) { + AttachToGraph( attach->mChildren[ cnt ], srcList ); + } + + cnt = 0; + for (std::vector<NodeAttachmentInfo>::iterator it = srcList.begin(); + it != srcList.end(); ++it) + { + if ((*it).attachToNode == attach && !(*it).resolved) + ++cnt; + } + + if (cnt) { + aiNode** n = new aiNode*[cnt+attach->mNumChildren]; + if (attach->mNumChildren) { + ::memcpy(n,attach->mChildren,sizeof(void*)*attach->mNumChildren); + delete[] attach->mChildren; + } + attach->mChildren = n; + + n += attach->mNumChildren; + attach->mNumChildren += cnt; + + for (unsigned int i = 0; i < srcList.size();++i) { + NodeAttachmentInfo& att = srcList[i]; + if (att.attachToNode == attach && !att.resolved) { + *n = att.node; + (**n).mParent = attach; + ++n; + + // mark this attachment as resolved + att.resolved = true; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::AttachToGraph ( aiScene* master, std::vector<NodeAttachmentInfo>& src) { + ai_assert(NULL != master); + AttachToGraph(master->mRootNode,src); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master, std::vector<AttachmentInfo>& srcList, unsigned int flags) { + if ( nullptr == _dest ) { + return; + } + + // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it + if (srcList.empty()) { + if (*_dest) { + SceneCombiner::CopySceneFlat(_dest,master); + } + else *_dest = master; + return; + } + if (*_dest) { + (*_dest)->~aiScene(); + new (*_dest) aiScene(); + } + else *_dest = new aiScene(); + + aiScene* dest = *_dest; + + std::vector<SceneHelper> src (srcList.size()+1); + src[0].scene = master; + for (unsigned int i = 0; i < srcList.size();++i) { + src[i+1] = SceneHelper( srcList[i].scene ); + } + + // this helper array specifies which scenes are duplicates of others + std::vector<unsigned int> duplicates(src.size(),UINT_MAX); + + // this helper array is used as lookup table several times + std::vector<unsigned int> offset(src.size()); + + // Find duplicate scenes + for (unsigned int i = 0; i < src.size();++i) { + if (duplicates[i] != i && duplicates[i] != UINT_MAX) { + continue; + } + + duplicates[i] = i; + for ( unsigned int a = i+1; a < src.size(); ++a) { + if (src[i].scene == src[a].scene) { + duplicates[a] = i; + } + } + } + + // Generate unique names for all named stuff? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) + { +#if 0 + // Construct a proper random number generator + boost::mt19937 rng( ); + boost::uniform_int<> dist(1u,1 << 24u); + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > rndGen(rng, dist); +#endif + for (unsigned int i = 1; i < src.size();++i) + { + //if (i != duplicates[i]) + //{ + // // duplicate scenes share the same UID + // ::strcpy( src[i].id, src[duplicates[i]].id ); + // src[i].idlen = src[duplicates[i]].idlen; + + // continue; + //} + + src[i].idlen = ai_snprintf(src[i].id, 32, "$%.6X$_",i); + + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + + // Compute hashes for all identifiers in this scene and store them + // in a sorted table (for convenience I'm using std::set). We hash + // just the node and animation channel names, all identifiers except + // the material names should be caught by doing this. + AddNodeHashes(src[i]->mRootNode,src[i].hashes); + + for (unsigned int a = 0; a < src[i]->mNumAnimations;++a) { + aiAnimation* anim = src[i]->mAnimations[a]; + src[i].hashes.insert(SuperFastHash(anim->mName.data,static_cast<uint32_t>(anim->mName.length))); + } + } + } + } + + unsigned int cnt; + + // First find out how large the respective output arrays must be + for ( unsigned int n = 0; n < src.size();++n ) + { + SceneHelper* cur = &src[n]; + + if (n == duplicates[n] || flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) { + dest->mNumTextures += (*cur)->mNumTextures; + dest->mNumMaterials += (*cur)->mNumMaterials; + dest->mNumMeshes += (*cur)->mNumMeshes; + } + + dest->mNumLights += (*cur)->mNumLights; + dest->mNumCameras += (*cur)->mNumCameras; + dest->mNumAnimations += (*cur)->mNumAnimations; + + // Combine the flags of all scenes + // We need to process them flag-by-flag here to get correct results + // dest->mFlags ; //|= (*cur)->mFlags; + if ((*cur)->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { + dest->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; + } + } + + // generate the output texture list + an offset table for all texture indices + if (dest->mNumTextures) + { + aiTexture** pip = dest->mTextures = new aiTexture*[dest->mNumMaterials]; + cnt = 0; + for ( unsigned int n = 0; n < src.size();++n ) + { + SceneHelper* cur = &src[n]; + for (unsigned int i = 0; i < (*cur)->mNumTextures;++i) + { + if (n != duplicates[n]) + { + if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) + Copy(pip,(*cur)->mTextures[i]); + + else continue; + } + else *pip = (*cur)->mTextures[i]; + ++pip; + } + + offset[n] = cnt; + cnt = (unsigned int)(pip - dest->mTextures); + } + } + + // generate the output material list + an offset table for all material indices + if (dest->mNumMaterials) + { + aiMaterial** pip = dest->mMaterials = new aiMaterial*[dest->mNumMaterials]; + cnt = 0; + for ( unsigned int n = 0; n < src.size();++n ) { + SceneHelper* cur = &src[n]; + for (unsigned int i = 0; i < (*cur)->mNumMaterials;++i) + { + if (n != duplicates[n]) + { + if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) + Copy(pip,(*cur)->mMaterials[i]); + + else continue; + } + else *pip = (*cur)->mMaterials[i]; + + if ((*cur)->mNumTextures != dest->mNumTextures) { + // We need to update all texture indices of the mesh. So we need to search for + // a material property called '$tex.file' + + for (unsigned int a = 0; a < (*pip)->mNumProperties;++a) + { + aiMaterialProperty* prop = (*pip)->mProperties[a]; + if (!strncmp(prop->mKey.data,"$tex.file",9)) + { + // Check whether this texture is an embedded texture. + // In this case the property looks like this: *<n>, + // where n is the index of the texture. + aiString& s = *((aiString*)prop->mData); + if ('*' == s.data[0]) { + // Offset the index and write it back .. + const unsigned int idx = strtoul10(&s.data[1]) + offset[n]; + ASSIMP_itoa10(&s.data[1],sizeof(s.data)-1,idx); + } + } + + // Need to generate new, unique material names? + else if (!::strcmp( prop->mKey.data,"$mat.name" ) && flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) + { + aiString* pcSrc = (aiString*) prop->mData; + PrefixString(*pcSrc, (*cur).id, (*cur).idlen); + } + } + } + ++pip; + } + + offset[n] = cnt; + cnt = (unsigned int)(pip - dest->mMaterials); + } + } + + // generate the output mesh list + again an offset table for all mesh indices + if (dest->mNumMeshes) + { + aiMesh** pip = dest->mMeshes = new aiMesh*[dest->mNumMeshes]; + cnt = 0; + for ( unsigned int n = 0; n < src.size();++n ) + { + SceneHelper* cur = &src[n]; + for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i) + { + if (n != duplicates[n]) { + if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) + Copy(pip, (*cur)->mMeshes[i]); + + else continue; + } + else *pip = (*cur)->mMeshes[i]; + + // update the material index of the mesh + (*pip)->mMaterialIndex += offset[n]; + ++pip; + } + + // reuse the offset array - store now the mesh offset in it + offset[n] = cnt; + cnt = (unsigned int)(pip - dest->mMeshes); + } + } + + std::vector <NodeAttachmentInfo> nodes; + nodes.reserve(srcList.size()); + + // ---------------------------------------------------------------------------- + // Now generate the output node graph. We need to make those + // names in the graph that are referenced by anims or lights + // or cameras unique. So we add a prefix to them ... $<rand>_ + // We could also use a counter, but using a random value allows us to + // use just one prefix if we are joining multiple scene hierarchies recursively. + // Chances are quite good we don't collide, so we try that ... + // ---------------------------------------------------------------------------- + + // Allocate space for light sources, cameras and animations + aiLight** ppLights = dest->mLights = (dest->mNumLights + ? new aiLight*[dest->mNumLights] : NULL); + + aiCamera** ppCameras = dest->mCameras = (dest->mNumCameras + ? new aiCamera*[dest->mNumCameras] : NULL); + + aiAnimation** ppAnims = dest->mAnimations = (dest->mNumAnimations + ? new aiAnimation*[dest->mNumAnimations] : NULL); + + for ( int n = static_cast<int>(src.size()-1); n >= 0 ;--n ) /* !!! important !!! */ + { + SceneHelper* cur = &src[n]; + aiNode* node; + + // To offset or not to offset, this is the question + if (n != (int)duplicates[n]) + { + // Get full scene-graph copy + Copy( &node, (*cur)->mRootNode ); + OffsetNodeMeshIndices(node,offset[duplicates[n]]); + + if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) { + // (note:) they are already 'offseted' by offset[duplicates[n]] + OffsetNodeMeshIndices(node,offset[n] - offset[duplicates[n]]); + } + } + else // if (n == duplicates[n]) + { + node = (*cur)->mRootNode; + OffsetNodeMeshIndices(node,offset[n]); + } + if (n) // src[0] is the master node + nodes.push_back(NodeAttachmentInfo( node,srcList[n-1].attachToNode,n )); + + // add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + + // or the whole scenegraph + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + AddNodePrefixesChecked(node,(*cur).id,(*cur).idlen,src,n); + } + else AddNodePrefixes(node,(*cur).id,(*cur).idlen); + + // meshes + for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i) { + aiMesh* mesh = (*cur)->mMeshes[i]; + + // rename all bones + for (unsigned int a = 0; a < mesh->mNumBones;++a) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch(mesh->mBones[a]->mName,src,n)) + continue; + } + PrefixString(mesh->mBones[a]->mName,(*cur).id,(*cur).idlen); + } + } + } + + // -------------------------------------------------------------------- + // Copy light sources + for (unsigned int i = 0; i < (*cur)->mNumLights;++i,++ppLights) + { + if (n != (int)duplicates[n]) // duplicate scene? + { + Copy(ppLights, (*cur)->mLights[i]); + } + else *ppLights = (*cur)->mLights[i]; + + + // Add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppLights)->mName,src,n)) + continue; + } + + PrefixString((*ppLights)->mName,(*cur).id,(*cur).idlen); + } + } + + // -------------------------------------------------------------------- + // Copy cameras + for (unsigned int i = 0; i < (*cur)->mNumCameras;++i,++ppCameras) { + if (n != (int)duplicates[n]) // duplicate scene? + { + Copy(ppCameras, (*cur)->mCameras[i]); + } + else *ppCameras = (*cur)->mCameras[i]; + + // Add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppCameras)->mName,src,n)) + continue; + } + + PrefixString((*ppCameras)->mName,(*cur).id,(*cur).idlen); + } + } + + // -------------------------------------------------------------------- + // Copy animations + for (unsigned int i = 0; i < (*cur)->mNumAnimations;++i,++ppAnims) { + if (n != (int)duplicates[n]) // duplicate scene? + { + Copy(ppAnims, (*cur)->mAnimations[i]); + } + else *ppAnims = (*cur)->mAnimations[i]; + + // Add name prefixes? + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppAnims)->mName,src,n)) + continue; + } + + PrefixString((*ppAnims)->mName,(*cur).id,(*cur).idlen); + + // don't forget to update all node animation channels + for (unsigned int a = 0; a < (*ppAnims)->mNumChannels;++a) { + if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { + if (!FindNameMatch((*ppAnims)->mChannels[a]->mNodeName,src,n)) + continue; + } + + PrefixString((*ppAnims)->mChannels[a]->mNodeName,(*cur).id,(*cur).idlen); + } + } + } + } + + // Now build the output graph + AttachToGraph ( master, nodes); + dest->mRootNode = master->mRootNode; + + // Check whether we succeeded at building the output graph + for (std::vector <NodeAttachmentInfo> ::iterator it = nodes.begin(); + it != nodes.end(); ++it) + { + if (!(*it).resolved) { + if (flags & AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS) { + // search for this attachment point in all other imported scenes, too. + for ( unsigned int n = 0; n < src.size();++n ) { + if (n != (*it).src_idx) { + AttachToGraph(src[n].scene,nodes); + if ((*it).resolved) + break; + } + } + } + if (!(*it).resolved) { + ASSIMP_LOG_ERROR_F( "SceneCombiner: Failed to resolve attachment ", (*it).node->mName.data, + " ", (*it).attachToNode->mName.data ); + } + } + } + + // now delete all input scenes. Make sure duplicate scenes aren't + // deleted more than one time + for ( unsigned int n = 0; n < src.size();++n ) { + if (n != duplicates[n]) // duplicate scene? + continue; + + aiScene* deleteMe = src[n].scene; + + // We need to delete the arrays before the destructor is called - + // we are reusing the array members + delete[] deleteMe->mMeshes; deleteMe->mMeshes = NULL; + delete[] deleteMe->mCameras; deleteMe->mCameras = NULL; + delete[] deleteMe->mLights; deleteMe->mLights = NULL; + delete[] deleteMe->mMaterials; deleteMe->mMaterials = NULL; + delete[] deleteMe->mAnimations; deleteMe->mAnimations = NULL; + + deleteMe->mRootNode = NULL; + + // Now we can safely delete the scene + delete deleteMe; + } + + // Check flags + if (!dest->mNumMeshes || !dest->mNumMaterials) { + dest->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + } + + // We're finished +} + +// ------------------------------------------------------------------------------------------------ +// Build a list of unique bones +void SceneCombiner::BuildUniqueBoneList(std::list<BoneWithHash>& asBones, + std::vector<aiMesh*>::const_iterator it, + std::vector<aiMesh*>::const_iterator end) +{ + unsigned int iOffset = 0; + for (; it != end;++it) { + for (unsigned int l = 0; l < (*it)->mNumBones;++l) { + aiBone* p = (*it)->mBones[l]; + uint32_t itml = SuperFastHash(p->mName.data,(unsigned int)p->mName.length); + + std::list<BoneWithHash>::iterator it2 = asBones.begin(); + std::list<BoneWithHash>::iterator end2 = asBones.end(); + + for (;it2 != end2;++it2) { + if ((*it2).first == itml) { + (*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset)); + break; + } + } + if (end2 == it2) { + // need to begin a new bone entry + asBones.push_back(BoneWithHash()); + BoneWithHash& btz = asBones.back(); + + // setup members + btz.first = itml; + btz.second = &p->mName; + btz.pSrcBones.push_back(BoneSrcIndex(p,iOffset)); + } + } + iOffset += (*it)->mNumVertices; + } +} + +// ------------------------------------------------------------------------------------------------ +// Merge a list of bones +void SceneCombiner::MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator it, + std::vector<aiMesh*>::const_iterator end) +{ + if ( nullptr == out || out->mNumBones == 0 ) { + return; + } + + // find we need to build an unique list of all bones. + // we work with hashes to make the comparisons MUCH faster, + // at least if we have many bones. + std::list<BoneWithHash> asBones; + BuildUniqueBoneList( asBones, it, end ); + + // now create the output bones + out->mNumBones = 0; + out->mBones = new aiBone*[asBones.size()]; + + for (std::list<BoneWithHash>::const_iterator boneIt = asBones.begin(),boneEnd = asBones.end(); boneIt != boneEnd; ++boneIt ) { + // Allocate a bone and setup it's name + aiBone* pc = out->mBones[out->mNumBones++] = new aiBone(); + pc->mName = aiString( *( boneIt->second )); + + std::vector< BoneSrcIndex >::const_iterator wend = boneIt->pSrcBones.end(); + + // Loop through all bones to be joined for this bone + for (std::vector< BoneSrcIndex >::const_iterator wmit = boneIt->pSrcBones.begin(); wmit != wend; ++wmit) { + pc->mNumWeights += (*wmit).first->mNumWeights; + + // NOTE: different offset matrices for bones with equal names + // are - at the moment - not handled correctly. + if (wmit != boneIt->pSrcBones.begin() && pc->mOffsetMatrix != wmit->first->mOffsetMatrix) { + ASSIMP_LOG_WARN("Bones with equal names but different offset matrices can't be joined at the moment"); + continue; + } + pc->mOffsetMatrix = wmit->first->mOffsetMatrix; + } + + // Allocate the vertex weight array + aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights]; + + // And copy the final weights - adjust the vertex IDs by the + // face index offset of the corresponding mesh. + for (std::vector< BoneSrcIndex >::const_iterator wmit = (*boneIt).pSrcBones.begin(); wmit != (*boneIt).pSrcBones.end(); ++wmit) { + if (wmit == wend) { + break; + } + + aiBone* pip = (*wmit).first; + for (unsigned int mp = 0; mp < pip->mNumWeights;++mp,++avw) { + const aiVertexWeight& vfi = pip->mWeights[mp]; + avw->mWeight = vfi.mWeight; + avw->mVertexId = vfi.mVertexId + (*wmit).second; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Merge a list of meshes +void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, + std::vector<aiMesh*>::const_iterator begin, + std::vector<aiMesh*>::const_iterator end) +{ + if ( nullptr == _out ) { + return; + } + + if (begin == end) { + *_out = NULL; // no meshes ... + return; + } + + // Allocate the output mesh + aiMesh* out = *_out = new aiMesh(); + out->mMaterialIndex = (*begin)->mMaterialIndex; + + std::string name; + // Find out how much output storage we'll need + for (std::vector<aiMesh*>::const_iterator it = begin; it != end; ++it) { + const char *meshName( (*it)->mName.C_Str() ); + name += std::string( meshName ); + if ( it != end - 1 ) { + name += "."; + } + out->mNumVertices += (*it)->mNumVertices; + out->mNumFaces += (*it)->mNumFaces; + out->mNumBones += (*it)->mNumBones; + + // combine primitive type flags + out->mPrimitiveTypes |= (*it)->mPrimitiveTypes; + } + out->mName.Set( name.c_str() ); + + if (out->mNumVertices) { + aiVector3D* pv2; + + // copy vertex positions + if ((**begin).HasPositions()) { + + pv2 = out->mVertices = new aiVector3D[out->mNumVertices]; + for (std::vector<aiMesh*>::const_iterator it = begin; it != end; ++it) { + if ((*it)->mVertices) { + ::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D)); + } + else ASSIMP_LOG_WARN("JoinMeshes: Positions expected but input mesh contains no positions"); + pv2 += (*it)->mNumVertices; + } + } + // copy normals + if ((**begin).HasNormals()) { + + pv2 = out->mNormals = new aiVector3D[out->mNumVertices]; + for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) { + if ((*it)->mNormals) { + ::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + ASSIMP_LOG_WARN( "JoinMeshes: Normals expected but input mesh contains no normals" ); + } + pv2 += (*it)->mNumVertices; + } + } + // copy tangents and bi-tangents + if ((**begin).HasTangentsAndBitangents()) { + + pv2 = out->mTangents = new aiVector3D[out->mNumVertices]; + aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices]; + + for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) { + if ((*it)->mTangents) { + ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D)); + ::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + ASSIMP_LOG_WARN( "JoinMeshes: Tangents expected but input mesh contains no tangents" ); + } + pv2 += (*it)->mNumVertices; + pv2b += (*it)->mNumVertices; + } + } + // copy texture coordinates + unsigned int n = 0; + while ((**begin).HasTextureCoords(n)) { + out->mNumUVComponents[n] = (*begin)->mNumUVComponents[n]; + + pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices]; + for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) { + if ((*it)->mTextureCoords[n]) { + ::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + ASSIMP_LOG_WARN( "JoinMeshes: UVs expected but input mesh contains no UVs" ); + } + pv2 += (*it)->mNumVertices; + } + ++n; + } + // copy vertex colors + n = 0; + while ((**begin).HasVertexColors(n)) { + aiColor4D *pVec2 = out->mColors[n] = new aiColor4D[out->mNumVertices]; + for ( std::vector<aiMesh*>::const_iterator it = begin; it != end; ++it ) { + if ((*it)->mColors[n]) { + ::memcpy( pVec2, (*it)->mColors[ n ], (*it)->mNumVertices * sizeof( aiColor4D ) ) ; + } else { + ASSIMP_LOG_WARN( "JoinMeshes: VCs expected but input mesh contains no VCs" ); + } + pVec2 += (*it)->mNumVertices; + } + ++n; + } + } + + if (out->mNumFaces) // just for safety + { + // copy faces + out->mFaces = new aiFace[out->mNumFaces]; + aiFace* pf2 = out->mFaces; + + unsigned int ofs = 0; + for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) { + for (unsigned int m = 0; m < (*it)->mNumFaces;++m,++pf2) { + aiFace& face = (*it)->mFaces[m]; + pf2->mNumIndices = face.mNumIndices; + pf2->mIndices = face.mIndices; + + if (ofs) { + // add the offset to the vertex + for (unsigned int q = 0; q < face.mNumIndices; ++q) + face.mIndices[q] += ofs; + } + face.mIndices = NULL; + } + ofs += (*it)->mNumVertices; + } + } + + // bones - as this is quite lengthy, I moved the code to a separate function + if (out->mNumBones) + MergeBones(out,begin,end); + + // delete all source meshes + for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) + delete *it; +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::MergeMaterials(aiMaterial** dest, + std::vector<aiMaterial*>::const_iterator begin, + std::vector<aiMaterial*>::const_iterator end) +{ + if ( nullptr == dest ) { + return; + } + + if (begin == end) { + *dest = NULL; // no materials ... + return; + } + + // Allocate the output material + aiMaterial* out = *dest = new aiMaterial(); + + // Get the maximal number of properties + unsigned int size = 0; + for (std::vector<aiMaterial*>::const_iterator it = begin; it != end; ++it) { + size += (*it)->mNumProperties; + } + + out->Clear(); + delete[] out->mProperties; + + out->mNumAllocated = size; + out->mNumProperties = 0; + out->mProperties = new aiMaterialProperty*[out->mNumAllocated]; + + for (std::vector<aiMaterial*>::const_iterator it = begin; it != end; ++it) { + for(unsigned int i = 0; i < (*it)->mNumProperties; ++i) { + aiMaterialProperty* sprop = (*it)->mProperties[i]; + + // Test if we already have a matching property + const aiMaterialProperty* prop_exist; + if(aiGetMaterialProperty(out, sprop->mKey.C_Str(), sprop->mSemantic, sprop->mIndex, &prop_exist) != AI_SUCCESS) { + // If not, we add it to the new material + aiMaterialProperty* prop = out->mProperties[out->mNumProperties] = new aiMaterialProperty(); + + prop->mDataLength = sprop->mDataLength; + prop->mData = new char[prop->mDataLength]; + ::memcpy(prop->mData, sprop->mData, prop->mDataLength); + + prop->mIndex = sprop->mIndex; + prop->mSemantic = sprop->mSemantic; + prop->mKey = sprop->mKey; + prop->mType = sprop->mType; + + out->mNumProperties++; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +template <typename Type> +inline +void CopyPtrArray (Type**& dest, const Type* const * src, ai_uint num) { + if (!num) { + dest = NULL; + return; + } + dest = new Type*[num]; + for (ai_uint i = 0; i < num;++i) { + SceneCombiner::Copy(&dest[i],src[i]); + } +} + +// ------------------------------------------------------------------------------------------------ +template <typename Type> +inline +void GetArrayCopy(Type*& dest, ai_uint num ) { + if ( !dest ) { + return; + } + Type* old = dest; + + dest = new Type[num]; + ::memcpy(dest, old, sizeof(Type) * num); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::CopySceneFlat(aiScene** _dest,const aiScene* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + // reuse the old scene or allocate a new? + if (*_dest) { + (*_dest)->~aiScene(); + new (*_dest) aiScene(); + } else { + *_dest = new aiScene(); + } + + ::memcpy(*_dest,src,sizeof(aiScene)); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + if (allocate) { + *_dest = new aiScene(); + } + aiScene* dest = *_dest; + ai_assert(nullptr != dest); + + // copy metadata + if ( nullptr != src->mMetaData ) { + dest->mMetaData = new aiMetadata( *src->mMetaData ); + } + + // copy animations + dest->mNumAnimations = src->mNumAnimations; + CopyPtrArray(dest->mAnimations,src->mAnimations, + dest->mNumAnimations); + + // copy textures + dest->mNumTextures = src->mNumTextures; + CopyPtrArray(dest->mTextures,src->mTextures, + dest->mNumTextures); + + // copy materials + dest->mNumMaterials = src->mNumMaterials; + CopyPtrArray(dest->mMaterials,src->mMaterials, + dest->mNumMaterials); + + // copy lights + dest->mNumLights = src->mNumLights; + CopyPtrArray(dest->mLights,src->mLights, + dest->mNumLights); + + // copy cameras + dest->mNumCameras = src->mNumCameras; + CopyPtrArray(dest->mCameras,src->mCameras, + dest->mNumCameras); + + // copy meshes + dest->mNumMeshes = src->mNumMeshes; + CopyPtrArray(dest->mMeshes,src->mMeshes, + dest->mNumMeshes); + + // now - copy the root node of the scene (deep copy, too) + Copy( &dest->mRootNode, src->mRootNode); + + // and keep the flags ... + dest->mFlags = src->mFlags; + + // source private data might be NULL if the scene is user-allocated (i.e. for use with the export API) + if (dest->mPrivate != NULL) { + ScenePriv(dest)->mPPStepsApplied = ScenePriv(src) ? ScenePriv(src)->mPPStepsApplied : 0; + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy( aiMesh** _dest, const aiMesh* src ) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiMesh* dest = *_dest = new aiMesh(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiMesh)); + + // and reallocate all arrays + GetArrayCopy( dest->mVertices, dest->mNumVertices ); + GetArrayCopy( dest->mNormals , dest->mNumVertices ); + GetArrayCopy( dest->mTangents, dest->mNumVertices ); + GetArrayCopy( dest->mBitangents, dest->mNumVertices ); + + unsigned int n = 0; + while (dest->HasTextureCoords(n)) + GetArrayCopy( dest->mTextureCoords[n++], dest->mNumVertices ); + + n = 0; + while (dest->HasVertexColors(n)) + GetArrayCopy( dest->mColors[n++], dest->mNumVertices ); + + // make a deep copy of all bones + CopyPtrArray(dest->mBones,dest->mBones,dest->mNumBones); + + // make a deep copy of all faces + GetArrayCopy(dest->mFaces,dest->mNumFaces); + for (unsigned int i = 0; i < dest->mNumFaces;++i) { + aiFace& f = dest->mFaces[i]; + GetArrayCopy(f.mIndices,f.mNumIndices); + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy (aiMaterial** _dest, const aiMaterial* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiMaterial* dest = (aiMaterial*) ( *_dest = new aiMaterial() ); + + dest->Clear(); + delete[] dest->mProperties; + + dest->mNumAllocated = src->mNumAllocated; + dest->mNumProperties = src->mNumProperties; + dest->mProperties = new aiMaterialProperty* [dest->mNumAllocated]; + + for (unsigned int i = 0; i < dest->mNumProperties;++i) + { + aiMaterialProperty* prop = dest->mProperties[i] = new aiMaterialProperty(); + aiMaterialProperty* sprop = src->mProperties[i]; + + prop->mDataLength = sprop->mDataLength; + prop->mData = new char[prop->mDataLength]; + ::memcpy(prop->mData,sprop->mData,prop->mDataLength); + + prop->mIndex = sprop->mIndex; + prop->mSemantic = sprop->mSemantic; + prop->mKey = sprop->mKey; + prop->mType = sprop->mType; + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiTexture** _dest, const aiTexture* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiTexture* dest = *_dest = new aiTexture(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiTexture)); + + // and reallocate all arrays. We must do it manually here + const char* old = (const char*)dest->pcData; + if (old) + { + unsigned int cpy; + if (!dest->mHeight)cpy = dest->mWidth; + else cpy = dest->mHeight * dest->mWidth * sizeof(aiTexel); + + if (!cpy) + { + dest->pcData = NULL; + return; + } + // the cast is legal, the aiTexel c'tor does nothing important + dest->pcData = (aiTexel*) new char[cpy]; + ::memcpy(dest->pcData, old, cpy); + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy( aiAnimation** _dest, const aiAnimation* src ) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiAnimation* dest = *_dest = new aiAnimation(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiAnimation)); + + // and reallocate all arrays + CopyPtrArray( dest->mChannels, src->mChannels, dest->mNumChannels ); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiNodeAnim** _dest, const aiNodeAnim* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiNodeAnim* dest = *_dest = new aiNodeAnim(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiNodeAnim)); + + // and reallocate all arrays + GetArrayCopy( dest->mPositionKeys, dest->mNumPositionKeys ); + GetArrayCopy( dest->mScalingKeys, dest->mNumScalingKeys ); + GetArrayCopy( dest->mRotationKeys, dest->mNumRotationKeys ); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy( aiCamera** _dest,const aiCamera* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiCamera* dest = *_dest = new aiCamera(); + + // get a flat copy, that's already OK + ::memcpy(dest,src,sizeof(aiCamera)); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiLight** _dest, const aiLight* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiLight* dest = *_dest = new aiLight(); + + // get a flat copy, that's already OK + ::memcpy(dest,src,sizeof(aiLight)); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiBone** _dest, const aiBone* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + aiBone* dest = *_dest = new aiBone(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiBone)); + + // and reallocate all arrays + GetArrayCopy( dest->mWeights, dest->mNumWeights ); +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy (aiNode** _dest, const aiNode* src) +{ + ai_assert(NULL != _dest && NULL != src); + + aiNode* dest = *_dest = new aiNode(); + + // get a flat copy + ::memcpy(dest,src,sizeof(aiNode)); + + if (src->mMetaData) { + Copy(&dest->mMetaData, src->mMetaData); + } + + // and reallocate all arrays + GetArrayCopy( dest->mMeshes, dest->mNumMeshes ); + CopyPtrArray( dest->mChildren, src->mChildren,dest->mNumChildren); + + // need to set the mParent fields to the created aiNode. + for( unsigned int i = 0; i < dest->mNumChildren; i ++ ) { + dest->mChildren[i]->mParent = dest; + } +} + +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) { + if ( nullptr == _dest || nullptr == src ) { + return; + } + + if ( 0 == src->mNumProperties ) { + return; + } + + aiMetadata* dest = *_dest = aiMetadata::Alloc( src->mNumProperties ); + std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys); + + dest->mValues = new aiMetadataEntry[src->mNumProperties]; + for (unsigned int i = 0; i < src->mNumProperties; ++i) { + aiMetadataEntry& in = src->mValues[i]; + aiMetadataEntry& out = dest->mValues[i]; + out.mType = in.mType; + switch (dest->mValues[i].mType) { + case AI_BOOL: + out.mData = new bool(*static_cast<bool*>(in.mData)); + break; + case AI_INT32: + out.mData = new int32_t(*static_cast<int32_t*>(in.mData)); + break; + case AI_UINT64: + out.mData = new uint64_t(*static_cast<uint64_t*>(in.mData)); + break; + case AI_FLOAT: + out.mData = new float(*static_cast<float*>(in.mData)); + break; + case AI_DOUBLE: + out.mData = new double(*static_cast<double*>(in.mData)); + break; + case AI_AISTRING: + out.mData = new aiString(*static_cast<aiString*>(in.mData)); + break; + case AI_AIVECTOR3D: + out.mData = new aiVector3D(*static_cast<aiVector3D*>(in.mData)); + break; + default: + ai_assert(false); + break; + } + } +} + +} // Namespace Assimp + diff --git a/thirdparty/assimp/code/ScenePreprocessor.cpp b/thirdparty/assimp/code/ScenePreprocessor.cpp new file mode 100644 index 0000000000..432a3d7666 --- /dev/null +++ b/thirdparty/assimp/code/ScenePreprocessor.cpp @@ -0,0 +1,261 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include "ScenePreprocessor.h" +#include <assimp/ai_assert.h> +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> + + +using namespace Assimp; + +// --------------------------------------------------------------------------------------------- +void ScenePreprocessor::ProcessScene () +{ + ai_assert(scene != NULL); + + // Process all meshes + for (unsigned int i = 0; i < scene->mNumMeshes;++i) + ProcessMesh(scene->mMeshes[i]); + + // - nothing to do for nodes for the moment + // - nothing to do for textures for the moment + // - nothing to do for lights for the moment + // - nothing to do for cameras for the moment + + // Process all animations + for (unsigned int i = 0; i < scene->mNumAnimations;++i) + ProcessAnimation(scene->mAnimations[i]); + + // Generate a default material if none was specified + if (!scene->mNumMaterials && scene->mNumMeshes) { + scene->mMaterials = new aiMaterial*[2]; + aiMaterial* helper; + + aiString name; + + scene->mMaterials[scene->mNumMaterials] = helper = new aiMaterial(); + aiColor3D clr(0.6f,0.6f,0.6f); + helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE); + + // setup the default name to make this material identifiable + name.Set(AI_DEFAULT_MATERIAL_NAME); + helper->AddProperty(&name,AI_MATKEY_NAME); + + ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'"); + + for (unsigned int i = 0; i < scene->mNumMeshes;++i) { + scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials; + } + + scene->mNumMaterials++; + } +} + +// --------------------------------------------------------------------------------------------- +void ScenePreprocessor::ProcessMesh (aiMesh* mesh) +{ + // If aiMesh::mNumUVComponents is *not* set assign the default value of 2 + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if (!mesh->mTextureCoords[i]) { + mesh->mNumUVComponents[i] = 0; + } else { + if (!mesh->mNumUVComponents[i]) + mesh->mNumUVComponents[i] = 2; + + aiVector3D* p = mesh->mTextureCoords[i], *end = p+mesh->mNumVertices; + + // Ensure unused components are zeroed. This will make 1D texture channels work + // as if they were 2D channels .. just in case an application doesn't handle + // this case + if (2 == mesh->mNumUVComponents[i]) { + for (; p != end; ++p) + p->z = 0.f; + } + else if (1 == mesh->mNumUVComponents[i]) { + for (; p != end; ++p) + p->z = p->y = 0.f; + } + else if (3 == mesh->mNumUVComponents[i]) { + // Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element + for (; p != end; ++p) { + if (p->z != 0) + break; + } + if (p == end) { + ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D."); + mesh->mNumUVComponents[i] = 2; + } + } + } + } + + // If the information which primitive types are there in the + // mesh is currently not available, compute it. + if (!mesh->mPrimitiveTypes) { + for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { + aiFace& face = mesh->mFaces[a]; + switch (face.mNumIndices) + { + case 3u: + mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + + case 2u: + mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + + case 1u: + mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + + default: + mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + } + } + } + + // If tangents and normals are given but no bitangents compute them + if (mesh->mTangents && mesh->mNormals && !mesh->mBitangents) { + + mesh->mBitangents = new aiVector3D[mesh->mNumVertices]; + for (unsigned int i = 0; i < mesh->mNumVertices;++i) { + mesh->mBitangents[i] = mesh->mNormals[i] ^ mesh->mTangents[i]; + } + } +} + +// --------------------------------------------------------------------------------------------- +void ScenePreprocessor::ProcessAnimation (aiAnimation* anim) +{ + double first = 10e10, last = -10e10; + for (unsigned int i = 0; i < anim->mNumChannels;++i) { + aiNodeAnim* channel = anim->mChannels[i]; + + /* If the exact duration of the animation is not given + * compute it now. + */ + if (anim->mDuration == -1.) { + + // Position keys + for (unsigned int j = 0; j < channel->mNumPositionKeys;++j) { + aiVectorKey& key = channel->mPositionKeys[j]; + first = std::min (first, key.mTime); + last = std::max (last, key.mTime); + } + + // Scaling keys + for (unsigned int j = 0; j < channel->mNumScalingKeys;++j ) { + aiVectorKey& key = channel->mScalingKeys[j]; + first = std::min (first, key.mTime); + last = std::max (last, key.mTime); + } + + // Rotation keys + for (unsigned int j = 0; j < channel->mNumRotationKeys;++j ) { + aiQuatKey& key = channel->mRotationKeys[ j ]; + first = std::min (first, key.mTime); + last = std::max (last, key.mTime); + } + } + + /* Check whether the animation channel has no rotation + * or position tracks. In this case we generate a dummy + * track from the information we have in the transformation + * matrix of the corresponding node. + */ + if (!channel->mNumRotationKeys || !channel->mNumPositionKeys || !channel->mNumScalingKeys) { + // Find the node that belongs to this animation + aiNode* node = scene->mRootNode->FindNode(channel->mNodeName); + if (node) // ValidateDS will complain later if 'node' is NULL + { + // Decompose the transformation matrix of the node + aiVector3D scaling, position; + aiQuaternion rotation; + + node->mTransformation.Decompose(scaling, rotation,position); + + // No rotation keys? Generate a dummy track + if (!channel->mNumRotationKeys) { + channel->mNumRotationKeys = 1; + channel->mRotationKeys = new aiQuatKey[1]; + aiQuatKey& q = channel->mRotationKeys[0]; + + q.mTime = 0.; + q.mValue = rotation; + + ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy rotation track has been generated"); + } + + // No scaling keys? Generate a dummy track + if (!channel->mNumScalingKeys) { + channel->mNumScalingKeys = 1; + channel->mScalingKeys = new aiVectorKey[1]; + aiVectorKey& q = channel->mScalingKeys[0]; + + q.mTime = 0.; + q.mValue = scaling; + + ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy scaling track has been generated"); + } + + // No position keys? Generate a dummy track + if (!channel->mNumPositionKeys) { + channel->mNumPositionKeys = 1; + channel->mPositionKeys = new aiVectorKey[1]; + aiVectorKey& q = channel->mPositionKeys[0]; + + q.mTime = 0.; + q.mValue = position; + + ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy position track has been generated"); + } + } + } + } + + if (anim->mDuration == -1.) { + ASSIMP_LOG_DEBUG("ScenePreprocessor: Setting animation duration"); + anim->mDuration = last - std::min( first, 0. ); + } +} diff --git a/thirdparty/assimp/code/ScenePreprocessor.h b/thirdparty/assimp/code/ScenePreprocessor.h new file mode 100644 index 0000000000..3f4c8d7c3f --- /dev/null +++ b/thirdparty/assimp/code/ScenePreprocessor.h @@ -0,0 +1,125 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to search all meshes for + degenerated faces */ +#ifndef AI_SCENE_PREPROCESSOR_H_INC +#define AI_SCENE_PREPROCESSOR_H_INC + +#include <assimp/defs.h> +#include <stddef.h> + +struct aiScene; +struct aiAnimation; +struct aiMesh; + +class ScenePreprocessorTest; +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** ScenePreprocessor: Preprocess a scene before any post-processing + * steps are executed. + * + * The step computes data that needn't necessarily be provided by the + * importer, such as aiMesh::mPrimitiveTypes. +*/ +// ---------------------------------------------------------------------------------- +class ASSIMP_API ScenePreprocessor +{ + // Make ourselves a friend of the corresponding test unit. + friend class ::ScenePreprocessorTest; +public: + + // ---------------------------------------------------------------- + /** Default c'tpr. Use SetScene() to assign a scene to the object. + */ + ScenePreprocessor() + : scene (NULL) + {} + + /** Constructs the object and assigns a specific scene to it + */ + ScenePreprocessor(aiScene* _scene) + : scene (_scene) + {} + + // ---------------------------------------------------------------- + /** Assign a (new) scene to the object. + * + * One 'SceneProcessor' can be used for multiple scenes. + * Call ProcessScene to have the scene preprocessed. + * @param sc Scene to be processed. + */ + void SetScene (aiScene* sc) { + scene = sc; + } + + // ---------------------------------------------------------------- + /** Preprocess the current scene + */ + void ProcessScene (); + +protected: + + // ---------------------------------------------------------------- + /** Preprocess an animation in the scene + * @param anim Anim to be preprocessed. + */ + void ProcessAnimation (aiAnimation* anim); + + + // ---------------------------------------------------------------- + /** Preprocess a mesh in the scene + * @param mesh Mesh to be preprocessed. + */ + void ProcessMesh (aiMesh* mesh); + +protected: + + //! Scene we're currently working on + aiScene* scene; +}; + + +} // ! end namespace Assimp + +#endif // include guard diff --git a/thirdparty/assimp/code/ScenePrivate.h b/thirdparty/assimp/code/ScenePrivate.h new file mode 100644 index 0000000000..f336aafc9a --- /dev/null +++ b/thirdparty/assimp/code/ScenePrivate.h @@ -0,0 +1,105 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Stuff to deal with aiScene::mPrivate + */ +#pragma once +#ifndef AI_SCENEPRIVATE_H_INCLUDED +#define AI_SCENEPRIVATE_H_INCLUDED + +#include <assimp/ai_assert.h> +#include <assimp/scene.h> + +namespace Assimp { + +// Forward declarations +class Importer; + +struct ScenePrivateData { + // The struct constructor. + ScenePrivateData() AI_NO_EXCEPT; + + // Importer that originally loaded the scene though the C-API + // If set, this object is owned by this private data instance. + Assimp::Importer* mOrigImporter; + + // List of post-processing steps already applied to the scene. + unsigned int mPPStepsApplied; + + // true if the scene is a copy made with aiCopyScene() + // or the corresponding C++ API. This means that user code + // may have made modifications to it, so mPPStepsApplied + // and mOrigImporter are no longer safe to rely on and only + // serve informative purposes. + bool mIsCopy; +}; + +inline +ScenePrivateData::ScenePrivateData() AI_NO_EXCEPT +: mOrigImporter( nullptr ) +, mPPStepsApplied( 0 ) +, mIsCopy( false ) { + // empty +} + +// Access private data stored in the scene +inline +ScenePrivateData* ScenePriv(aiScene* in) { + ai_assert( nullptr != in ); + if ( nullptr == in ) { + return nullptr; + } + return static_cast<ScenePrivateData*>(in->mPrivate); +} + +inline +const ScenePrivateData* ScenePriv(const aiScene* in) { + ai_assert( nullptr != in ); + if ( nullptr == in ) { + return nullptr; + } + return static_cast<const ScenePrivateData*>(in->mPrivate); +} + +} // Namespace Assimp + +#endif // AI_SCENEPRIVATE_H_INCLUDED diff --git a/thirdparty/assimp/code/SkeletonMeshBuilder.cpp b/thirdparty/assimp/code/SkeletonMeshBuilder.cpp new file mode 100644 index 0000000000..06cfe034e9 --- /dev/null +++ b/thirdparty/assimp/code/SkeletonMeshBuilder.cpp @@ -0,0 +1,270 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file SkeletonMeshBuilder.cpp + * @brief Implementation of a little class to construct a dummy mesh for a skeleton + */ + +#include <assimp/scene.h> +#include <assimp/SkeletonMeshBuilder.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// The constructor processes the given scene and adds a mesh there. +SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly) +{ + // nothing to do if there's mesh data already present at the scene + if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL) + return; + + if (!root) + root = pScene->mRootNode; + + mKnobsOnly = bKnobsOnly; + + // build some faces around each node + CreateGeometry( root ); + + // create a mesh to hold all the generated faces + pScene->mNumMeshes = 1; + pScene->mMeshes = new aiMesh*[1]; + pScene->mMeshes[0] = CreateMesh(); + // and install it at the root node + root->mNumMeshes = 1; + root->mMeshes = new unsigned int[1]; + root->mMeshes[0] = 0; + + // create a dummy material for the mesh + if(pScene->mNumMaterials==0){ + pScene->mNumMaterials = 1; + pScene->mMaterials = new aiMaterial*[1]; + pScene->mMaterials[0] = CreateMaterial(); + } +} + +// ------------------------------------------------------------------------------------------------ +// Recursively builds a simple mesh representation for the given node +void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode) +{ + // add a joint entry for the node. + const unsigned int vertexStartIndex = static_cast<unsigned int>(mVertices.size()); + + // now build the geometry. + if( pNode->mNumChildren > 0 && !mKnobsOnly) + { + // If the node has children, we build little pointers to each of them + for( unsigned int a = 0; a < pNode->mNumChildren; a++) + { + // find a suitable coordinate system + const aiMatrix4x4& childTransform = pNode->mChildren[a]->mTransformation; + aiVector3D childpos( childTransform.a4, childTransform.b4, childTransform.c4); + ai_real distanceToChild = childpos.Length(); + if( distanceToChild < 0.0001) + continue; + aiVector3D up = aiVector3D( childpos).Normalize(); + + aiVector3D orth( 1.0, 0.0, 0.0); + if( std::fabs( orth * up) > 0.99) + orth.Set( 0.0, 1.0, 0.0); + + aiVector3D front = (up ^ orth).Normalize(); + aiVector3D side = (front ^ up).Normalize(); + + unsigned int localVertexStart = static_cast<unsigned int>(mVertices.size()); + mVertices.push_back( -front * distanceToChild * (ai_real)0.1); + mVertices.push_back( childpos); + mVertices.push_back( -side * distanceToChild * (ai_real)0.1); + mVertices.push_back( -side * distanceToChild * (ai_real)0.1); + mVertices.push_back( childpos); + mVertices.push_back( front * distanceToChild * (ai_real)0.1); + mVertices.push_back( front * distanceToChild * (ai_real)0.1); + mVertices.push_back( childpos); + mVertices.push_back( side * distanceToChild * (ai_real)0.1); + mVertices.push_back( side * distanceToChild * (ai_real)0.1); + mVertices.push_back( childpos); + mVertices.push_back( -front * distanceToChild * (ai_real)0.1); + + mFaces.push_back( Face( localVertexStart + 0, localVertexStart + 1, localVertexStart + 2)); + mFaces.push_back( Face( localVertexStart + 3, localVertexStart + 4, localVertexStart + 5)); + mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8)); + mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11)); + } + } + else + { + // if the node has no children, it's an end node. Put a little knob there instead + aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4); + ai_real sizeEstimate = ownpos.Length() * ai_real( 0.18 ); + + mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); + mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); + mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); + mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); + + mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); + mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); + mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); + mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); + mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); + mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); + + mFaces.push_back( Face( vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2)); + mFaces.push_back( Face( vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5)); + mFaces.push_back( Face( vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8)); + mFaces.push_back( Face( vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11)); + mFaces.push_back( Face( vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14)); + mFaces.push_back( Face( vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17)); + mFaces.push_back( Face( vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20)); + mFaces.push_back( Face( vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23)); + } + + unsigned int numVertices = static_cast<unsigned int>(mVertices.size() - vertexStartIndex); + if( numVertices > 0) + { + // create a bone affecting all the newly created vertices + aiBone* bone = new aiBone; + mBones.push_back( bone); + bone->mName = pNode->mName; + + // calculate the bone offset matrix by concatenating the inverse transformations of all parents + bone->mOffsetMatrix = aiMatrix4x4( pNode->mTransformation).Inverse(); + for( aiNode* parent = pNode->mParent; parent != NULL; parent = parent->mParent) + bone->mOffsetMatrix = aiMatrix4x4( parent->mTransformation).Inverse() * bone->mOffsetMatrix; + + // add all the vertices to the bone's influences + bone->mNumWeights = numVertices; + bone->mWeights = new aiVertexWeight[numVertices]; + for( unsigned int a = 0; a < numVertices; a++) + bone->mWeights[a] = aiVertexWeight( vertexStartIndex + a, 1.0); + + // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding + // them to the array, but I'm tired now and I'm annoyed. + aiMatrix4x4 boneToMeshTransform = aiMatrix4x4( bone->mOffsetMatrix).Inverse(); + for( unsigned int a = vertexStartIndex; a < mVertices.size(); a++) + mVertices[a] = boneToMeshTransform * mVertices[a]; + } + + // and finally recurse into the children list + for( unsigned int a = 0; a < pNode->mNumChildren; a++) + CreateGeometry( pNode->mChildren[a]); +} + +// ------------------------------------------------------------------------------------------------ +// Creates the mesh from the internally accumulated stuff and returns it. +aiMesh* SkeletonMeshBuilder::CreateMesh() +{ + aiMesh* mesh = new aiMesh(); + + // add points + mesh->mNumVertices = static_cast<unsigned int>(mVertices.size()); + mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices); + + mesh->mNormals = new aiVector3D[mesh->mNumVertices]; + + // add faces + mesh->mNumFaces = static_cast<unsigned int>(mFaces.size()); + mesh->mFaces = new aiFace[mesh->mNumFaces]; + for( unsigned int a = 0; a < mesh->mNumFaces; a++) + { + const Face& inface = mFaces[a]; + aiFace& outface = mesh->mFaces[a]; + outface.mNumIndices = 3; + outface.mIndices = new unsigned int[3]; + outface.mIndices[0] = inface.mIndices[0]; + outface.mIndices[1] = inface.mIndices[1]; + outface.mIndices[2] = inface.mIndices[2]; + + // Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize + // the skeleton, so it's good if there's a visual difference to the rest of the geometry + aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^ + (mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]])); + + if (nor.Length() < 1e-5) /* ensure that FindInvalidData won't remove us ...*/ + nor = aiVector3D(1.0,0.0,0.0); + + for (unsigned int n = 0; n < 3; ++n) + mesh->mNormals[inface.mIndices[n]] = nor; + } + + // add the bones + mesh->mNumBones = static_cast<unsigned int>(mBones.size()); + mesh->mBones = new aiBone*[mesh->mNumBones]; + std::copy( mBones.begin(), mBones.end(), mesh->mBones); + + // default + mesh->mMaterialIndex = 0; + + return mesh; +} + +// ------------------------------------------------------------------------------------------------ +// Creates a dummy material and returns it. +aiMaterial* SkeletonMeshBuilder::CreateMaterial() +{ + aiMaterial* matHelper = new aiMaterial; + + // Name + aiString matName( std::string( "SkeletonMaterial")); + matHelper->AddProperty( &matName, AI_MATKEY_NAME); + + // Prevent backface culling + const int no_cull = 1; + matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED); + + return matHelper; +} diff --git a/thirdparty/assimp/code/SortByPTypeProcess.cpp b/thirdparty/assimp/code/SortByPTypeProcess.cpp new file mode 100644 index 0000000000..2e0cc54004 --- /dev/null +++ b/thirdparty/assimp/code/SortByPTypeProcess.cpp @@ -0,0 +1,403 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the DeterminePTypeHelperProcess and + * SortByPTypeProcess post-process steps. +*/ + + + +// internal headers +#include "ProcessHelper.h" +#include "SortByPTypeProcess.h" +#include <assimp/Exceptional.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +SortByPTypeProcess::SortByPTypeProcess() +{ + configRemoveMeshes = 0; +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +SortByPTypeProcess::~SortByPTypeProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool SortByPTypeProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_SortByPType) != 0; +} + +// ------------------------------------------------------------------------------------------------ +void SortByPTypeProcess::SetupProperties(const Importer* pImp) +{ + configRemoveMeshes = pImp->GetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE,0); +} + +// ------------------------------------------------------------------------------------------------ +// Update changed meshes in all nodes +void UpdateNodes(const std::vector<unsigned int>& replaceMeshIndex, aiNode* node) +{ + if (node->mNumMeshes) + { + unsigned int newSize = 0; + for (unsigned int m = 0; m< node->mNumMeshes; ++m) + { + unsigned int add = node->mMeshes[m]<<2; + for (unsigned int i = 0; i < 4;++i) + { + if (UINT_MAX != replaceMeshIndex[add+i])++newSize; + } + } + if (!newSize) + { + delete[] node->mMeshes; + node->mNumMeshes = 0; + node->mMeshes = NULL; + } + else + { + // Try to reuse the old array if possible + unsigned int* newMeshes = (newSize > node->mNumMeshes + ? new unsigned int[newSize] : node->mMeshes); + + for (unsigned int m = 0; m< node->mNumMeshes; ++m) + { + unsigned int add = node->mMeshes[m]<<2; + for (unsigned int i = 0; i < 4;++i) + { + if (UINT_MAX != replaceMeshIndex[add+i]) + *newMeshes++ = replaceMeshIndex[add+i]; + } + } + if (newSize > node->mNumMeshes) + delete[] node->mMeshes; + + node->mMeshes = newMeshes-(node->mNumMeshes = newSize); + } + } + + // call all subnodes recursively + for (unsigned int m = 0; m < node->mNumChildren; ++m) + UpdateNodes(replaceMeshIndex,node->mChildren[m]); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SortByPTypeProcess::Execute( aiScene* pScene) { + if ( 0 == pScene->mNumMeshes) { + ASSIMP_LOG_DEBUG("SortByPTypeProcess skipped, there are no meshes"); + return; + } + + ASSIMP_LOG_DEBUG("SortByPTypeProcess begin"); + + unsigned int aiNumMeshesPerPType[4] = {0,0,0,0}; + + std::vector<aiMesh*> outMeshes; + outMeshes.reserve(pScene->mNumMeshes<<1u); + + bool bAnyChanges = false; + + std::vector<unsigned int> replaceMeshIndex(pScene->mNumMeshes*4,UINT_MAX); + std::vector<unsigned int>::iterator meshIdx = replaceMeshIndex.begin(); + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + aiMesh* const mesh = pScene->mMeshes[i]; + ai_assert(0 != mesh->mPrimitiveTypes); + + // if there's just one primitive type in the mesh there's nothing to do for us + unsigned int num = 0; + if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT) { + ++aiNumMeshesPerPType[0]; + ++num; + } + if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) { + ++aiNumMeshesPerPType[1]; + ++num; + } + if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) { + ++aiNumMeshesPerPType[2]; + ++num; + } + if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON) { + ++aiNumMeshesPerPType[3]; + ++num; + } + + if (1 == num) { + if (!(configRemoveMeshes & mesh->mPrimitiveTypes)) { + *meshIdx = static_cast<unsigned int>( outMeshes.size() ); + outMeshes.push_back(mesh); + } else { + delete mesh; + pScene->mMeshes[ i ] = nullptr; + bAnyChanges = true; + } + + meshIdx += 4; + continue; + } + bAnyChanges = true; + + // reuse our current mesh arrays for the submesh + // with the largest number of primitives + unsigned int aiNumPerPType[4] = {0,0,0,0}; + aiFace* pFirstFace = mesh->mFaces; + aiFace* const pLastFace = pFirstFace + mesh->mNumFaces; + + unsigned int numPolyVerts = 0; + for (;pFirstFace != pLastFace; ++pFirstFace) { + if (pFirstFace->mNumIndices <= 3) + ++aiNumPerPType[pFirstFace->mNumIndices-1]; + else + { + ++aiNumPerPType[3]; + numPolyVerts += pFirstFace-> mNumIndices; + } + } + + VertexWeightTable* avw = ComputeVertexBoneWeightTable(mesh); + for (unsigned int real = 0; real < 4; ++real,++meshIdx) + { + if ( !aiNumPerPType[real] || configRemoveMeshes & (1u << real)) + { + continue; + } + + *meshIdx = (unsigned int) outMeshes.size(); + outMeshes.push_back(new aiMesh()); + aiMesh* out = outMeshes.back(); + + // the name carries the adjacency information between the meshes + out->mName = mesh->mName; + + // copy data members + out->mPrimitiveTypes = 1u << real; + out->mMaterialIndex = mesh->mMaterialIndex; + + // allocate output storage + out->mNumFaces = aiNumPerPType[real]; + aiFace* outFaces = out->mFaces = new aiFace[out->mNumFaces]; + + out->mNumVertices = (3 == real ? numPolyVerts : out->mNumFaces * (real+1)); + + aiVector3D *vert(nullptr), *nor(nullptr), *tan(nullptr), *bit(nullptr); + aiVector3D *uv [AI_MAX_NUMBER_OF_TEXTURECOORDS]; + aiColor4D *cols [AI_MAX_NUMBER_OF_COLOR_SETS]; + + if (mesh->mVertices) { + vert = out->mVertices = new aiVector3D[out->mNumVertices]; + } + + if (mesh->mNormals) { + nor = out->mNormals = new aiVector3D[out->mNumVertices]; + } + + if (mesh->mTangents) { + tan = out->mTangents = new aiVector3D[out->mNumVertices]; + bit = out->mBitangents = new aiVector3D[out->mNumVertices]; + } + + for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_TEXTURECOORDS;++j) { + uv[j] = nullptr; + if (mesh->mTextureCoords[j]) { + uv[j] = out->mTextureCoords[j] = new aiVector3D[out->mNumVertices]; + } + + out->mNumUVComponents[j] = mesh->mNumUVComponents[j]; + } + + for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_COLOR_SETS;++j) { + cols[j] = nullptr; + if (mesh->mColors[j]) { + cols[j] = out->mColors[j] = new aiColor4D[out->mNumVertices]; + } + } + + typedef std::vector< aiVertexWeight > TempBoneInfo; + std::vector< TempBoneInfo > tempBones(mesh->mNumBones); + + // try to guess how much storage we'll need + for (unsigned int q = 0; q < mesh->mNumBones;++q) + { + tempBones[q].reserve(mesh->mBones[q]->mNumWeights / (num-1)); + } + + unsigned int outIdx = 0; + for (unsigned int m = 0; m < mesh->mNumFaces; ++m) + { + aiFace& in = mesh->mFaces[m]; + if ((real == 3 && in.mNumIndices <= 3) || (real != 3 && in.mNumIndices != real+1)) + { + continue; + } + + outFaces->mNumIndices = in.mNumIndices; + outFaces->mIndices = in.mIndices; + + for (unsigned int q = 0; q < in.mNumIndices; ++q) + { + unsigned int idx = in.mIndices[q]; + + // process all bones of this index + if (avw) + { + VertexWeightTable& tbl = avw[idx]; + for (VertexWeightTable::const_iterator it = tbl.begin(), end = tbl.end(); + it != end; ++it) + { + tempBones[ (*it).first ].push_back( aiVertexWeight(outIdx, (*it).second) ); + } + } + + if (vert) + { + *vert++ = mesh->mVertices[idx]; + //mesh->mVertices[idx].x = get_qnan(); + } + if (nor )*nor++ = mesh->mNormals[idx]; + if (tan ) + { + *tan++ = mesh->mTangents[idx]; + *bit++ = mesh->mBitangents[idx]; + } + + for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp) + { + if (!uv[pp])break; + *uv[pp]++ = mesh->mTextureCoords[pp][idx]; + } + + for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp) + { + if (!cols[pp])break; + *cols[pp]++ = mesh->mColors[pp][idx]; + } + + in.mIndices[q] = outIdx++; + } + + in.mIndices = nullptr; + ++outFaces; + } + ai_assert(outFaces == out->mFaces + out->mNumFaces); + + // now generate output bones + for (unsigned int q = 0; q < mesh->mNumBones;++q) + if (!tempBones[q].empty())++out->mNumBones; + + if (out->mNumBones) + { + out->mBones = new aiBone*[out->mNumBones]; + for (unsigned int q = 0, real = 0; q < mesh->mNumBones;++q) + { + TempBoneInfo& in = tempBones[q]; + if (in.empty())continue; + + aiBone* srcBone = mesh->mBones[q]; + aiBone* bone = out->mBones[real] = new aiBone(); + + bone->mName = srcBone->mName; + bone->mOffsetMatrix = srcBone->mOffsetMatrix; + + bone->mNumWeights = (unsigned int)in.size(); + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + + ::memcpy(bone->mWeights,&in[0],bone->mNumWeights*sizeof(aiVertexWeight)); + + ++real; + } + } + } + + // delete the per-vertex bone weights table + delete[] avw; + + // delete the input mesh + delete mesh; + + // avoid invalid pointer + pScene->mMeshes[i] = NULL; + } + + if (outMeshes.empty()) + { + // This should not occur + throw DeadlyImportError("No meshes remaining"); + } + + // If we added at least one mesh process all nodes in the node + // graph and update their respective mesh indices. + if (bAnyChanges) + { + UpdateNodes(replaceMeshIndex,pScene->mRootNode); + } + + if (outMeshes.size() != pScene->mNumMeshes) + { + delete[] pScene->mMeshes; + pScene->mNumMeshes = (unsigned int)outMeshes.size(); + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + } + ::memcpy(pScene->mMeshes,&outMeshes[0],pScene->mNumMeshes*sizeof(void*)); + + if (!DefaultLogger::isNullLogger()) + { + char buffer[1024]; + ::ai_snprintf(buffer,1024,"Points: %u%s, Lines: %u%s, Triangles: %u%s, Polygons: %u%s (Meshes, X = removed)", + aiNumMeshesPerPType[0], ((configRemoveMeshes & aiPrimitiveType_POINT) ? "X" : ""), + aiNumMeshesPerPType[1], ((configRemoveMeshes & aiPrimitiveType_LINE) ? "X" : ""), + aiNumMeshesPerPType[2], ((configRemoveMeshes & aiPrimitiveType_TRIANGLE) ? "X" : ""), + aiNumMeshesPerPType[3], ((configRemoveMeshes & aiPrimitiveType_POLYGON) ? "X" : "")); + ASSIMP_LOG_INFO(buffer); + ASSIMP_LOG_DEBUG("SortByPTypeProcess finished"); + } +} + diff --git a/thirdparty/assimp/code/SortByPTypeProcess.h b/thirdparty/assimp/code/SortByPTypeProcess.h new file mode 100644 index 0000000000..c9d9924d8f --- /dev/null +++ b/thirdparty/assimp/code/SortByPTypeProcess.h @@ -0,0 +1,85 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to sort meshes by the types + of primitives they contain */ +#ifndef AI_SORTBYPTYPEPROCESS_H_INC +#define AI_SORTBYPTYPEPROCESS_H_INC + +#include "BaseProcess.h" +#include <assimp/mesh.h> + +class SortByPTypeProcessTest; +namespace Assimp { + + +// --------------------------------------------------------------------------- +/** SortByPTypeProcess: Sorts meshes by the types of primitives they contain. + * A mesh with 5 lines, 3 points and 145 triangles would be split in 3 + * submeshes. +*/ +class ASSIMP_API SortByPTypeProcess : public BaseProcess +{ +public: + + SortByPTypeProcess(); + ~SortByPTypeProcess(); + +public: + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + void SetupProperties(const Importer* pImp); + +private: + + int configRemoveMeshes; +}; + + +} // end of namespace Assimp + +#endif // !!AI_SORTBYPTYPEPROCESS_H_INC diff --git a/thirdparty/assimp/code/SpatialSort.cpp b/thirdparty/assimp/code/SpatialSort.cpp new file mode 100644 index 0000000000..a4f3a4e4b8 --- /dev/null +++ b/thirdparty/assimp/code/SpatialSort.cpp @@ -0,0 +1,342 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the helper class to quickly find vertices close to a given position */ + +#include <assimp/SpatialSort.h> +#include <assimp/ai_assert.h> + +using namespace Assimp; + +// CHAR_BIT seems to be defined under MVSC, but not under GCC. Pray that the correct value is 8. +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + +// ------------------------------------------------------------------------------------------------ +// Constructs a spatially sorted representation from the given position array. +SpatialSort::SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions, + unsigned int pElementOffset) + + // define the reference plane. We choose some arbitrary vector away from all basic axises + // in the hope that no model spreads all its vertices along this plane. + : mPlaneNormal(0.8523f, 0.34321f, 0.5736f) +{ + mPlaneNormal.Normalize(); + Fill(pPositions,pNumPositions,pElementOffset); +} + +// ------------------------------------------------------------------------------------------------ +SpatialSort :: SpatialSort() +: mPlaneNormal(0.8523f, 0.34321f, 0.5736f) +{ + mPlaneNormal.Normalize(); +} + +// ------------------------------------------------------------------------------------------------ +// Destructor +SpatialSort::~SpatialSort() +{ + // nothing to do here, everything destructs automatically +} + +// ------------------------------------------------------------------------------------------------ +void SpatialSort::Fill( const aiVector3D* pPositions, unsigned int pNumPositions, + unsigned int pElementOffset, + bool pFinalize /*= true */) +{ + mPositions.clear(); + Append(pPositions,pNumPositions,pElementOffset,pFinalize); +} + +// ------------------------------------------------------------------------------------------------ +void SpatialSort :: Finalize() +{ + std::sort( mPositions.begin(), mPositions.end()); +} + +// ------------------------------------------------------------------------------------------------ +void SpatialSort::Append( const aiVector3D* pPositions, unsigned int pNumPositions, + unsigned int pElementOffset, + bool pFinalize /*= true */) +{ + // store references to all given positions along with their distance to the reference plane + const size_t initial = mPositions.size(); + mPositions.reserve(initial + (pFinalize?pNumPositions:pNumPositions*2)); + for( unsigned int a = 0; a < pNumPositions; a++) + { + const char* tempPointer = reinterpret_cast<const char*> (pPositions); + const aiVector3D* vec = reinterpret_cast<const aiVector3D*> (tempPointer + a * pElementOffset); + + // store position by index and distance + ai_real distance = *vec * mPlaneNormal; + mPositions.push_back( Entry( static_cast<unsigned int>(a+initial), *vec, distance)); + } + + if (pFinalize) { + // now sort the array ascending by distance. + Finalize(); + } +} + +// ------------------------------------------------------------------------------------------------ +// Returns an iterator for all positions close to the given position. +void SpatialSort::FindPositions( const aiVector3D& pPosition, + ai_real pRadius, std::vector<unsigned int>& poResults) const +{ + const ai_real dist = pPosition * mPlaneNormal; + const ai_real minDist = dist - pRadius, maxDist = dist + pRadius; + + // clear the array + poResults.clear(); + + // quick check for positions outside the range + if( mPositions.size() == 0) + return; + if( maxDist < mPositions.front().mDistance) + return; + if( minDist > mPositions.back().mDistance) + return; + + // do a binary search for the minimal distance to start the iteration there + unsigned int index = (unsigned int)mPositions.size() / 2; + unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; + while( binaryStepSize > 1) + { + if( mPositions[index].mDistance < minDist) + index += binaryStepSize; + else + index -= binaryStepSize; + + binaryStepSize /= 2; + } + + // depending on the direction of the last step we need to single step a bit back or forth + // to find the actual beginning element of the range + while( index > 0 && mPositions[index].mDistance > minDist) + index--; + while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist) + index++; + + // Mow start iterating from there until the first position lays outside of the distance range. + // Add all positions inside the distance range within the given radius to the result aray + std::vector<Entry>::const_iterator it = mPositions.begin() + index; + const ai_real pSquared = pRadius*pRadius; + while( it->mDistance < maxDist) + { + if( (it->mPosition - pPosition).SquareLength() < pSquared) + poResults.push_back( it->mIndex); + ++it; + if( it == mPositions.end()) + break; + } + + // that's it +} + +namespace { + + // Binary, signed-integer representation of a single-precision floating-point value. + // IEEE 754 says: "If two floating-point numbers in the same format are ordered then they are + // ordered the same way when their bits are reinterpreted as sign-magnitude integers." + // This allows us to convert all floating-point numbers to signed integers of arbitrary size + // and then use them to work with ULPs (Units in the Last Place, for high-precision + // computations) or to compare them (integer comparisons are faster than floating-point + // comparisons on many platforms). + typedef ai_int BinFloat; + + // -------------------------------------------------------------------------------------------- + // Converts the bit pattern of a floating-point number to its signed integer representation. + BinFloat ToBinary( const ai_real & pValue) { + + // If this assertion fails, signed int is not big enough to store a float on your platform. + // Please correct the declaration of BinFloat a few lines above - but do it in a portable, + // #ifdef'd manner! + static_assert( sizeof(BinFloat) >= sizeof(ai_real), "sizeof(BinFloat) >= sizeof(ai_real)"); + + #if defined( _MSC_VER) + // If this assertion fails, Visual C++ has finally moved to ILP64. This means that this + // code has just become legacy code! Find out the current value of _MSC_VER and modify + // the #if above so it evaluates false on the current and all upcoming VC versions (or + // on the current platform, if LP64 or LLP64 are still used on other platforms). + static_assert( sizeof(BinFloat) == sizeof(ai_real), "sizeof(BinFloat) == sizeof(ai_real)"); + + // This works best on Visual C++, but other compilers have their problems with it. + const BinFloat binValue = reinterpret_cast<BinFloat const &>(pValue); + #else + // On many compilers, reinterpreting a float address as an integer causes aliasing + // problems. This is an ugly but more or less safe way of doing it. + union { + ai_real asFloat; + BinFloat asBin; + } conversion; + conversion.asBin = 0; // zero empty space in case sizeof(BinFloat) > sizeof(float) + conversion.asFloat = pValue; + const BinFloat binValue = conversion.asBin; + #endif + + // floating-point numbers are of sign-magnitude format, so find out what signed number + // representation we must convert negative values to. + // See http://en.wikipedia.org/wiki/Signed_number_representations. + + // Two's complement? + if( (-42 == (~42 + 1)) && (binValue & 0x80000000)) + return BinFloat(1 << (CHAR_BIT * sizeof(BinFloat) - 1)) - binValue; + // One's complement? + else if ( (-42 == ~42) && (binValue & 0x80000000)) + return BinFloat(-0) - binValue; + // Sign-magnitude? + else if( (-42 == (42 | (-0))) && (binValue & 0x80000000)) // -0 = 1000... binary + return binValue; + else + return binValue; + } + +} // namespace + +// ------------------------------------------------------------------------------------------------ +// Fills an array with indices of all positions identical to the given position. In opposite to +// FindPositions(), not an epsilon is used but a (very low) tolerance of four floating-point units. +void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition, + std::vector<unsigned int>& poResults) const +{ + // Epsilons have a huge disadvantage: they are of constant precision, while floating-point + // values are of log2 precision. If you apply e=0.01 to 100, the epsilon is rather small, but + // if you apply it to 0.001, it is enormous. + + // The best way to overcome this is the unit in the last place (ULP). A precision of 2 ULPs + // tells us that a float does not differ more than 2 bits from the "real" value. ULPs are of + // logarithmic precision - around 1, they are 1*(2^24) and around 10000, they are 0.00125. + + // For standard C math, we can assume a precision of 0.5 ULPs according to IEEE 754. The + // incoming vertex positions might have already been transformed, probably using rather + // inaccurate SSE instructions, so we assume a tolerance of 4 ULPs to safely identify + // identical vertex positions. + static const int toleranceInULPs = 4; + // An interesting point is that the inaccuracy grows linear with the number of operations: + // multiplying to numbers, each inaccurate to four ULPs, results in an inaccuracy of four ULPs + // plus 0.5 ULPs for the multiplication. + // To compute the distance to the plane, a dot product is needed - that is a multiplication and + // an addition on each number. + static const int distanceToleranceInULPs = toleranceInULPs + 1; + // The squared distance between two 3D vectors is computed the same way, but with an additional + // subtraction. + static const int distance3DToleranceInULPs = distanceToleranceInULPs + 1; + + // Convert the plane distance to its signed integer representation so the ULPs tolerance can be + // applied. For some reason, VC won't optimize two calls of the bit pattern conversion. + const BinFloat minDistBinary = ToBinary( pPosition * mPlaneNormal) - distanceToleranceInULPs; + const BinFloat maxDistBinary = minDistBinary + 2 * distanceToleranceInULPs; + + // clear the array in this strange fashion because a simple clear() would also deallocate + // the array which we want to avoid + poResults.resize( 0 ); + + // do a binary search for the minimal distance to start the iteration there + unsigned int index = (unsigned int)mPositions.size() / 2; + unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; + while( binaryStepSize > 1) + { + // Ugly, but conditional jumps are faster with integers than with floats + if( minDistBinary > ToBinary(mPositions[index].mDistance)) + index += binaryStepSize; + else + index -= binaryStepSize; + + binaryStepSize /= 2; + } + + // depending on the direction of the last step we need to single step a bit back or forth + // to find the actual beginning element of the range + while( index > 0 && minDistBinary < ToBinary(mPositions[index].mDistance) ) + index--; + while( index < (mPositions.size() - 1) && minDistBinary > ToBinary(mPositions[index].mDistance)) + index++; + + // Now start iterating from there until the first position lays outside of the distance range. + // Add all positions inside the distance range within the tolerance to the result array + std::vector<Entry>::const_iterator it = mPositions.begin() + index; + while( ToBinary(it->mDistance) < maxDistBinary) + { + if( distance3DToleranceInULPs >= ToBinary((it->mPosition - pPosition).SquareLength())) + poResults.push_back(it->mIndex); + ++it; + if( it == mPositions.end()) + break; + } + + // that's it +} + +// ------------------------------------------------------------------------------------------------ +unsigned int SpatialSort::GenerateMappingTable(std::vector<unsigned int>& fill, ai_real pRadius) const +{ + fill.resize(mPositions.size(),UINT_MAX); + ai_real dist, maxDist; + + unsigned int t=0; + const ai_real pSquared = pRadius*pRadius; + for (size_t i = 0; i < mPositions.size();) { + dist = mPositions[i].mPosition * mPlaneNormal; + maxDist = dist + pRadius; + + fill[mPositions[i].mIndex] = t; + const aiVector3D& oldpos = mPositions[i].mPosition; + for (++i; i < fill.size() && mPositions[i].mDistance < maxDist + && (mPositions[i].mPosition - oldpos).SquareLength() < pSquared; ++i) + { + fill[mPositions[i].mIndex] = t; + } + ++t; + } + +#ifdef ASSIMP_BUILD_DEBUG + + // debug invariant: mPositions[i].mIndex values must range from 0 to mPositions.size()-1 + for (size_t i = 0; i < fill.size(); ++i) { + ai_assert(fill[i]<mPositions.size()); + } + +#endif + return t; +} diff --git a/thirdparty/assimp/code/SplitByBoneCountProcess.cpp b/thirdparty/assimp/code/SplitByBoneCountProcess.cpp new file mode 100644 index 0000000000..2ef66a9afc --- /dev/null +++ b/thirdparty/assimp/code/SplitByBoneCountProcess.cpp @@ -0,0 +1,407 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + + +/// @file SplitByBoneCountProcess.cpp +/// Implementation of the SplitByBoneCount postprocessing step + +// internal headers of the post-processing framework +#include "SplitByBoneCountProcess.h" +#include <assimp/postprocess.h> +#include <assimp/DefaultLogger.hpp> + +#include <limits> +#include <assimp/TinyFormatter.h> + +using namespace Assimp; +using namespace Assimp::Formatter; + +// ------------------------------------------------------------------------------------------------ +// Constructor +SplitByBoneCountProcess::SplitByBoneCountProcess() +{ + // set default, might be overridden by importer config + mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES; +} + +// ------------------------------------------------------------------------------------------------ +// Destructor +SplitByBoneCountProcess::~SplitByBoneCountProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag. +bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const +{ + return !!(pFlags & aiProcess_SplitByBoneCount); +} + +// ------------------------------------------------------------------------------------------------ +// Updates internal properties +void SplitByBoneCountProcess::SetupProperties(const Importer* pImp) +{ + mMaxBoneCount = pImp->GetPropertyInteger(AI_CONFIG_PP_SBBC_MAX_BONES,AI_SBBC_DEFAULT_MAX_BONES); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitByBoneCountProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("SplitByBoneCountProcess begin"); + + // early out + bool isNecessary = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) + if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount ) + isNecessary = true; + + if( !isNecessary ) + { + ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess early-out: no meshes with more than " << mMaxBoneCount << " bones." ); + return; + } + + // we need to do something. Let's go. + mSubMeshIndices.clear(); + mSubMeshIndices.resize( pScene->mNumMeshes); + + // build a new array of meshes for the scene + std::vector<aiMesh*> meshes; + + for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) + { + aiMesh* srcMesh = pScene->mMeshes[a]; + + std::vector<aiMesh*> newMeshes; + SplitMesh( pScene->mMeshes[a], newMeshes); + + // mesh was split + if( !newMeshes.empty() ) + { + // store new meshes and indices of the new meshes + for( unsigned int b = 0; b < newMeshes.size(); ++b) + { + mSubMeshIndices[a].push_back( static_cast<unsigned int>(meshes.size())); + meshes.push_back( newMeshes[b]); + } + + // and destroy the source mesh. It should be completely contained inside the new submeshes + delete srcMesh; + } + else + { + // Mesh is kept unchanged - store it's new place in the mesh array + mSubMeshIndices[a].push_back( static_cast<unsigned int>(meshes.size())); + meshes.push_back( srcMesh); + } + } + + // rebuild the scene's mesh array + pScene->mNumMeshes = static_cast<unsigned int>(meshes.size()); + delete [] pScene->mMeshes; + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + std::copy( meshes.begin(), meshes.end(), pScene->mMeshes); + + // recurse through all nodes and translate the node's mesh indices to fit the new mesh array + UpdateNode( pScene->mRootNode); + + ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess end: split " << mSubMeshIndices.size() << " meshes into " << meshes.size() << " submeshes." ); +} + +// ------------------------------------------------------------------------------------------------ +// Splits the given mesh by bone count. +void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const +{ + // skip if not necessary + if( pMesh->mNumBones <= mMaxBoneCount ) + return; + + // necessary optimisation: build a list of all affecting bones for each vertex + // TODO: (thom) maybe add a custom allocator here to avoid allocating tens of thousands of small arrays + typedef std::pair<unsigned int, float> BoneWeight; + std::vector< std::vector<BoneWeight> > vertexBones( pMesh->mNumVertices); + for( unsigned int a = 0; a < pMesh->mNumBones; ++a) + { + const aiBone* bone = pMesh->mBones[a]; + for( unsigned int b = 0; b < bone->mNumWeights; ++b) + vertexBones[ bone->mWeights[b].mVertexId ].push_back( BoneWeight( a, bone->mWeights[b].mWeight)); + } + + unsigned int numFacesHandled = 0; + std::vector<bool> isFaceHandled( pMesh->mNumFaces, false); + while( numFacesHandled < pMesh->mNumFaces ) + { + // which bones are used in the current submesh + unsigned int numBones = 0; + std::vector<bool> isBoneUsed( pMesh->mNumBones, false); + // indices of the faces which are going to go into this submesh + std::vector<unsigned int> subMeshFaces; + subMeshFaces.reserve( pMesh->mNumFaces); + // accumulated vertex count of all the faces in this submesh + unsigned int numSubMeshVertices = 0; + // a small local array of new bones for the current face. State of all used bones for that face + // can only be updated AFTER the face is completely analysed. Thanks to imre for the fix. + std::vector<unsigned int> newBonesAtCurrentFace; + + // add faces to the new submesh as long as all bones affecting the faces' vertices fit in the limit + for( unsigned int a = 0; a < pMesh->mNumFaces; ++a) + { + // skip if the face is already stored in a submesh + if( isFaceHandled[a] ) + continue; + + const aiFace& face = pMesh->mFaces[a]; + // check every vertex if its bones would still fit into the current submesh + for( unsigned int b = 0; b < face.mNumIndices; ++b ) + { + const std::vector<BoneWeight>& vb = vertexBones[face.mIndices[b]]; + for( unsigned int c = 0; c < vb.size(); ++c) + { + unsigned int boneIndex = vb[c].first; + // if the bone is already used in this submesh, it's ok + if( isBoneUsed[boneIndex] ) + continue; + + // if it's not used, yet, we would need to add it. Store its bone index + if( std::find( newBonesAtCurrentFace.begin(), newBonesAtCurrentFace.end(), boneIndex) == newBonesAtCurrentFace.end() ) + newBonesAtCurrentFace.push_back( boneIndex); + } + } + + // leave out the face if the new bones required for this face don't fit the bone count limit anymore + if( numBones + newBonesAtCurrentFace.size() > mMaxBoneCount ) + continue; + + // mark all new bones as necessary + while( !newBonesAtCurrentFace.empty() ) + { + unsigned int newIndex = newBonesAtCurrentFace.back(); + newBonesAtCurrentFace.pop_back(); // this also avoids the deallocation which comes with a clear() + if( isBoneUsed[newIndex] ) + continue; + + isBoneUsed[newIndex] = true; + numBones++; + } + + // store the face index and the vertex count + subMeshFaces.push_back( a); + numSubMeshVertices += face.mNumIndices; + + // remember that this face is handled + isFaceHandled[a] = true; + numFacesHandled++; + } + + // create a new mesh to hold this subset of the source mesh + aiMesh* newMesh = new aiMesh; + if( pMesh->mName.length > 0 ) + newMesh->mName.Set( format() << pMesh->mName.data << "_sub" << poNewMeshes.size()); + newMesh->mMaterialIndex = pMesh->mMaterialIndex; + newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes; + poNewMeshes.push_back( newMesh); + + // create all the arrays for this mesh if the old mesh contained them + newMesh->mNumVertices = numSubMeshVertices; + newMesh->mNumFaces = static_cast<unsigned int>(subMeshFaces.size()); + newMesh->mVertices = new aiVector3D[newMesh->mNumVertices]; + if( pMesh->HasNormals() ) + newMesh->mNormals = new aiVector3D[newMesh->mNumVertices]; + if( pMesh->HasTangentsAndBitangents() ) + { + newMesh->mTangents = new aiVector3D[newMesh->mNumVertices]; + newMesh->mBitangents = new aiVector3D[newMesh->mNumVertices]; + } + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) + { + if( pMesh->HasTextureCoords( a) ) + newMesh->mTextureCoords[a] = new aiVector3D[newMesh->mNumVertices]; + newMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a]; + } + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) + { + if( pMesh->HasVertexColors( a) ) + newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices]; + } + + // and copy over the data, generating faces with linear indices along the way + newMesh->mFaces = new aiFace[subMeshFaces.size()]; + unsigned int nvi = 0; // next vertex index + std::vector<unsigned int> previousVertexIndices( numSubMeshVertices, std::numeric_limits<unsigned int>::max()); // per new vertex: its index in the source mesh + for( unsigned int a = 0; a < subMeshFaces.size(); ++a ) + { + const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]]; + aiFace& dstFace = newMesh->mFaces[a]; + dstFace.mNumIndices = srcFace.mNumIndices; + dstFace.mIndices = new unsigned int[dstFace.mNumIndices]; + + // accumulate linearly all the vertices of the source face + for( unsigned int b = 0; b < dstFace.mNumIndices; ++b ) + { + unsigned int srcIndex = srcFace.mIndices[b]; + dstFace.mIndices[b] = nvi; + previousVertexIndices[nvi] = srcIndex; + + newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex]; + if( pMesh->HasNormals() ) + newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex]; + if( pMesh->HasTangentsAndBitangents() ) + { + newMesh->mTangents[nvi] = pMesh->mTangents[srcIndex]; + newMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex]; + } + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c ) + { + if( pMesh->HasTextureCoords( c) ) + newMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex]; + } + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c ) + { + if( pMesh->HasVertexColors( c) ) + newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex]; + } + + nvi++; + } + } + + ai_assert( nvi == numSubMeshVertices ); + + // Create the bones for the new submesh: first create the bone array + newMesh->mNumBones = 0; + newMesh->mBones = new aiBone*[numBones]; + + std::vector<unsigned int> mappedBoneIndex( pMesh->mNumBones, std::numeric_limits<unsigned int>::max()); + for( unsigned int a = 0; a < pMesh->mNumBones; ++a ) + { + if( !isBoneUsed[a] ) + continue; + + // create the new bone + const aiBone* srcBone = pMesh->mBones[a]; + aiBone* dstBone = new aiBone; + mappedBoneIndex[a] = newMesh->mNumBones; + newMesh->mBones[newMesh->mNumBones++] = dstBone; + dstBone->mName = srcBone->mName; + dstBone->mOffsetMatrix = srcBone->mOffsetMatrix; + dstBone->mNumWeights = 0; + } + + ai_assert( newMesh->mNumBones == numBones ); + + // iterate over all new vertices and count which bones affected its old vertex in the source mesh + for( unsigned int a = 0; a < numSubMeshVertices; ++a ) + { + unsigned int oldIndex = previousVertexIndices[a]; + const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[oldIndex]; + + for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b ) + { + unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ]; + if( newBoneIndex != std::numeric_limits<unsigned int>::max() ) + newMesh->mBones[newBoneIndex]->mNumWeights++; + } + } + + // allocate all bone weight arrays accordingly + for( unsigned int a = 0; a < newMesh->mNumBones; ++a ) + { + aiBone* bone = newMesh->mBones[a]; + ai_assert( bone->mNumWeights > 0 ); + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + bone->mNumWeights = 0; // for counting up in the next step + } + + // now copy all the bone vertex weights for all the vertices which made it into the new submesh + for( unsigned int a = 0; a < numSubMeshVertices; ++a) + { + // find the source vertex for it in the source mesh + unsigned int previousIndex = previousVertexIndices[a]; + // these bones were affecting it + const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[previousIndex]; + // all of the bones affecting it should be present in the new submesh, or else + // the face it comprises shouldn't be present + for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b) + { + unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ]; + ai_assert( newBoneIndex != std::numeric_limits<unsigned int>::max() ); + aiVertexWeight* dstWeight = newMesh->mBones[newBoneIndex]->mWeights + newMesh->mBones[newBoneIndex]->mNumWeights; + newMesh->mBones[newBoneIndex]->mNumWeights++; + + dstWeight->mVertexId = a; + dstWeight->mWeight = bonesOnThisVertex[b].second; + } + } + + // I have the strange feeling that this will break apart at some point in time... + } +} + +// ------------------------------------------------------------------------------------------------ +// Recursively updates the node's mesh list to account for the changed mesh list +void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const +{ + // rebuild the node's mesh index list + if( pNode->mNumMeshes > 0 ) + { + std::vector<unsigned int> newMeshList; + for( unsigned int a = 0; a < pNode->mNumMeshes; ++a) + { + unsigned int srcIndex = pNode->mMeshes[a]; + const std::vector<unsigned int>& replaceMeshes = mSubMeshIndices[srcIndex]; + newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end()); + } + + delete [] pNode->mMeshes; + pNode->mNumMeshes = static_cast<unsigned int>(newMeshList.size()); + pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; + std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes); + } + + // do that also recursively for all children + for( unsigned int a = 0; a < pNode->mNumChildren; ++a ) + { + UpdateNode( pNode->mChildren[a]); + } +} diff --git a/thirdparty/assimp/code/SplitByBoneCountProcess.h b/thirdparty/assimp/code/SplitByBoneCountProcess.h new file mode 100644 index 0000000000..6c904a9df4 --- /dev/null +++ b/thirdparty/assimp/code/SplitByBoneCountProcess.h @@ -0,0 +1,111 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/// @file SplitByBoneCountProcess.h +/// Defines a post processing step to split meshes with many bones into submeshes +#ifndef AI_SPLITBYBONECOUNTPROCESS_H_INC +#define AI_SPLITBYBONECOUNTPROCESS_H_INC + +#include <vector> +#include "BaseProcess.h" + +#include <assimp/mesh.h> +#include <assimp/scene.h> + +namespace Assimp +{ + + +/** Postprocessing filter to split meshes with many bones into submeshes + * so that each submesh has a certain max bone count. + * + * Applied BEFORE the JoinVertices-Step occurs. + * Returns NON-UNIQUE vertices, splits by bone count. +*/ +class SplitByBoneCountProcess : public BaseProcess +{ +public: + + SplitByBoneCountProcess(); + ~SplitByBoneCountProcess(); + +public: + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. A + * bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + +protected: + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + /// Splits the given mesh by bone count. + /// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split. + /// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary. + void SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const; + + /// Recursively updates the node's mesh list to account for the changed mesh list + void UpdateNode( aiNode* pNode) const; + +public: + /// Max bone count. Splitting occurs if a mesh has more than that number of bones. + size_t mMaxBoneCount; + + /// Per mesh index: Array of indices of the new submeshes. + std::vector< std::vector<unsigned int> > mSubMeshIndices; +}; + +} // end of namespace Assimp + +#endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC diff --git a/thirdparty/assimp/code/SplitLargeMeshes.cpp b/thirdparty/assimp/code/SplitLargeMeshes.cpp new file mode 100644 index 0000000000..1797b28d5a --- /dev/null +++ b/thirdparty/assimp/code/SplitLargeMeshes.cpp @@ -0,0 +1,623 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** + * @file Implementation of the SplitLargeMeshes postprocessing step + */ + +// internal headers of the post-processing framework +#include "SplitLargeMeshes.h" +#include "ProcessHelper.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +SplitLargeMeshesProcess_Triangle::SplitLargeMeshesProcess_Triangle() { + LIMIT = AI_SLM_DEFAULT_MAX_TRIANGLES; +} + +// ------------------------------------------------------------------------------------------------ +SplitLargeMeshesProcess_Triangle::~SplitLargeMeshesProcess_Triangle() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool SplitLargeMeshesProcess_Triangle::IsActive( unsigned int pFlags) const { + return (pFlags & aiProcess_SplitLargeMeshes) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene) { + if (0xffffffff == this->LIMIT || nullptr == pScene ) { + return; + } + + ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle begin"); + std::vector<std::pair<aiMesh*, unsigned int> > avList; + + for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) { + this->SplitMesh(a, pScene->mMeshes[a],avList); + } + + if (avList.size() != pScene->mNumMeshes) { + // it seems something has been split. rebuild the mesh list + delete[] pScene->mMeshes; + pScene->mNumMeshes = (unsigned int)avList.size(); + pScene->mMeshes = new aiMesh*[avList.size()]; + + for (unsigned int i = 0; i < avList.size();++i) { + pScene->mMeshes[i] = avList[i].first; + } + + // now we need to update all nodes + this->UpdateNode(pScene->mRootNode,avList); + ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split"); + } else { + ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties +void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp) { + // get the current value of the split property + this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); +} + +// ------------------------------------------------------------------------------------------------ +// Update a node after some meshes have been split +void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode, + const std::vector<std::pair<aiMesh*, unsigned int> >& avList) { + // for every index in out list build a new entry + std::vector<unsigned int> aiEntries; + aiEntries.reserve(pcNode->mNumMeshes + 1); + for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) { + for (unsigned int a = 0; a < avList.size();++a) { + if (avList[a].second == pcNode->mMeshes[i]) { + aiEntries.push_back(a); + } + } + } + + // now build the new list + delete[] pcNode->mMeshes; + pcNode->mNumMeshes = (unsigned int)aiEntries.size(); + pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; + + for (unsigned int b = 0; b < pcNode->mNumMeshes;++b) { + pcNode->mMeshes[b] = aiEntries[b]; + } + + // recusively update all other nodes + for (unsigned int i = 0; i < pcNode->mNumChildren;++i) { + UpdateNode ( pcNode->mChildren[i], avList ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitLargeMeshesProcess_Triangle::SplitMesh( + unsigned int a, + aiMesh* pMesh, + std::vector<std::pair<aiMesh*, unsigned int> >& avList) { + if (pMesh->mNumFaces > SplitLargeMeshesProcess_Triangle::LIMIT) { + ASSIMP_LOG_INFO("Mesh exceeds the triangle limit. It will be split ..."); + + // we need to split this mesh into sub meshes + // determine the size of a submesh + const unsigned int iSubMeshes = (pMesh->mNumFaces / LIMIT) + 1; + + const unsigned int iOutFaceNum = pMesh->mNumFaces / iSubMeshes; + const unsigned int iOutVertexNum = iOutFaceNum * 3; + + // now generate all submeshes + for (unsigned int i = 0; i < iSubMeshes;++i) { + aiMesh* pcMesh = new aiMesh; + pcMesh->mNumFaces = iOutFaceNum; + pcMesh->mMaterialIndex = pMesh->mMaterialIndex; + + // the name carries the adjacency information between the meshes + pcMesh->mName = pMesh->mName; + + if (i == iSubMeshes-1) { + pcMesh->mNumFaces = iOutFaceNum + ( + pMesh->mNumFaces - iOutFaceNum * iSubMeshes); + } + // copy the list of faces + pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; + + const unsigned int iBase = iOutFaceNum * i; + + // get the total number of indices + unsigned int iCnt = 0; + for (unsigned int p = iBase; p < pcMesh->mNumFaces + iBase;++p) { + iCnt += pMesh->mFaces[p].mNumIndices; + } + pcMesh->mNumVertices = iCnt; + + // allocate storage + if (pMesh->mVertices != nullptr) { + pcMesh->mVertices = new aiVector3D[iCnt]; + } + + if (pMesh->HasNormals()) { + pcMesh->mNormals = new aiVector3D[iCnt]; + } + + if (pMesh->HasTangentsAndBitangents()) { + pcMesh->mTangents = new aiVector3D[iCnt]; + pcMesh->mBitangents = new aiVector3D[iCnt]; + } + + // texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c]; + if (pMesh->HasTextureCoords( c)) { + pcMesh->mTextureCoords[c] = new aiVector3D[iCnt]; + } + } + + // vertex colors + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { + if (pMesh->HasVertexColors( c)) { + pcMesh->mColors[c] = new aiColor4D[iCnt]; + } + } + + if (pMesh->HasBones()) { + // assume the number of bones won't change in most cases + pcMesh->mBones = new aiBone*[pMesh->mNumBones]; + + // iterate through all bones of the mesh and find those which + // need to be copied to the split mesh + std::vector<aiVertexWeight> avTempWeights; + for (unsigned int p = 0; p < pcMesh->mNumBones;++p) { + aiBone* const bone = pcMesh->mBones[p]; + avTempWeights.clear(); + avTempWeights.reserve(bone->mNumWeights / iSubMeshes); + + for (unsigned int q = 0; q < bone->mNumWeights;++q) { + aiVertexWeight& weight = bone->mWeights[q]; + if(weight.mVertexId >= iBase && weight.mVertexId < iBase + iOutVertexNum) { + avTempWeights.push_back(weight); + weight = avTempWeights.back(); + weight.mVertexId -= iBase; + } + } + + if (!avTempWeights.empty()) { + // we'll need this bone. Copy it ... + aiBone* pc = new aiBone(); + pcMesh->mBones[pcMesh->mNumBones++] = pc; + pc->mName = aiString(bone->mName); + pc->mNumWeights = (unsigned int)avTempWeights.size(); + pc->mOffsetMatrix = bone->mOffsetMatrix; + + // no need to reallocate the array for the last submesh. + // Here we can reuse the (large) source array, although + // we'll waste some memory + if (iSubMeshes-1 == i) { + pc->mWeights = bone->mWeights; + bone->mWeights = nullptr; + } else { + pc->mWeights = new aiVertexWeight[pc->mNumWeights]; + } + + // copy the weights + ::memcpy(pc->mWeights,&avTempWeights[0],sizeof(aiVertexWeight)*pc->mNumWeights); + } + } + } + + // (we will also need to copy the array of indices) + unsigned int iCurrent = 0; + for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) { + pcMesh->mFaces[p].mNumIndices = 3; + // allocate a new array + const unsigned int iTemp = p + iBase; + const unsigned int iNumIndices = pMesh->mFaces[iTemp].mNumIndices; + + // setup face type and number of indices + pcMesh->mFaces[p].mNumIndices = iNumIndices; + unsigned int* pi = pMesh->mFaces[iTemp].mIndices; + unsigned int* piOut = pcMesh->mFaces[p].mIndices = new unsigned int[iNumIndices]; + + // need to update the output primitive types + switch (iNumIndices) { + case 1: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 2: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 3: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + } + + // and copy the contents of the old array, offset by current base + for (unsigned int v = 0; v < iNumIndices;++v) { + unsigned int iIndex = pi[v]; + unsigned int iIndexOut = iCurrent++; + piOut[v] = iIndexOut; + + // copy positions + if (pMesh->mVertices != nullptr) { + pcMesh->mVertices[iIndexOut] = pMesh->mVertices[iIndex]; + } + + // copy normals + if (pMesh->HasNormals()) { + pcMesh->mNormals[iIndexOut] = pMesh->mNormals[iIndex]; + } + + // copy tangents/bitangents + if (pMesh->HasTangentsAndBitangents()) { + pcMesh->mTangents[iIndexOut] = pMesh->mTangents[iIndex]; + pcMesh->mBitangents[iIndexOut] = pMesh->mBitangents[iIndex]; + } + + // texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + if (pMesh->HasTextureCoords( c ) ) { + pcMesh->mTextureCoords[c][iIndexOut] = pMesh->mTextureCoords[c][iIndex]; + } + } + // vertex colors + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { + if (pMesh->HasVertexColors( c)) { + pcMesh->mColors[c][iIndexOut] = pMesh->mColors[c][iIndex]; + } + } + } + } + + // add the newly created mesh to the list + avList.push_back(std::pair<aiMesh*, unsigned int>(pcMesh,a)); + } + + // now delete the old mesh data + delete pMesh; + } else { + avList.push_back(std::pair<aiMesh*, unsigned int>(pMesh,a)); + } +} + +// ------------------------------------------------------------------------------------------------ +SplitLargeMeshesProcess_Vertex::SplitLargeMeshesProcess_Vertex() { + LIMIT = AI_SLM_DEFAULT_MAX_VERTICES; +} + +// ------------------------------------------------------------------------------------------------ +SplitLargeMeshesProcess_Vertex::~SplitLargeMeshesProcess_Vertex() { + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool SplitLargeMeshesProcess_Vertex::IsActive( unsigned int pFlags) const { + return (pFlags & aiProcess_SplitLargeMeshes) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene) { + if (0xffffffff == this->LIMIT || nullptr == pScene ) { + return; + } + + ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Vertex begin"); + + std::vector<std::pair<aiMesh*, unsigned int> > avList; + + //Check for point cloud first, + //Do not process point cloud, splitMesh works only with faces data + for (unsigned int a = 0; a < pScene->mNumMeshes; a++) { + if ( pScene->mMeshes[a]->mPrimitiveTypes == aiPrimitiveType_POINT ) { + return; + } + } + + for( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { + this->SplitMesh(a, pScene->mMeshes[a], avList); + } + + if (avList.size() != pScene->mNumMeshes) { + // it seems something has been split. rebuild the mesh list + delete[] pScene->mMeshes; + pScene->mNumMeshes = (unsigned int)avList.size(); + pScene->mMeshes = new aiMesh*[avList.size()]; + + for (unsigned int i = 0; i < avList.size();++i) { + pScene->mMeshes[i] = avList[i].first; + } + + // now we need to update all nodes + SplitLargeMeshesProcess_Triangle::UpdateNode(pScene->mRootNode,avList); + ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Vertex finished. Meshes have been split"); + } else { + ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Vertex finished. There was nothing to do"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties +void SplitLargeMeshesProcess_Vertex::SetupProperties( const Importer* pImp) { + this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitLargeMeshesProcess_Vertex::SplitMesh( + unsigned int a, + aiMesh* pMesh, + std::vector<std::pair<aiMesh*, unsigned int> >& avList) { + if (pMesh->mNumVertices > SplitLargeMeshesProcess_Vertex::LIMIT) { + typedef std::vector< std::pair<unsigned int,float> > VertexWeightTable; + + // build a per-vertex weight list if necessary + VertexWeightTable* avPerVertexWeights = ComputeVertexBoneWeightTable(pMesh); + + // we need to split this mesh into sub meshes + // determine the estimated size of a submesh + // (this could be too large. Max waste is a single digit percentage) + const unsigned int iSubMeshes = (pMesh->mNumVertices / SplitLargeMeshesProcess_Vertex::LIMIT) + 1; + + // create a std::vector<unsigned int> to indicate which vertices + // have already been copied + std::vector<unsigned int> avWasCopied; + avWasCopied.resize(pMesh->mNumVertices,0xFFFFFFFF); + + // try to find a good estimate for the number of output faces + // per mesh. Add 12.5% as buffer + unsigned int iEstimatedSize = pMesh->mNumFaces / iSubMeshes; + iEstimatedSize += iEstimatedSize >> 3; + + // now generate all submeshes + unsigned int iBase( 0 ); + while (true) { + const unsigned int iOutVertexNum = SplitLargeMeshesProcess_Vertex::LIMIT; + aiMesh* pcMesh = new aiMesh; + pcMesh->mNumVertices = 0; + pcMesh->mMaterialIndex = pMesh->mMaterialIndex; + + // the name carries the adjacency information between the meshes + pcMesh->mName = pMesh->mName; + + typedef std::vector<aiVertexWeight> BoneWeightList; + if (pMesh->HasBones()) { + pcMesh->mBones = new aiBone*[pMesh->mNumBones]; + ::memset(pcMesh->mBones,0,sizeof(void*)*pMesh->mNumBones); + } + + // clear the temporary helper array + if (iBase) { + // we can't use memset here we unsigned int needn' be 32 bits + for (auto &elem : avWasCopied) { + elem = 0xffffffff; + } + } + + // output vectors + std::vector<aiFace> vFaces; + + // reserve enough storage for most cases + if (pMesh->HasPositions()) { + pcMesh->mVertices = new aiVector3D[iOutVertexNum]; + } + if (pMesh->HasNormals()) { + pcMesh->mNormals = new aiVector3D[iOutVertexNum]; + } + if (pMesh->HasTangentsAndBitangents()) { + pcMesh->mTangents = new aiVector3D[iOutVertexNum]; + pcMesh->mBitangents = new aiVector3D[iOutVertexNum]; + } + for (unsigned int c = 0; pMesh->HasVertexColors(c);++c) { + pcMesh->mColors[c] = new aiColor4D[iOutVertexNum]; + } + for (unsigned int c = 0; pMesh->HasTextureCoords(c);++c) { + pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c]; + pcMesh->mTextureCoords[c] = new aiVector3D[iOutVertexNum]; + } + vFaces.reserve(iEstimatedSize); + + // (we will also need to copy the array of indices) + while (iBase < pMesh->mNumFaces) { + // allocate a new array + const unsigned int iNumIndices = pMesh->mFaces[iBase].mNumIndices; + + // doesn't catch degenerates but is quite fast + unsigned int iNeed = 0; + for (unsigned int v = 0; v < iNumIndices;++v) { + unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v]; + + // check whether we do already have this vertex + if (0xFFFFFFFF == avWasCopied[iIndex]) { + iNeed++; + } + } + if (pcMesh->mNumVertices + iNeed > iOutVertexNum) { + // don't use this face + break; + } + + vFaces.push_back(aiFace()); + aiFace& rFace = vFaces.back(); + + // setup face type and number of indices + rFace.mNumIndices = iNumIndices; + rFace.mIndices = new unsigned int[iNumIndices]; + + // need to update the output primitive types + switch (rFace.mNumIndices) { + case 1: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 2: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 3: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + } + + // and copy the contents of the old array, offset by current base + for (unsigned int v = 0; v < iNumIndices;++v) { + unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v]; + + // check whether we do already have this vertex + if (0xFFFFFFFF != avWasCopied[iIndex]) { + rFace.mIndices[v] = avWasCopied[iIndex]; + continue; + } + + // copy positions + pcMesh->mVertices[pcMesh->mNumVertices] = (pMesh->mVertices[iIndex]); + + // copy normals + if (pMesh->HasNormals()) { + pcMesh->mNormals[pcMesh->mNumVertices] = (pMesh->mNormals[iIndex]); + } + + // copy tangents/bitangents + if (pMesh->HasTangentsAndBitangents()) { + pcMesh->mTangents[pcMesh->mNumVertices] = (pMesh->mTangents[iIndex]); + pcMesh->mBitangents[pcMesh->mNumVertices] = (pMesh->mBitangents[iIndex]); + } + + // texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + if (pMesh->HasTextureCoords( c)) { + pcMesh->mTextureCoords[c][pcMesh->mNumVertices] = pMesh->mTextureCoords[c][iIndex]; + } + } + // vertex colors + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { + if (pMesh->HasVertexColors( c)) { + pcMesh->mColors[c][pcMesh->mNumVertices] = pMesh->mColors[c][iIndex]; + } + } + // check whether we have bone weights assigned to this vertex + rFace.mIndices[v] = pcMesh->mNumVertices; + if (avPerVertexWeights) { + VertexWeightTable& table = avPerVertexWeights[ pcMesh->mNumVertices ]; + if( !table.empty() ) { + for (VertexWeightTable::const_iterator iter = table.begin(); + iter != table.end();++iter) { + // allocate the bone weight array if necessary + BoneWeightList* pcWeightList = (BoneWeightList*)pcMesh->mBones[(*iter).first]; + if (nullptr == pcWeightList) { + pcMesh->mBones[(*iter).first] = (aiBone*)(pcWeightList = new BoneWeightList()); + } + pcWeightList->push_back(aiVertexWeight(pcMesh->mNumVertices,(*iter).second)); + } + } + } + + avWasCopied[iIndex] = pcMesh->mNumVertices; + pcMesh->mNumVertices++; + } + ++iBase; + if(pcMesh->mNumVertices == iOutVertexNum) { + // break here. The face is only added if it was complete + break; + } + } + + // check which bones we'll need to create for this submesh + if (pMesh->HasBones()) { + aiBone** ppCurrent = pcMesh->mBones; + for (unsigned int k = 0; k < pMesh->mNumBones;++k) { + // check whether the bone is existing + BoneWeightList* pcWeightList; + if ((pcWeightList = (BoneWeightList*)pcMesh->mBones[k])) { + aiBone* pcOldBone = pMesh->mBones[k]; + aiBone* pcOut( nullptr ); + *ppCurrent++ = pcOut = new aiBone(); + pcOut->mName = aiString(pcOldBone->mName); + pcOut->mOffsetMatrix = pcOldBone->mOffsetMatrix; + pcOut->mNumWeights = (unsigned int)pcWeightList->size(); + pcOut->mWeights = new aiVertexWeight[pcOut->mNumWeights]; + + // copy the vertex weights + ::memcpy(pcOut->mWeights,&pcWeightList->operator[](0), + pcOut->mNumWeights * sizeof(aiVertexWeight)); + + // delete the temporary bone weight list + delete pcWeightList; + pcMesh->mNumBones++; + } + } + } + + // copy the face list to the mesh + pcMesh->mFaces = new aiFace[vFaces.size()]; + pcMesh->mNumFaces = (unsigned int)vFaces.size(); + + for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) { + pcMesh->mFaces[p] = vFaces[p]; + } + + // add the newly created mesh to the list + avList.push_back(std::pair<aiMesh*, unsigned int>(pcMesh,a)); + + if (iBase == pMesh->mNumFaces) { + // have all faces ... finish the outer loop, too + break; + } + } + + // delete the per-vertex weight list again + delete[] avPerVertexWeights; + + // now delete the old mesh data + delete pMesh; + return; + } + avList.push_back(std::pair<aiMesh*, unsigned int>(pMesh,a)); +} diff --git a/thirdparty/assimp/code/SplitLargeMeshes.h b/thirdparty/assimp/code/SplitLargeMeshes.h new file mode 100644 index 0000000000..77f089ce7e --- /dev/null +++ b/thirdparty/assimp/code/SplitLargeMeshes.h @@ -0,0 +1,210 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to split large meshes into submeshes + */ +#ifndef AI_SPLITLARGEMESHES_H_INC +#define AI_SPLITLARGEMESHES_H_INC + +#include <vector> +#include "BaseProcess.h" + +#include <assimp/mesh.h> +#include <assimp/scene.h> + +class SplitLargeMeshesTest; + +namespace Assimp +{ + +class SplitLargeMeshesProcess_Triangle; +class SplitLargeMeshesProcess_Vertex; + +// NOTE: If you change these limits, don't forget to change the +// corresponding values in all Assimp ports + +// ********************************************************** +// Java: ConfigProperty.java, +// ConfigProperty.DEFAULT_VERTEX_SPLIT_LIMIT +// ConfigProperty.DEFAULT_TRIANGLE_SPLIT_LIMIT +// ********************************************************** + +// default limit for vertices +#if (!defined AI_SLM_DEFAULT_MAX_VERTICES) +# define AI_SLM_DEFAULT_MAX_VERTICES 1000000 +#endif + +// default limit for triangles +#if (!defined AI_SLM_DEFAULT_MAX_TRIANGLES) +# define AI_SLM_DEFAULT_MAX_TRIANGLES 1000000 +#endif + +// --------------------------------------------------------------------------- +/** Post-processing filter to split large meshes into sub-meshes + * + * Applied BEFORE the JoinVertices-Step occurs. + * Returns NON-UNIQUE vertices, splits by triangle number. +*/ +class ASSIMP_API SplitLargeMeshesProcess_Triangle : public BaseProcess +{ + friend class SplitLargeMeshesProcess_Vertex; + +public: + + SplitLargeMeshesProcess_Triangle(); + ~SplitLargeMeshesProcess_Triangle(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. A + * bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + + + //! Set the split limit - needed for unit testing + inline void SetLimit(unsigned int l) + {LIMIT = l;} + + //! Get the split limit + inline unsigned int GetLimit() const + {return LIMIT;} + +public: + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + //! Apply the algorithm to a given mesh + void SplitMesh (unsigned int a, aiMesh* pcMesh, + std::vector<std::pair<aiMesh*, unsigned int> >& avList); + + // ------------------------------------------------------------------- + //! Update a node in the asset after a few of its meshes + //! have been split + static void UpdateNode(aiNode* pcNode, + const std::vector<std::pair<aiMesh*, unsigned int> >& avList); + +public: + //! Triangle limit + unsigned int LIMIT; +}; + + +// --------------------------------------------------------------------------- +/** Post-processing filter to split large meshes into sub-meshes + * + * Applied AFTER the JoinVertices-Step occurs. + * Returns UNIQUE vertices, splits by vertex number. +*/ +class ASSIMP_API SplitLargeMeshesProcess_Vertex : public BaseProcess +{ +public: + + SplitLargeMeshesProcess_Vertex(); + ~SplitLargeMeshesProcess_Vertex(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + + + //! Set the split limit - needed for unit testing + inline void SetLimit(unsigned int l) + {LIMIT = l;} + + //! Get the split limit + inline unsigned int GetLimit() const + {return LIMIT;} + +public: + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + //! Apply the algorithm to a given mesh + void SplitMesh (unsigned int a, aiMesh* pcMesh, + std::vector<std::pair<aiMesh*, unsigned int> >& avList); + + // NOTE: Reuse SplitLargeMeshesProcess_Triangle::UpdateNode() + +public: + //! Triangle limit + unsigned int LIMIT; +}; + +} // end of namespace Assimp + +#endif // !!AI_SPLITLARGEMESHES_H_INC diff --git a/thirdparty/assimp/code/StandardShapes.cpp b/thirdparty/assimp/code/StandardShapes.cpp new file mode 100644 index 0000000000..2e5100130f --- /dev/null +++ b/thirdparty/assimp/code/StandardShapes.cpp @@ -0,0 +1,507 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file StandardShapes.cpp + * @brief Implementation of the StandardShapes class + * + * The primitive geometry data comes from + * http://geometrictools.com/Documentation/PlatonicSolids.pdf. + */ + +#include <assimp/StandardShapes.h> +#include <assimp/StringComparison.h> +#include <stddef.h> +#include <assimp/Defines.h> +#include <assimp/mesh.h> + +namespace Assimp { + + +# define ADD_TRIANGLE(n0,n1,n2) \ + positions.push_back(n0); \ + positions.push_back(n1); \ + positions.push_back(n2); + +# define ADD_PENTAGON(n0,n1,n2,n3,n4) \ + if (polygons) \ + { \ + positions.push_back(n0); \ + positions.push_back(n1); \ + positions.push_back(n2); \ + positions.push_back(n3); \ + positions.push_back(n4); \ + } \ + else \ + { \ + ADD_TRIANGLE(n0, n1, n2) \ + ADD_TRIANGLE(n0, n2, n3) \ + ADD_TRIANGLE(n0, n3, n4) \ + } + +# define ADD_QUAD(n0,n1,n2,n3) \ + if (polygons) \ + { \ + positions.push_back(n0); \ + positions.push_back(n1); \ + positions.push_back(n2); \ + positions.push_back(n3); \ + } \ + else \ + { \ + ADD_TRIANGLE(n0, n1, n2) \ + ADD_TRIANGLE(n0, n2, n3) \ + } + + +// ------------------------------------------------------------------------------------------------ +// Fast subdivision for a mesh whose verts have a magnitude of 1 +void Subdivide(std::vector<aiVector3D>& positions) +{ + // assume this to be constant - (fixme: must be 1.0? I think so) + const ai_real fl1 = positions[0].Length(); + + unsigned int origSize = (unsigned int)positions.size(); + for (unsigned int i = 0 ; i < origSize ; i+=3) + { + aiVector3D& tv0 = positions[i]; + aiVector3D& tv1 = positions[i+1]; + aiVector3D& tv2 = positions[i+2]; + + aiVector3D a = tv0, b = tv1, c = tv2; + aiVector3D v1 = aiVector3D(a.x+b.x, a.y+b.y, a.z+b.z).Normalize()*fl1; + aiVector3D v2 = aiVector3D(a.x+c.x, a.y+c.y, a.z+c.z).Normalize()*fl1; + aiVector3D v3 = aiVector3D(b.x+c.x, b.y+c.y, b.z+c.z).Normalize()*fl1; + + tv0 = v1; tv1 = v3; tv2 = v2; // overwrite the original + ADD_TRIANGLE(v1, v2, a); + ADD_TRIANGLE(v2, v3, c); + ADD_TRIANGLE(v3, v1, b); + } +} + +// ------------------------------------------------------------------------------------------------ +// Construct a mesh from given vertex positions +aiMesh* StandardShapes::MakeMesh(const std::vector<aiVector3D>& positions, + unsigned int numIndices) +{ + if (positions.empty() || !numIndices) return NULL; + + // Determine which kinds of primitives the mesh consists of + aiMesh* out = new aiMesh(); + switch (numIndices) { + case 1: + out->mPrimitiveTypes = aiPrimitiveType_POINT; + break; + case 2: + out->mPrimitiveTypes = aiPrimitiveType_LINE; + break; + case 3: + out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + break; + default: + out->mPrimitiveTypes = aiPrimitiveType_POLYGON; + break; + }; + + out->mNumFaces = (unsigned int)positions.size() / numIndices; + out->mFaces = new aiFace[out->mNumFaces]; + for (unsigned int i = 0, a = 0; i < out->mNumFaces;++i) { + aiFace& f = out->mFaces[i]; + f.mNumIndices = numIndices; + f.mIndices = new unsigned int[numIndices]; + for (unsigned int j = 0; i < numIndices; ++i, ++a) { + f.mIndices[j] = a; + } + } + out->mNumVertices = (unsigned int)positions.size(); + out->mVertices = new aiVector3D[out->mNumVertices]; + ::memcpy(out->mVertices,&positions[0],out->mNumVertices*sizeof(aiVector3D)); + + return out; +} + +// ------------------------------------------------------------------------------------------------ +// Construct a mesh with a specific shape (callback) +aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)( + std::vector<aiVector3D>&)) +{ + std::vector<aiVector3D> temp; + unsigned num = (*GenerateFunc)(temp); + return MakeMesh(temp,num); +} + +// ------------------------------------------------------------------------------------------------ +// Construct a mesh with a specific shape (callback) +aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)( + std::vector<aiVector3D>&, bool)) +{ + std::vector<aiVector3D> temp; + unsigned num = (*GenerateFunc)(temp,true); + return MakeMesh(temp,num); +} + +// ------------------------------------------------------------------------------------------------ +// Construct a mesh with a specific shape (callback) +aiMesh* StandardShapes::MakeMesh (unsigned int num, void (*GenerateFunc)( + unsigned int,std::vector<aiVector3D>&)) +{ + std::vector<aiVector3D> temp; + (*GenerateFunc)(num,temp); + return MakeMesh(temp,3); +} + +// ------------------------------------------------------------------------------------------------ +// Build an incosahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeIcosahedron(std::vector<aiVector3D>& positions) +{ + positions.reserve(positions.size()+60); + + const ai_real t = ( ai_real( 1.0 )+ ai_real( 2.236067977 ) ) / ai_real( 2.0 ); + const ai_real s = std::sqrt(ai_real(1.0) + t*t); + + const aiVector3D v0 = aiVector3D(t,1.0, 0.0)/s; + const aiVector3D v1 = aiVector3D(-t,1.0, 0.0)/s; + const aiVector3D v2 = aiVector3D(t,-1.0, 0.0)/s; + const aiVector3D v3 = aiVector3D(-t,-1.0, 0.0)/s; + const aiVector3D v4 = aiVector3D(1.0, 0.0, t)/s; + const aiVector3D v5 = aiVector3D(1.0, 0.0,-t)/s; + const aiVector3D v6 = aiVector3D(-1.0, 0.0,t)/s; + const aiVector3D v7 = aiVector3D(-1.0, 0.0,-t)/s; + const aiVector3D v8 = aiVector3D(0.0, t, 1.0)/s; + const aiVector3D v9 = aiVector3D(0.0,-t, 1.0)/s; + const aiVector3D v10 = aiVector3D(0.0, t,-1.0)/s; + const aiVector3D v11 = aiVector3D(0.0,-t,-1.0)/s; + + ADD_TRIANGLE(v0,v8,v4); + ADD_TRIANGLE(v0,v5,v10); + ADD_TRIANGLE(v2,v4,v9); + ADD_TRIANGLE(v2,v11,v5); + + ADD_TRIANGLE(v1,v6,v8); + ADD_TRIANGLE(v1,v10,v7); + ADD_TRIANGLE(v3,v9,v6); + ADD_TRIANGLE(v3,v7,v11); + + ADD_TRIANGLE(v0,v10,v8); + ADD_TRIANGLE(v1,v8,v10); + ADD_TRIANGLE(v2,v9,v11); + ADD_TRIANGLE(v3,v11,v9); + + ADD_TRIANGLE(v4,v2,v0); + ADD_TRIANGLE(v5,v0,v2); + ADD_TRIANGLE(v6,v1,v3); + ADD_TRIANGLE(v7,v3,v1); + + ADD_TRIANGLE(v8,v6,v4); + ADD_TRIANGLE(v9,v4,v6); + ADD_TRIANGLE(v10,v5,v7); + ADD_TRIANGLE(v11,v7,v5); + return 3; +} + +// ------------------------------------------------------------------------------------------------ +// Build a dodecahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeDodecahedron(std::vector<aiVector3D>& positions, + bool polygons /*= false*/) +{ + positions.reserve(positions.size()+108); + + const ai_real a = ai_real( 1.0 ) / ai_real(1.7320508); + const ai_real b = std::sqrt(( ai_real( 3.0 )- ai_real( 2.23606797))/ ai_real( 6.0) ); + const ai_real c = std::sqrt(( ai_real( 3.0 )+ ai_real( 2.23606797f))/ ai_real( 6.0) ); + + const aiVector3D v0 = aiVector3D(a,a,a); + const aiVector3D v1 = aiVector3D(a,a,-a); + const aiVector3D v2 = aiVector3D(a,-a,a); + const aiVector3D v3 = aiVector3D(a,-a,-a); + const aiVector3D v4 = aiVector3D(-a,a,a); + const aiVector3D v5 = aiVector3D(-a,a,-a); + const aiVector3D v6 = aiVector3D(-a,-a,a); + const aiVector3D v7 = aiVector3D(-a,-a,-a); + const aiVector3D v8 = aiVector3D(b,c,0.0); + const aiVector3D v9 = aiVector3D(-b,c,0.0); + const aiVector3D v10 = aiVector3D(b,-c,0.0); + const aiVector3D v11 = aiVector3D(-b,-c,0.0); + const aiVector3D v12 = aiVector3D(c, 0.0, b); + const aiVector3D v13 = aiVector3D(c, 0.0, -b); + const aiVector3D v14 = aiVector3D(-c, 0.0, b); + const aiVector3D v15 = aiVector3D(-c, 0.0, -b); + const aiVector3D v16 = aiVector3D(0.0, b, c); + const aiVector3D v17 = aiVector3D(0.0, -b, c); + const aiVector3D v18 = aiVector3D(0.0, b, -c); + const aiVector3D v19 = aiVector3D(0.0, -b, -c); + + ADD_PENTAGON(v0, v8, v9, v4, v16); + ADD_PENTAGON(v0, v12, v13, v1, v8); + ADD_PENTAGON(v0, v16, v17, v2, v12); + ADD_PENTAGON(v8, v1, v18, v5, v9); + ADD_PENTAGON(v12, v2, v10, v3, v13); + ADD_PENTAGON(v16, v4, v14, v6, v17); + ADD_PENTAGON(v9, v5, v15, v14, v4); + + ADD_PENTAGON(v6, v11, v10, v2, v17); + ADD_PENTAGON(v3, v19, v18, v1, v13); + ADD_PENTAGON(v7, v15, v5, v18, v19); + ADD_PENTAGON(v7, v11, v6, v14, v15); + ADD_PENTAGON(v7, v19, v3, v10, v11); + return (polygons ? 5 : 3); +} + +// ------------------------------------------------------------------------------------------------ +// Build an octahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeOctahedron(std::vector<aiVector3D>& positions) +{ + positions.reserve(positions.size()+24); + + const aiVector3D v0 = aiVector3D(1.0, 0.0, 0.0) ; + const aiVector3D v1 = aiVector3D(-1.0, 0.0, 0.0); + const aiVector3D v2 = aiVector3D(0.0, 1.0, 0.0); + const aiVector3D v3 = aiVector3D(0.0, -1.0, 0.0); + const aiVector3D v4 = aiVector3D(0.0, 0.0, 1.0); + const aiVector3D v5 = aiVector3D(0.0, 0.0, -1.0); + + ADD_TRIANGLE(v4,v0,v2); + ADD_TRIANGLE(v4,v2,v1); + ADD_TRIANGLE(v4,v1,v3); + ADD_TRIANGLE(v4,v3,v0); + + ADD_TRIANGLE(v5,v2,v0); + ADD_TRIANGLE(v5,v1,v2); + ADD_TRIANGLE(v5,v3,v1); + ADD_TRIANGLE(v5,v0,v3); + return 3; +} + +// ------------------------------------------------------------------------------------------------ +// Build a tetrahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeTetrahedron(std::vector<aiVector3D>& positions) +{ + positions.reserve(positions.size()+9); + + const ai_real invThree = ai_real( 1.0 ) / ai_real( 3.0 ); + const ai_real a = ai_real( 1.41421 ) * invThree; + const ai_real b = ai_real( 2.4494 ) * invThree; + + const aiVector3D v0 = aiVector3D(0.0,0.0,1.0); + const aiVector3D v1 = aiVector3D(2*a,0,-invThree ); + const aiVector3D v2 = aiVector3D(-a,b,-invThree ); + const aiVector3D v3 = aiVector3D(-a,-b,-invThree ); + + ADD_TRIANGLE(v0,v1,v2); + ADD_TRIANGLE(v0,v2,v3); + ADD_TRIANGLE(v0,v3,v1); + ADD_TRIANGLE(v1,v3,v2); + return 3; +} + +// ------------------------------------------------------------------------------------------------ +// Build a hexahedron with points.magnitude == 1 +unsigned int StandardShapes::MakeHexahedron(std::vector<aiVector3D>& positions, + bool polygons /*= false*/) +{ + positions.reserve(positions.size()+36); + const ai_real length = ai_real(1.0)/ai_real(1.73205080); + + const aiVector3D v0 = aiVector3D(-1.0,-1.0,-1.0)*length; + const aiVector3D v1 = aiVector3D(1.0,-1.0,-1.0)*length; + const aiVector3D v2 = aiVector3D(1.0,1.0,-1.0)*length; + const aiVector3D v3 = aiVector3D(-1.0,1.0,-1.0)*length; + const aiVector3D v4 = aiVector3D(-1.0,-1.0,1.0)*length; + const aiVector3D v5 = aiVector3D(1.0,-1.0,1.0)*length; + const aiVector3D v6 = aiVector3D(1.0,1.0,1.0)*length; + const aiVector3D v7 = aiVector3D(-1.0,1.0,1.0)*length; + + ADD_QUAD(v0,v3,v2,v1); + ADD_QUAD(v0,v1,v5,v4); + ADD_QUAD(v0,v4,v7,v3); + ADD_QUAD(v6,v5,v1,v2); + ADD_QUAD(v6,v2,v3,v7); + ADD_QUAD(v6,v7,v4,v5); + return (polygons ? 4 : 3); +} + +// Cleanup ... +#undef ADD_TRIANGLE +#undef ADD_QUAD +#undef ADD_PENTAGON + +// ------------------------------------------------------------------------------------------------ +// Create a subdivision sphere +void StandardShapes::MakeSphere(unsigned int tess, + std::vector<aiVector3D>& positions) +{ + // Reserve enough storage. Every subdivision + // splits each triangle in 4, the icosahedron consists of 60 verts + positions.reserve(positions.size()+60 * integer_pow(4, tess)); + + // Construct an icosahedron to start with + MakeIcosahedron(positions); + + // ... and subdivide it until the requested output + // tessellation is reached + for (unsigned int i = 0; i<tess;++i) + Subdivide(positions); +} + +// ------------------------------------------------------------------------------------------------ +// Build a cone +void StandardShapes::MakeCone(ai_real height,ai_real radius1, + ai_real radius2,unsigned int tess, + std::vector<aiVector3D>& positions,bool bOpen /*= false */) +{ + // Sorry, a cone with less than 3 segments makes ABSOLUTELY NO SENSE + if (tess < 3 || !height) + return; + + size_t old = positions.size(); + + // No negative radii + radius1 = std::fabs(radius1); + radius2 = std::fabs(radius2); + + ai_real halfHeight = height / ai_real(2.0); + + // radius1 is always the smaller one + if (radius2 > radius1) + { + std::swap(radius2,radius1); + halfHeight = -halfHeight; + } + else old = SIZE_MAX; + + // Use a large epsilon to check whether the cone is pointy + if (radius1 < (radius2-radius1)*10e-3)radius1 = 0.0; + + // We will need 3*2 verts per segment + 3*2 verts per segment + // if the cone is closed + const unsigned int mem = tess*6 + (!bOpen ? tess*3 * (radius1 ? 2 : 1) : 0); + positions.reserve(positions.size () + mem); + + // Now construct all segments + const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess; + const ai_real angle_max = (ai_real)AI_MATH_TWO_PI; + + ai_real s = 1.0; // std::cos(angle == 0); + ai_real t = 0.0; // std::sin(angle == 0); + + for (ai_real angle = 0.0; angle < angle_max; ) + { + const aiVector3D v1 = aiVector3D (s * radius1, -halfHeight, t * radius1 ); + const aiVector3D v2 = aiVector3D (s * radius2, halfHeight, t * radius2 ); + + const ai_real next = angle + angle_delta; + ai_real s2 = std::cos(next); + ai_real t2 = std::sin(next); + + const aiVector3D v3 = aiVector3D (s2 * radius2, halfHeight, t2 * radius2 ); + const aiVector3D v4 = aiVector3D (s2 * radius1, -halfHeight, t2 * radius1 ); + + positions.push_back(v1); + positions.push_back(v2); + positions.push_back(v3); + positions.push_back(v4); + positions.push_back(v1); + positions.push_back(v3); + + if (!bOpen) + { + // generate the end 'cap' + positions.push_back(aiVector3D(s * radius2, halfHeight, t * radius2 )); + positions.push_back(aiVector3D(s2 * radius2, halfHeight, t2 * radius2 )); + positions.push_back(aiVector3D(0.0, halfHeight, 0.0)); + + + if (radius1) + { + // generate the other end 'cap' + positions.push_back(aiVector3D(s * radius1, -halfHeight, t * radius1 )); + positions.push_back(aiVector3D(s2 * radius1, -halfHeight, t2 * radius1 )); + positions.push_back(aiVector3D(0.0, -halfHeight, 0.0)); + + } + } + s = s2; + t = t2; + angle = next; + } + + // Need to flip face order? + if ( SIZE_MAX != old ) { + for (size_t p = old; p < positions.size();p += 3) { + std::swap(positions[p],positions[p+1]); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Build a circle +void StandardShapes::MakeCircle(ai_real radius, unsigned int tess, + std::vector<aiVector3D>& positions) +{ + // Sorry, a circle with less than 3 segments makes ABSOLUTELY NO SENSE + if (tess < 3 || !radius) + return; + + radius = std::fabs(radius); + + // We will need 3 vertices per segment + positions.reserve(positions.size()+tess*3); + + const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess; + const ai_real angle_max = (ai_real)AI_MATH_TWO_PI; + + ai_real s = 1.0; // std::cos(angle == 0); + ai_real t = 0.0; // std::sin(angle == 0); + + for (ai_real angle = 0.0; angle < angle_max; ) + { + positions.push_back(aiVector3D(s * radius,0.0,t * radius)); + angle += angle_delta; + s = std::cos(angle); + t = std::sin(angle); + positions.push_back(aiVector3D(s * radius,0.0,t * radius)); + + positions.push_back(aiVector3D(0.0,0.0,0.0)); + } +} + +} // ! Assimp diff --git a/thirdparty/assimp/code/StdOStreamLogStream.h b/thirdparty/assimp/code/StdOStreamLogStream.h new file mode 100644 index 0000000000..893e261a2b --- /dev/null +++ b/thirdparty/assimp/code/StdOStreamLogStream.h @@ -0,0 +1,101 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file StdOStreamLogStream.h +* @brief Implementation of StdOStreamLogStream +*/ + +#ifndef AI_STROSTREAMLOGSTREAM_H_INC +#define AI_STROSTREAMLOGSTREAM_H_INC + +#include <assimp/LogStream.hpp> +#include <ostream> + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** @class StdOStreamLogStream + * @brief Logs into a std::ostream + */ +class StdOStreamLogStream : public LogStream { +public: + /** @brief Construction from an existing std::ostream + * @param _ostream Output stream to be used + */ + explicit StdOStreamLogStream(std::ostream& _ostream); + + /** @brief Destructor */ + ~StdOStreamLogStream(); + + /** @brief Writer */ + void write(const char* message); + +private: + std::ostream& mOstream; +}; + +// --------------------------------------------------------------------------- +// Default constructor +inline StdOStreamLogStream::StdOStreamLogStream(std::ostream& _ostream) +: mOstream (_ostream){ + // empty +} + +// --------------------------------------------------------------------------- +// Default constructor +inline StdOStreamLogStream::~StdOStreamLogStream() { + // empty +} + +// --------------------------------------------------------------------------- +// Write method +inline void StdOStreamLogStream::write(const char* message) { + mOstream << message; + mOstream.flush(); +} + +// --------------------------------------------------------------------------- + +} // Namespace Assimp + +#endif // guard diff --git a/thirdparty/assimp/code/Subdivision.cpp b/thirdparty/assimp/code/Subdivision.cpp new file mode 100644 index 0000000000..19db223a55 --- /dev/null +++ b/thirdparty/assimp/code/Subdivision.cpp @@ -0,0 +1,588 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include <assimp/Subdivision.h> +#include <assimp/SceneCombiner.h> +#include <assimp/SpatialSort.h> +#include "ProcessHelper.h" +#include <assimp/Vertex.h> +#include <assimp/ai_assert.h> +#include <stdio.h> + +using namespace Assimp; +void mydummy() {} + +// ------------------------------------------------------------------------------------------------ +/** Subdivider stub class to implement the Catmull-Clarke subdivision algorithm. The + * implementation is basing on recursive refinement. Directly evaluating the result is also + * possible and much quicker, but it depends on lengthy matrix lookup tables. */ +// ------------------------------------------------------------------------------------------------ +class CatmullClarkSubdivider : public Subdivider +{ +public: + void Subdivide (aiMesh* mesh, aiMesh*& out, unsigned int num, bool discard_input); + void Subdivide (aiMesh** smesh, size_t nmesh, + aiMesh** out, unsigned int num, bool discard_input); + + // --------------------------------------------------------------------------- + /** Intermediate description of an edge between two corners of a polygon*/ + // --------------------------------------------------------------------------- + struct Edge + { + Edge() + : ref(0) + {} + Vertex edge_point, midpoint; + unsigned int ref; + }; + + typedef std::vector<unsigned int> UIntVector; + typedef std::map<uint64_t,Edge> EdgeMap; + + // --------------------------------------------------------------------------- + // Hashing function to derive an index into an #EdgeMap from two given + // 'unsigned int' vertex coordinates (!!distinct coordinates - same + // vertex position == same index!!). + // NOTE - this leads to rare hash collisions if a) sizeof(unsigned int)>4 + // and (id[0]>2^32-1 or id[0]>2^32-1). + // MAKE_EDGE_HASH() uses temporaries, so INIT_EDGE_HASH() needs to be put + // at the head of every function which is about to use MAKE_EDGE_HASH(). + // Reason is that the hash is that hash construction needs to hold the + // invariant id0<id1 to identify an edge - else two hashes would refer + // to the same edge. + // --------------------------------------------------------------------------- +#define MAKE_EDGE_HASH(id0,id1) (eh_tmp0__=id0,eh_tmp1__=id1,\ + (eh_tmp0__<eh_tmp1__?std::swap(eh_tmp0__,eh_tmp1__):mydummy()),(uint64_t)eh_tmp0__^((uint64_t)eh_tmp1__<<32u)) + + +#define INIT_EDGE_HASH_TEMPORARIES()\ + unsigned int eh_tmp0__, eh_tmp1__; + +private: + void InternSubdivide (const aiMesh* const * smesh, + size_t nmesh,aiMesh** out, unsigned int num); +}; + + +// ------------------------------------------------------------------------------------------------ +// Construct a subdivider of a specific type +Subdivider* Subdivider::Create (Algorithm algo) +{ + switch (algo) + { + case CATMULL_CLARKE: + return new CatmullClarkSubdivider(); + }; + + ai_assert(false); + return NULL; // shouldn't happen +} + +// ------------------------------------------------------------------------------------------------ +// Call the Catmull Clark subdivision algorithm for one mesh +void CatmullClarkSubdivider::Subdivide ( + aiMesh* mesh, + aiMesh*& out, + unsigned int num, + bool discard_input + ) +{ + ai_assert(mesh != out); + + Subdivide(&mesh,1,&out,num,discard_input); +} + +// ------------------------------------------------------------------------------------------------ +// Call the Catmull Clark subdivision algorithm for multiple meshes +void CatmullClarkSubdivider::Subdivide ( + aiMesh** smesh, + size_t nmesh, + aiMesh** out, + unsigned int num, + bool discard_input + ) +{ + ai_assert( NULL != smesh ); + ai_assert( NULL != out ); + + // course, both regions may not overlap + ai_assert(smesh<out || smesh+nmesh>out+nmesh); + if (!num) { + // No subdivision at all. Need to copy all the meshes .. argh. + if (discard_input) { + for (size_t s = 0; s < nmesh; ++s) { + out[s] = smesh[s]; + smesh[s] = NULL; + } + } + else { + for (size_t s = 0; s < nmesh; ++s) { + SceneCombiner::Copy(out+s,smesh[s]); + } + } + return; + } + + std::vector<aiMesh*> inmeshes; + std::vector<aiMesh*> outmeshes; + std::vector<unsigned int> maptbl; + + inmeshes.reserve(nmesh); + outmeshes.reserve(nmesh); + maptbl.reserve(nmesh); + + // Remove pure line and point meshes from the working set to reduce the + // number of edge cases the subdivider is forced to deal with. Line and + // point meshes are simply passed through. + for (size_t s = 0; s < nmesh; ++s) { + aiMesh* i = smesh[s]; + // FIX - mPrimitiveTypes might not yet be initialized + if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE|aiPrimitiveType_POINT))==i->mPrimitiveTypes) { + ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Skipping pure line/point mesh"); + + if (discard_input) { + out[s] = i; + smesh[s] = NULL; + } + else { + SceneCombiner::Copy(out+s,i); + } + continue; + } + + outmeshes.push_back(NULL);inmeshes.push_back(i); + maptbl.push_back(static_cast<unsigned int>(s)); + } + + // Do the actual subdivision on the preallocated storage. InternSubdivide + // *always* assumes that enough storage is available, it does not bother + // checking any ranges. + ai_assert(inmeshes.size()==outmeshes.size()&&inmeshes.size()==maptbl.size()); + if (inmeshes.empty()) { + ASSIMP_LOG_WARN("Catmull-Clark Subdivider: Pure point/line scene, I can't do anything"); + return; + } + InternSubdivide(&inmeshes.front(),inmeshes.size(),&outmeshes.front(),num); + for (unsigned int i = 0; i < maptbl.size(); ++i) { + ai_assert(nullptr != outmeshes[i]); + out[maptbl[i]] = outmeshes[i]; + } + + if (discard_input) { + for (size_t s = 0; s < nmesh; ++s) { + delete smesh[s]; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Note - this is an implementation of the standard (recursive) Cm-Cl algorithm without further +// optimizations (except we're using some nice LUTs). A description of the algorithm can be found +// here: http://en.wikipedia.org/wiki/Catmull-Clark_subdivision_surface +// +// The code is mostly O(n), however parts are O(nlogn) which is therefore the algorithm's +// expected total runtime complexity. The implementation is able to work in-place on the same +// mesh arrays. Calling #InternSubdivide() directly is not encouraged. The code can operate +// in-place unless 'smesh' and 'out' are equal (no strange overlaps or reorderings). +// Previous data is replaced/deleted then. +// ------------------------------------------------------------------------------------------------ +void CatmullClarkSubdivider::InternSubdivide ( + const aiMesh* const * smesh, + size_t nmesh, + aiMesh** out, + unsigned int num + ) +{ + ai_assert(NULL != smesh && NULL != out); + INIT_EDGE_HASH_TEMPORARIES(); + + // no subdivision requested or end of recursive refinement + if (!num) { + return; + } + + UIntVector maptbl; + SpatialSort spatial; + + // --------------------------------------------------------------------- + // 0. Offset table to index all meshes continuously, generate a spatially + // sorted representation of all vertices in all meshes. + // --------------------------------------------------------------------- + typedef std::pair<unsigned int,unsigned int> IntPair; + std::vector<IntPair> moffsets(nmesh); + unsigned int totfaces = 0, totvert = 0; + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh* mesh = smesh[t]; + + spatial.Append(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D),false); + moffsets[t] = IntPair(totfaces,totvert); + + totfaces += mesh->mNumFaces; + totvert += mesh->mNumVertices; + } + + spatial.Finalize(); + const unsigned int num_unique = spatial.GenerateMappingTable(maptbl,ComputePositionEpsilon(smesh,nmesh)); + + +#define FLATTEN_VERTEX_IDX(mesh_idx, vert_idx) (moffsets[mesh_idx].second+vert_idx) +#define FLATTEN_FACE_IDX(mesh_idx, face_idx) (moffsets[mesh_idx].first+face_idx) + + // --------------------------------------------------------------------- + // 1. Compute the centroid point for all faces + // --------------------------------------------------------------------- + std::vector<Vertex> centroids(totfaces); + unsigned int nfacesout = 0; + for (size_t t = 0, n = 0; t < nmesh; ++t) { + const aiMesh* mesh = smesh[t]; + for (unsigned int i = 0; i < mesh->mNumFaces;++i,++n) + { + const aiFace& face = mesh->mFaces[i]; + Vertex& c = centroids[n]; + + for (unsigned int a = 0; a < face.mNumIndices;++a) { + c += Vertex(mesh,face.mIndices[a]); + } + + c /= static_cast<float>(face.mNumIndices); + nfacesout += face.mNumIndices; + } + } + + { + // we want edges to go away before the recursive calls so begin a new scope + EdgeMap edges; + + // --------------------------------------------------------------------- + // 2. Set each edge point to be the average of all neighbouring + // face points and original points. Every edge exists twice + // if there is a neighboring face. + // --------------------------------------------------------------------- + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh* mesh = smesh[t]; + + for (unsigned int i = 0; i < mesh->mNumFaces;++i) { + const aiFace& face = mesh->mFaces[i]; + + for (unsigned int p =0; p< face.mNumIndices; ++p) { + const unsigned int id[] = { + face.mIndices[p], + face.mIndices[p==face.mNumIndices-1?0:p+1] + }; + const unsigned int mp[] = { + maptbl[FLATTEN_VERTEX_IDX(t,id[0])], + maptbl[FLATTEN_VERTEX_IDX(t,id[1])] + }; + + Edge& e = edges[MAKE_EDGE_HASH(mp[0],mp[1])]; + e.ref++; + if (e.ref<=2) { + if (e.ref==1) { // original points (end points) - add only once + e.edge_point = e.midpoint = Vertex(mesh,id[0])+Vertex(mesh,id[1]); + e.midpoint *= 0.5f; + } + e.edge_point += centroids[FLATTEN_FACE_IDX(t,i)]; + } + } + } + } + + // --------------------------------------------------------------------- + // 3. Normalize edge points + // --------------------------------------------------------------------- + {unsigned int bad_cnt = 0; + for (EdgeMap::iterator it = edges.begin(); it != edges.end(); ++it) { + if ((*it).second.ref < 2) { + ai_assert((*it).second.ref); + ++bad_cnt; + } + (*it).second.edge_point *= 1.f/((*it).second.ref+2.f); + } + + if (bad_cnt) { + // Report the number of bad edges. bad edges are referenced by less than two + // faces in the mesh. They occur at outer model boundaries in non-closed + // shapes. + ASSIMP_LOG_DEBUG_F("Catmull-Clark Subdivider: got ", bad_cnt, " bad edges touching only one face (totally ", + static_cast<unsigned int>(edges.size()), " edges). "); + }} + + // --------------------------------------------------------------------- + // 4. Compute a vertex-face adjacency table. We can't reuse the code + // from VertexTriangleAdjacency because we need the table for multiple + // meshes and out vertex indices need to be mapped to distinct values + // first. + // --------------------------------------------------------------------- + UIntVector faceadjac(nfacesout), cntadjfac(maptbl.size(),0), ofsadjvec(maptbl.size()+1,0); { + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh* const minp = smesh[t]; + for (unsigned int i = 0; i < minp->mNumFaces; ++i) { + + const aiFace& f = minp->mFaces[i]; + for (unsigned int n = 0; n < f.mNumIndices; ++n) { + ++cntadjfac[maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]; + } + } + } + unsigned int cur = 0; + for (size_t i = 0; i < cntadjfac.size(); ++i) { + ofsadjvec[i+1] = cur; + cur += cntadjfac[i]; + } + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh* const minp = smesh[t]; + for (unsigned int i = 0; i < minp->mNumFaces; ++i) { + + const aiFace& f = minp->mFaces[i]; + for (unsigned int n = 0; n < f.mNumIndices; ++n) { + faceadjac[ofsadjvec[1+maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]++] = FLATTEN_FACE_IDX(t,i); + } + } + } + + // check the other way round for consistency +#ifdef ASSIMP_BUILD_DEBUG + + for (size_t t = 0; t < ofsadjvec.size()-1; ++t) { + for (unsigned int m = 0; m < cntadjfac[t]; ++m) { + const unsigned int fidx = faceadjac[ofsadjvec[t]+m]; + ai_assert(fidx < totfaces); + for (size_t n = 1; n < nmesh; ++n) { + + if (moffsets[n].first > fidx) { + const aiMesh* msh = smesh[--n]; + const aiFace& f = msh->mFaces[fidx-moffsets[n].first]; + + bool haveit = false; + for (unsigned int i = 0; i < f.mNumIndices; ++i) { + if (maptbl[FLATTEN_VERTEX_IDX(n,f.mIndices[i])]==(unsigned int)t) { + haveit = true; + break; + } + } + ai_assert(haveit); + if (!haveit) { + ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Index not used"); + } + break; + } + } + } + } + +#endif + } + +#define GET_ADJACENT_FACES_AND_CNT(vidx,fstartout,numout) \ + fstartout = &faceadjac[ofsadjvec[vidx]], numout = cntadjfac[vidx] + + typedef std::pair<bool,Vertex> TouchedOVertex; + std::vector<TouchedOVertex > new_points(num_unique,TouchedOVertex(false,Vertex())); + // --------------------------------------------------------------------- + // 5. Spawn a quad from each face point to the corresponding edge points + // the original points being the fourth quad points. + // --------------------------------------------------------------------- + for (size_t t = 0; t < nmesh; ++t) { + const aiMesh* const minp = smesh[t]; + aiMesh* const mout = out[t] = new aiMesh(); + + for (unsigned int a = 0; a < minp->mNumFaces; ++a) { + mout->mNumFaces += minp->mFaces[a].mNumIndices; + } + + // We need random access to the old face buffer, so reuse is not possible. + mout->mFaces = new aiFace[mout->mNumFaces]; + + mout->mNumVertices = mout->mNumFaces*4; + mout->mVertices = new aiVector3D[mout->mNumVertices]; + + // quads only, keep material index + mout->mPrimitiveTypes = aiPrimitiveType_POLYGON; + mout->mMaterialIndex = minp->mMaterialIndex; + + if (minp->HasNormals()) { + mout->mNormals = new aiVector3D[mout->mNumVertices]; + } + + if (minp->HasTangentsAndBitangents()) { + mout->mTangents = new aiVector3D[mout->mNumVertices]; + mout->mBitangents = new aiVector3D[mout->mNumVertices]; + } + + for(unsigned int i = 0; minp->HasTextureCoords(i); ++i) { + mout->mTextureCoords[i] = new aiVector3D[mout->mNumVertices]; + mout->mNumUVComponents[i] = minp->mNumUVComponents[i]; + } + + for(unsigned int i = 0; minp->HasVertexColors(i); ++i) { + mout->mColors[i] = new aiColor4D[mout->mNumVertices]; + } + + mout->mNumVertices = mout->mNumFaces<<2u; + for (unsigned int i = 0, v = 0, n = 0; i < minp->mNumFaces;++i) { + + const aiFace& face = minp->mFaces[i]; + for (unsigned int a = 0; a < face.mNumIndices;++a) { + + // Get a clean new face. + aiFace& faceOut = mout->mFaces[n++]; + faceOut.mIndices = new unsigned int [faceOut.mNumIndices = 4]; + + // Spawn a new quadrilateral (ccw winding) for this original point between: + // a) face centroid + centroids[FLATTEN_FACE_IDX(t,i)].SortBack(mout,faceOut.mIndices[0]=v++); + + // b) adjacent edge on the left, seen from the centroid + const Edge& e0 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])], + maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a==face.mNumIndices-1?0:a+1]) + ])]; // fixme: replace with mod face.mNumIndices? + + // c) adjacent edge on the right, seen from the centroid + const Edge& e1 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])], + maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[!a?face.mNumIndices-1:a-1]) + ])]; // fixme: replace with mod face.mNumIndices? + + e0.edge_point.SortBack(mout,faceOut.mIndices[3]=v++); + e1.edge_point.SortBack(mout,faceOut.mIndices[1]=v++); + + // d= original point P with distinct index i + // F := 0 + // R := 0 + // n := 0 + // for each face f containing i + // F := F+ centroid of f + // R := R+ midpoint of edge of f from i to i+1 + // n := n+1 + // + // (F+2R+(n-3)P)/n + const unsigned int org = maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])]; + TouchedOVertex& ov = new_points[org]; + + if (!ov.first) { + ov.first = true; + + const unsigned int* adj; unsigned int cnt; + GET_ADJACENT_FACES_AND_CNT(org,adj,cnt); + + if (cnt < 3) { + ov.second = Vertex(minp,face.mIndices[a]); + } + else { + + Vertex F,R; + for (unsigned int o = 0; o < cnt; ++o) { + ai_assert(adj[o] < totfaces); + F += centroids[adj[o]]; + + // adj[0] is a global face index - search the face in the mesh list + const aiMesh* mp = NULL; + size_t nidx; + + if (adj[o] < moffsets[0].first) { + mp = smesh[nidx=0]; + } + else { + for (nidx = 1; nidx<= nmesh; ++nidx) { + if (nidx == nmesh ||moffsets[nidx].first > adj[o]) { + mp = smesh[--nidx]; + break; + } + } + } + + ai_assert(adj[o]-moffsets[nidx].first < mp->mNumFaces); + const aiFace& f = mp->mFaces[adj[o]-moffsets[nidx].first]; + bool haveit = false; + + // find our original point in the face + for (unsigned int m = 0; m < f.mNumIndices; ++m) { + if (maptbl[FLATTEN_VERTEX_IDX(nidx,f.mIndices[m])] == org) { + + // add *both* edges. this way, we can be sure that we add + // *all* adjacent edges to R. In a closed shape, every + // edge is added twice - so we simply leave out the + // factor 2.f in the amove formula and get the right + // result. + + const Edge& c0 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX( + nidx,f.mIndices[!m?f.mNumIndices-1:m-1])])]; + // fixme: replace with mod face.mNumIndices? + + const Edge& c1 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX( + nidx,f.mIndices[m==f.mNumIndices-1?0:m+1])])]; + // fixme: replace with mod face.mNumIndices? + R += c0.midpoint+c1.midpoint; + + haveit = true; + break; + } + } + + // this invariant *must* hold if the vertex-to-face adjacency table is valid + ai_assert(haveit); + if ( !haveit ) { + ASSIMP_LOG_WARN( "OBJ: no name for material library specified." ); + } + } + + const float div = static_cast<float>(cnt), divsq = 1.f/(div*div); + ov.second = Vertex(minp,face.mIndices[a])*((div-3.f) / div) + R*divsq + F*divsq; + } + } + ov.second.SortBack(mout,faceOut.mIndices[2]=v++); + } + } + } + } // end of scope for edges, freeing its memory + + // --------------------------------------------------------------------- + // 7. Apply the next subdivision step. + // --------------------------------------------------------------------- + if (num != 1) { + std::vector<aiMesh*> tmp(nmesh); + InternSubdivide (out,nmesh,&tmp.front(),num-1); + for (size_t i = 0; i < nmesh; ++i) { + delete out[i]; + out[i] = tmp[i]; + } + } +} diff --git a/thirdparty/assimp/code/TargetAnimation.cpp b/thirdparty/assimp/code/TargetAnimation.cpp new file mode 100644 index 0000000000..b8062499ff --- /dev/null +++ b/thirdparty/assimp/code/TargetAnimation.cpp @@ -0,0 +1,248 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include "TargetAnimation.h" +#include <algorithm> +#include <assimp/ai_assert.h> + +using namespace Assimp; + + +// ------------------------------------------------------------------------------------------------ +KeyIterator::KeyIterator(const std::vector<aiVectorKey>* _objPos, + const std::vector<aiVectorKey>* _targetObjPos, + const aiVector3D* defaultObjectPos /*= NULL*/, + const aiVector3D* defaultTargetPos /*= NULL*/) + + : reachedEnd (false) + , curTime (-1.) + , objPos (_objPos) + , targetObjPos (_targetObjPos) + , nextObjPos (0) + , nextTargetObjPos(0) +{ + // Generate default transformation tracks if necessary + if (!objPos || objPos->empty()) + { + defaultObjPos.resize(1); + defaultObjPos.front().mTime = 10e10; + + if (defaultObjectPos) + defaultObjPos.front().mValue = *defaultObjectPos; + + objPos = & defaultObjPos; + } + if (!targetObjPos || targetObjPos->empty()) + { + defaultTargetObjPos.resize(1); + defaultTargetObjPos.front().mTime = 10e10; + + if (defaultTargetPos) + defaultTargetObjPos.front().mValue = *defaultTargetPos; + + targetObjPos = & defaultTargetObjPos; + } +} + +// ------------------------------------------------------------------------------------------------ +template <class T> +inline T Interpolate(const T& one, const T& two, ai_real val) +{ + return one + (two-one)*val; +} + +// ------------------------------------------------------------------------------------------------ +void KeyIterator::operator ++() +{ + // If we are already at the end of all keyframes, return + if (reachedEnd) { + return; + } + + // Now search in all arrays for the time value closest + // to our current position on the time line + double d0,d1; + + d0 = objPos->at ( std::min ( nextObjPos, static_cast<unsigned int>(objPos->size()-1)) ).mTime; + d1 = targetObjPos->at( std::min ( nextTargetObjPos, static_cast<unsigned int>(targetObjPos->size()-1)) ).mTime; + + // Easiest case - all are identical. In this + // case we don't need to interpolate so we can + // return earlier + if ( d0 == d1 ) + { + curTime = d0; + curPosition = objPos->at(nextObjPos).mValue; + curTargetPosition = targetObjPos->at(nextTargetObjPos).mValue; + + // increment counters + if (objPos->size() != nextObjPos-1) + ++nextObjPos; + + if (targetObjPos->size() != nextTargetObjPos-1) + ++nextTargetObjPos; + } + + // An object position key is closest to us + else if (d0 < d1) + { + curTime = d0; + + // interpolate the other + if (1 == targetObjPos->size() || !nextTargetObjPos) { + curTargetPosition = targetObjPos->at(0).mValue; + } + else + { + const aiVectorKey& last = targetObjPos->at(nextTargetObjPos); + const aiVectorKey& first = targetObjPos->at(nextTargetObjPos-1); + + curTargetPosition = Interpolate(first.mValue, last.mValue, (ai_real) ( + (curTime-first.mTime) / (last.mTime-first.mTime) )); + } + + if (objPos->size() != nextObjPos-1) + ++nextObjPos; + } + // A target position key is closest to us + else + { + curTime = d1; + + // interpolate the other + if (1 == objPos->size() || !nextObjPos) { + curPosition = objPos->at(0).mValue; + } + else + { + const aiVectorKey& last = objPos->at(nextObjPos); + const aiVectorKey& first = objPos->at(nextObjPos-1); + + curPosition = Interpolate(first.mValue, last.mValue, (ai_real) ( + (curTime-first.mTime) / (last.mTime-first.mTime))); + } + + if (targetObjPos->size() != nextTargetObjPos-1) + ++nextTargetObjPos; + } + + if (nextObjPos >= objPos->size()-1 && + nextTargetObjPos >= targetObjPos->size()-1) + { + // We reached the very last keyframe + reachedEnd = true; + } +} + +// ------------------------------------------------------------------------------------------------ +void TargetAnimationHelper::SetTargetAnimationChannel ( + const std::vector<aiVectorKey>* _targetPositions) +{ + ai_assert(NULL != _targetPositions); + targetPositions = _targetPositions; +} + +// ------------------------------------------------------------------------------------------------ +void TargetAnimationHelper::SetMainAnimationChannel ( + const std::vector<aiVectorKey>* _objectPositions) +{ + ai_assert(NULL != _objectPositions); + objectPositions = _objectPositions; +} + +// ------------------------------------------------------------------------------------------------ +void TargetAnimationHelper::SetFixedMainAnimationChannel( + const aiVector3D& fixed) +{ + objectPositions = NULL; // just to avoid confusion + fixedMain = fixed; +} + +// ------------------------------------------------------------------------------------------------ +void TargetAnimationHelper::Process(std::vector<aiVectorKey>* distanceTrack) +{ + ai_assert(NULL != targetPositions && NULL != distanceTrack); + + // TODO: in most cases we won't need the extra array + std::vector<aiVectorKey> real; + + std::vector<aiVectorKey>* fill = (distanceTrack == objectPositions ? &real : distanceTrack); + fill->reserve(std::max( objectPositions->size(), targetPositions->size() )); + + // Iterate through all object keys and interpolate their values if necessary. + // Then get the corresponding target position, compute the difference + // vector between object and target position. Then compute a rotation matrix + // that rotates the base vector of the object coordinate system at that time + // to match the diff vector. + + KeyIterator iter(objectPositions,targetPositions,&fixedMain); + for (;!iter.Finished();++iter) + { + const aiVector3D& position = iter.GetCurPosition(); + const aiVector3D& tposition = iter.GetCurTargetPosition(); + + // diff vector + aiVector3D diff = tposition - position; + ai_real f = diff.Length(); + + // output distance vector + if (f) + { + fill->push_back(aiVectorKey()); + aiVectorKey& v = fill->back(); + v.mTime = iter.GetCurTime(); + v.mValue = diff; + + diff /= f; + } + else + { + // FIXME: handle this + } + + // diff is now the vector in which our camera is pointing + } + + if (real.size()) { + *distanceTrack = real; + } +} diff --git a/thirdparty/assimp/code/TargetAnimation.h b/thirdparty/assimp/code/TargetAnimation.h new file mode 100644 index 0000000000..91634ab5aa --- /dev/null +++ b/thirdparty/assimp/code/TargetAnimation.h @@ -0,0 +1,183 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a helper class for the ASE and 3DS loaders to + help them compute camera and spot light animation channels */ +#ifndef AI_TARGET_ANIMATION_H_INC +#define AI_TARGET_ANIMATION_H_INC + +#include <assimp/anim.h> +#include <vector> + +namespace Assimp { + + + +// --------------------------------------------------------------------------- +/** Helper class to iterate through all keys in an animation channel. + * + * Missing tracks are interpolated. This is a helper class for + * TargetAnimationHelper, but it can be freely used for other purposes. +*/ +class KeyIterator +{ +public: + + + // ------------------------------------------------------------------ + /** Constructs a new key iterator + * + * @param _objPos Object position track. May be NULL. + * @param _targetObjPos Target object position track. May be NULL. + * @param defaultObjectPos Default object position to be used if + * no animated track is available. May be NULL. + * @param defaultTargetPos Default target position to be used if + * no animated track is available. May be NULL. + */ + KeyIterator(const std::vector<aiVectorKey>* _objPos, + const std::vector<aiVectorKey>* _targetObjPos, + const aiVector3D* defaultObjectPos = NULL, + const aiVector3D* defaultTargetPos = NULL); + + // ------------------------------------------------------------------ + /** Returns true if all keys have been processed + */ + bool Finished() const + {return reachedEnd;} + + // ------------------------------------------------------------------ + /** Increment the iterator + */ + void operator++(); + inline void operator++(int) + {return ++(*this);} + + + + // ------------------------------------------------------------------ + /** Getters to retrieve the current state of the iterator + */ + inline const aiVector3D& GetCurPosition() const + {return curPosition;} + + inline const aiVector3D& GetCurTargetPosition() const + {return curTargetPosition;} + + inline double GetCurTime() const + {return curTime;} + +private: + + //! Did we reach the end? + bool reachedEnd; + + //! Represents the current position of the iterator + aiVector3D curPosition, curTargetPosition; + + double curTime; + + //! Input tracks and the next key to process + const std::vector<aiVectorKey>* objPos,*targetObjPos; + + unsigned int nextObjPos, nextTargetObjPos; + std::vector<aiVectorKey> defaultObjPos,defaultTargetObjPos; +}; + +// --------------------------------------------------------------------------- +/** Helper class for the 3DS and ASE loaders to compute camera and spot light + * animations. + * + * 3DS and ASE store the differently to Assimp - there is an animation + * channel for the camera/spot light itself and a separate position + * animation channels specifying the position of the camera/spot light + * look-at target */ +class TargetAnimationHelper +{ +public: + + TargetAnimationHelper() + : targetPositions (NULL) + , objectPositions (NULL) + {} + + + // ------------------------------------------------------------------ + /** Sets the target animation channel + * + * This channel specifies the position of the camera/spot light + * target at a specific position. + * + * @param targetPositions Translation channel*/ + void SetTargetAnimationChannel (const + std::vector<aiVectorKey>* targetPositions); + + + // ------------------------------------------------------------------ + /** Sets the main animation channel + * + * @param objectPositions Translation channel */ + void SetMainAnimationChannel ( const + std::vector<aiVectorKey>* objectPositions); + + // ------------------------------------------------------------------ + /** Sets the main animation channel to a fixed value + * + * @param fixed Fixed value for the main animation channel*/ + void SetFixedMainAnimationChannel(const aiVector3D& fixed); + + + // ------------------------------------------------------------------ + /** Computes final animation channels + * @param distanceTrack Receive camera translation keys ... != NULL. */ + void Process( std::vector<aiVectorKey>* distanceTrack ); + + +private: + + const std::vector<aiVectorKey>* targetPositions,*objectPositions; + aiVector3D fixedMain; +}; + + +} // ! end namespace Assimp + +#endif // include guard diff --git a/thirdparty/assimp/code/TextureTransform.cpp b/thirdparty/assimp/code/TextureTransform.cpp new file mode 100644 index 0000000000..8ae2ba7218 --- /dev/null +++ b/thirdparty/assimp/code/TextureTransform.cpp @@ -0,0 +1,566 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file A helper class that processes texture transformations */ + + + +#include <assimp/Importer.hpp> +#include <assimp/postprocess.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/scene.h> + +#include "TextureTransform.h" +#include <assimp/StringUtils.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +TextureTransformStep::TextureTransformStep() : + configFlags() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +TextureTransformStep::~TextureTransformStep() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool TextureTransformStep::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_TransformUVCoords) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties +void TextureTransformStep::SetupProperties(const Importer* pImp) +{ + configFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_TUV_EVALUATE,AI_UVTRAFO_ALL); +} + +// ------------------------------------------------------------------------------------------------ +void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info) +{ + /* This function tries to simplify the input UV transformation. + * That's very important as it allows us to reduce the number + * of output UV channels. The order in which the transformations + * are applied is - as always - scaling, rotation, translation. + */ + + char szTemp[512]; + int rounded = 0; + + + /* Optimize the rotation angle. That's slightly difficult as + * we have an inprecise floating-point number (when comparing + * UV transformations we'll take that into account by using + * an epsilon of 5 degrees). If there is a rotation value, we can't + * perform any further optimizations. + */ + if (info.mRotation) + { + float out = info.mRotation; + if ((rounded = static_cast<int>((info.mRotation / static_cast<float>(AI_MATH_TWO_PI))))) + { + out -= rounded * static_cast<float>(AI_MATH_PI); + ASSIMP_LOG_INFO_F("Texture coordinate rotation ", info.mRotation, " can be simplified to ", out); + } + + // Next step - convert negative rotation angles to positives + if (out < 0.f) + out = (float)AI_MATH_TWO_PI * 2 + out; + + info.mRotation = out; + return; + } + + + /* Optimize UV translation in the U direction. To determine whether + * or not we can optimize we need to look at the requested mapping + * type (e.g. if mirroring is active there IS a difference between + * offset 2 and 3) + */ + if ((rounded = (int)info.mTranslation.x)) { + float out = 0.0f; + szTemp[0] = 0; + if (aiTextureMapMode_Wrap == info.mapU) { + // Wrap - simple take the fraction of the field + out = info.mTranslation.x-(float)rounded; + ai_snprintf(szTemp, 512, "[w] UV U offset %f can be simplified to %f", info.mTranslation.x, out); + } + else if (aiTextureMapMode_Mirror == info.mapU && 1 != rounded) { + // Mirror + if (rounded % 2) + rounded--; + out = info.mTranslation.x-(float)rounded; + + ai_snprintf(szTemp,512,"[m/d] UV U offset %f can be simplified to %f",info.mTranslation.x,out); + } + else if (aiTextureMapMode_Clamp == info.mapU || aiTextureMapMode_Decal == info.mapU) { + // Clamp - translations beyond 1,1 are senseless + ai_snprintf(szTemp,512,"[c] UV U offset %f can be clamped to 1.0f",info.mTranslation.x); + + out = 1.f; + } + if (szTemp[0]) { + ASSIMP_LOG_INFO(szTemp); + info.mTranslation.x = out; + } + } + + /* Optimize UV translation in the V direction. To determine whether + * or not we can optimize we need to look at the requested mapping + * type (e.g. if mirroring is active there IS a difference between + * offset 2 and 3) + */ + if ((rounded = (int)info.mTranslation.y)) { + float out = 0.0f; + szTemp[0] = 0; + if (aiTextureMapMode_Wrap == info.mapV) { + // Wrap - simple take the fraction of the field + out = info.mTranslation.y-(float)rounded; + ::ai_snprintf(szTemp,512,"[w] UV V offset %f can be simplified to %f",info.mTranslation.y,out); + } + else if (aiTextureMapMode_Mirror == info.mapV && 1 != rounded) { + // Mirror + if (rounded % 2) + rounded--; + out = info.mTranslation.x-(float)rounded; + + ::ai_snprintf(szTemp,512,"[m/d] UV V offset %f can be simplified to %f",info.mTranslation.y,out); + } + else if (aiTextureMapMode_Clamp == info.mapV || aiTextureMapMode_Decal == info.mapV) { + // Clamp - translations beyond 1,1 are senseless + ::ai_snprintf(szTemp,512,"[c] UV V offset %f canbe clamped to 1.0f",info.mTranslation.y); + + out = 1.f; + } + if (szTemp[0]) { + ASSIMP_LOG_INFO(szTemp); + info.mTranslation.y = out; + } + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void UpdateUVIndex(const std::list<TTUpdateInfo>& l, unsigned int n) +{ + // Don't set if == 0 && wasn't set before + for (std::list<TTUpdateInfo>::const_iterator it = l.begin();it != l.end(); ++it) { + const TTUpdateInfo& info = *it; + + if (info.directShortcut) + *info.directShortcut = n; + else if (!n) + { + info.mat->AddProperty<int>((int*)&n,1,AI_MATKEY_UVWSRC(info.semantic,info.index)); + } + } +} + +// ------------------------------------------------------------------------------------------------ +inline const char* MappingModeToChar(aiTextureMapMode map) +{ + if (aiTextureMapMode_Wrap == map) + return "-w"; + + if (aiTextureMapMode_Mirror == map) + return "-m"; + + return "-c"; +} + +// ------------------------------------------------------------------------------------------------ +void TextureTransformStep::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("TransformUVCoordsProcess begin"); + + + /* We build a per-mesh list of texture transformations we'll need + * to apply. To achieve this, we iterate through all materials, + * find all textures and get their transformations and UV indices. + * Then we search for all meshes using this material. + */ + typedef std::list<STransformVecInfo> MeshTrafoList; + std::vector<MeshTrafoList> meshLists(pScene->mNumMeshes); + + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { + + aiMaterial* mat = pScene->mMaterials[i]; + for (unsigned int a = 0; a < mat->mNumProperties;++a) { + + aiMaterialProperty* prop = mat->mProperties[a]; + if (!::strcmp( prop->mKey.data, "$tex.file")) { + STransformVecInfo info; + + // Setup a shortcut structure to allow for a fast updating + // of the UV index later + TTUpdateInfo update; + update.mat = (aiMaterial*) mat; + update.semantic = prop->mSemantic; + update.index = prop->mIndex; + + // Get textured properties and transform + for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2) { + aiMaterialProperty* prop2 = mat->mProperties[a2]; + if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex) { + continue; + } + + if ( !::strcmp( prop2->mKey.data, "$tex.uvwsrc")) { + info.uvIndex = *((int*)prop2->mData); + + // Store a direct pointer for later use + update.directShortcut = (unsigned int*) prop2->mData; + } + + else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodeu")) { + info.mapU = *((aiTextureMapMode*)prop2->mData); + } + else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodev")) { + info.mapV = *((aiTextureMapMode*)prop2->mData); + } + else if ( !::strcmp( prop2->mKey.data, "$tex.uvtrafo")) { + // ValidateDS should check this + ai_assert(prop2->mDataLength >= 20); + ::memcpy(&info.mTranslation.x,prop2->mData,sizeof(float)*5); + + // Directly remove this property from the list + mat->mNumProperties--; + for (unsigned int a3 = a2; a3 < mat->mNumProperties;++a3) { + mat->mProperties[a3] = mat->mProperties[a3+1]; + } + + delete prop2; + + // Warn: could be an underflow, but this does not invoke undefined behaviour + --a2; + } + } + + // Find out which transformations are to be evaluated + if (!(configFlags & AI_UVTRAFO_ROTATION)) { + info.mRotation = 0.f; + } + if (!(configFlags & AI_UVTRAFO_SCALING)) { + info.mScaling = aiVector2D(1.f,1.f); + } + if (!(configFlags & AI_UVTRAFO_TRANSLATION)) { + info.mTranslation = aiVector2D(0.f,0.f); + } + + // Do some preprocessing + PreProcessUVTransform(info); + info.uvIndex = std::min(info.uvIndex,AI_MAX_NUMBER_OF_TEXTURECOORDS -1u); + + // Find out whether this material is used by more than + // one mesh. This will make our task much, much more difficult! + unsigned int cnt = 0; + for (unsigned int n = 0; n < pScene->mNumMeshes;++n) { + if (pScene->mMeshes[n]->mMaterialIndex == i) + ++cnt; + } + + if (!cnt) + continue; + else if (1 != cnt) { + // This material is referenced by more than one mesh! + // So we need to make sure the UV index for the texture + // is identical for each of it ... + info.lockedPos = AI_TT_UV_IDX_LOCK_TBD; + } + + // Get all corresponding meshes + for (unsigned int n = 0; n < pScene->mNumMeshes;++n) { + aiMesh* mesh = pScene->mMeshes[n]; + if (mesh->mMaterialIndex != i || !mesh->mTextureCoords[0]) + continue; + + unsigned int uv = info.uvIndex; + if (!mesh->mTextureCoords[uv]) { + // If the requested UV index is not available, take the first one instead. + uv = 0; + } + + if (mesh->mNumUVComponents[info.uvIndex] >= 3){ + ASSIMP_LOG_WARN("UV transformations on 3D mapping channels are not supported"); + continue; + } + + MeshTrafoList::iterator it; + + // Check whether we have this transform setup already + for (it = meshLists[n].begin();it != meshLists[n].end(); ++it) { + + if ((*it) == info && (*it).uvIndex == uv) { + (*it).updateList.push_back(update); + break; + } + } + + if (it == meshLists[n].end()) { + meshLists[n].push_back(info); + meshLists[n].back().uvIndex = uv; + meshLists[n].back().updateList.push_back(update); + } + } + } + } + } + + char buffer[1024]; // should be sufficiently large + unsigned int outChannels = 0, inChannels = 0, transformedChannels = 0; + + // Now process all meshes. Important: we don't remove unreferenced UV channels. + // This is a job for the RemoveUnreferencedData-Step. + for (unsigned int q = 0; q < pScene->mNumMeshes;++q) { + + aiMesh* mesh = pScene->mMeshes[q]; + MeshTrafoList& trafo = meshLists[q]; + + inChannels += mesh->GetNumUVChannels(); + + if (!mesh->mTextureCoords[0] || trafo.empty() || (trafo.size() == 1 && trafo.begin()->IsUntransformed())) { + outChannels += mesh->GetNumUVChannels(); + continue; + } + + // Move untransformed UV channels to the first position in the list .... + // except if we need a new locked index which should be as small as possible + bool veto = false, need = false; + unsigned int cnt = 0; + unsigned int untransformed = 0; + + MeshTrafoList::iterator it,it2; + for (it = trafo.begin();it != trafo.end(); ++it,++cnt) { + + if (!(*it).IsUntransformed()) { + need = true; + } + + if ((*it).lockedPos == AI_TT_UV_IDX_LOCK_TBD) { + // Lock this index and make sure it won't be changed + (*it).lockedPos = cnt; + veto = true; + continue; + } + + if (!veto && it != trafo.begin() && (*it).IsUntransformed()) { + for (it2 = trafo.begin();it2 != it; ++it2) { + if (!(*it2).IsUntransformed()) + break; + } + trafo.insert(it2,*it); + trafo.erase(it); + break; + } + } + if (!need) + continue; + + // Find all that are not at their 'locked' position and move them to it. + // Conflicts are possible but quite unlikely. + cnt = 0; + for (it = trafo.begin();it != trafo.end(); ++it,++cnt) { + if ((*it).lockedPos != AI_TT_UV_IDX_LOCK_NONE && (*it).lockedPos != cnt) { + it2 = trafo.begin();unsigned int t = 0; + while (t != (*it).lockedPos) + ++it2; + + if ((*it2).lockedPos != AI_TT_UV_IDX_LOCK_NONE) { + ASSIMP_LOG_ERROR("Channel mismatch, can't compute all transformations properly [design bug]"); + continue; + } + + std::swap(*it2,*it); + if ((*it).lockedPos == untransformed) + untransformed = cnt; + } + } + + // ... and add dummies for all unreferenced channels + // at the end of the list + bool ref[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) + ref[n] = (!mesh->mTextureCoords[n] ? true : false); + + for (it = trafo.begin();it != trafo.end(); ++it) + ref[(*it).uvIndex] = true; + + for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) { + if (ref[n]) + continue; + trafo.push_back(STransformVecInfo()); + trafo.back().uvIndex = n; + } + + // Then check whether this list breaks the channel limit. + // The unimportant ones are at the end of the list, so + // it shouldn't be too worse if we remove them. + unsigned int size = (unsigned int)trafo.size(); + if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) { + + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_ERROR_F(static_cast<unsigned int>(trafo.size()), " UV channels required but just ", + AI_MAX_NUMBER_OF_TEXTURECOORDS, " available"); + } + size = AI_MAX_NUMBER_OF_TEXTURECOORDS; + } + + + aiVector3D* old[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) + old[n] = mesh->mTextureCoords[n]; + + // Now continue and generate the output channels. Channels + // that we're not going to need later can be overridden. + it = trafo.begin(); + for (unsigned int n = 0; n < trafo.size();++n,++it) { + + if (n >= size) { + // Try to use an untransformed channel for all channels we threw over board + UpdateUVIndex((*it).updateList,untransformed); + continue; + } + + outChannels++; + + // Write to the log + if (!DefaultLogger::isNullLogger()) { + ::ai_snprintf(buffer,1024,"Mesh %u, channel %u: t(%.3f,%.3f), s(%.3f,%.3f), r(%.3f), %s%s", + q,n, + (*it).mTranslation.x, + (*it).mTranslation.y, + (*it).mScaling.x, + (*it).mScaling.y, + AI_RAD_TO_DEG( (*it).mRotation), + MappingModeToChar ((*it).mapU), + MappingModeToChar ((*it).mapV)); + + ASSIMP_LOG_INFO(buffer); + } + + // Check whether we need a new buffer here + if (mesh->mTextureCoords[n]) { + + it2 = it;++it2; + for (unsigned int m = n+1; m < size;++m, ++it2) { + + if ((*it2).uvIndex == n){ + it2 = trafo.begin(); + break; + } + } + if (it2 == trafo.begin()){ + mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices]; + } + } + else mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices]; + + aiVector3D* src = old[(*it).uvIndex]; + aiVector3D* dest, *end; + dest = mesh->mTextureCoords[n]; + + ai_assert(NULL != src); + + // Copy the data to the destination array + if (dest != src) + ::memcpy(dest,src,sizeof(aiVector3D)*mesh->mNumVertices); + + end = dest + mesh->mNumVertices; + + // Build a transformation matrix and transform all UV coords with it + if (!(*it).IsUntransformed()) { + const aiVector2D& trl = (*it).mTranslation; + const aiVector2D& scl = (*it).mScaling; + + // fixme: simplify .. + ++transformedChannels; + aiMatrix3x3 matrix; + + aiMatrix3x3 m2,m3,m4,m5; + + m4.a1 = scl.x; + m4.b2 = scl.y; + + m2.a3 = m2.b3 = 0.5f; + m3.a3 = m3.b3 = -0.5f; + + if ((*it).mRotation > AI_TT_ROTATION_EPSILON ) + aiMatrix3x3::RotationZ((*it).mRotation,matrix); + + m5.a3 += trl.x; m5.b3 += trl.y; + matrix = m2 * m4 * matrix * m3 * m5; + + for (src = dest; src != end; ++src) { /* manual homogenious divide */ + src->z = 1.f; + *src = matrix * *src; + src->x /= src->z; + src->y /= src->z; + src->z = 0.f; + } + } + + // Update all UV indices + UpdateUVIndex((*it).updateList,n); + } + } + + // Print some detailed statistics into the log + if (!DefaultLogger::isNullLogger()) { + + if (transformedChannels) { + ASSIMP_LOG_INFO_F("TransformUVCoordsProcess end: ", outChannels, " output channels (in: ", inChannels, ", modified: ", transformedChannels,")"); + } else { + ASSIMP_LOG_DEBUG("TransformUVCoordsProcess finished"); + } + } +} + + diff --git a/thirdparty/assimp/code/TextureTransform.h b/thirdparty/assimp/code/TextureTransform.h new file mode 100644 index 0000000000..c556ff5d8c --- /dev/null +++ b/thirdparty/assimp/code/TextureTransform.h @@ -0,0 +1,232 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Definition of a helper step that processes texture transformations */ +#ifndef AI_TEXTURE_TRANSFORM_H_INCLUDED +#define AI_TEXTURE_TRANSFORM_H_INCLUDED + +#include <assimp/BaseImporter.h> +#include "BaseProcess.h" + +#include <assimp/material.h> +#include <list> + +struct aiNode; +struct aiMaterial; + +namespace Assimp { + +#define AI_TT_UV_IDX_LOCK_TBD 0xffffffff +#define AI_TT_UV_IDX_LOCK_NONE 0xeeeeeeee + + +#define AI_TT_ROTATION_EPSILON ((float)AI_DEG_TO_RAD(0.5)) + +// --------------------------------------------------------------------------- +/** Small helper structure representing a shortcut into the material list + * to be able to update some values quickly. +*/ +struct TTUpdateInfo { + TTUpdateInfo() AI_NO_EXCEPT + : directShortcut(nullptr) + , mat(nullptr) + , semantic(0) + , index(0) { + // empty + } + + //! Direct shortcut, if available + unsigned int* directShortcut; + + //! Material + aiMaterial *mat; + + //! Texture type and index + unsigned int semantic, index; +}; + + +// --------------------------------------------------------------------------- +/** Helper class representing texture coordinate transformations +*/ +struct STransformVecInfo : public aiUVTransform { + STransformVecInfo() AI_NO_EXCEPT + : uvIndex(0) + , mapU(aiTextureMapMode_Wrap) + , mapV(aiTextureMapMode_Wrap) + , lockedPos(AI_TT_UV_IDX_LOCK_NONE) { + // empty + } + + //! Source texture coordinate index + unsigned int uvIndex; + + //! Texture mapping mode in the u, v direction + aiTextureMapMode mapU,mapV; + + //! Locked destination UV index + //! AI_TT_UV_IDX_LOCK_TBD - to be determined + //! AI_TT_UV_IDX_LOCK_NONE - none (default) + unsigned int lockedPos; + + //! Update info - shortcuts into all materials + //! that are referencing this transform setup + std::list<TTUpdateInfo> updateList; + + + // ------------------------------------------------------------------- + /** Compare two transform setups + */ + inline bool operator== (const STransformVecInfo& other) const + { + // We use a small epsilon here + const static float epsilon = 0.05f; + + if (std::fabs( mTranslation.x - other.mTranslation.x ) > epsilon || + std::fabs( mTranslation.y - other.mTranslation.y ) > epsilon) + { + return false; + } + + if (std::fabs( mScaling.x - other.mScaling.x ) > epsilon || + std::fabs( mScaling.y - other.mScaling.y ) > epsilon) + { + return false; + } + + if (std::fabs( mRotation - other.mRotation) > epsilon) + { + return false; + } + return true; + } + + inline bool operator!= (const STransformVecInfo& other) const + { + return !(*this == other); + } + + + // ------------------------------------------------------------------- + /** Returns whether this is an untransformed texture coordinate set + */ + inline bool IsUntransformed() const + { + return (1.0f == mScaling.x && 1.f == mScaling.y && + !mTranslation.x && !mTranslation.y && + mRotation < AI_TT_ROTATION_EPSILON); + } + + // ------------------------------------------------------------------- + /** Build a 3x3 matrix from the transformations + */ + inline void GetMatrix(aiMatrix3x3& mOut) + { + mOut = aiMatrix3x3(); + + if (1.0f != mScaling.x || 1.0f != mScaling.y) + { + aiMatrix3x3 mScale; + mScale.a1 = mScaling.x; + mScale.b2 = mScaling.y; + mOut = mScale; + } + if (mRotation) + { + aiMatrix3x3 mRot; + mRot.a1 = mRot.b2 = std::cos(mRotation); + mRot.a2 = mRot.b1 = std::sin(mRotation); + mRot.a2 = -mRot.a2; + mOut *= mRot; + } + if (mTranslation.x || mTranslation.y) + { + aiMatrix3x3 mTrans; + mTrans.a3 = mTranslation.x; + mTrans.b3 = mTranslation.y; + mOut *= mTrans; + } + } +}; + + +// --------------------------------------------------------------------------- +/** Helper step to compute final UV coordinate sets if there are scalings + * or rotations in the original data read from the file. +*/ +class TextureTransformStep : public BaseProcess +{ +public: + + TextureTransformStep(); + ~TextureTransformStep(); + +public: + + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + void SetupProperties(const Importer* pImp); + + +protected: + + + // ------------------------------------------------------------------- + /** Preprocess a specific UV transformation setup + * + * @param info Transformation setup to be preprocessed. + */ + void PreProcessUVTransform(STransformVecInfo& info); + +private: + + unsigned int configFlags; +}; + +} + +#endif //! AI_TEXTURE_TRANSFORM_H_INCLUDED diff --git a/thirdparty/assimp/code/TriangulateProcess.cpp b/thirdparty/assimp/code/TriangulateProcess.cpp new file mode 100644 index 0000000000..0f68f47ddb --- /dev/null +++ b/thirdparty/assimp/code/TriangulateProcess.cpp @@ -0,0 +1,530 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file TriangulateProcess.cpp + * @brief Implementation of the post processing step to split up + * all faces with more than three indices into triangles. + * + * + * The triangulation algorithm will handle concave or convex polygons. + * Self-intersecting or non-planar polygons are not rejected, but + * they're probably not triangulated correctly. + * + * DEBUG SWITCHES - do not enable any of them in release builds: + * + * AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING + * - generates vertex colors to represent the face winding order. + * the first vertex of a polygon becomes red, the last blue. + * AI_BUILD_TRIANGULATE_DEBUG_POLYS + * - dump all polygons and their triangulation sequences to + * a file + */ +#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS +#include "TriangulateProcess.h" +#include "ProcessHelper.h" +#include "PolyTools.h" +#include <memory> + +//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING +//#define AI_BUILD_TRIANGULATE_DEBUG_POLYS + +#define POLY_GRID_Y 40 +#define POLY_GRID_X 70 +#define POLY_GRID_XPAD 20 +#define POLY_OUTPUT_FILE "assimp_polygons_debug.txt" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +TriangulateProcess::TriangulateProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +TriangulateProcess::~TriangulateProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool TriangulateProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_Triangulate) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void TriangulateProcess::Execute( aiScene* pScene) +{ + ASSIMP_LOG_DEBUG("TriangulateProcess begin"); + + bool bHas = false; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + { + if (pScene->mMeshes[ a ]) { + if ( TriangulateMesh( pScene->mMeshes[ a ] ) ) { + bHas = true; + } + } + } + if ( bHas ) { + ASSIMP_LOG_INFO( "TriangulateProcess finished. All polygons have been triangulated." ); + } else { + ASSIMP_LOG_DEBUG( "TriangulateProcess finished. There was nothing to be done." ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Triangulates the given mesh. +bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) +{ + // Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases + if (!pMesh->mPrimitiveTypes) { + bool bNeed = false; + + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { + const aiFace& face = pMesh->mFaces[a]; + + if( face.mNumIndices != 3) { + bNeed = true; + } + } + if (!bNeed) + return false; + } + else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) { + return false; + } + + // Find out how many output faces we'll get + unsigned int numOut = 0, max_out = 0; + bool get_normals = true; + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { + aiFace& face = pMesh->mFaces[a]; + if (face.mNumIndices <= 4) { + get_normals = false; + } + if( face.mNumIndices <= 3) { + numOut++; + + } + else { + numOut += face.mNumIndices-2; + max_out = std::max(max_out,face.mNumIndices); + } + } + + // Just another check whether aiMesh::mPrimitiveTypes is correct + ai_assert(numOut != pMesh->mNumFaces); + + aiVector3D* nor_out = NULL; + + // if we don't have normals yet, but expect them to be a cheap side + // product of triangulation anyway, allocate storage for them. + if (!pMesh->mNormals && get_normals) { + // XXX need a mechanism to inform the GenVertexNormals process to treat these normals as preprocessed per-face normals + // nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + } + + // the output mesh will contain triangles, but no polys anymore + pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON; + + aiFace* out = new aiFace[numOut](), *curOut = out; + std::vector<aiVector3D> temp_verts3d(max_out+2); /* temporary storage for vertices */ + std::vector<aiVector2D> temp_verts(max_out+2); + + // Apply vertex colors to represent the face winding? +#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING + if (!pMesh->mColors[0]) + pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices]; + else + new(pMesh->mColors[0]) aiColor4D[pMesh->mNumVertices]; + + aiColor4D* clr = pMesh->mColors[0]; +#endif + +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS + FILE* fout = fopen(POLY_OUTPUT_FILE,"a"); +#endif + + const aiVector3D* verts = pMesh->mVertices; + + // use std::unique_ptr to avoid slow std::vector<bool> specialiations + std::unique_ptr<bool[]> done(new bool[max_out]); + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { + aiFace& face = pMesh->mFaces[a]; + + unsigned int* idx = face.mIndices; + int num = (int)face.mNumIndices, ear = 0, tmp, prev = num-1, next = 0, max = num; + + // Apply vertex colors to represent the face winding? +#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING + for (unsigned int i = 0; i < face.mNumIndices; ++i) { + aiColor4D& c = clr[idx[i]]; + c.r = (i+1) / (float)max; + c.b = 1.f - c.r; + } +#endif + + aiFace* const last_face = curOut; + + // if it's a simple point,line or triangle: just copy it + if( face.mNumIndices <= 3) + { + aiFace& nface = *curOut++; + nface.mNumIndices = face.mNumIndices; + nface.mIndices = face.mIndices; + + face.mIndices = NULL; + continue; + } + // optimized code for quadrilaterals + else if ( face.mNumIndices == 4) { + + // quads can have at maximum one concave vertex. Determine + // this vertex (if it exists) and start tri-fanning from + // it. + unsigned int start_vertex = 0; + for (unsigned int i = 0; i < 4; ++i) { + const aiVector3D& v0 = verts[face.mIndices[(i+3) % 4]]; + const aiVector3D& v1 = verts[face.mIndices[(i+2) % 4]]; + const aiVector3D& v2 = verts[face.mIndices[(i+1) % 4]]; + + const aiVector3D& v = verts[face.mIndices[i]]; + + aiVector3D left = (v0-v); + aiVector3D diag = (v1-v); + aiVector3D right = (v2-v); + + left.Normalize(); + diag.Normalize(); + right.Normalize(); + + const float angle = std::acos(left*diag) + std::acos(right*diag); + if (angle > AI_MATH_PI_F) { + // this is the concave point + start_vertex = i; + break; + } + } + + const unsigned int temp[] = {face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3]}; + + aiFace& nface = *curOut++; + nface.mNumIndices = 3; + nface.mIndices = face.mIndices; + + nface.mIndices[0] = temp[start_vertex]; + nface.mIndices[1] = temp[(start_vertex + 1) % 4]; + nface.mIndices[2] = temp[(start_vertex + 2) % 4]; + + aiFace& sface = *curOut++; + sface.mNumIndices = 3; + sface.mIndices = new unsigned int[3]; + + sface.mIndices[0] = temp[start_vertex]; + sface.mIndices[1] = temp[(start_vertex + 2) % 4]; + sface.mIndices[2] = temp[(start_vertex + 3) % 4]; + + // prevent double deletion of the indices field + face.mIndices = NULL; + continue; + } + else + { + // A polygon with more than 3 vertices can be either concave or convex. + // Usually everything we're getting is convex and we could easily + // triangulate by tri-fanning. However, LightWave is probably the only + // modeling suite to make extensive use of highly concave, monster polygons ... + // so we need to apply the full 'ear cutting' algorithm to get it right. + + // RERQUIREMENT: polygon is expected to be simple and *nearly* planar. + // We project it onto a plane to get a 2d triangle. + + // Collect all vertices of of the polygon. + for (tmp = 0; tmp < max; ++tmp) { + temp_verts3d[tmp] = verts[idx[tmp]]; + } + + // Get newell normal of the polygon. Store it for future use if it's a polygon-only mesh + aiVector3D n; + NewellNormal<3,3,3>(n,max,&temp_verts3d.front().x,&temp_verts3d.front().y,&temp_verts3d.front().z); + if (nor_out) { + for (tmp = 0; tmp < max; ++tmp) + nor_out[idx[tmp]] = n; + } + + // Select largest normal coordinate to ignore for projection + const float ax = (n.x>0 ? n.x : -n.x); + const float ay = (n.y>0 ? n.y : -n.y); + const float az = (n.z>0 ? n.z : -n.z); + + unsigned int ac = 0, bc = 1; /* no z coord. projection to xy */ + float inv = n.z; + if (ax > ay) { + if (ax > az) { /* no x coord. projection to yz */ + ac = 1; bc = 2; + inv = n.x; + } + } + else if (ay > az) { /* no y coord. projection to zy */ + ac = 2; bc = 0; + inv = n.y; + } + + // Swap projection axes to take the negated projection vector into account + if (inv < 0.f) { + std::swap(ac,bc); + } + + for (tmp =0; tmp < max; ++tmp) { + temp_verts[tmp].x = verts[idx[tmp]][ac]; + temp_verts[tmp].y = verts[idx[tmp]][bc]; + done[tmp] = false; + } + +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS + // plot the plane onto which we mapped the polygon to a 2D ASCII pic + aiVector2D bmin,bmax; + ArrayBounds(&temp_verts[0],max,bmin,bmax); + + char grid[POLY_GRID_Y][POLY_GRID_X+POLY_GRID_XPAD]; + std::fill_n((char*)grid,POLY_GRID_Y*(POLY_GRID_X+POLY_GRID_XPAD),' '); + + for (int i =0; i < max; ++i) { + const aiVector2D& v = (temp_verts[i] - bmin) / (bmax-bmin); + const size_t x = static_cast<size_t>(v.x*(POLY_GRID_X-1)), y = static_cast<size_t>(v.y*(POLY_GRID_Y-1)); + char* loc = grid[y]+x; + if (grid[y][x] != ' ') { + for(;*loc != ' '; ++loc); + *loc++ = '_'; + } + *(loc+::ai_snprintf(loc, POLY_GRID_XPAD,"%i",i)) = ' '; + } + + + for(size_t y = 0; y < POLY_GRID_Y; ++y) { + grid[y][POLY_GRID_X+POLY_GRID_XPAD-1] = '\0'; + fprintf(fout,"%s\n",grid[y]); + } + + fprintf(fout,"\ntriangulation sequence: "); +#endif + + // + // FIXME: currently this is the slow O(kn) variant with a worst case + // complexity of O(n^2) (I think). Can be done in O(n). + while (num > 3) { + + // Find the next ear of the polygon + int num_found = 0; + for (ear = next;;prev = ear,ear = next) { + + // break after we looped two times without a positive match + for (next=ear+1;done[(next>=max?next=0:next)];++next); + if (next < ear) { + if (++num_found == 2) { + break; + } + } + const aiVector2D* pnt1 = &temp_verts[ear], + *pnt0 = &temp_verts[prev], + *pnt2 = &temp_verts[next]; + + // Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1. + if (OnLeftSideOfLine2D(*pnt0,*pnt2,*pnt1)) { + continue; + } + + // and no other point may be contained in this triangle + for ( tmp = 0; tmp < max; ++tmp) { + + // We need to compare the actual values because it's possible that multiple indexes in + // the polygon are referring to the same position. concave_polygon.obj is a sample + // + // FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in + // PointInTriangle() I'm guessing that it's actually possible to construct + // input data that would cause us to end up with no ears. The problem is, + // which epsilon? If we chose a too large value, we'd get wrong results + const aiVector2D& vtmp = temp_verts[tmp]; + if ( vtmp != *pnt1 && vtmp != *pnt2 && vtmp != *pnt0 && PointInTriangle2D(*pnt0,*pnt1,*pnt2,vtmp)) { + break; + } + } + if (tmp != max) { + continue; + } + + // this vertex is an ear + break; + } + if (num_found == 2) { + + // Due to the 'two ear theorem', every simple polygon with more than three points must + // have 2 'ears'. Here's definitely something wrong ... but we don't give up yet. + // + + // Instead we're continuing with the standard tri-fanning algorithm which we'd + // use if we had only convex polygons. That's life. + ASSIMP_LOG_ERROR("Failed to triangulate polygon (no ear found). Probably not a simple polygon?"); + +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS + fprintf(fout,"critical error here, no ear found! "); +#endif + num = 0; + break; + + curOut -= (max-num); /* undo all previous work */ + for (tmp = 0; tmp < max-2; ++tmp) { + aiFace& nface = *curOut++; + + nface.mNumIndices = 3; + if (!nface.mIndices) + nface.mIndices = new unsigned int[3]; + + nface.mIndices[0] = 0; + nface.mIndices[1] = tmp+1; + nface.mIndices[2] = tmp+2; + + } + num = 0; + break; + } + + aiFace& nface = *curOut++; + nface.mNumIndices = 3; + + if (!nface.mIndices) { + nface.mIndices = new unsigned int[3]; + } + + // setup indices for the new triangle ... + nface.mIndices[0] = prev; + nface.mIndices[1] = ear; + nface.mIndices[2] = next; + + // exclude the ear from most further processing + done[ear] = true; + --num; + } + if (num > 0) { + // We have three indices forming the last 'ear' remaining. Collect them. + aiFace& nface = *curOut++; + nface.mNumIndices = 3; + if (!nface.mIndices) { + nface.mIndices = new unsigned int[3]; + } + + for (tmp = 0; done[tmp]; ++tmp); + nface.mIndices[0] = tmp; + + for (++tmp; done[tmp]; ++tmp); + nface.mIndices[1] = tmp; + + for (++tmp; done[tmp]; ++tmp); + nface.mIndices[2] = tmp; + + } + } + +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS + + for(aiFace* f = last_face; f != curOut; ++f) { + unsigned int* i = f->mIndices; + fprintf(fout," (%i %i %i)",i[0],i[1],i[2]); + } + + fprintf(fout,"\n*********************************************************************\n"); + fflush(fout); + +#endif + + for(aiFace* f = last_face; f != curOut; ) { + unsigned int* i = f->mIndices; + + // drop dumb 0-area triangles - deactivated for now: + //FindDegenerates post processing step can do the same thing + //if (std::fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) { + // ASSIMP_LOG_DEBUG("Dropping triangle with area 0"); + // --curOut; + + // delete[] f->mIndices; + // f->mIndices = nullptr; + + // for(aiFace* ff = f; ff != curOut; ++ff) { + // ff->mNumIndices = (ff+1)->mNumIndices; + // ff->mIndices = (ff+1)->mIndices; + // (ff+1)->mIndices = nullptr; + // } + // continue; + //} + + i[0] = idx[i[0]]; + i[1] = idx[i[1]]; + i[2] = idx[i[2]]; + ++f; + } + + delete[] face.mIndices; + face.mIndices = NULL; + } + +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS + fclose(fout); +#endif + + // kill the old faces + delete [] pMesh->mFaces; + + // ... and store the new ones + pMesh->mFaces = out; + pMesh->mNumFaces = (unsigned int)(curOut-out); /* not necessarily equal to numOut */ + return true; +} + +#endif // !! ASSIMP_BUILD_NO_TRIANGULATE_PROCESS diff --git a/thirdparty/assimp/code/TriangulateProcess.h b/thirdparty/assimp/code/TriangulateProcess.h new file mode 100644 index 0000000000..47bd2115ad --- /dev/null +++ b/thirdparty/assimp/code/TriangulateProcess.h @@ -0,0 +1,95 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a post processing step to triangulate all faces + with more than three vertices. + */ +#ifndef AI_TRIANGULATEPROCESS_H_INC +#define AI_TRIANGULATEPROCESS_H_INC + +#include "BaseProcess.h" + +struct aiMesh; + +class TriangulateProcessTest; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** The TriangulateProcess splits up all faces with more than three indices + * into triangles. You usually want this to happen because the graphics cards + * need their data as triangles. + */ +class ASSIMP_API TriangulateProcess : public BaseProcess +{ +public: + + TriangulateProcess(); + ~TriangulateProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + +public: + // ------------------------------------------------------------------- + /** Triangulates the given mesh. + * @param pMesh The mesh to triangulate. + */ + bool TriangulateMesh( aiMesh* pMesh); +}; + +} // end of namespace Assimp + +#endif // AI_TRIANGULATEPROCESS_H_INC diff --git a/thirdparty/assimp/code/ValidateDataStructure.cpp b/thirdparty/assimp/code/ValidateDataStructure.cpp new file mode 100644 index 0000000000..657b0361b7 --- /dev/null +++ b/thirdparty/assimp/code/ValidateDataStructure.cpp @@ -0,0 +1,986 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file ValidateDataStructure.cpp + * @brief Implementation of the post processing step to validate + * the data structure returned by Assimp. + */ + + + +// internal headers +#include "ValidateDataStructure.h" +#include <assimp/BaseImporter.h> +#include <assimp/fast_atof.h> +#include "ProcessHelper.h" +#include <memory> + +// CRT headers +#include <stdarg.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +ValidateDSProcess::ValidateDSProcess() : + mScene() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +ValidateDSProcess::~ValidateDSProcess() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool ValidateDSProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_ValidateDataStructure) != 0; +} +// ------------------------------------------------------------------------------------------------ +AI_WONT_RETURN void ValidateDSProcess::ReportError(const char* msg,...) +{ + ai_assert(NULL != msg); + + va_list args; + va_start(args,msg); + + char szBuffer[3000]; + const int iLen = vsprintf(szBuffer,msg,args); + ai_assert(iLen > 0); + + va_end(args); + + throw DeadlyImportError("Validation failed: " + std::string(szBuffer,iLen)); +} +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::ReportWarning(const char* msg,...) +{ + ai_assert(NULL != msg); + + va_list args; + va_start(args,msg); + + char szBuffer[3000]; + const int iLen = vsprintf(szBuffer,msg,args); + ai_assert(iLen > 0); + + va_end(args); + ASSIMP_LOG_WARN("Validation warning: " + std::string(szBuffer,iLen)); +} + +// ------------------------------------------------------------------------------------------------ +inline int HasNameMatch(const aiString& in, aiNode* node) +{ + int result = (node->mName == in ? 1 : 0 ); + for (unsigned int i = 0; i < node->mNumChildren;++i) { + result += HasNameMatch(in,node->mChildren[i]); + } + return result; +} + +// ------------------------------------------------------------------------------------------------ +template <typename T> +inline void ValidateDSProcess::DoValidation(T** parray, unsigned int size, + const char* firstName, const char* secondName) +{ + // validate all entries + if (size) + { + if (!parray) + { + ReportError("aiScene::%s is NULL (aiScene::%s is %i)", + firstName, secondName, size); + } + for (unsigned int i = 0; i < size;++i) + { + if (!parray[i]) + { + ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)", + firstName,i,secondName,size); + } + Validate(parray[i]); + } + } +} + +// ------------------------------------------------------------------------------------------------ +template <typename T> +inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size, + const char* firstName, const char* secondName) +{ + // validate all entries + if (size) + { + if (!parray) { + ReportError("aiScene::%s is NULL (aiScene::%s is %i)", + firstName, secondName, size); + } + for (unsigned int i = 0; i < size;++i) + { + if (!parray[i]) + { + ReportError("aiScene::%s[%u] is NULL (aiScene::%s is %u)", + firstName,i,secondName,size); + } + Validate(parray[i]); + + // check whether there are duplicate names + for (unsigned int a = i+1; a < size;++a) + { + if (parray[i]->mName == parray[a]->mName) + { + ReportError("aiScene::%s[%u] has the same name as " + "aiScene::%s[%u]",firstName, i,secondName, a); + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +template <typename T> +inline +void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size, const char* firstName, const char* secondName) { + // validate all entries + DoValidationEx(array,size,firstName,secondName); + + for (unsigned int i = 0; i < size;++i) { + int res = HasNameMatch(array[i]->mName,mScene->mRootNode); + if (0 == res) { + const std::string name = static_cast<char*>(array[i]->mName.data); + ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)", + firstName,i, name.c_str()); + } else if (1 != res) { + const std::string name = static_cast<char*>(array[i]->mName.data); + ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name", + firstName,i, name.c_str()); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void ValidateDSProcess::Execute( aiScene* pScene) +{ + this->mScene = pScene; + ASSIMP_LOG_DEBUG("ValidateDataStructureProcess begin"); + + // validate the node graph of the scene + Validate(pScene->mRootNode); + + // validate all meshes + if (pScene->mNumMeshes) { + DoValidation(pScene->mMeshes,pScene->mNumMeshes,"mMeshes","mNumMeshes"); + } + else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { + ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there"); + } + else if (pScene->mMeshes) { + ReportError("aiScene::mMeshes is non-null although there are no meshes"); + } + + // validate all animations + if (pScene->mNumAnimations) { + DoValidation(pScene->mAnimations,pScene->mNumAnimations, + "mAnimations","mNumAnimations"); + } + else if (pScene->mAnimations) { + ReportError("aiScene::mAnimations is non-null although there are no animations"); + } + + // validate all cameras + if (pScene->mNumCameras) { + DoValidationWithNameCheck(pScene->mCameras,pScene->mNumCameras, + "mCameras","mNumCameras"); + } + else if (pScene->mCameras) { + ReportError("aiScene::mCameras is non-null although there are no cameras"); + } + + // validate all lights + if (pScene->mNumLights) { + DoValidationWithNameCheck(pScene->mLights,pScene->mNumLights, + "mLights","mNumLights"); + } + else if (pScene->mLights) { + ReportError("aiScene::mLights is non-null although there are no lights"); + } + + // validate all textures + if (pScene->mNumTextures) { + DoValidation(pScene->mTextures,pScene->mNumTextures, + "mTextures","mNumTextures"); + } + else if (pScene->mTextures) { + ReportError("aiScene::mTextures is non-null although there are no textures"); + } + + // validate all materials + if (pScene->mNumMaterials) { + DoValidation(pScene->mMaterials,pScene->mNumMaterials,"mMaterials","mNumMaterials"); + } +#if 0 + // NOTE: ScenePreprocessor generates a default material if none is there + else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { + ReportError("aiScene::mNumMaterials is 0. At least one material must be there"); + } +#endif + else if (pScene->mMaterials) { + ReportError("aiScene::mMaterials is non-null although there are no materials"); + } + +// if (!has)ReportError("The aiScene data structure is empty"); + ASSIMP_LOG_DEBUG("ValidateDataStructureProcess end"); +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiLight* pLight) +{ + if (pLight->mType == aiLightSource_UNDEFINED) + ReportWarning("aiLight::mType is aiLightSource_UNDEFINED"); + + if (!pLight->mAttenuationConstant && + !pLight->mAttenuationLinear && + !pLight->mAttenuationQuadratic) { + ReportWarning("aiLight::mAttenuationXXX - all are zero"); + } + + if (pLight->mAngleInnerCone > pLight->mAngleOuterCone) + ReportError("aiLight::mAngleInnerCone is larger than aiLight::mAngleOuterCone"); + + if (pLight->mColorDiffuse.IsBlack() && pLight->mColorAmbient.IsBlack() + && pLight->mColorSpecular.IsBlack()) + { + ReportWarning("aiLight::mColorXXX - all are black and won't have any influence"); + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiCamera* pCamera) +{ + if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear) + ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear"); + + // FIX: there are many 3ds files with invalid FOVs. No reason to + // reject them at all ... a warning is appropriate. + if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI) + ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV",pCamera->mHorizontalFOV); +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiMesh* pMesh) +{ + // validate the material index of the mesh + if (mScene->mNumMaterials && pMesh->mMaterialIndex >= mScene->mNumMaterials) + { + ReportError("aiMesh::mMaterialIndex is invalid (value: %i maximum: %i)", + pMesh->mMaterialIndex,mScene->mNumMaterials-1); + } + + Validate(&pMesh->mName); + + for (unsigned int i = 0; i < pMesh->mNumFaces; ++i) + { + aiFace& face = pMesh->mFaces[i]; + + if (pMesh->mPrimitiveTypes) + { + switch (face.mNumIndices) + { + case 0: + ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i); + break; + case 1: + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT)) + { + ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimitiveTypes " + "does not report the POINT flag",i); + } + break; + case 2: + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_LINE)) + { + ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimitiveTypes " + "does not report the LINE flag",i); + } + break; + case 3: + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE)) + { + ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimitiveTypes " + "does not report the TRIANGLE flag",i); + } + break; + default: + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) + { + this->ReportError("aiMesh::mFaces[%i] is a POLYGON but aiMesh::mPrimitiveTypes " + "does not report the POLYGON flag",i); + } + break; + }; + } + + if (!face.mIndices) + ReportError("aiMesh::mFaces[%i].mIndices is NULL",i); + } + + // positions must always be there ... + if (!pMesh->mNumVertices || (!pMesh->mVertices && !mScene->mFlags)) { + ReportError("The mesh %s contains no vertices", pMesh->mName.C_Str()); + } + + if (pMesh->mNumVertices > AI_MAX_VERTICES) { + ReportError("Mesh has too many vertices: %u, but the limit is %u",pMesh->mNumVertices,AI_MAX_VERTICES); + } + if (pMesh->mNumFaces > AI_MAX_FACES) { + ReportError("Mesh has too many faces: %u, but the limit is %u",pMesh->mNumFaces,AI_MAX_FACES); + } + + // if tangents are there there must also be bitangent vectors ... + if ((pMesh->mTangents != NULL) != (pMesh->mBitangents != NULL)) { + ReportError("If there are tangents, bitangent vectors must be present as well"); + } + + // faces, too + if (!pMesh->mNumFaces || (!pMesh->mFaces && !mScene->mFlags)) { + ReportError("Mesh %s contains no faces", pMesh->mName.C_Str()); + } + + // now check whether the face indexing layout is correct: + // unique vertices, pseudo-indexed. + std::vector<bool> abRefList; + abRefList.resize(pMesh->mNumVertices,false); + for (unsigned int i = 0; i < pMesh->mNumFaces;++i) + { + aiFace& face = pMesh->mFaces[i]; + if (face.mNumIndices > AI_MAX_FACE_INDICES) { + ReportError("Face %u has too many faces: %u, but the limit is %u",i,face.mNumIndices,AI_MAX_FACE_INDICES); + } + + for (unsigned int a = 0; a < face.mNumIndices;++a) + { + if (face.mIndices[a] >= pMesh->mNumVertices) { + ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a); + } + // the MSB flag is temporarily used by the extra verbose + // mode to tell us that the JoinVerticesProcess might have + // been executed already. + /*if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && !(this->mScene->mFlags & AI_SCENE_FLAGS_ALLOW_SHARED) && + abRefList[face.mIndices[a]]) + { + ReportError("aiMesh::mVertices[%i] is referenced twice - second " + "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a); + }*/ + abRefList[face.mIndices[a]] = true; + } + } + + // check whether there are vertices that aren't referenced by a face + bool b = false; + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { + if (!abRefList[i])b = true; + } + abRefList.clear(); + if (b) { + ReportWarning("There are unreferenced vertices"); + } + + // texture channel 2 may not be set if channel 1 is zero ... + { + unsigned int i = 0; + for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) + { + if (!pMesh->HasTextureCoords(i))break; + } + for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) + if (pMesh->HasTextureCoords(i)) + { + ReportError("Texture coordinate channel %i exists " + "although the previous channel was NULL.",i); + } + } + // the same for the vertex colors + { + unsigned int i = 0; + for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) + { + if (!pMesh->HasVertexColors(i))break; + } + for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) + if (pMesh->HasVertexColors(i)) + { + ReportError("Vertex color channel %i is exists " + "although the previous channel was NULL.",i); + } + } + + + // now validate all bones + if (pMesh->mNumBones) + { + if (!pMesh->mBones) + { + ReportError("aiMesh::mBones is NULL (aiMesh::mNumBones is %i)", + pMesh->mNumBones); + } + std::unique_ptr<float[]> afSum(nullptr); + if (pMesh->mNumVertices) + { + afSum.reset(new float[pMesh->mNumVertices]); + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) + afSum[i] = 0.0f; + } + + // check whether there are duplicate bone names + for (unsigned int i = 0; i < pMesh->mNumBones;++i) + { + const aiBone* bone = pMesh->mBones[i]; + if (bone->mNumWeights > AI_MAX_BONE_WEIGHTS) { + ReportError("Bone %u has too many weights: %u, but the limit is %u",i,bone->mNumWeights,AI_MAX_BONE_WEIGHTS); + } + + if (!pMesh->mBones[i]) + { + ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)", + i,pMesh->mNumBones); + } + Validate(pMesh,pMesh->mBones[i],afSum.get()); + + for (unsigned int a = i+1; a < pMesh->mNumBones;++a) + { + if (pMesh->mBones[i]->mName == pMesh->mBones[a]->mName) + { + const char *name = "unknown"; + if (nullptr != pMesh->mBones[ i ]->mName.C_Str()) { + name = pMesh->mBones[ i ]->mName.C_Str(); + } + ReportError("aiMesh::mBones[%i], name = \"%s\" has the same name as " + "aiMesh::mBones[%i]", i, name, a ); + } + } + } + // check whether all bone weights for a vertex sum to 1.0 ... + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) + { + if (afSum[i] && (afSum[i] <= 0.94 || afSum[i] >= 1.05)) { + ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]); + } + } + } + else if (pMesh->mBones) + { + ReportError("aiMesh::mBones is non-null although there are no bones"); + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiMesh* pMesh, + const aiBone* pBone,float* afSum) +{ + this->Validate(&pBone->mName); + + if (!pBone->mNumWeights) { + ReportError("aiBone::mNumWeights is zero"); + } + + // check whether all vertices affected by this bone are valid + for (unsigned int i = 0; i < pBone->mNumWeights;++i) + { + if (pBone->mWeights[i].mVertexId >= pMesh->mNumVertices) { + ReportError("aiBone::mWeights[%i].mVertexId is out of range",i); + } + else if (!pBone->mWeights[i].mWeight || pBone->mWeights[i].mWeight > 1.0f) { + ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value",i); + } + afSum[pBone->mWeights[i].mVertexId] += pBone->mWeights[i].mWeight; + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiAnimation* pAnimation) +{ + Validate(&pAnimation->mName); + + // validate all materials + if (pAnimation->mNumChannels) + { + if (!pAnimation->mChannels) { + ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)", + pAnimation->mNumChannels); + } + for (unsigned int i = 0; i < pAnimation->mNumChannels;++i) + { + if (!pAnimation->mChannels[i]) + { + ReportError("aiAnimation::mChannels[%i] is NULL (aiAnimation::mNumChannels is %i)", + i, pAnimation->mNumChannels); + } + Validate(pAnimation, pAnimation->mChannels[i]); + } + } + else { + ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there."); + } + + // Animation duration is allowed to be zero in cases where the anim contains only a single key frame. + // if (!pAnimation->mDuration)this->ReportError("aiAnimation::mDuration is zero"); +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial, + aiTextureType type) +{ + const char* szType = TextureTypeToString(type); + + // **************************************************************************** + // Search all keys of the material ... + // textures must be specified with ascending indices + // (e.g. diffuse #2 may not be specified if diffuse #1 is not there ...) + // **************************************************************************** + + int iNumIndices = 0; + int iIndex = -1; + for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) { + aiMaterialProperty* prop = pMaterial->mProperties[ i ]; + ai_assert(nullptr != prop); + if ( !::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == static_cast<unsigned int>(type)) { + iIndex = std::max(iIndex, (int) prop->mIndex); + ++iNumIndices; + + if (aiPTI_String != prop->mType) { + ReportError("Material property %s is expected to be a string", prop->mKey.data); + } + } + } + if (iIndex +1 != iNumIndices) { + ReportError("%s #%i is set, but there are only %i %s textures", + szType,iIndex,iNumIndices,szType); + } + if (!iNumIndices)return; + std::vector<aiTextureMapping> mappings(iNumIndices); + + // Now check whether all UV indices are valid ... + bool bNoSpecified = true; + for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) + { + aiMaterialProperty* prop = pMaterial->mProperties[i]; + if (prop->mSemantic != type)continue; + + if ((int)prop->mIndex >= iNumIndices) + { + ReportError("Found texture property with index %i, although there " + "are only %i textures of type %s", + prop->mIndex, iNumIndices, szType); + } + + if (!::strcmp(prop->mKey.data,"$tex.mapping")) { + if (aiPTI_Integer != prop->mType || prop->mDataLength < sizeof(aiTextureMapping)) + { + ReportError("Material property %s%i is expected to be an integer (size is %i)", + prop->mKey.data,prop->mIndex,prop->mDataLength); + } + mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData); + } + else if (!::strcmp(prop->mKey.data,"$tex.uvtrafo")) { + if (aiPTI_Float != prop->mType || prop->mDataLength < sizeof(aiUVTransform)) + { + ReportError("Material property %s%i is expected to be 5 floats large (size is %i)", + prop->mKey.data,prop->mIndex, prop->mDataLength); + } + mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData); + } + else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) { + if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength) + { + ReportError("Material property %s%i is expected to be an integer (size is %i)", + prop->mKey.data,prop->mIndex,prop->mDataLength); + } + bNoSpecified = false; + + // Ignore UV indices for texture channels that are not there ... + + // Get the value + iIndex = *((unsigned int*)prop->mData); + + // Check whether there is a mesh using this material + // which has not enough UV channels ... + for (unsigned int a = 0; a < mScene->mNumMeshes;++a) + { + aiMesh* mesh = this->mScene->mMeshes[a]; + if(mesh->mMaterialIndex == (unsigned int)i) + { + int iChannels = 0; + while (mesh->HasTextureCoords(iChannels))++iChannels; + if (iIndex >= iChannels) + { + ReportWarning("Invalid UV index: %i (key %s). Mesh %i has only %i UV channels", + iIndex,prop->mKey.data,a,iChannels); + } + } + } + } + } + if (bNoSpecified) + { + // Assume that all textures are using the first UV channel + for (unsigned int a = 0; a < mScene->mNumMeshes;++a) + { + aiMesh* mesh = mScene->mMeshes[a]; + if(mesh->mMaterialIndex == (unsigned int)iIndex && mappings[0] == aiTextureMapping_UV) + { + if (!mesh->mTextureCoords[0]) + { + // This is a special case ... it could be that the + // original mesh format intended the use of a special + // mapping here. + ReportWarning("UV-mapped texture, but there are no UV coords"); + } + } + } + } +} +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiMaterial* pMaterial) +{ + // check whether there are material keys that are obviously not legal + for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) + { + const aiMaterialProperty* prop = pMaterial->mProperties[i]; + if (!prop) { + ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)", + i,pMaterial->mNumProperties); + } + if (!prop->mDataLength || !prop->mData) { + ReportError("aiMaterial::mProperties[%i].mDataLength or " + "aiMaterial::mProperties[%i].mData is 0",i,i); + } + // check all predefined types + if (aiPTI_String == prop->mType) { + // FIX: strings are now stored in a less expensive way, but we can't use the + // validation routine for 'normal' aiStrings + if (prop->mDataLength < 5 || prop->mDataLength < 4 + (*reinterpret_cast<uint32_t*>(prop->mData)) + 1) { + ReportError("aiMaterial::mProperties[%i].mDataLength is " + "too small to contain a string (%i, needed: %i)", + i,prop->mDataLength,static_cast<int>(sizeof(aiString))); + } + if(prop->mData[prop->mDataLength-1]) { + ReportError("Missing null-terminator in string material property"); + } + // Validate((const aiString*)prop->mData); + } + else if (aiPTI_Float == prop->mType) { + if (prop->mDataLength < sizeof(float)) { + ReportError("aiMaterial::mProperties[%i].mDataLength is " + "too small to contain a float (%i, needed: %i)", + i,prop->mDataLength, static_cast<int>(sizeof(float))); + } + } + else if (aiPTI_Integer == prop->mType) { + if (prop->mDataLength < sizeof(int)) { + ReportError("aiMaterial::mProperties[%i].mDataLength is " + "too small to contain an integer (%i, needed: %i)", + i,prop->mDataLength, static_cast<int>(sizeof(int))); + } + } + // TODO: check whether there is a key with an unknown name ... + } + + // make some more specific tests + ai_real fTemp; + int iShading; + if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading)) { + switch ((aiShadingMode)iShading) + { + case aiShadingMode_Blinn: + case aiShadingMode_CookTorrance: + case aiShadingMode_Phong: + + if (AI_SUCCESS != aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS,&fTemp)) { + ReportWarning("A specular shading model is specified but there is no " + "AI_MATKEY_SHININESS key"); + } + if (AI_SUCCESS == aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS_STRENGTH,&fTemp) && !fTemp) { + ReportWarning("A specular shading model is specified but the value of the " + "AI_MATKEY_SHININESS_STRENGTH key is 0.0"); + } + break; + default: ; + }; + } + + if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01)) { + ReportWarning("Invalid opacity value (must be 0 < opacity < 1.0)"); + } + + // Check whether there are invalid texture keys + // TODO: that's a relict of the past, where texture type and index were baked + // into the material string ... we could do that in one single pass. + SearchForInvalidTextures(pMaterial,aiTextureType_DIFFUSE); + SearchForInvalidTextures(pMaterial,aiTextureType_SPECULAR); + SearchForInvalidTextures(pMaterial,aiTextureType_AMBIENT); + SearchForInvalidTextures(pMaterial,aiTextureType_EMISSIVE); + SearchForInvalidTextures(pMaterial,aiTextureType_OPACITY); + SearchForInvalidTextures(pMaterial,aiTextureType_SHININESS); + SearchForInvalidTextures(pMaterial,aiTextureType_HEIGHT); + SearchForInvalidTextures(pMaterial,aiTextureType_NORMALS); + SearchForInvalidTextures(pMaterial,aiTextureType_DISPLACEMENT); + SearchForInvalidTextures(pMaterial,aiTextureType_LIGHTMAP); + SearchForInvalidTextures(pMaterial,aiTextureType_REFLECTION); +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiTexture* pTexture) +{ + // the data section may NEVER be NULL + if (!pTexture->pcData) { + ReportError("aiTexture::pcData is NULL"); + } + if (pTexture->mHeight) + { + if (!pTexture->mWidth){ + ReportError("aiTexture::mWidth is zero (aiTexture::mHeight is %i, uncompressed texture)", + pTexture->mHeight); + } + } + else + { + if (!pTexture->mWidth) { + ReportError("aiTexture::mWidth is zero (compressed texture)"); + } + if ('\0' != pTexture->achFormatHint[3]) { + ReportWarning("aiTexture::achFormatHint must be zero-terminated"); + } + else if ('.' == pTexture->achFormatHint[0]) { + ReportWarning("aiTexture::achFormatHint should contain a file extension " + "without a leading dot (format hint: %s).",pTexture->achFormatHint); + } + } + + const char* sz = pTexture->achFormatHint; + if ((sz[0] >= 'A' && sz[0] <= 'Z') || + (sz[1] >= 'A' && sz[1] <= 'Z') || + (sz[2] >= 'A' && sz[2] <= 'Z') || + (sz[3] >= 'A' && sz[3] <= 'Z')) { + ReportError("aiTexture::achFormatHint contains non-lowercase letters"); + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiAnimation* pAnimation, + const aiNodeAnim* pNodeAnim) +{ + Validate(&pNodeAnim->mNodeName); + + if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys) { + ReportError("Empty node animation channel"); + } + // otherwise check whether one of the keys exceeds the total duration of the animation + if (pNodeAnim->mNumPositionKeys) + { + if (!pNodeAnim->mPositionKeys) + { + ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)", + pNodeAnim->mNumPositionKeys); + } + double dLast = -10e10; + for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys;++i) + { + // ScenePreprocessor will compute the duration if still the default value + // (Aramis) Add small epsilon, comparison tended to fail if max_time == duration, + // seems to be due the compilers register usage/width. + if (pAnimation->mDuration > 0. && pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration+0.001) + { + ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger " + "than aiAnimation::mDuration (which is %.5f)",i, + (float)pNodeAnim->mPositionKeys[i].mTime, + (float)pAnimation->mDuration); + } + if (i && pNodeAnim->mPositionKeys[i].mTime <= dLast) + { + ReportWarning("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is smaller " + "than aiAnimation::mPositionKeys[%i] (which is %.5f)",i, + (float)pNodeAnim->mPositionKeys[i].mTime, + i-1, (float)dLast); + } + dLast = pNodeAnim->mPositionKeys[i].mTime; + } + } + // rotation keys + if (pNodeAnim->mNumRotationKeys) + { + if (!pNodeAnim->mRotationKeys) + { + ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)", + pNodeAnim->mNumRotationKeys); + } + double dLast = -10e10; + for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys;++i) + { + if (pAnimation->mDuration > 0. && pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration+0.001) + { + ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger " + "than aiAnimation::mDuration (which is %.5f)",i, + (float)pNodeAnim->mRotationKeys[i].mTime, + (float)pAnimation->mDuration); + } + if (i && pNodeAnim->mRotationKeys[i].mTime <= dLast) + { + ReportWarning("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is smaller " + "than aiAnimation::mRotationKeys[%i] (which is %.5f)",i, + (float)pNodeAnim->mRotationKeys[i].mTime, + i-1, (float)dLast); + } + dLast = pNodeAnim->mRotationKeys[i].mTime; + } + } + // scaling keys + if (pNodeAnim->mNumScalingKeys) + { + if (!pNodeAnim->mScalingKeys) { + ReportError("aiNodeAnim::mScalingKeys is NULL (aiNodeAnim::mNumScalingKeys is %i)", + pNodeAnim->mNumScalingKeys); + } + double dLast = -10e10; + for (unsigned int i = 0; i < pNodeAnim->mNumScalingKeys;++i) + { + if (pAnimation->mDuration > 0. && pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration+0.001) + { + ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger " + "than aiAnimation::mDuration (which is %.5f)",i, + (float)pNodeAnim->mScalingKeys[i].mTime, + (float)pAnimation->mDuration); + } + if (i && pNodeAnim->mScalingKeys[i].mTime <= dLast) + { + ReportWarning("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is smaller " + "than aiAnimation::mScalingKeys[%i] (which is %.5f)",i, + (float)pNodeAnim->mScalingKeys[i].mTime, + i-1, (float)dLast); + } + dLast = pNodeAnim->mScalingKeys[i].mTime; + } + } + + if (!pNodeAnim->mNumScalingKeys && !pNodeAnim->mNumRotationKeys && + !pNodeAnim->mNumPositionKeys) + { + ReportError("A node animation channel must have at least one subtrack"); + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiNode* pNode) +{ + if (!pNode) { + ReportError("A node of the scenegraph is NULL"); + } + // Validate node name string first so that it's safe to use in below expressions + this->Validate(&pNode->mName); + const char* nodeName = (&pNode->mName)->C_Str(); + if (pNode != mScene->mRootNode && !pNode->mParent){ + ReportError("Non-root node %s lacks a valid parent (aiNode::mParent is NULL) ", nodeName); + } + + // validate all meshes + if (pNode->mNumMeshes) + { + if (!pNode->mMeshes) + { + ReportError("aiNode::mMeshes is NULL for node %s (aiNode::mNumMeshes is %i)", + nodeName, pNode->mNumMeshes); + } + std::vector<bool> abHadMesh; + abHadMesh.resize(mScene->mNumMeshes,false); + for (unsigned int i = 0; i < pNode->mNumMeshes;++i) + { + if (pNode->mMeshes[i] >= mScene->mNumMeshes) + { + ReportError("aiNode::mMeshes[%i] is out of range for node %s (maximum is %i)", + pNode->mMeshes[i], nodeName, mScene->mNumMeshes-1); + } + if (abHadMesh[pNode->mMeshes[i]]) + { + ReportError("aiNode::mMeshes[%i] is already referenced by this node %s (value: %i)", + i, nodeName, pNode->mMeshes[i]); + } + abHadMesh[pNode->mMeshes[i]] = true; + } + } + if (pNode->mNumChildren) + { + if (!pNode->mChildren) { + ReportError("aiNode::mChildren is NULL for node %s (aiNode::mNumChildren is %i)", + nodeName, pNode->mNumChildren); + } + for (unsigned int i = 0; i < pNode->mNumChildren;++i) { + Validate(pNode->mChildren[i]); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiString* pString) +{ + if (pString->length > MAXLEN) + { + ReportError("aiString::length is too large (%lu, maximum is %lu)", + pString->length,MAXLEN); + } + const char* sz = pString->data; + while (true) + { + if ('\0' == *sz) + { + if (pString->length != (unsigned int)(sz-pString->data)) { + ReportError("aiString::data is invalid: the terminal zero is at a wrong offset"); + } + break; + } + else if (sz >= &pString->data[MAXLEN]) { + ReportError("aiString::data is invalid. There is no terminal character"); + } + ++sz; + } +} diff --git a/thirdparty/assimp/code/ValidateDataStructure.h b/thirdparty/assimp/code/ValidateDataStructure.h new file mode 100644 index 0000000000..bd21e88545 --- /dev/null +++ b/thirdparty/assimp/code/ValidateDataStructure.h @@ -0,0 +1,188 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a (dummy) post processing step to validate the loader's + * output data structure (for debugging) + */ +#ifndef AI_VALIDATEPROCESS_H_INC +#define AI_VALIDATEPROCESS_H_INC + +#include <assimp/types.h> +#include <assimp/material.h> +#include "BaseProcess.h" + +struct aiBone; +struct aiMesh; +struct aiAnimation; +struct aiNodeAnim; +struct aiTexture; +struct aiMaterial; +struct aiNode; +struct aiString; +struct aiCamera; +struct aiLight; + +namespace Assimp { + +// -------------------------------------------------------------------------------------- +/** Validates the whole ASSIMP scene data structure for correctness. + * ImportErrorException is thrown of the scene is corrupt.*/ +// -------------------------------------------------------------------------------------- +class ValidateDSProcess : public BaseProcess +{ +public: + + ValidateDSProcess(); + ~ValidateDSProcess(); + +public: + // ------------------------------------------------------------------- + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + void Execute( aiScene* pScene); + +protected: + + // ------------------------------------------------------------------- + /** Report a validation error. This will throw an exception, + * control won't return. + * @param msg Format string for sprintf().*/ + AI_WONT_RETURN void ReportError(const char* msg,...) AI_WONT_RETURN_SUFFIX; + + + // ------------------------------------------------------------------- + /** Report a validation warning. This won't throw an exception, + * control will return to the caller. + * @param msg Format string for sprintf().*/ + void ReportWarning(const char* msg,...); + + + // ------------------------------------------------------------------- + /** Validates a mesh + * @param pMesh Input mesh*/ + void Validate( const aiMesh* pMesh); + + // ------------------------------------------------------------------- + /** Validates a bone + * @param pMesh Input mesh + * @param pBone Input bone*/ + void Validate( const aiMesh* pMesh,const aiBone* pBone,float* afSum); + + // ------------------------------------------------------------------- + /** Validates an animation + * @param pAnimation Input animation*/ + void Validate( const aiAnimation* pAnimation); + + // ------------------------------------------------------------------- + /** Validates a material + * @param pMaterial Input material*/ + void Validate( const aiMaterial* pMaterial); + + // ------------------------------------------------------------------- + /** Search the material data structure for invalid or corrupt + * texture keys. + * @param pMaterial Input material + * @param type Type of the texture*/ + void SearchForInvalidTextures(const aiMaterial* pMaterial, + aiTextureType type); + + // ------------------------------------------------------------------- + /** Validates a texture + * @param pTexture Input texture*/ + void Validate( const aiTexture* pTexture); + + // ------------------------------------------------------------------- + /** Validates a light source + * @param pLight Input light + */ + void Validate( const aiLight* pLight); + + // ------------------------------------------------------------------- + /** Validates a camera + * @param pCamera Input camera*/ + void Validate( const aiCamera* pCamera); + + // ------------------------------------------------------------------- + /** Validates a bone animation channel + * @param pAnimation Animation channel. + * @param pBoneAnim Input bone animation */ + void Validate( const aiAnimation* pAnimation, + const aiNodeAnim* pBoneAnim); + + // ------------------------------------------------------------------- + /** Validates a node and all of its subnodes + * @param Node Input node*/ + void Validate( const aiNode* pNode); + + // ------------------------------------------------------------------- + /** Validates a string + * @param pString Input string*/ + void Validate( const aiString* pString); + +private: + + // template to validate one of the aiScene::mXXX arrays + template <typename T> + inline void DoValidation(T** array, unsigned int size, + const char* firstName, const char* secondName); + + // extended version: checks whether T::mName occurs twice + template <typename T> + inline void DoValidationEx(T** array, unsigned int size, + const char* firstName, const char* secondName); + + // extension to the first template which does also search + // the nodegraph for an item with the same name + template <typename T> + inline void DoValidationWithNameCheck(T** array, unsigned int size, + const char* firstName, const char* secondName); + + aiScene* mScene; +}; + + + + +} // end of namespace Assimp + +#endif // AI_VALIDATEPROCESS_H_INC diff --git a/thirdparty/assimp/code/Version.cpp b/thirdparty/assimp/code/Version.cpp new file mode 100644 index 0000000000..cc94340ac8 --- /dev/null +++ b/thirdparty/assimp/code/Version.cpp @@ -0,0 +1,185 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +// Actually just a dummy, used by the compiler to build the precompiled header. + +#include <assimp/version.h> +#include <assimp/scene.h> +#include "ScenePrivate.h" + +static const unsigned int MajorVersion = 4; +static const unsigned int MinorVersion = 1; + +// -------------------------------------------------------------------------------- +// Legal information string - don't remove this. +static const char* LEGAL_INFORMATION = + +"Open Asset Import Library (Assimp).\n" +"A free C/C++ library to import various 3D file formats into applications\n\n" + +"(c) 2008-2017, assimp team\n" +"License under the terms and conditions of the 3-clause BSD license\n" +"http://assimp.sourceforge.net\n" +; + +// ------------------------------------------------------------------------------------------------ +// Get legal string +ASSIMP_API const char* aiGetLegalString () { + return LEGAL_INFORMATION; +} + +// ------------------------------------------------------------------------------------------------ +// Get Assimp minor version +ASSIMP_API unsigned int aiGetVersionMinor () { + return MinorVersion; +} + +// ------------------------------------------------------------------------------------------------ +// Get Assimp major version +ASSIMP_API unsigned int aiGetVersionMajor () { + return MajorVersion; +} + +// ------------------------------------------------------------------------------------------------ +// Get flags used for compilation +ASSIMP_API unsigned int aiGetCompileFlags () { + + unsigned int flags = 0; + +#ifdef ASSIMP_BUILD_BOOST_WORKAROUND + flags |= ASSIMP_CFLAGS_NOBOOST; +#endif +#ifdef ASSIMP_BUILD_SINGLETHREADED + flags |= ASSIMP_CFLAGS_SINGLETHREADED; +#endif +#ifdef ASSIMP_BUILD_DEBUG + flags |= ASSIMP_CFLAGS_DEBUG; +#endif +#ifdef ASSIMP_BUILD_DLL_EXPORT + flags |= ASSIMP_CFLAGS_SHARED; +#endif +#ifdef _STLPORT_VERSION + flags |= ASSIMP_CFLAGS_STLPORT; +#endif + + return flags; +} + +// include current build revision, which is even updated from time to time -- :-) +#include "revision.h" + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API unsigned int aiGetVersionRevision() { + return GitVersion; +} + +ASSIMP_API const char *aiGetBranchName() { + return GitBranch; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiScene::aiScene() +: mFlags(0) +, mRootNode(nullptr) +, mNumMeshes(0) +, mMeshes(nullptr) +, mNumMaterials(0) +, mMaterials(nullptr) +, mNumAnimations(0) +, mAnimations(nullptr) +, mNumTextures(0) +, mTextures(nullptr) +, mNumLights(0) +, mLights(nullptr) +, mNumCameras(0) +, mCameras(nullptr) +, mMetaData(nullptr) +, mPrivate(new Assimp::ScenePrivateData()) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiScene::~aiScene() { + // delete all sub-objects recursively + delete mRootNode; + + // To make sure we won't crash if the data is invalid it's + // much better to check whether both mNumXXX and mXXX are + // valid instead of relying on just one of them. + if (mNumMeshes && mMeshes) + for( unsigned int a = 0; a < mNumMeshes; a++) + delete mMeshes[a]; + delete [] mMeshes; + + if (mNumMaterials && mMaterials) { + for (unsigned int a = 0; a < mNumMaterials; ++a ) { + delete mMaterials[ a ]; + } + } + delete [] mMaterials; + + if (mNumAnimations && mAnimations) + for( unsigned int a = 0; a < mNumAnimations; a++) + delete mAnimations[a]; + delete [] mAnimations; + + if (mNumTextures && mTextures) + for( unsigned int a = 0; a < mNumTextures; a++) + delete mTextures[a]; + delete [] mTextures; + + if (mNumLights && mLights) + for( unsigned int a = 0; a < mNumLights; a++) + delete mLights[a]; + delete [] mLights; + + if (mNumCameras && mCameras) + for( unsigned int a = 0; a < mNumCameras; a++) + delete mCameras[a]; + delete [] mCameras; + + aiMetadata::Dealloc(mMetaData); + mMetaData = nullptr; + + delete static_cast<Assimp::ScenePrivateData*>( mPrivate ); +} + diff --git a/thirdparty/assimp/code/VertexTriangleAdjacency.cpp b/thirdparty/assimp/code/VertexTriangleAdjacency.cpp new file mode 100644 index 0000000000..7cfd1a3505 --- /dev/null +++ b/thirdparty/assimp/code/VertexTriangleAdjacency.cpp @@ -0,0 +1,134 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the VertexTriangleAdjacency helper class + */ + +// internal headers +#include "VertexTriangleAdjacency.h" +#include <assimp/mesh.h> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces, + unsigned int iNumFaces, + unsigned int iNumVertices /*= 0*/, + bool bComputeNumTriangles /*= false*/) +{ + // compute the number of referenced vertices if it wasn't specified by the caller + const aiFace* const pcFaceEnd = pcFaces + iNumFaces; + if (!iNumVertices) { + for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) { + ai_assert( nullptr != pcFace ); + ai_assert(3 == pcFace->mNumIndices); + iNumVertices = std::max(iNumVertices,pcFace->mIndices[0]); + iNumVertices = std::max(iNumVertices,pcFace->mIndices[1]); + iNumVertices = std::max(iNumVertices,pcFace->mIndices[2]); + } + } + + mNumVertices = iNumVertices; + + unsigned int* pi; + + // allocate storage + if (bComputeNumTriangles) { + pi = mLiveTriangles = new unsigned int[iNumVertices+1]; + ::memset(mLiveTriangles,0,sizeof(unsigned int)*(iNumVertices+1)); + mOffsetTable = new unsigned int[iNumVertices+2]+1; + } else { + pi = mOffsetTable = new unsigned int[iNumVertices+2]+1; + ::memset(mOffsetTable,0,sizeof(unsigned int)*(iNumVertices+1)); + mLiveTriangles = NULL; // important, otherwise the d'tor would crash + } + + // get a pointer to the end of the buffer + unsigned int* piEnd = pi+iNumVertices; + *piEnd++ = 0u; + + // first pass: compute the number of faces referencing each vertex + for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) + { + unsigned nind = pcFace->mNumIndices; + unsigned * ind = pcFace->mIndices; + if (nind > 0) pi[ind[0]]++; + if (nind > 1) pi[ind[1]]++; + if (nind > 2) pi[ind[2]]++; + } + + // second pass: compute the final offset table + unsigned int iSum = 0; + unsigned int* piCurOut = this->mOffsetTable; + for (unsigned int* piCur = pi; piCur != piEnd;++piCur,++piCurOut) { + + unsigned int iLastSum = iSum; + iSum += *piCur; + *piCurOut = iLastSum; + } + pi = this->mOffsetTable; + + // third pass: compute the final table + this->mAdjacencyTable = new unsigned int[iSum]; + iSum = 0; + for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace,++iSum) { + unsigned nind = pcFace->mNumIndices; + unsigned * ind = pcFace->mIndices; + + if (nind > 0) mAdjacencyTable[pi[ind[0]]++] = iSum; + if (nind > 1) mAdjacencyTable[pi[ind[1]]++] = iSum; + if (nind > 2) mAdjacencyTable[pi[ind[2]]++] = iSum; + } + // fourth pass: undo the offset computations made during the third pass + // We could do this in a separate buffer, but this would be TIMES slower. + --mOffsetTable; + *mOffsetTable = 0u; +} +// ------------------------------------------------------------------------------------------------ +VertexTriangleAdjacency::~VertexTriangleAdjacency() +{ + // delete allocated storage + delete[] mOffsetTable; + delete[] mAdjacencyTable; + delete[] mLiveTriangles; +} diff --git a/thirdparty/assimp/code/VertexTriangleAdjacency.h b/thirdparty/assimp/code/VertexTriangleAdjacency.h new file mode 100644 index 0000000000..f3be47612d --- /dev/null +++ b/thirdparty/assimp/code/VertexTriangleAdjacency.h @@ -0,0 +1,117 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a helper class to compute a vertex-triangle adjacency map */ +#ifndef AI_VTADJACENCY_H_INC +#define AI_VTADJACENCY_H_INC + +#include "BaseProcess.h" +#include <assimp/types.h> +#include <assimp/ai_assert.h> + +struct aiMesh; +struct aiFace; + +namespace Assimp { + +// -------------------------------------------------------------------------------------------- +/** @brief The VertexTriangleAdjacency class computes a vertex-triangle + * adjacency map from a given index buffer. + * + * @note Although it is called #VertexTriangleAdjacency, the current version does also + * support arbitrary polygons. */ +// -------------------------------------------------------------------------------------------- +class ASSIMP_API VertexTriangleAdjacency { +public: + // ---------------------------------------------------------------------------- + /** @brief Construction from an existing index buffer + * @param pcFaces Index buffer + * @param iNumFaces Number of faces in the buffer + * @param iNumVertices Number of referenced vertices. This value + * is computed automatically if 0 is specified. + * @param bComputeNumTriangles If you want the class to compute + * a list containing the number of referenced triangles per vertex + * per vertex - pass true. */ + VertexTriangleAdjacency(aiFace* pcFaces,unsigned int iNumFaces, + unsigned int iNumVertices = 0, + bool bComputeNumTriangles = true); + + // ---------------------------------------------------------------------------- + /** @brief Destructor */ + ~VertexTriangleAdjacency(); + + // ---------------------------------------------------------------------------- + /** @brief Get all triangles adjacent to a vertex + * @param iVertIndex Index of the vertex + * @return A pointer to the adjacency list. */ + unsigned int* GetAdjacentTriangles(unsigned int iVertIndex) const { + ai_assert(iVertIndex < mNumVertices); + return &mAdjacencyTable[ mOffsetTable[iVertIndex]]; + } + + // ---------------------------------------------------------------------------- + /** @brief Get the number of triangles that are referenced by + * a vertex. This function returns a reference that can be modified + * @param iVertIndex Index of the vertex + * @return Number of referenced triangles */ + unsigned int& GetNumTrianglesPtr(unsigned int iVertIndex) { + ai_assert( iVertIndex < mNumVertices ); + ai_assert( nullptr != mLiveTriangles ); + return mLiveTriangles[iVertIndex]; + } + + //! Offset table + unsigned int* mOffsetTable; + + //! Adjacency table + unsigned int* mAdjacencyTable; + + //! Table containing the number of referenced triangles per vertex + unsigned int* mLiveTriangles; + + //! Debug: Number of referenced vertices + unsigned int mNumVertices; +}; + +} //! ns Assimp + +#endif // !! AI_VTADJACENCY_H_INC diff --git a/thirdparty/assimp/code/Win32DebugLogStream.h b/thirdparty/assimp/code/Win32DebugLogStream.h new file mode 100644 index 0000000000..a6063a261e --- /dev/null +++ b/thirdparty/assimp/code/Win32DebugLogStream.h @@ -0,0 +1,95 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Win32DebugLogStream.h +* @brief Implementation of Win32DebugLogStream +*/ +#ifndef AI_WIN32DEBUGLOGSTREAM_H_INC +#define AI_WIN32DEBUGLOGSTREAM_H_INC + +#ifdef _WIN32 + +#include <assimp/LogStream.hpp> +#include "windows.h" + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** @class Win32DebugLogStream + * @brief Logs into the debug stream from win32. + */ +class Win32DebugLogStream : public LogStream { +public: + /** @brief Default constructor */ + Win32DebugLogStream(); + + /** @brief Destructor */ + ~Win32DebugLogStream(); + + /** @brief Writer */ + void write(const char* messgae); +}; + +// --------------------------------------------------------------------------- +inline +Win32DebugLogStream::Win32DebugLogStream(){ + // empty +} + +// --------------------------------------------------------------------------- +inline +Win32DebugLogStream::~Win32DebugLogStream(){ + // empty +} + +// --------------------------------------------------------------------------- +inline +void Win32DebugLogStream::write(const char* message) { + ::OutputDebugStringA( message); +} + +// --------------------------------------------------------------------------- +} // Namespace Assimp + +#endif // ! _WIN32 +#endif // guard diff --git a/thirdparty/assimp/code/res/resource.h b/thirdparty/assimp/code/res/resource.h new file mode 100644 index 0000000000..37d39284fe --- /dev/null +++ b/thirdparty/assimp/code/res/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by assimp.rc + +// Nächste Standardwerte für neue Objekte +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/thirdparty/assimp/code/revision.h b/thirdparty/assimp/code/revision.h new file mode 100644 index 0000000000..88872aef22 --- /dev/null +++ b/thirdparty/assimp/code/revision.h @@ -0,0 +1,7 @@ +#ifndef ASSIMP_REVISION_H_INC +#define ASSIMP_REVISION_H_INC + +#define GitVersion 0x00000000 +#define GitBranch "master" + +#endif // ASSIMP_REVISION_H_INC diff --git a/thirdparty/assimp/code/scene.cpp b/thirdparty/assimp/code/scene.cpp new file mode 100644 index 0000000000..2acb348d81 --- /dev/null +++ b/thirdparty/assimp/code/scene.cpp @@ -0,0 +1,140 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#include <assimp/scene.h> + +aiNode::aiNode() +: mName("") +, mParent(NULL) +, mNumChildren(0) +, mChildren(NULL) +, mNumMeshes(0) +, mMeshes(NULL) +, mMetaData(NULL) { + // empty +} + +aiNode::aiNode(const std::string& name) +: mName(name) +, mParent(NULL) +, mNumChildren(0) +, mChildren(NULL) +, mNumMeshes(0) +, mMeshes(NULL) +, mMetaData(NULL) { + // empty +} + +/** Destructor */ +aiNode::~aiNode() { + // delete all children recursively + // to make sure we won't crash if the data is invalid ... + if (mChildren && mNumChildren) + { + for (unsigned int a = 0; a < mNumChildren; a++) + delete mChildren[a]; + } + delete[] mChildren; + delete[] mMeshes; + delete mMetaData; +} + +const aiNode *aiNode::FindNode(const char* name) const { + if (nullptr == name) { + return nullptr; + } + if (!::strcmp(mName.data, name)) { + return this; + } + for (unsigned int i = 0; i < mNumChildren; ++i) { + const aiNode* const p = mChildren[i]->FindNode(name); + if (p) { + return p; + } + } + // there is definitely no sub-node with this name + return nullptr; +} + +aiNode *aiNode::FindNode(const char* name) { + if (!::strcmp(mName.data, name))return this; + for (unsigned int i = 0; i < mNumChildren; ++i) + { + aiNode* const p = mChildren[i]->FindNode(name); + if (p) { + return p; + } + } + // there is definitely no sub-node with this name + return nullptr; +} + +void aiNode::addChildren(unsigned int numChildren, aiNode **children) { + if (nullptr == children || 0 == numChildren) { + return; + } + + for (unsigned int i = 0; i < numChildren; i++) { + aiNode *child = children[i]; + if (nullptr != child) { + child->mParent = this; + } + } + + if (mNumChildren > 0) { + aiNode **tmp = new aiNode*[mNumChildren]; + ::memcpy(tmp, mChildren, sizeof(aiNode*) * mNumChildren); + delete[] mChildren; + mChildren = new aiNode*[mNumChildren + numChildren]; + ::memcpy(mChildren, tmp, sizeof(aiNode*) * mNumChildren); + ::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode*)* numChildren); + mNumChildren += numChildren; + delete[] tmp; + } + else { + mChildren = new aiNode*[numChildren]; + for (unsigned int i = 0; i < numChildren; i++) { + mChildren[i] = children[i]; + } + mNumChildren = numChildren; + } +} diff --git a/thirdparty/assimp/code/simd.cpp b/thirdparty/assimp/code/simd.cpp new file mode 100644 index 0000000000..04615f408e --- /dev/null +++ b/thirdparty/assimp/code/simd.cpp @@ -0,0 +1,79 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#include "simd.h" + +namespace Assimp { + +bool CPUSupportsSSE2() { +#if defined(__x86_64__) || defined(_M_X64) + //* x86_64 always has SSE2 instructions */ + return true; +#elif defined(__GNUC__) && defined(i386) + // for GCC x86 we check cpuid + unsigned int d; + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=d" ( d ) + :"a" ( 1 ) ); + return ( d & 0x04000000 ) != 0; +#elif (defined(_MSC_VER) && defined(_M_IX86)) + // also check cpuid for MSVC x86 + unsigned int d; + __asm { + xor eax, eax + inc eax + push ebx + cpuid + pop ebx + mov d, edx + } + return ( d & 0x04000000 ) != 0; +#else + return false; +#endif +} + + +} // Namespace Assimp diff --git a/thirdparty/assimp/code/simd.h b/thirdparty/assimp/code/simd.h new file mode 100644 index 0000000000..3eecdd4581 --- /dev/null +++ b/thirdparty/assimp/code/simd.h @@ -0,0 +1,53 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#pragma once + +#include <assimp/defs.h> + +namespace Assimp { + +/// @brief Checks if the platform supports SSE2 optimization +/// @return true, if SSE2 is supported. false if SSE2 is not supported. +bool ASSIMP_API CPUSupportsSSE2(); + +} // Namespace Assimp diff --git a/thirdparty/assimp/contrib/utf8cpp/source/utf8.h b/thirdparty/assimp/contrib/utf8cpp/source/utf8.h new file mode 100644 index 0000000000..82b13f59f9 --- /dev/null +++ b/thirdparty/assimp/contrib/utf8cpp/source/utf8.h @@ -0,0 +1,34 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "utf8/checked.h" +#include "utf8/unchecked.h" + +#endif // header guard diff --git a/thirdparty/assimp/contrib/utf8cpp/source/utf8/checked.h b/thirdparty/assimp/contrib/utf8cpp/source/utf8/checked.h new file mode 100644 index 0000000000..1331155138 --- /dev/null +++ b/thirdparty/assimp/contrib/utf8cpp/source/utf8/checked.h @@ -0,0 +1,327 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "core.h" +#include <stdexcept> + +namespace utf8 +{ + // Base for the exceptions that may be thrown from the library + class exception : public ::std::exception { + }; + + // Exceptions that may be thrown from the library functions. + class invalid_code_point : public exception { + uint32_t cp; + public: + invalid_code_point(uint32_t cp) : cp(cp) {} + virtual const char* what() const throw() { return "Invalid code point"; } + uint32_t code_point() const {return cp;} + }; + + class invalid_utf8 : public exception { + uint8_t u8; + public: + invalid_utf8 (uint8_t u) : u8(u) {} + virtual const char* what() const throw() { return "Invalid UTF-8"; } + uint8_t utf8_octet() const {return u8;} + }; + + class invalid_utf16 : public exception { + uint16_t u16; + public: + invalid_utf16 (uint16_t u) : u16(u) {} + virtual const char* what() const throw() { return "Invalid UTF-16"; } + uint16_t utf16_word() const {return u16;} + }; + + class not_enough_room : public exception { + public: + virtual const char* what() const throw() { return "Not enough space"; } + }; + + /// The library API - functions intended to be called by the users + + template <typename octet_iterator> + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (!utf8::internal::is_code_point_valid(cp)) + throw invalid_code_point(cp); + + if (cp < 0x80) // one octet + *(result++) = static_cast<uint8_t>(cp); + else if (cp < 0x800) { // two octets + *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) { // three octets + *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0); + *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else { // four octets + *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0); + *(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + return result; + } + + template <typename octet_iterator, typename output_iterator> + output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) + { + while (start != end) { + octet_iterator sequence_start = start; + internal::utf_error err_code = utf8::internal::validate_next(start, end); + switch (err_code) { + case internal::UTF8_OK : + for (octet_iterator it = sequence_start; it != start; ++it) + *out++ = *it; + break; + case internal::NOT_ENOUGH_ROOM: + throw not_enough_room(); + case internal::INVALID_LEAD: + out = utf8::append (replacement, out); + ++start; + break; + case internal::INCOMPLETE_SEQUENCE: + case internal::OVERLONG_SEQUENCE: + case internal::INVALID_CODE_POINT: + out = utf8::append (replacement, out); + ++start; + // just one replacement mark for the sequence + while (start != end && utf8::internal::is_trail(*start)) + ++start; + break; + } + } + return out; + } + + template <typename octet_iterator, typename output_iterator> + inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) + { + static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); + return utf8::replace_invalid(start, end, out, replacement_marker); + } + + template <typename octet_iterator> + uint32_t next(octet_iterator& it, octet_iterator end) + { + uint32_t cp = 0; + internal::utf_error err_code = utf8::internal::validate_next(it, end, cp); + switch (err_code) { + case internal::UTF8_OK : + break; + case internal::NOT_ENOUGH_ROOM : + throw not_enough_room(); + case internal::INVALID_LEAD : + case internal::INCOMPLETE_SEQUENCE : + case internal::OVERLONG_SEQUENCE : + throw invalid_utf8(*it); + case internal::INVALID_CODE_POINT : + throw invalid_code_point(cp); + } + return cp; + } + + template <typename octet_iterator> + uint32_t peek_next(octet_iterator it, octet_iterator end) + { + return utf8::next(it, end); + } + + template <typename octet_iterator> + uint32_t prior(octet_iterator& it, octet_iterator start) + { + // can't do much if it == start + if (it == start) + throw not_enough_room(); + + octet_iterator end = it; + // Go back until we hit either a lead octet or start + while (utf8::internal::is_trail(*(--it))) + if (it == start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + return utf8::peek_next(it, end); + } + + /// Deprecated in versions that include "prior" + template <typename octet_iterator> + uint32_t previous(octet_iterator& it, octet_iterator pass_start) + { + octet_iterator end = it; + while (utf8::internal::is_trail(*(--it))) + if (it == pass_start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + octet_iterator temp = it; + return utf8::next(temp, end); + } + + template <typename octet_iterator, typename distance_type> + void advance (octet_iterator& it, distance_type n, octet_iterator end) + { + for (distance_type i = 0; i < n; ++i) + utf8::next(it, end); + } + + template <typename octet_iterator> + typename std::iterator_traits<octet_iterator>::difference_type + distance (octet_iterator first, octet_iterator last) + { + typename std::iterator_traits<octet_iterator>::difference_type dist; + for (dist = 0; first < last; ++dist) + utf8::next(first, last); + return dist; + } + + template <typename u16bit_iterator, typename octet_iterator> + octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) { + uint32_t cp = utf8::internal::mask16(*start++); + // Take care of surrogate pairs first + if (utf8::internal::is_lead_surrogate(cp)) { + if (start != end) { + uint32_t trail_surrogate = utf8::internal::mask16(*start++); + if (utf8::internal::is_trail_surrogate(trail_surrogate)) + cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; + else + throw invalid_utf16(static_cast<uint16_t>(trail_surrogate)); + } + else + throw invalid_utf16(static_cast<uint16_t>(cp)); + + } + // Lone trail surrogate + else if (utf8::internal::is_trail_surrogate(cp)) + throw invalid_utf16(static_cast<uint16_t>(cp)); + + result = utf8::append(cp, result); + } + return result; + } + + template <typename u16bit_iterator, typename octet_iterator> + u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + { + while (start != end) { + uint32_t cp = utf8::next(start, end); + if (cp > 0xffff) { //make a surrogate pair + *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET); + *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); + } + else + *result++ = static_cast<uint16_t>(cp); + } + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + { + while (start != end) + result = utf8::append(*(start++), result); + + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + { + while (start != end) + (*result++) = utf8::next(start, end); + + return result; + } + + // The iterator class + template <typename octet_iterator> + class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> { + octet_iterator it; + octet_iterator range_start; + octet_iterator range_end; + public: + iterator () {} + explicit iterator (const octet_iterator& octet_it, + const octet_iterator& range_start, + const octet_iterator& range_end) : + it(octet_it), range_start(range_start), range_end(range_end) + { + if (it < range_start || it > range_end) + throw std::out_of_range("Invalid utf-8 iterator position"); + } + // the default "big three" are OK + octet_iterator base () const { return it; } + uint32_t operator * () const + { + octet_iterator temp = it; + return utf8::next(temp, range_end); + } + bool operator == (const iterator& rhs) const + { + if (range_start != rhs.range_start || range_end != rhs.range_end) + throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); + return (it == rhs.it); + } + bool operator != (const iterator& rhs) const + { + return !(operator == (rhs)); + } + iterator& operator ++ () + { + utf8::next(it, range_end); + return *this; + } + iterator operator ++ (int) + { + iterator temp = *this; + utf8::next(it, range_end); + return temp; + } + iterator& operator -- () + { + utf8::prior(it, range_start); + return *this; + } + iterator operator -- (int) + { + iterator temp = *this; + utf8::prior(it, range_start); + return temp; + } + }; // class iterator + +} // namespace utf8 + +#endif //header guard + + diff --git a/thirdparty/assimp/contrib/utf8cpp/source/utf8/core.h b/thirdparty/assimp/contrib/utf8cpp/source/utf8/core.h new file mode 100644 index 0000000000..693d388c07 --- /dev/null +++ b/thirdparty/assimp/contrib/utf8cpp/source/utf8/core.h @@ -0,0 +1,329 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include <iterator> + +namespace utf8 +{ + // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers + // You may need to change them to match your system. + // These typedefs have the same names as ones from cstdint, or boost/cstdint + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + +// Helper code - not intended to be directly called by the library users. May be changed at any time +namespace internal +{ + // Unicode constants + // Leading (high) surrogates: 0xd800 - 0xdbff + // Trailing (low) surrogates: 0xdc00 - 0xdfff + const uint16_t LEAD_SURROGATE_MIN = 0xd800u; + const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; + const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; + const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; + const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); + const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; + + // Maximum valid value for a Unicode code point + const uint32_t CODE_POINT_MAX = 0x0010ffffu; + + template<typename octet_type> + inline uint8_t mask8(octet_type oc) + { + return static_cast<uint8_t>(0xff & oc); + } + template<typename u16_type> + inline uint16_t mask16(u16_type oc) + { + return static_cast<uint16_t>(0xffff & oc); + } + template<typename octet_type> + inline bool is_trail(octet_type oc) + { + return ((utf8::internal::mask8(oc) >> 6) == 0x2); + } + + template <typename u16> + inline bool is_lead_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); + } + + template <typename u16> + inline bool is_trail_surrogate(u16 cp) + { + return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } + + template <typename u16> + inline bool is_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } + + template <typename u32> + inline bool is_code_point_valid(u32 cp) + { + return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); + } + + template <typename octet_iterator> + inline typename std::iterator_traits<octet_iterator>::difference_type + sequence_length(octet_iterator lead_it) + { + uint8_t lead = utf8::internal::mask8(*lead_it); + if (lead < 0x80) + return 1; + else if ((lead >> 5) == 0x6) + return 2; + else if ((lead >> 4) == 0xe) + return 3; + else if ((lead >> 3) == 0x1e) + return 4; + else + return 0; + } + + template <typename octet_difference_type> + inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) + { + if (cp < 0x80) { + if (length != 1) + return true; + } + else if (cp < 0x800) { + if (length != 2) + return true; + } + else if (cp < 0x10000) { + if (length != 3) + return true; + } + + return false; + } + + enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; + + /// Helper for get_sequence_x + template <typename octet_iterator> + utf_error increase_safely(octet_iterator& it, octet_iterator end) + { + if (++it == end) + return NOT_ENOUGH_ROOM; + + if (!utf8::internal::is_trail(*it)) + return INCOMPLETE_SEQUENCE; + + return UTF8_OK; + } + + #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} + + /// get_sequence_x functions decode utf-8 sequences of the length x + template <typename octet_iterator> + utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + return UTF8_OK; + } + + template <typename octet_iterator> + utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); + + return UTF8_OK; + } + + template <typename octet_iterator> + utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (*it) & 0x3f; + + return UTF8_OK; + } + + template <typename octet_iterator> + utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (*it) & 0x3f; + + return UTF8_OK; + } + + #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR + + template <typename octet_iterator> + utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + // Save the original value of it so we can go back in case of failure + // Of course, it does not make much sense with i.e. stream iterators + octet_iterator original_it = it; + + uint32_t cp = 0; + // Determine the sequence length based on the lead octet + typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type; + const octet_difference_type length = utf8::internal::sequence_length(it); + + // Get trail octets and calculate the code point + utf_error err = UTF8_OK; + switch (length) { + case 0: + return INVALID_LEAD; + case 1: + err = utf8::internal::get_sequence_1(it, end, cp); + break; + case 2: + err = utf8::internal::get_sequence_2(it, end, cp); + break; + case 3: + err = utf8::internal::get_sequence_3(it, end, cp); + break; + case 4: + err = utf8::internal::get_sequence_4(it, end, cp); + break; + } + + if (err == UTF8_OK) { + // Decoding succeeded. Now, security checks... + if (utf8::internal::is_code_point_valid(cp)) { + if (!utf8::internal::is_overlong_sequence(cp, length)){ + // Passed! Return here. + code_point = cp; + ++it; + return UTF8_OK; + } + else + err = OVERLONG_SEQUENCE; + } + else + err = INVALID_CODE_POINT; + } + + // Failure branch - restore the original value of the iterator + it = original_it; + return err; + } + + template <typename octet_iterator> + inline utf_error validate_next(octet_iterator& it, octet_iterator end) { + uint32_t ignored; + return utf8::internal::validate_next(it, end, ignored); + } + +} // namespace internal + + /// The library API - functions intended to be called by the users + + // Byte order mark + const uint8_t bom[] = {0xef, 0xbb, 0xbf}; + + template <typename octet_iterator> + octet_iterator find_invalid(octet_iterator start, octet_iterator end) + { + octet_iterator result = start; + while (result != end) { + utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end); + if (err_code != internal::UTF8_OK) + return result; + } + return result; + } + + template <typename octet_iterator> + inline bool is_valid(octet_iterator start, octet_iterator end) + { + return (utf8::find_invalid(start, end) == end); + } + + template <typename octet_iterator> + inline bool starts_with_bom (octet_iterator it, octet_iterator end) + { + return ( + ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && + ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && + ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) + ); + } + + //Deprecated in release 2.3 + template <typename octet_iterator> + inline bool is_bom (octet_iterator it) + { + return ( + (utf8::internal::mask8(*it++)) == bom[0] && + (utf8::internal::mask8(*it++)) == bom[1] && + (utf8::internal::mask8(*it)) == bom[2] + ); + } +} // namespace utf8 + +#endif // header guard + + diff --git a/thirdparty/assimp/contrib/utf8cpp/source/utf8/unchecked.h b/thirdparty/assimp/contrib/utf8cpp/source/utf8/unchecked.h new file mode 100644 index 0000000000..cb2427166b --- /dev/null +++ b/thirdparty/assimp/contrib/utf8cpp/source/utf8/unchecked.h @@ -0,0 +1,228 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "core.h" + +namespace utf8 +{ + namespace unchecked + { + template <typename octet_iterator> + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (cp < 0x80) // one octet + *(result++) = static_cast<uint8_t>(cp); + else if (cp < 0x800) { // two octets + *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) { // three octets + *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0); + *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + else { // four octets + *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0); + *(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f)| 0x80); + *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80); + } + return result; + } + + template <typename octet_iterator> + uint32_t next(octet_iterator& it) + { + uint32_t cp = utf8::internal::mask8(*it); + typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it); + switch (length) { + case 1: + break; + case 2: + it++; + cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); + break; + case 3: + ++it; + cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); + ++it; + cp += (*it) & 0x3f; + break; + case 4: + ++it; + cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + ++it; + cp += (utf8::internal::mask8(*it) << 6) & 0xfff; + ++it; + cp += (*it) & 0x3f; + break; + } + ++it; + return cp; + } + + template <typename octet_iterator> + uint32_t peek_next(octet_iterator it) + { + return utf8::unchecked::next(it); + } + + template <typename octet_iterator> + uint32_t prior(octet_iterator& it) + { + while (utf8::internal::is_trail(*(--it))) ; + octet_iterator temp = it; + return utf8::unchecked::next(temp); + } + + // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous) + template <typename octet_iterator> + inline uint32_t previous(octet_iterator& it) + { + return utf8::unchecked::prior(it); + } + + template <typename octet_iterator, typename distance_type> + void advance (octet_iterator& it, distance_type n) + { + for (distance_type i = 0; i < n; ++i) + utf8::unchecked::next(it); + } + + template <typename octet_iterator> + typename std::iterator_traits<octet_iterator>::difference_type + distance (octet_iterator first, octet_iterator last) + { + typename std::iterator_traits<octet_iterator>::difference_type dist; + for (dist = 0; first < last; ++dist) + utf8::unchecked::next(first); + return dist; + } + + template <typename u16bit_iterator, typename octet_iterator> + octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) { + uint32_t cp = utf8::internal::mask16(*start++); + // Take care of surrogate pairs first + if (utf8::internal::is_lead_surrogate(cp)) { + uint32_t trail_surrogate = utf8::internal::mask16(*start++); + cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; + } + result = utf8::unchecked::append(cp, result); + } + return result; + } + + template <typename u16bit_iterator, typename octet_iterator> + u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + { + while (start < end) { + uint32_t cp = utf8::unchecked::next(start); + if (cp > 0xffff) { //make a surrogate pair + *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET); + *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); + } + else + *result++ = static_cast<uint16_t>(cp); + } + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + { + while (start != end) + result = utf8::unchecked::append(*(start++), result); + + return result; + } + + template <typename octet_iterator, typename u32bit_iterator> + u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + { + while (start < end) + (*result++) = utf8::unchecked::next(start); + + return result; + } + + // The iterator class + template <typename octet_iterator> + class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> { + octet_iterator it; + public: + iterator () {} + explicit iterator (const octet_iterator& octet_it): it(octet_it) {} + // the default "big three" are OK + octet_iterator base () const { return it; } + uint32_t operator * () const + { + octet_iterator temp = it; + return utf8::unchecked::next(temp); + } + bool operator == (const iterator& rhs) const + { + return (it == rhs.it); + } + bool operator != (const iterator& rhs) const + { + return !(operator == (rhs)); + } + iterator& operator ++ () + { + ::std::advance(it, utf8::internal::sequence_length(it)); + return *this; + } + iterator operator ++ (int) + { + iterator temp = *this; + ::std::advance(it, utf8::internal::sequence_length(it)); + return temp; + } + iterator& operator -- () + { + utf8::unchecked::prior(it); + return *this; + } + iterator operator -- (int) + { + iterator temp = *this; + utf8::unchecked::prior(it); + return temp; + } + }; // class iterator + + } // namespace utf8::unchecked +} // namespace utf8 + + +#endif // header guard + diff --git a/thirdparty/assimp/include/assimp/.editorconfig b/thirdparty/assimp/include/assimp/.editorconfig new file mode 100644 index 0000000000..9ea66423ad --- /dev/null +++ b/thirdparty/assimp/include/assimp/.editorconfig @@ -0,0 +1,8 @@ +# See <http://EditorConfig.org> for details + +[*.{h,hpp,inl}] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_size = 4 +indent_style = space diff --git a/thirdparty/assimp/include/assimp/BaseImporter.h b/thirdparty/assimp/include/assimp/BaseImporter.h new file mode 100644 index 0000000000..48dfc8ed8b --- /dev/null +++ b/thirdparty/assimp/include/assimp/BaseImporter.h @@ -0,0 +1,361 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Definition of the base class for all importer worker classes. */ +#ifndef INCLUDED_AI_BASEIMPORTER_H +#define INCLUDED_AI_BASEIMPORTER_H + +#include "Exceptional.h" + +#include <vector> +#include <set> +#include <assimp/types.h> +#include <assimp/ProgressHandler.hpp> + +struct aiScene; +struct aiImporterDesc; + +namespace Assimp { + +class Importer; +class IOSystem; +class BaseProcess; +class SharedPostProcessInfo; +class IOStream; + +// utility to do char4 to uint32 in a portable manner +#define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \ + (string[1] << 16) + (string[2] << 8) + string[3])) + + +// --------------------------------------------------------------------------- +/** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface + * for all importer worker classes. + * + * The interface defines two functions: CanRead() is used to check if the + * importer can handle the format of the given file. If an implementation of + * this function returns true, the importer then calls ReadFile() which + * imports the given file. ReadFile is not overridable, it just calls + * InternReadFile() and catches any ImportErrorException that might occur. + */ +class ASSIMP_API BaseImporter { + friend class Importer; + +public: + + /** Constructor to be privately used by #Importer */ + BaseImporter() AI_NO_EXCEPT; + + /** Destructor, private as well */ + virtual ~BaseImporter(); + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * + * The implementation should be as quick as possible. A check for + * the file extension is enough. If no suitable loader is found with + * this strategy, CanRead() is called again, the 'checkSig' parameter + * set to true this time. Now the implementation is expected to + * perform a full check of the file structure, possibly searching the + * first bytes of the file for magic identifiers or keywords. + * + * @param pFile Path and file name of the file to be examined. + * @param pIOHandler The IO handler to use for accessing any file. + * @param checkSig Set to true if this method is called a second time. + * This time, the implementation may take more time to examine the + * contents of the file to be loaded for magic bytes, keywords, etc + * to be able to load files with unknown/not existent file extensions. + * @return true if the class can read this file, false if not. + */ + virtual bool CanRead( + const std::string& pFile, + IOSystem* pIOHandler, + bool checkSig + ) const = 0; + + // ------------------------------------------------------------------- + /** Imports the given file and returns the imported data. + * If the import succeeds, ownership of the data is transferred to + * the caller. If the import fails, NULL is returned. The function + * takes care that any partially constructed data is destroyed + * beforehand. + * + * @param pImp #Importer object hosting this loader. + * @param pFile Path of the file to be imported. + * @param pIOHandler IO-Handler used to open this and possible other files. + * @return The imported data or NULL if failed. If it failed a + * human-readable error description can be retrieved by calling + * GetErrorText() + * + * @note This function is not intended to be overridden. Implement + * InternReadFile() to do the import. If an exception is thrown somewhere + * in InternReadFile(), this function will catch it and transform it into + * a suitable response to the caller. + */ + aiScene* ReadFile( + const Importer* pImp, + const std::string& pFile, + IOSystem* pIOHandler + ); + + // ------------------------------------------------------------------- + /** Returns the error description of the last error that occurred. + * @return A description of the last error that occurred. An empty + * string if there was no error. + */ + const std::string& GetErrorText() const { + return m_ErrorText; + } + + // ------------------------------------------------------------------- + /** Called prior to ReadFile(). + * The function is a request to the importer to update its configuration + * basing on the Importer's configuration property list. + * @param pImp Importer instance + */ + virtual void SetupProperties( + const Importer* pImp + ); + + // ------------------------------------------------------------------- + /** Called by #Importer::GetImporterInfo to get a description of + * some loader features. Importers must provide this information. */ + virtual const aiImporterDesc* GetInfo() const = 0; + + // ------------------------------------------------------------------- + /** Called by #Importer::GetExtensionList for each loaded importer. + * Take the extension list contained in the structure returned by + * #GetInfo and insert all file extensions into the given set. + * @param extension set to collect file extensions in*/ + void GetExtensionList(std::set<std::string>& extensions); + +protected: + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. The + * function is expected to throw an ImportErrorException if there is + * an error. If it terminates normally, the data in aiScene is + * expected to be correct. Override this function to implement the + * actual importing. + * <br> + * The output scene must meet the following requirements:<br> + * <ul> + * <li>At least a root node must be there, even if its only purpose + * is to reference one mesh.</li> + * <li>aiMesh::mPrimitiveTypes may be 0. The types of primitives + * in the mesh are determined automatically in this case.</li> + * <li>the vertex data is stored in a pseudo-indexed "verbose" format. + * In fact this means that every vertex that is referenced by + * a face is unique. Or the other way round: a vertex index may + * not occur twice in a single aiMesh.</li> + * <li>aiAnimation::mDuration may be -1. Assimp determines the length + * of the animation automatically in this case as the length of + * the longest animation channel.</li> + * <li>aiMesh::mBitangents may be NULL if tangents and normals are + * given. In this case bitangents are computed as the cross product + * between normal and tangent.</li> + * <li>There needn't be a material. If none is there a default material + * is generated. However, it is recommended practice for loaders + * to generate a default material for yourself that matches the + * default material setting for the file format better than Assimp's + * generic default material. Note that default materials *should* + * be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded + * or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy) + * texture. </li> + * </ul> + * If the AI_SCENE_FLAGS_INCOMPLETE-Flag is <b>not</b> set:<ul> + * <li> at least one mesh must be there</li> + * <li> there may be no meshes with 0 vertices or faces</li> + * </ul> + * This won't be checked (except by the validation step): Assimp will + * crash if one of the conditions is not met! + * + * @param pFile Path of the file to be imported. + * @param pScene The scene object to hold the imported data. + * NULL is not a valid parameter. + * @param pIOHandler The IO handler to use for any file access. + * NULL is not a valid parameter. */ + virtual void InternReadFile( + const std::string& pFile, + aiScene* pScene, + IOSystem* pIOHandler + ) = 0; + +public: // static utilities + + // ------------------------------------------------------------------- + /** A utility for CanRead(). + * + * The function searches the header of a file for a specific token + * and returns true if this token is found. This works for text + * files only. There is a rudimentary handling of UNICODE files. + * The comparison is case independent. + * + * @param pIOSystem IO System to work with + * @param file File name of the file + * @param tokens List of tokens to search for + * @param numTokens Size of the token array + * @param searchBytes Number of bytes to be searched for the tokens. + */ + static bool SearchFileHeaderForToken( + IOSystem* pIOSystem, + const std::string& file, + const char** tokens, + unsigned int numTokens, + unsigned int searchBytes = 200, + bool tokensSol = false, + bool noAlphaBeforeTokens = false); + + // ------------------------------------------------------------------- + /** @brief Check whether a file has a specific file extension + * @param pFile Input file + * @param ext0 Extension to check for. Lowercase characters only, no dot! + * @param ext1 Optional second extension + * @param ext2 Optional third extension + * @note Case-insensitive + */ + static bool SimpleExtensionCheck ( + const std::string& pFile, + const char* ext0, + const char* ext1 = NULL, + const char* ext2 = NULL); + + // ------------------------------------------------------------------- + /** @brief Extract file extension from a string + * @param pFile Input file + * @return Extension without trailing dot, all lowercase + */ + static std::string GetExtension ( + const std::string& pFile); + + // ------------------------------------------------------------------- + /** @brief Check whether a file starts with one or more magic tokens + * @param pFile Input file + * @param pIOHandler IO system to be used + * @param magic n magic tokens + * @params num Size of magic + * @param offset Offset from file start where tokens are located + * @param Size of one token, in bytes. Maximally 16 bytes. + * @return true if one of the given tokens was found + * + * @note For convenience, the check is also performed for the + * byte-swapped variant of all tokens (big endian). Only for + * tokens of size 2,4. + */ + static bool CheckMagicToken( + IOSystem* pIOHandler, + const std::string& pFile, + const void* magic, + unsigned int num, + unsigned int offset = 0, + unsigned int size = 4); + + // ------------------------------------------------------------------- + /** An utility for all text file loaders. It converts a file to our + * UTF8 character set. Errors are reported, but ignored. + * + * @param data File buffer to be converted to UTF8 data. The buffer + * is resized as appropriate. */ + static void ConvertToUTF8( + std::vector<char>& data); + + // ------------------------------------------------------------------- + /** An utility for all text file loaders. It converts a file from our + * UTF8 character set back to ISO-8859-1. Errors are reported, but ignored. + * + * @param data File buffer to be converted from UTF8 to ISO-8859-1. The buffer + * is resized as appropriate. */ + static void ConvertUTF8toISO8859_1( + std::string& data); + + // ------------------------------------------------------------------- + /// @brief Enum to define, if empty files are ok or not. + enum TextFileMode { + ALLOW_EMPTY, + FORBID_EMPTY + }; + + // ------------------------------------------------------------------- + /** Utility for text file loaders which copies the contents of the + * file into a memory buffer and converts it to our UTF8 + * representation. + * @param stream Stream to read from. + * @param data Output buffer to be resized and filled with the + * converted text file data. The buffer is terminated with + * a binary 0. + * @param mode Whether it is OK to load empty text files. */ + static void TextFileToBuffer( + IOStream* stream, + std::vector<char>& data, + TextFileMode mode = FORBID_EMPTY); + + // ------------------------------------------------------------------- + /** Utility function to move a std::vector into a aiScene array + * @param vec The vector to be moved + * @param out The output pointer to the allocated array. + * @param numOut The output count of elements copied. */ + template<typename T> + AI_FORCE_INLINE + static void CopyVector( + std::vector<T>& vec, + T*& out, + unsigned int& outLength) + { + outLength = unsigned(vec.size()); + if (outLength) { + out = new T[outLength]; + std::swap_ranges(vec.begin(), vec.end(), out); + } + } + +protected: + /// Error description in case there was one. + std::string m_ErrorText; + /// Currently set progress handler. + ProgressHandler* m_progress; +}; + + + +} // end of namespace Assimp + +#endif // AI_BASEIMPORTER_H_INC diff --git a/thirdparty/assimp/include/assimp/Bitmap.h b/thirdparty/assimp/include/assimp/Bitmap.h new file mode 100644 index 0000000000..e6b5fb1327 --- /dev/null +++ b/thirdparty/assimp/include/assimp/Bitmap.h @@ -0,0 +1,125 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Bitmap.h + * @brief Defines bitmap format helper for textures + * + * Used for file formats which embed their textures into the model file. + */ + +#ifndef AI_BITMAP_H_INC +#define AI_BITMAP_H_INC + +#include "defs.h" +#include <stdint.h> +#include <cstddef> + +struct aiTexture; + +namespace Assimp { + +class IOStream; + +class ASSIMP_API Bitmap { +protected: + + struct Header { + uint16_t type; + uint32_t size; + uint16_t reserved1; + uint16_t reserved2; + uint32_t offset; + + // We define the struct size because sizeof(Header) might return a wrong result because of structure padding. + // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field). + static const std::size_t header_size = + sizeof(uint16_t) + // type + sizeof(uint32_t) + // size + sizeof(uint16_t) + // reserved1 + sizeof(uint16_t) + // reserved2 + sizeof(uint32_t); // offset + }; + + struct DIB { + uint32_t size; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t bits_per_pixel; + uint32_t compression; + uint32_t image_size; + int32_t x_resolution; + int32_t y_resolution; + uint32_t nb_colors; + uint32_t nb_important_colors; + + // We define the struct size because sizeof(DIB) might return a wrong result because of structure padding. + // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field). + static const std::size_t dib_size = + sizeof(uint32_t) + // size + sizeof(int32_t) + // width + sizeof(int32_t) + // height + sizeof(uint16_t) + // planes + sizeof(uint16_t) + // bits_per_pixel + sizeof(uint32_t) + // compression + sizeof(uint32_t) + // image_size + sizeof(int32_t) + // x_resolution + sizeof(int32_t) + // y_resolution + sizeof(uint32_t) + // nb_colors + sizeof(uint32_t); // nb_important_colors + }; + + static const std::size_t mBytesPerPixel = 4; + +public: + static void Save(aiTexture* texture, IOStream* file); + +protected: + static void WriteHeader(Header& header, IOStream* file); + static void WriteDIB(DIB& dib, IOStream* file); + static void WriteData(aiTexture* texture, IOStream* file); +}; + +} + +#endif // AI_BITMAP_H_INC diff --git a/thirdparty/assimp/include/assimp/BlobIOSystem.h b/thirdparty/assimp/include/assimp/BlobIOSystem.h new file mode 100644 index 0000000000..d005e5c119 --- /dev/null +++ b/thirdparty/assimp/include/assimp/BlobIOSystem.h @@ -0,0 +1,338 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Provides cheat implementations for IOSystem and IOStream to + * redirect exporter output to a blob chain.*/ + +#ifndef AI_BLOBIOSYSTEM_H_INCLUDED +#define AI_BLOBIOSYSTEM_H_INCLUDED + +#include <assimp/IOStream.hpp> +#include <assimp/cexport.h> +#include <assimp/IOSystem.hpp> +#include <assimp/DefaultLogger.hpp> +#include <stdint.h> +#include <set> +#include <vector> + +namespace Assimp { + class BlobIOSystem; + +// -------------------------------------------------------------------------------------------- +/** Redirect IOStream to a blob */ +// -------------------------------------------------------------------------------------------- +class BlobIOStream : public IOStream +{ +public: + + BlobIOStream(BlobIOSystem* creator, const std::string& file, size_t initial = 4096) + : buffer() + , cur_size() + , file_size() + , cursor() + , initial(initial) + , file(file) + , creator(creator) + { + } + + + virtual ~BlobIOStream(); + +public: + + // ------------------------------------------------------------------- + aiExportDataBlob* GetBlob() + { + aiExportDataBlob* blob = new aiExportDataBlob(); + blob->size = file_size; + blob->data = buffer; + + buffer = NULL; + + return blob; + } + + +public: + + + // ------------------------------------------------------------------- + virtual size_t Read( void *, + size_t, + size_t ) + { + return 0; + } + + // ------------------------------------------------------------------- + virtual size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount) + { + pSize *= pCount; + if (cursor + pSize > cur_size) { + Grow(cursor + pSize); + } + + memcpy(buffer+cursor, pvBuffer, pSize); + cursor += pSize; + + file_size = std::max(file_size,cursor); + return pCount; + } + + // ------------------------------------------------------------------- + virtual aiReturn Seek(size_t pOffset, + aiOrigin pOrigin) + { + switch(pOrigin) + { + case aiOrigin_CUR: + cursor += pOffset; + break; + + case aiOrigin_END: + cursor = file_size - pOffset; + break; + + case aiOrigin_SET: + cursor = pOffset; + break; + + default: + return AI_FAILURE; + } + + if (cursor > file_size) { + Grow(cursor); + } + + file_size = std::max(cursor,file_size); + return AI_SUCCESS; + } + + // ------------------------------------------------------------------- + virtual size_t Tell() const + { + return cursor; + } + + // ------------------------------------------------------------------- + virtual size_t FileSize() const + { + return file_size; + } + + // ------------------------------------------------------------------- + virtual void Flush() + { + // ignore + } + + + +private: + + // ------------------------------------------------------------------- + void Grow(size_t need = 0) + { + // 1.5 and phi are very heap-friendly growth factors (the first + // allows for frequent re-use of heap blocks, the second + // forms a fibonacci sequence with similar characteristics - + // since this heavily depends on the heap implementation + // and other factors as well, i'll just go with 1.5 since + // it is quicker to compute). + size_t new_size = std::max(initial, std::max( need, cur_size+(cur_size>>1) )); + + const uint8_t* const old = buffer; + buffer = new uint8_t[new_size]; + + if (old) { + memcpy(buffer,old,cur_size); + delete[] old; + } + + cur_size = new_size; + } + +private: + + uint8_t* buffer; + size_t cur_size,file_size, cursor, initial; + + const std::string file; + BlobIOSystem* const creator; +}; + + +#define AI_BLOBIO_MAGIC "$blobfile" + +// -------------------------------------------------------------------------------------------- +/** Redirect IOSystem to a blob */ +// -------------------------------------------------------------------------------------------- +class BlobIOSystem : public IOSystem +{ + + friend class BlobIOStream; + typedef std::pair<std::string, aiExportDataBlob*> BlobEntry; + +public: + + BlobIOSystem() + { + } + + virtual ~BlobIOSystem() + { + for(BlobEntry& blobby : blobs) { + delete blobby.second; + } + } + +public: + + // ------------------------------------------------------------------- + const char* GetMagicFileName() const + { + return AI_BLOBIO_MAGIC; + } + + + // ------------------------------------------------------------------- + aiExportDataBlob* GetBlobChain() + { + // one must be the master + aiExportDataBlob* master = NULL, *cur; + for(const BlobEntry& blobby : blobs) { + if (blobby.first == AI_BLOBIO_MAGIC) { + master = blobby.second; + break; + } + } + if (!master) { + ASSIMP_LOG_ERROR("BlobIOSystem: no data written or master file was not closed properly."); + return NULL; + } + + master->name.Set(""); + + cur = master; + for(const BlobEntry& blobby : blobs) { + if (blobby.second == master) { + continue; + } + + cur->next = blobby.second; + cur = cur->next; + + // extract the file extension from the file written + const std::string::size_type s = blobby.first.find_first_of('.'); + cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s+1)); + } + + // give up blob ownership + blobs.clear(); + return master; + } + +public: + + // ------------------------------------------------------------------- + virtual bool Exists( const char* pFile) const { + return created.find(std::string(pFile)) != created.end(); + } + + + // ------------------------------------------------------------------- + virtual char getOsSeparator() const { + return '/'; + } + + + // ------------------------------------------------------------------- + virtual IOStream* Open(const char* pFile, + const char* pMode) + { + if (pMode[0] != 'w') { + return NULL; + } + + created.insert(std::string(pFile)); + return new BlobIOStream(this,std::string(pFile)); + } + + // ------------------------------------------------------------------- + virtual void Close( IOStream* pFile) + { + delete pFile; + } + +private: + + // ------------------------------------------------------------------- + void OnDestruct(const std::string& filename, BlobIOStream* child) + { + // we don't know in which the files are closed, so we + // can't reliably say that the first must be the master + // file ... + blobs.push_back( BlobEntry(filename,child->GetBlob()) ); + } + +private: + std::set<std::string> created; + std::vector< BlobEntry > blobs; +}; + + +// -------------------------------------------------------------------------------------------- +BlobIOStream :: ~BlobIOStream() +{ + creator->OnDestruct(file,this); + delete[] buffer; +} + + +} // end Assimp + +#endif diff --git a/thirdparty/assimp/include/assimp/ByteSwapper.h b/thirdparty/assimp/include/assimp/ByteSwapper.h new file mode 100644 index 0000000000..20a2463fb8 --- /dev/null +++ b/thirdparty/assimp/include/assimp/ByteSwapper.h @@ -0,0 +1,287 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Helper class tp perform various byte oder swappings + (e.g. little to big endian) */ +#ifndef AI_BYTESWAPPER_H_INC +#define AI_BYTESWAPPER_H_INC + +#include <assimp/ai_assert.h> +#include <assimp/types.h> +#include <stdint.h> + +#if _MSC_VER >= 1400 +#include <stdlib.h> +#endif + +namespace Assimp { +// -------------------------------------------------------------------------------------- +/** Defines some useful byte order swap routines. + * + * This is required to read big-endian model formats on little-endian machines, + * and vice versa. Direct use of this class is DEPRECATED. Use #StreamReader instead. */ +// -------------------------------------------------------------------------------------- +class ByteSwap { + ByteSwap() AI_NO_EXCEPT {} + +public: + + // ---------------------------------------------------------------------- + /** Swap two bytes of data + * @param[inout] _szOut A void* to save the reintcasts for the caller. */ + static inline void Swap2(void* _szOut) + { + ai_assert(_szOut); + +#if _MSC_VER >= 1400 + uint16_t* const szOut = reinterpret_cast<uint16_t*>(_szOut); + *szOut = _byteswap_ushort(*szOut); +#else + uint8_t* const szOut = reinterpret_cast<uint8_t*>(_szOut); + std::swap(szOut[0],szOut[1]); +#endif + } + + // ---------------------------------------------------------------------- + /** Swap four bytes of data + * @param[inout] _szOut A void* to save the reintcasts for the caller. */ + static inline void Swap4(void* _szOut) + { + ai_assert(_szOut); + +#if _MSC_VER >= 1400 + uint32_t* const szOut = reinterpret_cast<uint32_t*>(_szOut); + *szOut = _byteswap_ulong(*szOut); +#else + uint8_t* const szOut = reinterpret_cast<uint8_t*>(_szOut); + std::swap(szOut[0],szOut[3]); + std::swap(szOut[1],szOut[2]); +#endif + } + + // ---------------------------------------------------------------------- + /** Swap eight bytes of data + * @param[inout] _szOut A void* to save the reintcasts for the caller. */ + static inline void Swap8(void* _szOut) + { + ai_assert(_szOut); + +#if _MSC_VER >= 1400 + uint64_t* const szOut = reinterpret_cast<uint64_t*>(_szOut); + *szOut = _byteswap_uint64(*szOut); +#else + uint8_t* const szOut = reinterpret_cast<uint8_t*>(_szOut); + std::swap(szOut[0],szOut[7]); + std::swap(szOut[1],szOut[6]); + std::swap(szOut[2],szOut[5]); + std::swap(szOut[3],szOut[4]); +#endif + } + + // ---------------------------------------------------------------------- + /** ByteSwap a float. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(float* fOut) { + Swap4(fOut); + } + + // ---------------------------------------------------------------------- + /** ByteSwap a double. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(double* fOut) { + Swap8(fOut); + } + + + // ---------------------------------------------------------------------- + /** ByteSwap an int16t. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(int16_t* fOut) { + Swap2(fOut); + } + + static inline void Swap(uint16_t* fOut) { + Swap2(fOut); + } + + // ---------------------------------------------------------------------- + /** ByteSwap an int32t. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(int32_t* fOut){ + Swap4(fOut); + } + + static inline void Swap(uint32_t* fOut){ + Swap4(fOut); + } + + // ---------------------------------------------------------------------- + /** ByteSwap an int64t. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(int64_t* fOut) { + Swap8(fOut); + } + + static inline void Swap(uint64_t* fOut) { + Swap8(fOut); + } + + // ---------------------------------------------------------------------- + //! Templatized ByteSwap + //! \returns param tOut as swapped + template<typename Type> + static inline Type Swapped(Type tOut) + { + return _swapper<Type,sizeof(Type)>()(tOut); + } + +private: + + template <typename T, size_t size> struct _swapper; +}; + +template <typename T> struct ByteSwap::_swapper<T,2> { + T operator() (T tOut) { + Swap2(&tOut); + return tOut; + } +}; + +template <typename T> struct ByteSwap::_swapper<T,4> { + T operator() (T tOut) { + Swap4(&tOut); + return tOut; + } +}; + +template <typename T> struct ByteSwap::_swapper<T,8> { + T operator() (T tOut) { + Swap8(&tOut); + return tOut; + } +}; + + +// -------------------------------------------------------------------------------------- +// ByteSwap macros for BigEndian/LittleEndian support +// -------------------------------------------------------------------------------------- +#if (defined AI_BUILD_BIG_ENDIAN) +# define AI_LE(t) (t) +# define AI_BE(t) ByteSwap::Swapped(t) +# define AI_LSWAP2(p) +# define AI_LSWAP4(p) +# define AI_LSWAP8(p) +# define AI_LSWAP2P(p) +# define AI_LSWAP4P(p) +# define AI_LSWAP8P(p) +# define LE_NCONST const +# define AI_SWAP2(p) ByteSwap::Swap2(&(p)) +# define AI_SWAP4(p) ByteSwap::Swap4(&(p)) +# define AI_SWAP8(p) ByteSwap::Swap8(&(p)) +# define AI_SWAP2P(p) ByteSwap::Swap2((p)) +# define AI_SWAP4P(p) ByteSwap::Swap4((p)) +# define AI_SWAP8P(p) ByteSwap::Swap8((p)) +# define BE_NCONST +#else +# define AI_BE(t) (t) +# define AI_LE(t) ByteSwap::Swapped(t) +# define AI_SWAP2(p) +# define AI_SWAP4(p) +# define AI_SWAP8(p) +# define AI_SWAP2P(p) +# define AI_SWAP4P(p) +# define AI_SWAP8P(p) +# define BE_NCONST const +# define AI_LSWAP2(p) ByteSwap::Swap2(&(p)) +# define AI_LSWAP4(p) ByteSwap::Swap4(&(p)) +# define AI_LSWAP8(p) ByteSwap::Swap8(&(p)) +# define AI_LSWAP2P(p) ByteSwap::Swap2((p)) +# define AI_LSWAP4P(p) ByteSwap::Swap4((p)) +# define AI_LSWAP8P(p) ByteSwap::Swap8((p)) +# define LE_NCONST +#endif + + +namespace Intern { + +// -------------------------------------------------------------------------------------------- +template <typename T, bool doit> +struct ByteSwapper { + void operator() (T* inout) { + ByteSwap::Swap(inout); + } +}; + +template <typename T> +struct ByteSwapper<T,false> { + void operator() (T*) { + } +}; + +// -------------------------------------------------------------------------------------------- +template <bool SwapEndianess, typename T, bool RuntimeSwitch> +struct Getter { + void operator() (T* inout, bool le) { +#ifdef AI_BUILD_BIG_ENDIAN + le = le; +#else + le = !le; +#endif + if (le) { + ByteSwapper<T,(sizeof(T)>1?true:false)> () (inout); + } + else ByteSwapper<T,false> () (inout); + } +}; + +template <bool SwapEndianess, typename T> +struct Getter<SwapEndianess,T,false> { + + void operator() (T* inout, bool /*le*/) { + // static branch + ByteSwapper<T,(SwapEndianess && sizeof(T)>1)> () (inout); + } +}; +} // end Intern +} // end Assimp + +#endif //!! AI_BYTESWAPPER_H_INC diff --git a/thirdparty/assimp/include/assimp/Compiler/poppack1.h b/thirdparty/assimp/include/assimp/Compiler/poppack1.h new file mode 100644 index 0000000000..e033bc1472 --- /dev/null +++ b/thirdparty/assimp/include/assimp/Compiler/poppack1.h @@ -0,0 +1,22 @@ + +// =============================================================================== +// May be included multiple times - resets structure packing to the defaults +// for all supported compilers. Reverts the changes made by #include <pushpack1.h> +// +// Currently this works on the following compilers: +// MSVC 7,8,9 +// GCC +// BORLAND (complains about 'pack state changed but not reverted', but works) +// =============================================================================== + +#ifndef AI_PUSHPACK_IS_DEFINED +# error pushpack1.h must be included after poppack1.h +#endif + +// reset packing to the original value +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack( pop ) +#endif +#undef PACK_STRUCT + +#undef AI_PUSHPACK_IS_DEFINED diff --git a/thirdparty/assimp/include/assimp/Compiler/pstdint.h b/thirdparty/assimp/include/assimp/Compiler/pstdint.h new file mode 100644 index 0000000000..4de4ce2a90 --- /dev/null +++ b/thirdparty/assimp/include/assimp/Compiler/pstdint.h @@ -0,0 +1,912 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2016 Paul Hsieh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************** + * + * Version 0.1.15.4 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is + * critically important for making portable several classes of + * applications including cryptography, hashing, variable length + * integer libraries and so on. But for most developers its likely + * useful just for programming sanity. + * + * The problem is that some compiler vendors chose to ignore the C99 + * standard and some older compilers have no opportunity to be updated. + * Because of this situation, simply including stdint.h in your code + * makes it unportable. + * + * So that's what this file is all about. Its an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. Even compilers + * that already come with stdint.h can use this file instead without + * any loss of functionality. A few things that should be noted about + * this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current version, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_UINT8_DEC_WIDTH + * PRINTF_UINTMAX_DEC_WIDTH + * PRINTF_UINT64_DEC_WIDTH + * PRINTF_UINT32_DEC_WIDTH + * PRINTF_UINT16_DEC_WIDTH + * PRINTF_UINT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * John Dill + * Florian Wobbe + * Christopher Sean Morrison + * Mikkel Fahnoe Jorgensen + * + */ + +#include <stddef.h> +#include <limits.h> +#include <signal.h> + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__SUNPRO_C) && __SUNPRO_C >= 0x570) || (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (__GNUC__ > 3 || defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED) +#include <stdint.h> +#define _PSTDINT_H_INCLUDED +# if defined(__GNUC__) && (defined(__x86_64__) || defined(__ppc64__)) && !(defined(__APPLE__) && defined(__MACH__)) +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "l" +# endif +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# else +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# ifndef PRINTF_INT32_MODIFIER +# if (UINT_MAX == UINT32_MAX) +# define PRINTF_INT32_MODIFIER "" +# else +# define PRINTF_INT32_MODIFIER "l" +# endif +# endif +# endif +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_UINT64_HEX_WIDTH +# define PRINTF_UINT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_UINT32_HEX_WIDTH +# define PRINTF_UINT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_UINT16_HEX_WIDTH +# define PRINTF_UINT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_UINT8_HEX_WIDTH +# define PRINTF_UINT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "19" +# endif +# ifndef PRINTF_UINT64_DEC_WIDTH +# define PRINTF_UINT64_DEC_WIDTH "20" +# endif +# ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_UINT32_DEC_WIDTH +# define PRINTF_UINT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_UINT16_DEC_WIDTH +# define PRINTF_UINT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_UINT8_DEC_WIDTH +# define PRINTF_UINT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH +# endif +# ifndef PRINTF_UINTMAX_HEX_WIDTH +# define PRINTF_UINTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH +# endif +# ifndef PRINTF_UINTMAX_DEC_WIDTH +# define PRINTF_UINTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH +# endif + +/* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + +# if defined (__WATCOMC__) && __WATCOMC__ >= 1250 +# if !defined (INT64_C) +# define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) +# endif +# if !defined (UINT64_C) +# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) +# endif +# if !defined (INT32_C) +# define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) +# endif +# if !defined (UINT32_C) +# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) +# endif +# if !defined (INT16_C) +# define INT16_C(x) (x) +# endif +# if !defined (UINT16_C) +# define UINT16_C(x) (x) +# endif +# if !defined (INT8_C) +# define INT8_C(x) (x) +# endif +# if !defined (UINT8_C) +# define UINT8_C(x) (x) +# endif +# if !defined (UINT64_MAX) +# define UINT64_MAX 18446744073709551615ULL +# endif +# if !defined (INT64_MAX) +# define INT64_MAX 9223372036854775807LL +# endif +# if !defined (UINT32_MAX) +# define UINT32_MAX 4294967295UL +# endif +# if !defined (INT32_MAX) +# define INT32_MAX 2147483647L +# endif +# if !defined (INTMAX_MAX) +# define INTMAX_MAX INT64_MAX +# endif +# if !defined (INTMAX_MIN) +# define INTMAX_MIN INT64_MIN +# endif +# endif +#endif + +/* + * I have no idea what is the truly correct thing to do on older Solaris. + * From some online discussions, this seems to be what is being + * recommended. For people who actually are developing on older Solaris, + * what I would like to know is, does this define all of the relevant + * macros of a complete stdint.h? Remember, in pstdint.h 64 bit is + * considered optional. + */ + +#if (defined(__SUNPRO_C) && __SUNPRO_C >= 0x420) && !defined(_PSTDINT_H_INCLUDED) +#include <sys/inttypes.h> +#define _PSTDINT_H_INCLUDED +#endif + +#ifndef _PSTDINT_H_INCLUDED +#define _PSTDINT_H_INCLUDED + +#ifndef SIZE_MAX +# define SIZE_MAX (~(size_t)0) +#endif + +/* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + +#ifndef UINT8_MAX +# define UINT8_MAX 0xff +#endif +#if !defined(uint8_t) && !defined(_UINT8_T) && !defined(vxWorks) +# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; +# define UINT8_C(v) ((uint8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef INT8_MAX +# define INT8_MAX 0x7f +#endif +#ifndef INT8_MIN +# define INT8_MIN INT8_C(0x80) +#endif +#if !defined(int8_t) && !defined(_INT8_T) && !defined(vxWorks) +# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; +# define INT8_C(v) ((int8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef UINT16_MAX +# define UINT16_MAX 0xffff +#endif +#if !defined(uint16_t) && !defined(_UINT16_T) && !defined(vxWorks) +#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +# define UINT16_C(v) ((uint16_t) (v)) +#elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; +# define UINT16_C(v) ((uint16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT16_MAX +# define INT16_MAX 0x7fff +#endif +#ifndef INT16_MIN +# define INT16_MIN INT16_C(0x8000) +#endif +#if !defined(int16_t) && !defined(_INT16_T) && !defined(vxWorks) +#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +#elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef UINT32_MAX +# define UINT32_MAX (0xffffffffUL) +#endif +#if !defined(uint32_t) && !defined(_UINT32_T) && !defined(vxWorks) +#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; +# define UINT32_C(v) v ## UL +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# define UINT32_C(v) v ## U +#elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; +# define UINT32_C(v) ((unsigned short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT32_MAX +# define INT32_MAX (0x7fffffffL) +#endif +#ifndef INT32_MIN +# define INT32_MIN INT32_C(0x80000000) +#endif +#if !defined(int32_t) && !defined(_INT32_T) && !defined(vxWorks) +#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; +# define INT32_C(v) v ## L +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; +# define INT32_C(v) v +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; +# define INT32_C(v) ((short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +/* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + +#undef stdint_int64_defined +#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) +# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# endif +#endif + +#if !defined (stdint_int64_defined) +# if defined(__GNUC__) && !defined(vxWorks) +# define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) +# define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# define UINT64_C(v) v ## UI64 +# define INT64_C(v) v ## I64 +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "I64" +# endif +# endif +#endif + +#if !defined (LONG_LONG_MAX) && defined (INT64_C) +# define LONG_LONG_MAX INT64_C (9223372036854775807) +#endif +#ifndef ULONG_LONG_MAX +# define ULONG_LONG_MAX UINT64_C (18446744073709551615) +#endif + +#if !defined (INT64_MAX) && defined (INT64_C) +# define INT64_MAX INT64_C (9223372036854775807) +#endif +#if !defined (INT64_MIN) && defined (INT64_C) +# define INT64_MIN INT64_C (-9223372036854775808) +#endif +#if !defined (UINT64_MAX) && defined (INT64_C) +# define UINT64_MAX UINT64_C (18446744073709551615) +#endif + +/* + * Width of hexadecimal for number field. + */ + +#ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +#endif +#ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +#endif +#ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +#endif +#ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +#endif +#ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "19" +#endif +#ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +#endif +#ifndef PRINTF_UINT64_DEC_WIDTH +# define PRINTF_UINT64_DEC_WIDTH "20" +#endif +#ifndef PRINTF_UINT32_DEC_WIDTH +# define PRINTF_UINT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_UINT16_DEC_WIDTH +# define PRINTF_UINT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_UINT8_DEC_WIDTH +# define PRINTF_UINT8_DEC_WIDTH "3" +#endif + +/* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + +#ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; +# define INTMAX_MAX INT64_MAX +# define INTMAX_MIN INT64_MIN +# define UINTMAX_MAX UINT64_MAX +# define UINTMAX_C(v) UINT64_C(v) +# define INTMAX_C(v) INT64_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif +#else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; +# define INTMAX_MAX INT32_MAX +# define UINTMAX_MAX UINT32_MAX +# define UINTMAX_C(v) UINT32_C(v) +# define INTMAX_C(v) INT32_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH +# endif +#endif + +/* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + +#ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; +# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER +# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER +# define UINT_LEAST8_MAX UINT8_MAX +# define INT_LEAST8_MAX INT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define INT_LEAST16_MAX INT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; +# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER +# define UINT_LEAST64_MAX UINT64_MAX +# define INT_LEAST64_MAX INT64_MAX +# define INT_LEAST64_MIN INT64_MIN +# endif +#endif +#undef stdint_least_defined + +/* + * The ANSI C committee pretending to know or specify anything about + * performance is the epitome of misguided arrogance. The mandate of + * this file is to *ONLY* ever support that absolute minimum + * definition of the fast integer types, for compatibility purposes. + * No extensions, and no attempt to suggest what may or may not be a + * faster integer type will ever be made in this file. Developers are + * warned to stay away from these types when using this or any other + * stdint.h. + */ + +typedef int_least8_t int_fast8_t; +typedef uint_least8_t uint_fast8_t; +typedef int_least16_t int_fast16_t; +typedef uint_least16_t uint_fast16_t; +typedef int_least32_t int_fast32_t; +typedef uint_least32_t uint_fast32_t; +#define UINT_FAST8_MAX UINT_LEAST8_MAX +#define INT_FAST8_MAX INT_LEAST8_MAX +#define UINT_FAST16_MAX UINT_LEAST16_MAX +#define INT_FAST16_MAX INT_LEAST16_MAX +#define UINT_FAST32_MAX UINT_LEAST32_MAX +#define INT_FAST32_MAX INT_LEAST32_MAX +#define INT_FAST8_MIN INT_LEAST8_MIN +#define INT_FAST16_MIN INT_LEAST16_MIN +#define INT_FAST32_MIN INT_LEAST32_MIN +#ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; +# define UINT_FAST64_MAX UINT_LEAST64_MAX +# define INT_FAST64_MAX INT_LEAST64_MAX +# define INT_FAST64_MIN INT_LEAST64_MIN +#endif + +#undef stdint_int64_defined + +/* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) && !defined(vxWorks) +# include <wchar.h> +# ifndef WCHAR_MIN +# define WCHAR_MIN 0 +# endif +# ifndef WCHAR_MAX +# define WCHAR_MAX ((wchar_t)-1) +# endif +#endif + +/* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + +#if (defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED)) || defined (_UINTPTR_T) +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +#ifndef STDINT_H_UINTPTR_T_DEFINED +# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) || defined (__ppc64__) +# define stdint_intptr_bits 64 +# elif defined (__WATCOMC__) || defined (__TURBOC__) +# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) +# define stdint_intptr_bits 16 +# else +# define stdint_intptr_bits 32 +# endif +# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) || defined (__ppc64__) +# define stdint_intptr_bits 32 +# elif defined (__INTEL_COMPILER) +/* TODO -- what did Intel do about x86-64? */ +# else +/* #error "This platform might not be supported yet" */ +# endif + +# ifdef stdint_intptr_bits +# define stdint_intptr_glue3_i(a,b,c) a##b##c +# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) +# ifndef PRINTF_INTPTR_MODIFIER +# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) +# endif +# ifndef PTRDIFF_MAX +# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef PTRDIFF_MIN +# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef UINTPTR_MAX +# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MAX +# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MIN +# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef INTPTR_C +# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) +# endif +# ifndef UINTPTR_C +# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) +# endif + typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t; + typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t; +# else +/* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; +# endif +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +/* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + +#ifndef SIG_ATOMIC_MAX +# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) +#endif + +#endif + +#if defined (__TEST_PSTDINT_FOR_CORRECTNESS) + +/* + * Please compile with the maximum warning settings to make sure macros are + * not defined more than once. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define glue3_aux(x,y,z) x ## y ## z +#define glue3(x,y,z) glue3_aux(x,y,z) + +#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,) = glue3(UINT,bits,_C) (0); +#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,) = glue3(INT,bits,_C) (0); + +#define DECL(us,bits) glue3(DECL,us,) (bits) + +#define TESTUMAX(bits) glue3(u,bits,) = ~glue3(u,bits,); if (glue3(UINT,bits,_MAX) != glue3(u,bits,)) printf ("Something wrong with UINT%d_MAX\n", bits) + +#define REPORTERROR(msg) { err_n++; if (err_first <= 0) err_first = __LINE__; printf msg; } + +int main () { + int err_n = 0; + int err_first = 0; + DECL(I,8) + DECL(U,8) + DECL(I,16) + DECL(U,16) + DECL(I,32) + DECL(U,32) +#ifdef INT64_MAX + DECL(I,64) + DECL(U,64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%" PRINTF_INT32_MODIFIER "d", INT32_C(2147483647)); + if (0 != strcmp (str0, "2147483647")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_INT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_INT32_DEC_WIDTH : %s\n", PRINTF_INT32_DEC_WIDTH)); + sprintf (str0, "%" PRINTF_INT32_MODIFIER "u", UINT32_C(4294967295)); + if (0 != strcmp (str0, "4294967295")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_UINT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_UINT32_DEC_WIDTH : %s\n", PRINTF_UINT32_DEC_WIDTH)); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d", INT64_C(9223372036854775807)); + if (0 != strcmp (str1, "9223372036854775807")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_INT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_INT64_DEC_WIDTH : %s, %d\n", PRINTF_INT64_DEC_WIDTH, (int) strlen(str1))); + sprintf (str1, "%" PRINTF_INT64_MODIFIER "u", UINT64_C(18446744073709550591)); + if (0 != strcmp (str1, "18446744073709550591")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_UINT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_UINT64_DEC_WIDTH : %s, %d\n", PRINTF_UINT64_DEC_WIDTH, (int) strlen(str1))); +#endif + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i8 : %s\n", str1)); + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u8 : %s\n", str1)); + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i16 : %s\n", str1)); + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u16 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i32 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u32 : %s\n", str1)); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i64 : %s\n", str1)); +#endif + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with imax : %s\n", str1)); + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with umax : %s\n", str1)); + + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); +#ifdef INT64_MAX + TESTUMAX(64); +#endif + +#define STR(v) #v +#define Q(v) printf ("sizeof " STR(v) " = %u\n", (unsigned) sizeof (v)); + if (err_n) { + printf ("pstdint.h is not correct. Please use sizes below to correct it:\n"); + } + + Q(int) + Q(unsigned) + Q(long int) + Q(short int) + Q(int8_t) + Q(int16_t) + Q(int32_t) +#ifdef INT64_MAX + Q(int64_t) +#endif + + return EXIT_SUCCESS; +} + +#endif diff --git a/thirdparty/assimp/include/assimp/Compiler/pushpack1.h b/thirdparty/assimp/include/assimp/Compiler/pushpack1.h new file mode 100644 index 0000000000..4c9fbb857f --- /dev/null +++ b/thirdparty/assimp/include/assimp/Compiler/pushpack1.h @@ -0,0 +1,43 @@ + + +// =============================================================================== +// May be included multiple times - sets structure packing to 1 +// for all supported compilers. #include <poppack1.h> reverts the changes. +// +// Currently this works on the following compilers: +// MSVC 7,8,9 +// GCC +// BORLAND (complains about 'pack state changed but not reverted', but works) +// Clang +// +// +// USAGE: +// +// struct StructToBePacked { +// } PACK_STRUCT; +// +// =============================================================================== + +#ifdef AI_PUSHPACK_IS_DEFINED +# error poppack1.h must be included after pushpack1.h +#endif + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack(push,1) +# define PACK_STRUCT +#elif defined( __GNUC__ ) || defined(__clang__) +# if !defined(HOST_MINGW) +# define PACK_STRUCT __attribute__((__packed__)) +# else +# define PACK_STRUCT __attribute__((gcc_struct, __packed__)) +# endif +#else +# error Compiler not supported +#endif + +#if defined(_MSC_VER) +// C4103: Packing was changed after the inclusion of the header, probably missing #pragma pop +# pragma warning (disable : 4103) +#endif + +#define AI_PUSHPACK_IS_DEFINED diff --git a/thirdparty/assimp/include/assimp/CreateAnimMesh.h b/thirdparty/assimp/include/assimp/CreateAnimMesh.h new file mode 100644 index 0000000000..a60173588f --- /dev/null +++ b/thirdparty/assimp/include/assimp/CreateAnimMesh.h @@ -0,0 +1,58 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file CreateAnimMesh.h + * Create AnimMesh from Mesh + */ +#ifndef INCLUDED_AI_CREATE_ANIM_MESH_H +#define INCLUDED_AI_CREATE_ANIM_MESH_H + +#include <assimp/mesh.h> + +namespace Assimp { + +/** Create aiAnimMesh from aiMesh. */ +ASSIMP_API aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh); + +} // end of namespace Assimp +#endif // INCLUDED_AI_CREATE_ANIM_MESH_H + diff --git a/thirdparty/assimp/include/assimp/DefaultIOStream.h b/thirdparty/assimp/include/assimp/DefaultIOStream.h new file mode 100644 index 0000000000..994d728ff5 --- /dev/null +++ b/thirdparty/assimp/include/assimp/DefaultIOStream.h @@ -0,0 +1,140 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Default file I/O using fXXX()-family of functions */ +#ifndef AI_DEFAULTIOSTREAM_H_INC +#define AI_DEFAULTIOSTREAM_H_INC + +#include <stdio.h> +#include <assimp/IOStream.hpp> +#include <assimp/importerdesc.h> +#include <assimp/Defines.h> + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +//! @class DefaultIOStream +//! @brief Default IO implementation, use standard IO operations +//! @note An instance of this class can exist without a valid file handle +//! attached to it. All calls fail, but the instance can nevertheless be +//! used with no restrictions. +class ASSIMP_API DefaultIOStream : public IOStream +{ + friend class DefaultIOSystem; +#if __ANDROID__ +# if __ANDROID_API__ > 9 +# if defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT) + friend class AndroidJNIIOSystem; +# endif // defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT) +# endif // __ANDROID_API__ > 9 +#endif // __ANDROID__ + +protected: + DefaultIOStream() AI_NO_EXCEPT; + DefaultIOStream(FILE* pFile, const std::string &strFilename); + +public: + /** Destructor public to allow simple deletion to close the file. */ + ~DefaultIOStream (); + + // ------------------------------------------------------------------- + /// Read from stream + size_t Read(void* pvBuffer, + size_t pSize, + size_t pCount); + + + // ------------------------------------------------------------------- + /// Write to stream + size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount); + + // ------------------------------------------------------------------- + /// Seek specific position + aiReturn Seek(size_t pOffset, + aiOrigin pOrigin); + + // ------------------------------------------------------------------- + /// Get current seek position + size_t Tell() const; + + // ------------------------------------------------------------------- + /// Get size of file + size_t FileSize() const; + + // ------------------------------------------------------------------- + /// Flush file contents + void Flush(); + +private: + // File data-structure, using clib + FILE* mFile; + // Filename + std::string mFilename; + // Cached file size + mutable size_t mCachedSize; +}; + +// ---------------------------------------------------------------------------------- +inline +DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT +: mFile(nullptr) +, mFilename("") +, mCachedSize(SIZE_MAX) { + // empty +} + +// ---------------------------------------------------------------------------------- +inline +DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename) +: mFile(pFile) +, mFilename(strFilename) +, mCachedSize(SIZE_MAX) { + // empty +} +// ---------------------------------------------------------------------------------- + +} // ns assimp + +#endif //!!AI_DEFAULTIOSTREAM_H_INC + diff --git a/thirdparty/assimp/include/assimp/DefaultIOSystem.h b/thirdparty/assimp/include/assimp/DefaultIOSystem.h new file mode 100644 index 0000000000..2dd5c801b5 --- /dev/null +++ b/thirdparty/assimp/include/assimp/DefaultIOSystem.h @@ -0,0 +1,93 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Default implementation of IOSystem using the standard C file functions */ +#ifndef AI_DEFAULTIOSYSTEM_H_INC +#define AI_DEFAULTIOSYSTEM_H_INC + +#include <assimp/IOSystem.hpp> + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** Default implementation of IOSystem using the standard C file functions */ +class ASSIMP_API DefaultIOSystem : public IOSystem { +public: + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists( const char* pFile) const; + + // ------------------------------------------------------------------- + /** Returns the directory separator. */ + char getOsSeparator() const; + + // ------------------------------------------------------------------- + /** Open a new file with a given path. */ + IOStream* Open( const char* pFile, const char* pMode = "rb"); + + // ------------------------------------------------------------------- + /** Closes the given file and releases all resources associated with it. */ + void Close( IOStream* pFile); + + // ------------------------------------------------------------------- + /** Compare two paths */ + bool ComparePaths (const char* one, const char* second) const; + + /** @brief get the file name of a full filepath + * example: /tmp/archive.tar.gz -> archive.tar.gz + */ + static std::string fileName( const std::string &path ); + + /** @brief get the complete base name of a full filepath + * example: /tmp/archive.tar.gz -> archive.tar + */ + static std::string completeBaseName( const std::string &path); + + /** @brief get the path of a full filepath + * example: /tmp/archive.tar.gz -> /tmp/ + */ + static std::string absolutePath( const std::string &path); +}; + +} //!ns Assimp + +#endif //AI_DEFAULTIOSYSTEM_H_INC diff --git a/thirdparty/assimp/include/assimp/DefaultLogger.hpp b/thirdparty/assimp/include/assimp/DefaultLogger.hpp new file mode 100644 index 0000000000..1946e250a6 --- /dev/null +++ b/thirdparty/assimp/include/assimp/DefaultLogger.hpp @@ -0,0 +1,188 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/** @file DefaultLogger.hpp +*/ + +#ifndef INCLUDED_AI_DEFAULTLOGGER +#define INCLUDED_AI_DEFAULTLOGGER + +#include "Logger.hpp" +#include "LogStream.hpp" +#include "NullLogger.hpp" +#include <vector> + +namespace Assimp { +// ------------------------------------------------------------------------------------ +class IOStream; +struct LogStreamInfo; + +/** default name of logfile */ +#define ASSIMP_DEFAULT_LOG_NAME "AssimpLog.txt" + +// ------------------------------------------------------------------------------------ +/** @brief CPP-API: Primary logging facility of Assimp. + * + * The library stores its primary #Logger as a static member of this class. + * #get() returns this primary logger. By default the underlying implementation is + * just a #NullLogger which rejects all log messages. By calling #create(), logging + * is turned on. To capture the log output multiple log streams (#LogStream) can be + * attach to the logger. Some default streams for common streaming locations (such as + * a file, std::cout, OutputDebugString()) are also provided. + * + * If you wish to customize the logging at an even deeper level supply your own + * implementation of #Logger to #set(). + * @note The whole logging stuff causes a small extra overhead for all imports. */ +class ASSIMP_API DefaultLogger : + public Logger { + +public: + + // ---------------------------------------------------------------------- + /** @brief Creates a logging instance. + * @param name Name for log file. Only valid in combination + * with the aiDefaultLogStream_FILE flag. + * @param severity Log severity, VERBOSE turns on debug messages + * @param defStreams Default log streams to be attached. Any bitwise + * combination of the aiDefaultLogStream enumerated values. + * If #aiDefaultLogStream_FILE is specified but an empty string is + * passed for 'name', no log file is created at all. + * @param io IOSystem to be used to open external files (such as the + * log file). Pass NULL to rely on the default implementation. + * This replaces the default #NullLogger with a #DefaultLogger instance. */ + static Logger *create(const char* name = ASSIMP_DEFAULT_LOG_NAME, + LogSeverity severity = NORMAL, + unsigned int defStreams = aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE, + IOSystem* io = NULL); + + // ---------------------------------------------------------------------- + /** @brief Setup a custom #Logger implementation. + * + * Use this if the provided #DefaultLogger class doesn't fit into + * your needs. If the provided message formatting is OK for you, + * it's much easier to use #create() and to attach your own custom + * output streams to it. + * @param logger Pass NULL to setup a default NullLogger*/ + static void set (Logger *logger); + + // ---------------------------------------------------------------------- + /** @brief Getter for singleton instance + * @return Only instance. This is never null, but it could be a + * NullLogger. Use isNullLogger to check this.*/ + static Logger *get(); + + // ---------------------------------------------------------------------- + /** @brief Return whether a #NullLogger is currently active + * @return true if the current logger is a #NullLogger. + * Use create() or set() to setup a logger that does actually do + * something else than just rejecting all log messages. */ + static bool isNullLogger(); + + // ---------------------------------------------------------------------- + /** @brief Kills the current singleton logger and replaces it with a + * #NullLogger instance. */ + static void kill(); + + // ---------------------------------------------------------------------- + /** @copydoc Logger::attachStream */ + bool attachStream(LogStream *pStream, + unsigned int severity); + + // ---------------------------------------------------------------------- + /** @copydoc Logger::detatchStream */ + bool detatchStream(LogStream *pStream, + unsigned int severity); + +private: + // ---------------------------------------------------------------------- + /** @briefPrivate construction for internal use by create(). + * @param severity Logging granularity */ + explicit DefaultLogger(LogSeverity severity); + + // ---------------------------------------------------------------------- + /** @briefDestructor */ + ~DefaultLogger(); + + /** @brief Logs debug infos, only been written when severity level VERBOSE is set */ + void OnDebug(const char* message); + + /** @brief Logs an info message */ + void OnInfo(const char* message); + + /** @brief Logs a warning message */ + void OnWarn(const char* message); + + /** @brief Logs an error message */ + void OnError(const char* message); + + // ---------------------------------------------------------------------- + /** @brief Writes a message to all streams */ + void WriteToStreams(const char* message, ErrorSeverity ErrorSev ); + + // ---------------------------------------------------------------------- + /** @brief Returns the thread id. + * @note This is an OS specific feature, if not supported, a + * zero will be returned. + */ + unsigned int GetThreadID(); + +private: + // Aliases for stream container + typedef std::vector<LogStreamInfo*> StreamArray; + typedef std::vector<LogStreamInfo*>::iterator StreamIt; + typedef std::vector<LogStreamInfo*>::const_iterator ConstStreamIt; + + //! only logging instance + static Logger *m_pLogger; + static NullLogger s_pNullLogger; + + //! Attached streams + StreamArray m_StreamArray; + + bool noRepeatMsg; + char lastMsg[MAX_LOG_MESSAGE_LENGTH*2]; + size_t lastLen; +}; +// ------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif // !! INCLUDED_AI_DEFAULTLOGGER diff --git a/thirdparty/assimp/include/assimp/Defines.h b/thirdparty/assimp/include/assimp/Defines.h new file mode 100644 index 0000000000..15e1d83c26 --- /dev/null +++ b/thirdparty/assimp/include/assimp/Defines.h @@ -0,0 +1,49 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +// We need those constants, workaround for any platforms where nobody defined them yet +#if (!defined SIZE_MAX) +# define SIZE_MAX (~((size_t)0)) +#endif + +#if (!defined UINT_MAX) +# define UINT_MAX (~((unsigned int)0)) +#endif + diff --git a/thirdparty/assimp/include/assimp/Exceptional.h b/thirdparty/assimp/include/assimp/Exceptional.h new file mode 100644 index 0000000000..5109b8f077 --- /dev/null +++ b/thirdparty/assimp/include/assimp/Exceptional.h @@ -0,0 +1,125 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2008, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef INCLUDED_EXCEPTIONAL_H +#define INCLUDED_EXCEPTIONAL_H + +#include <stdexcept> +#include <assimp/DefaultIOStream.h> +using std::runtime_error; + +#ifdef _MSC_VER +# pragma warning(disable : 4275) +#endif + +// --------------------------------------------------------------------------- +/** FOR IMPORTER PLUGINS ONLY: Simple exception class to be thrown if an + * unrecoverable error occurs while importing. Loading APIs return + * NULL instead of a valid aiScene then. */ +class DeadlyImportError + : public runtime_error +{ +public: + /** Constructor with arguments */ + explicit DeadlyImportError( const std::string& errorText) + : runtime_error(errorText) + { + } + +private: +}; + +typedef DeadlyImportError DeadlyExportError; + +#ifdef _MSC_VER +# pragma warning(default : 4275) +#endif + +// --------------------------------------------------------------------------- +template <typename T> +struct ExceptionSwallower { + T operator ()() const { + return T(); + } +}; + +// --------------------------------------------------------------------------- +template <typename T> +struct ExceptionSwallower<T*> { + T* operator ()() const { + return NULL; + } +}; + +// --------------------------------------------------------------------------- +template <> +struct ExceptionSwallower<aiReturn> { + aiReturn operator ()() const { + try { + throw; + } + catch (std::bad_alloc&) { + return aiReturn_OUTOFMEMORY; + } + catch (...) { + return aiReturn_FAILURE; + } + } +}; + +// --------------------------------------------------------------------------- +template <> +struct ExceptionSwallower<void> { + void operator ()() const { + return; + } +}; + +#define ASSIMP_BEGIN_EXCEPTION_REGION()\ +{\ + try { + +#define ASSIMP_END_EXCEPTION_REGION(type)\ + } catch(...) {\ + return ExceptionSwallower<type>()();\ + }\ +} + +#endif // INCLUDED_EXCEPTIONAL_H diff --git a/thirdparty/assimp/include/assimp/Exporter.hpp b/thirdparty/assimp/include/assimp/Exporter.hpp new file mode 100644 index 0000000000..bf0096e7e9 --- /dev/null +++ b/thirdparty/assimp/include/assimp/Exporter.hpp @@ -0,0 +1,505 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Exporter.hpp +* @brief Defines the CPP-API for the Assimp export interface +*/ +#pragma once +#ifndef AI_EXPORT_HPP_INC +#define AI_EXPORT_HPP_INC + +#ifndef ASSIMP_BUILD_NO_EXPORT + +#include "cexport.h" +#include <map> + +namespace Assimp { + +class ExporterPimpl; +class IOSystem; +class ProgressHandler; + +// ---------------------------------------------------------------------------------- +/** CPP-API: The Exporter class forms an C++ interface to the export functionality + * of the Open Asset Import Library. Note that the export interface is available + * only if Assimp has been built with ASSIMP_BUILD_NO_EXPORT not defined. + * + * The interface is modeled after the importer interface and mostly + * symmetric. The same rules for threading etc. apply. + * + * In a nutshell, there are two export interfaces: #Export, which writes the + * output file(s) either to the regular file system or to a user-supplied + * #IOSystem, and #ExportToBlob which returns a linked list of memory + * buffers (blob), each referring to one output file (in most cases + * there will be only one output file of course, but this extra complexity is + * needed since Assimp aims at supporting a wide range of file formats). + * + * #ExportToBlob is especially useful if you intend to work + * with the data in-memory. +*/ +class ASSIMP_API ExportProperties; + +class ASSIMP_API Exporter { +public: + /** Function pointer type of a Export worker function */ + typedef void (*fpExportFunc)(const char*, IOSystem*, const aiScene*, const ExportProperties*); + + /** Internal description of an Assimp export format option */ + struct ExportFormatEntry { + /// Public description structure to be returned by aiGetExportFormatDescription() + aiExportFormatDesc mDescription; + + // Worker function to do the actual exporting + fpExportFunc mExportFunction; + + // Post-processing steps to be executed PRIOR to invoking mExportFunction + unsigned int mEnforcePP; + + // Constructor to fill all entries + ExportFormatEntry( const char* pId, const char* pDesc, const char* pExtension, fpExportFunc pFunction, unsigned int pEnforcePP = 0u) + { + mDescription.id = pId; + mDescription.description = pDesc; + mDescription.fileExtension = pExtension; + mExportFunction = pFunction; + mEnforcePP = pEnforcePP; + } + + ExportFormatEntry() : + mExportFunction() + , mEnforcePP() + { + mDescription.id = NULL; + mDescription.description = NULL; + mDescription.fileExtension = NULL; + } + }; + + /** + * @brief The class constructor. + */ + Exporter(); + + /** + * @brief The class destructor. + */ + ~Exporter(); + + // ------------------------------------------------------------------- + /** Supplies a custom IO handler to the exporter to use to open and + * access files. + * + * If you need #Export to use custom IO logic to access the files, + * you need to supply a custom implementation of IOSystem and + * IOFile to the exporter. + * + * #Exporter takes ownership of the object and will destroy it + * afterwards. The previously assigned handler will be deleted. + * Pass NULL to take again ownership of your IOSystem and reset Assimp + * to use its default implementation, which uses plain file IO. + * + * @param pIOHandler The IO handler to be used in all file accesses + * of the Importer. */ + void SetIOHandler( IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Retrieves the IO handler that is currently set. + * You can use #IsDefaultIOHandler() to check whether the returned + * interface is the default IO handler provided by ASSIMP. The default + * handler is active as long the application doesn't supply its own + * custom IO handler via #SetIOHandler(). + * @return A valid IOSystem interface, never NULL. */ + IOSystem* GetIOHandler() const; + + // ------------------------------------------------------------------- + /** Checks whether a default IO handler is active + * A default handler is active as long the application doesn't + * supply its own custom IO handler via #SetIOHandler(). + * @return true by default */ + bool IsDefaultIOHandler() const; + + // ------------------------------------------------------------------- + /** Supplies a custom progress handler to the exporter. This + * interface exposes an #Update() callback, which is called + * more or less periodically (please don't sue us if it + * isn't as periodically as you'd like it to have ...). + * This can be used to implement progress bars and loading + * timeouts. + * @param pHandler Progress callback interface. Pass nullptr to + * disable progress reporting. + * @note Progress handlers can be used to abort the loading + * at almost any time.*/ + void SetProgressHandler(ProgressHandler* pHandler); + + // ------------------------------------------------------------------- + /** Exports the given scene to a chosen file format. Returns the exported + * data as a binary blob which you can write into a file or something. + * When you're done with the data, simply let the #Exporter instance go + * out of scope to have it released automatically. + * @param pScene The scene to export. Stays in possession of the caller, + * is not changed by the function. + * @param pFormatId ID string to specify to which format you want to + * export to. Use + * #GetExportFormatCount / #GetExportFormatDescription to learn which + * export formats are available. + * @param pPreprocessing See the documentation for #Export + * @return the exported data or NULL in case of error. + * @note If the Exporter instance did already hold a blob from + * a previous call to #ExportToBlob, it will be disposed. + * Any IO handlers set via #SetIOHandler are ignored here. + * @note Use aiCopyScene() to get a modifiable copy of a previously + * imported scene. */ + const aiExportDataBlob* ExportToBlob(const aiScene* pScene, const char* pFormatId, + unsigned int pPreprocessing = 0u, const ExportProperties* = nullptr); + const aiExportDataBlob* ExportToBlob( const aiScene* pScene, const std::string& pFormatId, + unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); + + // ------------------------------------------------------------------- + /** Convenience function to export directly to a file. Use + * #SetIOSystem to supply a custom IOSystem to gain fine-grained control + * about the output data flow of the export process. + * @param pBlob A data blob obtained from a previous call to #aiExportScene. Must not be NULL. + * @param pPath Full target file name. Target must be accessible. + * @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated + * flags, but in reality only a subset of them makes sense here. Specifying + * 'preprocessing' flags is useful if the input scene does not conform to + * Assimp's default conventions as specified in the @link data Data Structures Page @endlink. + * In short, this means the geometry data should use a right-handed coordinate systems, face + * winding should be counter-clockwise and the UV coordinate origin is assumed to be in + * the upper left. The #aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and + * #aiProcess_FlipWindingOrder flags are used in the import side to allow users + * to have those defaults automatically adapted to their conventions. Specifying those flags + * for exporting has the opposite effect, respectively. Some other of the + * #aiPostProcessSteps enumerated values may be useful as well, but you'll need + * to try out what their effect on the exported file is. Many formats impose + * their own restrictions on the structure of the geometry stored therein, + * so some preprocessing may have little or no effect at all, or may be + * redundant as exporters would apply them anyhow. A good example + * is triangulation - whilst you can enforce it by specifying + * the #aiProcess_Triangulate flag, most export formats support only + * triangulate data so they would run the step even if it wasn't requested. + * + * If assimp detects that the input scene was directly taken from the importer side of + * the library (i.e. not copied using aiCopyScene and potentially modified afterwards), + * any post-processing steps already applied to the scene will not be applied again, unless + * they show non-idempotent behavior (#aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and + * #aiProcess_FlipWindingOrder). + * @return AI_SUCCESS if everything was fine. + * @note Use aiCopyScene() to get a modifiable copy of a previously + * imported scene.*/ + aiReturn Export( const aiScene* pScene, const char* pFormatId, const char* pPath, + unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); + aiReturn Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath, + unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); + + // ------------------------------------------------------------------- + /** Returns an error description of an error that occurred in #Export + * or #ExportToBlob + * + * Returns an empty string if no error occurred. + * @return A description of the last error, an empty string if no + * error occurred. The string is never NULL. + * + * @note The returned function remains valid until one of the + * following methods is called: #Export, #ExportToBlob, #FreeBlob */ + const char* GetErrorString() const; + + // ------------------------------------------------------------------- + /** Return the blob obtained from the last call to #ExportToBlob */ + const aiExportDataBlob* GetBlob() const; + + // ------------------------------------------------------------------- + /** Orphan the blob from the last call to #ExportToBlob. This means + * the caller takes ownership and is thus responsible for calling + * the C API function #aiReleaseExportBlob to release it. */ + const aiExportDataBlob* GetOrphanedBlob() const; + + // ------------------------------------------------------------------- + /** Frees the current blob. + * + * The function does nothing if no blob has previously been + * previously produced via #ExportToBlob. #FreeBlob is called + * automatically by the destructor. The only reason to call + * it manually would be to reclaim as much storage as possible + * without giving up the #Exporter instance yet. */ + void FreeBlob( ); + + // ------------------------------------------------------------------- + /** Returns the number of export file formats available in the current + * Assimp build. Use #Exporter::GetExportFormatDescription to + * retrieve infos of a specific export format. + * + * This includes built-in exporters as well as exporters registered + * using #RegisterExporter. + **/ + size_t GetExportFormatCount() const; + + // ------------------------------------------------------------------- + /** Returns a description of the nth export file format. Use # + * #Exporter::GetExportFormatCount to learn how many export + * formats are supported. + * + * The returned pointer is of static storage duration if the + * pIndex pertains to a built-in exporter (i.e. one not registered + * via #RegistrerExporter). It is restricted to the life-time of the + * #Exporter instance otherwise. + * + * @param pIndex Index of the export format to retrieve information + * for. Valid range is 0 to #Exporter::GetExportFormatCount + * @return A description of that specific export format. + * NULL if pIndex is out of range. */ + const aiExportFormatDesc* GetExportFormatDescription( size_t pIndex ) const; + + // ------------------------------------------------------------------- + /** Register a custom exporter. Custom export formats are limited to + * to the current #Exporter instance and do not affect the + * library globally. The indexes under which the format's + * export format description can be queried are assigned + * monotonously. + * @param desc Exporter description. + * @return aiReturn_SUCCESS if the export format was successfully + * registered. A common cause that would prevent an exporter + * from being registered is that its format id is already + * occupied by another format. */ + aiReturn RegisterExporter(const ExportFormatEntry& desc); + + // ------------------------------------------------------------------- + /** Remove an export format previously registered with #RegisterExporter + * from the #Exporter instance (this can also be used to drop + * built-in exporters because those are implicitly registered + * using #RegisterExporter). + * @param id Format id to be unregistered, this refers to the + * 'id' field of #aiExportFormatDesc. + * @note Calling this method on a format description not yet registered + * has no effect.*/ + void UnregisterExporter(const char* id); + +protected: + // Just because we don't want you to know how we're hacking around. + ExporterPimpl* pimpl; +}; + +class ASSIMP_API ExportProperties { +public: + // Data type to store the key hash + typedef unsigned int KeyType; + + // typedefs for our four configuration maps. + // We don't need more, so there is no need for a generic solution + typedef std::map<KeyType, int> IntPropertyMap; + typedef std::map<KeyType, ai_real> FloatPropertyMap; + typedef std::map<KeyType, std::string> StringPropertyMap; + typedef std::map<KeyType, aiMatrix4x4> MatrixPropertyMap; + +public: + /** Standard constructor + * @see ExportProperties() + */ + ExportProperties(); + + // ------------------------------------------------------------------- + /** Copy constructor. + * + * This copies the configuration properties of another ExportProperties. + * @see ExportProperties(const ExportProperties& other) + */ + ExportProperties(const ExportProperties& other); + + // ------------------------------------------------------------------- + /** Set an integer configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX and are simple strings). + * @param iValue New value of the property + * @return true if the property was set before. The new value replaces + * the previous value in this case. + * @note Property of different types (float, int, string ..) are kept + * on different stacks, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + bool SetPropertyInteger(const char* szName, int iValue); + + // ------------------------------------------------------------------- + /** Set a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see SetPropertyInteger() + */ + bool SetPropertyBool(const char* szName, bool value) { + return SetPropertyInteger(szName,value); + } + + // ------------------------------------------------------------------- + /** Set a floating-point configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyFloat(const char* szName, ai_real fValue); + + // ------------------------------------------------------------------- + /** Set a string configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyString(const char* szName, const std::string& sValue); + + // ------------------------------------------------------------------- + /** Set a matrix configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyMatrix(const char* szName, const aiMatrix4x4& sValue); + + // ------------------------------------------------------------------- + /** Get a configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX). + * @param iErrorReturn Value that is returned if the property + * is not found. + * @return Current value of the property + * @note Property of different types (float, int, string ..) are kept + * on different lists, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + int GetPropertyInteger(const char* szName, + int iErrorReturn = 0xffffffff) const; + + // ------------------------------------------------------------------- + /** Get a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see GetPropertyInteger() + */ + bool GetPropertyBool(const char* szName, bool bErrorReturn = false) const { + return GetPropertyInteger(szName,bErrorReturn)!=0; + } + + // ------------------------------------------------------------------- + /** Get a floating-point configuration property + * @see GetPropertyInteger() + */ + ai_real GetPropertyFloat(const char* szName, + ai_real fErrorReturn = 10e10f) const; + + // ------------------------------------------------------------------- + /** Get a string configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const std::string GetPropertyString(const char* szName, + const std::string& sErrorReturn = "") const; + + // ------------------------------------------------------------------- + /** Get a matrix configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const aiMatrix4x4 GetPropertyMatrix(const char* szName, + const aiMatrix4x4& sErrorReturn = aiMatrix4x4()) const; + + // ------------------------------------------------------------------- + /** Determine a integer configuration property has been set. + * @see HasPropertyInteger() + */ + bool HasPropertyInteger(const char* szName) const; + + /** Determine a boolean configuration property has been set. + * @see HasPropertyBool() + */ + bool HasPropertyBool(const char* szName) const; + + /** Determine a boolean configuration property has been set. + * @see HasPropertyFloat() + */ + bool HasPropertyFloat(const char* szName) const; + + /** Determine a String configuration property has been set. + * @see HasPropertyString() + */ + bool HasPropertyString(const char* szName) const; + + /** Determine a Matrix configuration property has been set. + * @see HasPropertyMatrix() + */ + bool HasPropertyMatrix(const char* szName) const; + +protected: + + /** List of integer properties */ + IntPropertyMap mIntProperties; + + /** List of floating-point properties */ + FloatPropertyMap mFloatProperties; + + /** List of string properties */ + StringPropertyMap mStringProperties; + + /** List of Matrix properties */ + MatrixPropertyMap mMatrixProperties; +}; + +// ---------------------------------------------------------------------------------- +inline +const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const std::string& pFormatId, + unsigned int pPreprocessing, const ExportProperties* pProperties) +{ + return ExportToBlob(pScene,pFormatId.c_str(),pPreprocessing, pProperties); +} + +// ---------------------------------------------------------------------------------- +inline +aiReturn Exporter :: Export( const aiScene* pScene, const std::string& pFormatId, + const std::string& pPath, unsigned int pPreprocessing, + const ExportProperties* pProperties) +{ + return Export(pScene,pFormatId.c_str(),pPath.c_str(),pPreprocessing, pProperties); +} + +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_EXPORT +#endif // AI_EXPORT_HPP_INC diff --git a/thirdparty/assimp/include/assimp/GenericProperty.h b/thirdparty/assimp/include/assimp/GenericProperty.h new file mode 100644 index 0000000000..183ecd5197 --- /dev/null +++ b/thirdparty/assimp/include/assimp/GenericProperty.h @@ -0,0 +1,133 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_GENERIC_PROPERTY_H_INCLUDED +#define AI_GENERIC_PROPERTY_H_INCLUDED + +#include <assimp/Importer.hpp> +#include <assimp/ai_assert.h> +#include "Hash.h" + +#include <map> + +// ------------------------------------------------------------------------------------------------ +template <class T> +inline +bool SetGenericProperty(std::map< unsigned int, T >& list, + const char* szName, const T& value) { + ai_assert(nullptr != szName); + const uint32_t hash = SuperFastHash(szName); + + typename std::map<unsigned int, T>::iterator it = list.find(hash); + if (it == list.end()) { + list.insert(std::pair<unsigned int, T>( hash, value )); + return false; + } + (*it).second = value; + + return true; +} + +// ------------------------------------------------------------------------------------------------ +template <class T> +inline +const T& GetGenericProperty(const std::map< unsigned int, T >& list, + const char* szName, const T& errorReturn) { + ai_assert(nullptr != szName); + const uint32_t hash = SuperFastHash(szName); + + typename std::map<unsigned int, T>::const_iterator it = list.find(hash); + if (it == list.end()) { + return errorReturn; + } + + return (*it).second; +} + +// ------------------------------------------------------------------------------------------------ +// Special version for pointer types - they will be deleted when replaced with another value +// passing NULL removes the whole property +template <class T> +inline +void SetGenericPropertyPtr(std::map< unsigned int, T* >& list, + const char* szName, T* value, bool* bWasExisting = nullptr ) { + ai_assert(nullptr != szName); + const uint32_t hash = SuperFastHash(szName); + + typename std::map<unsigned int, T*>::iterator it = list.find(hash); + if (it == list.end()) { + if (bWasExisting) { + *bWasExisting = false; + } + + list.insert(std::pair<unsigned int,T*>( hash, value )); + return; + } + if ((*it).second != value) { + delete (*it).second; + (*it).second = value; + } + if (!value) { + list.erase(it); + } + if (bWasExisting) { + *bWasExisting = true; + } +} + +// ------------------------------------------------------------------------------------------------ +template <class T> +inline +bool HasGenericProperty(const std::map< unsigned int, T >& list, + const char* szName) { + ai_assert(nullptr != szName); + const uint32_t hash = SuperFastHash(szName); + + typename std::map<unsigned int, T>::const_iterator it = list.find(hash); + if (it == list.end()) { + return false; + } + + return true; +} + +#endif // !! AI_GENERIC_PROPERTY_H_INCLUDED diff --git a/thirdparty/assimp/include/assimp/Hash.h b/thirdparty/assimp/include/assimp/Hash.h new file mode 100644 index 0000000000..30657be198 --- /dev/null +++ b/thirdparty/assimp/include/assimp/Hash.h @@ -0,0 +1,118 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_HASH_H_INCLUDED +#define AI_HASH_H_INCLUDED + +#include <stdint.h> +#include <string.h> + +// ------------------------------------------------------------------------------------------------ +// Hashing function taken from +// http://www.azillionmonkeys.com/qed/hash.html +// (incremental version) +// +// This code is Copyright 2004-2008 by Paul Hsieh. It is used here in the belief that +// Assimp's license is considered compatible with Pauls's derivative license as specified +// on his web page. +// +// (stdint.h should have been been included here) +// ------------------------------------------------------------------------------------------------ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif + +// ------------------------------------------------------------------------------------------------ +inline uint32_t SuperFastHash (const char * data, uint32_t len = 0, uint32_t hash = 0) { +uint32_t tmp; +int rem; + + if (!data) return 0; + if (!len)len = (uint32_t)::strlen(data); + + rem = len & 3; + len >>= 2; + + /* Main loop */ + for (;len > 0; len--) { + hash += get16bits (data); + tmp = (get16bits (data+2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + data += 2*sizeof (uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) { + case 3: hash += get16bits (data); + hash ^= hash << 16; + hash ^= data[sizeof (uint16_t)] << 18; + hash += hash >> 11; + break; + case 2: hash += get16bits (data); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += *data; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} + +#endif // !! AI_HASH_H_INCLUDED diff --git a/thirdparty/assimp/include/assimp/IOStream.hpp b/thirdparty/assimp/include/assimp/IOStream.hpp new file mode 100644 index 0000000000..0623d0f70b --- /dev/null +++ b/thirdparty/assimp/include/assimp/IOStream.hpp @@ -0,0 +1,142 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file IOStream.hpp + * @brief File I/O wrappers for C++. + */ + +#pragma once +#ifndef AI_IOSTREAM_H_INC +#define AI_IOSTREAM_H_INC + +#include "types.h" + +#ifndef __cplusplus +# error This header requires C++ to be used. aiFileIO.h is the \ + corresponding C interface. +#endif + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** @brief CPP-API: Class to handle file I/O for C++ + * + * Derive an own implementation from this interface to provide custom IO handling + * to the Importer. If you implement this interface, be sure to also provide an + * implementation for IOSystem that creates instances of your custom IO class. +*/ +class ASSIMP_API IOStream +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +protected: + /** Constructor protected, use IOSystem::Open() to create an instance. */ + IOStream() AI_NO_EXCEPT; + +public: + // ------------------------------------------------------------------- + /** @brief Destructor. Deleting the object closes the underlying file, + * alternatively you may use IOSystem::Close() to release the file. + */ + virtual ~IOStream(); + + // ------------------------------------------------------------------- + /** @brief Read from the file + * + * See fread() for more details + * This fails for write-only files */ + virtual size_t Read(void* pvBuffer, + size_t pSize, + size_t pCount) = 0; + + // ------------------------------------------------------------------- + /** @brief Write to the file + * + * See fwrite() for more details + * This fails for read-only files */ + virtual size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount) = 0; + + // ------------------------------------------------------------------- + /** @brief Set the read/write cursor of the file + * + * Note that the offset is _negative_ for aiOrigin_END. + * See fseek() for more details */ + virtual aiReturn Seek(size_t pOffset, + aiOrigin pOrigin) = 0; + + // ------------------------------------------------------------------- + /** @brief Get the current position of the read/write cursor + * + * See ftell() for more details */ + virtual size_t Tell() const = 0; + + // ------------------------------------------------------------------- + /** @brief Returns filesize + * Returns the filesize. */ + virtual size_t FileSize() const = 0; + + // ------------------------------------------------------------------- + /** @brief Flush the contents of the file buffer (for writers) + * See fflush() for more details. + */ + virtual void Flush() = 0; +}; //! class IOStream + +// ---------------------------------------------------------------------------------- +inline +IOStream::IOStream() AI_NO_EXCEPT { + // empty +} + +// ---------------------------------------------------------------------------------- +inline +IOStream::~IOStream() { + // empty +} +// ---------------------------------------------------------------------------------- + +} //!namespace Assimp + +#endif //!!AI_IOSTREAM_H_INC diff --git a/thirdparty/assimp/include/assimp/IOStreamBuffer.h b/thirdparty/assimp/include/assimp/IOStreamBuffer.h new file mode 100644 index 0000000000..58abd97a02 --- /dev/null +++ b/thirdparty/assimp/include/assimp/IOStreamBuffer.h @@ -0,0 +1,355 @@ +#pragma once + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include <assimp/types.h> +#include <assimp/IOStream.hpp> + +#include "ParsingUtils.h" + +#include <vector> + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** + * Implementation of a cached stream buffer. + */ +template<class T> +class IOStreamBuffer { +public: + /// @brief The class constructor. + IOStreamBuffer( size_t cache = 4096 * 4096 ); + + /// @brief The class destructor. + ~IOStreamBuffer(); + + /// @brief Will open the cached access for a given stream. + /// @param stream The stream to cache. + /// @return true if successful. + bool open( IOStream *stream ); + + /// @brief Will close the cached access. + /// @return true if successful. + bool close(); + + /// @brief Returns the file-size. + /// @return The file-size. + size_t size() const; + + /// @brief Returns the cache size. + /// @return The cache size. + size_t cacheSize() const; + + /// @brief Will read the next block. + /// @return true if successful. + bool readNextBlock(); + + /// @brief Returns the number of blocks to read. + /// @return The number of blocks. + size_t getNumBlocks() const; + + /// @brief Returns the current block index. + /// @return The current block index. + size_t getCurrentBlockIndex() const; + + /// @brief Returns the current file pos. + /// @return The current file pos. + size_t getFilePos() const; + + /// @brief Will read the next line. + /// @param buffer The buffer for the next line. + /// @return true if successful. + bool getNextDataLine( std::vector<T> &buffer, T continuationToken ); + + /// @brief Will read the next line ascii or binary end line char. + /// @param buffer The buffer for the next line. + /// @return true if successful. + bool getNextLine(std::vector<T> &buffer); + + /// @brief Will read the next block. + /// @param buffer The buffer for the next block. + /// @return true if successful. + bool getNextBlock( std::vector<T> &buffer ); + +private: + IOStream *m_stream; + size_t m_filesize; + size_t m_cacheSize; + size_t m_numBlocks; + size_t m_blockIdx; + std::vector<T> m_cache; + size_t m_cachePos; + size_t m_filePos; +}; + +template<class T> +inline +IOStreamBuffer<T>::IOStreamBuffer( size_t cache ) +: m_stream( nullptr ) +, m_filesize( 0 ) +, m_cacheSize( cache ) +, m_numBlocks( 0 ) +, m_blockIdx( 0 ) +, m_cachePos( 0 ) +, m_filePos( 0 ) { + m_cache.resize( cache ); + std::fill( m_cache.begin(), m_cache.end(), '\n' ); +} + +template<class T> +inline +IOStreamBuffer<T>::~IOStreamBuffer() { + // empty +} + +template<class T> +inline +bool IOStreamBuffer<T>::open( IOStream *stream ) { + // file still opened! + if ( nullptr != m_stream ) { + return false; + } + + // Invalid stream pointer + if ( nullptr == stream ) { + return false; + } + + m_stream = stream; + m_filesize = m_stream->FileSize(); + if ( m_filesize == 0 ) { + return false; + } + if ( m_filesize < m_cacheSize ) { + m_cacheSize = m_filesize; + } + + m_numBlocks = m_filesize / m_cacheSize; + if ( ( m_filesize % m_cacheSize ) > 0 ) { + m_numBlocks++; + } + + return true; +} + +template<class T> +inline +bool IOStreamBuffer<T>::close() { + if ( nullptr == m_stream ) { + return false; + } + + // init counters and state vars + m_stream = nullptr; + m_filesize = 0; + m_numBlocks = 0; + m_blockIdx = 0; + m_cachePos = 0; + m_filePos = 0; + + return true; +} + +template<class T> +inline +size_t IOStreamBuffer<T>::size() const { + return m_filesize; +} + +template<class T> +inline +size_t IOStreamBuffer<T>::cacheSize() const { + return m_cacheSize; +} + +template<class T> +inline +bool IOStreamBuffer<T>::readNextBlock() { + m_stream->Seek( m_filePos, aiOrigin_SET ); + size_t readLen = m_stream->Read( &m_cache[ 0 ], sizeof( T ), m_cacheSize ); + if ( readLen == 0 ) { + return false; + } + if ( readLen < m_cacheSize ) { + m_cacheSize = readLen; + } + m_filePos += m_cacheSize; + m_cachePos = 0; + m_blockIdx++; + + return true; +} + +template<class T> +inline +size_t IOStreamBuffer<T>::getNumBlocks() const { + return m_numBlocks; +} + +template<class T> +inline +size_t IOStreamBuffer<T>::getCurrentBlockIndex() const { + return m_blockIdx; +} + +template<class T> +inline +size_t IOStreamBuffer<T>::getFilePos() const { + return m_filePos; +} + +template<class T> +inline +bool IOStreamBuffer<T>::getNextDataLine( std::vector<T> &buffer, T continuationToken ) { + buffer.resize( m_cacheSize ); + if ( m_cachePos >= m_cacheSize || 0 == m_filePos ) { + if ( !readNextBlock() ) { + return false; + } + } + + bool continuationFound( false ); + size_t i = 0; + for( ;; ) { + if ( continuationToken == m_cache[ m_cachePos ] ) { + continuationFound = true; + ++m_cachePos; + } + if ( IsLineEnd( m_cache[ m_cachePos ] ) ) { + if ( !continuationFound ) { + // the end of the data line + break; + } else { + // skip line end + while ( m_cache[m_cachePos] != '\n') { + ++m_cachePos; + } + ++m_cachePos; + continuationFound = false; + } + } + + buffer[ i ] = m_cache[ m_cachePos ]; + ++m_cachePos; + ++i; + if (m_cachePos >= size()) { + break; + } + if ( m_cachePos >= m_cacheSize ) { + if ( !readNextBlock() ) { + return false; + } + } + } + + buffer[ i ] = '\n'; + ++m_cachePos; + + return true; +} + +static inline +bool isEndOfCache( size_t pos, size_t cacheSize ) { + return ( pos == cacheSize ); +} + +template<class T> +inline +bool IOStreamBuffer<T>::getNextLine(std::vector<T> &buffer) { + buffer.resize(m_cacheSize); + if ( isEndOfCache( m_cachePos, m_cacheSize ) || 0 == m_filePos) { + if (!readNextBlock()) { + return false; + } + } + + if (IsLineEnd(m_cache[m_cachePos])) { + // skip line end + while (m_cache[m_cachePos] != '\n') { + ++m_cachePos; + } + ++m_cachePos; + if ( isEndOfCache( m_cachePos, m_cacheSize ) ) { + if ( !readNextBlock() ) { + return false; + } + } + } + + size_t i( 0 ); + while (!IsLineEnd(m_cache[ m_cachePos ])) { + buffer[i] = m_cache[ m_cachePos ]; + ++m_cachePos; + ++i; + if (m_cachePos >= m_cacheSize) { + if (!readNextBlock()) { + return false; + } + } + } + buffer[i] = '\n'; + ++m_cachePos; + + return true; +} + +template<class T> +inline +bool IOStreamBuffer<T>::getNextBlock( std::vector<T> &buffer) { + // Return the last block-value if getNextLine was used before + if ( 0 != m_cachePos ) { + buffer = std::vector<T>( m_cache.begin() + m_cachePos, m_cache.end() ); + m_cachePos = 0; + } else { + if ( !readNextBlock() ) { + return false; + } + + buffer = std::vector<T>(m_cache.begin(), m_cache.end()); + } + + return true; +} + +} // !ns Assimp diff --git a/thirdparty/assimp/include/assimp/IOSystem.hpp b/thirdparty/assimp/include/assimp/IOSystem.hpp new file mode 100644 index 0000000000..78139c2839 --- /dev/null +++ b/thirdparty/assimp/include/assimp/IOSystem.hpp @@ -0,0 +1,357 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file IOSystem.hpp + * @brief File system wrapper for C++. Inherit this class to supply + * custom file handling logic to the Import library. +*/ + +#pragma once +#ifndef AI_IOSYSTEM_H_INC +#define AI_IOSYSTEM_H_INC + +#ifndef __cplusplus +# error This header requires C++ to be used. aiFileIO.h is the \ + corresponding C interface. +#endif + +#include "types.h" + +#ifdef _WIN32 +# include <direct.h> +# include <stdlib.h> +# include <stdio.h> +#else +# include <sys/stat.h> +# include <sys/types.h> +# include <unistd.h> +#endif // _WIN32 + +#include <vector> + +namespace Assimp { + + class IOStream; + +// --------------------------------------------------------------------------- +/** @brief CPP-API: Interface to the file system. + * + * Derive an own implementation from this interface to supply custom file handling + * to the importer library. If you implement this interface, you also want to + * supply a custom implementation for IOStream. + * + * @see Importer::SetIOHandler() + */ +class ASSIMP_API IOSystem +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +public: + + // ------------------------------------------------------------------- + /** @brief Default constructor. + * + * Create an instance of your derived class and assign it to an + * #Assimp::Importer instance by calling Importer::SetIOHandler(). + */ + IOSystem() AI_NO_EXCEPT; + + // ------------------------------------------------------------------- + /** @brief Virtual destructor. + * + * It is safe to be called from within DLL Assimp, we're constructed + * on Assimp's heap. + */ + virtual ~IOSystem(); + + // ------------------------------------------------------------------- + /** @brief For backward compatibility + * @see Exists(const char*) + */ + AI_FORCE_INLINE bool Exists( const std::string& pFile) const; + + // ------------------------------------------------------------------- + /** @brief Tests for the existence of a file at the given path. + * + * @param pFile Path to the file + * @return true if there is a file with this path, else false. + */ + virtual bool Exists( const char* pFile) const = 0; + + // ------------------------------------------------------------------- + /** @brief Returns the system specific directory separator + * @return System specific directory separator + */ + virtual char getOsSeparator() const = 0; + + // ------------------------------------------------------------------- + /** @brief Open a new file with a given path. + * + * When the access to the file is finished, call Close() to release + * all associated resources (or the virtual dtor of the IOStream). + * + * @param pFile Path to the file + * @param pMode Desired file I/O mode. Required are: "wb", "w", "wt", + * "rb", "r", "rt". + * + * @return New IOStream interface allowing the lib to access + * the underlying file. + * @note When implementing this class to provide custom IO handling, + * you probably have to supply an own implementation of IOStream as well. + */ + virtual IOStream* Open(const char* pFile, + const char* pMode = "rb") = 0; + + // ------------------------------------------------------------------- + /** @brief For backward compatibility + * @see Open(const char*, const char*) + */ + inline IOStream* Open(const std::string& pFile, + const std::string& pMode = std::string("rb")); + + // ------------------------------------------------------------------- + /** @brief Closes the given file and releases all resources + * associated with it. + * @param pFile The file instance previously created by Open(). + */ + virtual void Close( IOStream* pFile) = 0; + + // ------------------------------------------------------------------- + /** @brief Compares two paths and check whether the point to + * identical files. + * + * The dummy implementation of this virtual member performs a + * case-insensitive comparison of the given strings. The default IO + * system implementation uses OS mechanisms to convert relative into + * absolute paths, so the result can be trusted. + * @param one First file + * @param second Second file + * @return true if the paths point to the same file. The file needn't + * be existing, however. + */ + virtual bool ComparePaths (const char* one, + const char* second) const; + + // ------------------------------------------------------------------- + /** @brief For backward compatibility + * @see ComparePaths(const char*, const char*) + */ + inline bool ComparePaths (const std::string& one, + const std::string& second) const; + + // ------------------------------------------------------------------- + /** @brief Pushes a new directory onto the directory stack. + * @param path Path to push onto the stack. + * @return True, when push was successful, false if path is empty. + */ + virtual bool PushDirectory( const std::string &path ); + + // ------------------------------------------------------------------- + /** @brief Returns the top directory from the stack. + * @return The directory on the top of the stack. + * Returns empty when no directory was pushed to the stack. + */ + virtual const std::string &CurrentDirectory() const; + + // ------------------------------------------------------------------- + /** @brief Returns the number of directories stored on the stack. + * @return The number of directories of the stack. + */ + virtual size_t StackSize() const; + + // ------------------------------------------------------------------- + /** @brief Pops the top directory from the stack. + * @return True, when a directory was on the stack. False if no + * directory was on the stack. + */ + virtual bool PopDirectory(); + + // ------------------------------------------------------------------- + /** @brief CReates an new directory at the given path. + * @param path [in] The path to create. + * @return True, when a directory was created. False if the directory + * cannot be created. + */ + virtual bool CreateDirectory( const std::string &path ); + + // ------------------------------------------------------------------- + /** @brief Will change the current directory to the given path. + * @param path [in] The path to change to. + * @return True, when the directory has changed successfully. + */ + virtual bool ChangeDirectory( const std::string &path ); + + virtual bool DeleteFile( const std::string &file ); + +private: + std::vector<std::string> m_pathStack; +}; + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +IOSystem::IOSystem() AI_NO_EXCEPT +: m_pathStack() { + // empty +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +IOSystem::~IOSystem() { + // empty +} + +// ---------------------------------------------------------------------------- +// For compatibility, the interface of some functions taking a std::string was +// changed to const char* to avoid crashes between binary incompatible STL +// versions. This code her is inlined, so it shouldn't cause any problems. +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +IOStream* IOSystem::Open(const std::string& pFile, const std::string& pMode) { + // NOTE: + // For compatibility, interface was changed to const char* to + // avoid crashes between binary incompatible STL versions + return Open(pFile.c_str(),pMode.c_str()); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::Exists( const std::string& pFile) const { + // NOTE: + // For compatibility, interface was changed to const char* to + // avoid crashes between binary incompatible STL versions + return Exists(pFile.c_str()); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::ComparePaths (const std::string& one, const std::string& second) const { + // NOTE: + // For compatibility, interface was changed to const char* to + // avoid crashes between binary incompatible STL versions + return ComparePaths(one.c_str(),second.c_str()); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::PushDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + + m_pathStack.push_back( path ); + + return true; +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +const std::string &IOSystem::CurrentDirectory() const { + if ( m_pathStack.empty() ) { + static const std::string Dummy(""); + return Dummy; + } + return m_pathStack[ m_pathStack.size()-1 ]; +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +size_t IOSystem::StackSize() const { + return m_pathStack.size(); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::PopDirectory() { + if ( m_pathStack.empty() ) { + return false; + } + + m_pathStack.pop_back(); + + return true; +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::CreateDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + +#ifdef _WIN32 + return 0 != ::_mkdir( path.c_str() ); +#else + return 0 != ::mkdir( path.c_str(), 0777 ); +#endif // _WIN32 +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::ChangeDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + +#ifdef _WIN32 + return 0 != ::_chdir( path.c_str() ); +#else + return 0 != ::chdir( path.c_str() ); +#endif // _WIN32 +} + + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::DeleteFile( const std::string &file ) { + if ( file.empty() ) { + return false; + } + const int retCode( ::remove( file.c_str() ) ); + return ( 0 == retCode ); +} +} //!ns Assimp + +#endif //AI_IOSYSTEM_H_INC diff --git a/thirdparty/assimp/include/assimp/Importer.hpp b/thirdparty/assimp/include/assimp/Importer.hpp new file mode 100644 index 0000000000..4941df4122 --- /dev/null +++ b/thirdparty/assimp/include/assimp/Importer.hpp @@ -0,0 +1,659 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Importer.hpp + * @brief Defines the C++-API to the Open Asset Import Library. + */ +#pragma once +#ifndef AI_ASSIMP_HPP_INC +#define AI_ASSIMP_HPP_INC + +#ifndef __cplusplus +# error This header requires C++ to be used. Use assimp.h for plain C. +#endif // __cplusplus + +// Public ASSIMP data structures +#include <assimp/types.h> + +namespace Assimp { + // ======================================================================= + // Public interface to Assimp + class Importer; + class IOStream; + class IOSystem; + class ProgressHandler; + + // ======================================================================= + // Plugin development + // + // Include the following headers for the declarations: + // BaseImporter.h + // BaseProcess.h + class BaseImporter; + class BaseProcess; + class SharedPostProcessInfo; + class BatchLoader; + + // ======================================================================= + // Holy stuff, only for members of the high council of the Jedi. + class ImporterPimpl; +} //! namespace Assimp + +#define AI_PROPERTY_WAS_NOT_EXISTING 0xffffffff + +struct aiScene; + +// importerdesc.h +struct aiImporterDesc; + +/** @namespace Assimp Assimp's CPP-API and all internal APIs */ +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** CPP-API: The Importer class forms an C++ interface to the functionality of the +* Open Asset Import Library. +* +* Create an object of this class and call ReadFile() to import a file. +* If the import succeeds, the function returns a pointer to the imported data. +* The data remains property of the object, it is intended to be accessed +* read-only. The imported data will be destroyed along with the Importer +* object. If the import fails, ReadFile() returns a NULL pointer. In this +* case you can retrieve a human-readable error description be calling +* GetErrorString(). You can call ReadFile() multiple times with a single Importer +* instance. Actually, constructing Importer objects involves quite many +* allocations and may take some time, so it's better to reuse them as often as +* possible. +* +* If you need the Importer to do custom file handling to access the files, +* implement IOSystem and IOStream and supply an instance of your custom +* IOSystem implementation by calling SetIOHandler() before calling ReadFile(). +* If you do not assign a custion IO handler, a default handler using the +* standard C++ IO logic will be used. +* +* @note One Importer instance is not thread-safe. If you use multiple +* threads for loading, each thread should maintain its own Importer instance. +*/ +class ASSIMP_API Importer { +public: + /** + * @brief The upper limit for hints. + */ + static const unsigned int MaxLenHint = 200; + +public: + + // ------------------------------------------------------------------- + /** Constructor. Creates an empty importer object. + * + * Call ReadFile() to start the import process. The configuration + * property table is initially empty. + */ + Importer(); + + // ------------------------------------------------------------------- + /** Copy constructor. + * + * This copies the configuration properties of another Importer. + * If this Importer owns a scene it won't be copied. + * Call ReadFile() to start the import process. + */ + Importer(const Importer& other)=delete; + + // ------------------------------------------------------------------- + /** Assignment operator has been deleted + */ + Importer &operator=(const Importer &) = delete; + + // ------------------------------------------------------------------- + /** Destructor. The object kept ownership of the imported data, + * which now will be destroyed along with the object. + */ + ~Importer(); + + + // ------------------------------------------------------------------- + /** Registers a new loader. + * + * @param pImp Importer to be added. The Importer instance takes + * ownership of the pointer, so it will be automatically deleted + * with the Importer instance. + * @return AI_SUCCESS if the loader has been added. The registration + * fails if there is already a loader for a specific file extension. + */ + aiReturn RegisterLoader(BaseImporter* pImp); + + // ------------------------------------------------------------------- + /** Unregisters a loader. + * + * @param pImp Importer to be unregistered. + * @return AI_SUCCESS if the loader has been removed. The function + * fails if the loader is currently in use (this could happen + * if the #Importer instance is used by more than one thread) or + * if it has not yet been registered. + */ + aiReturn UnregisterLoader(BaseImporter* pImp); + + // ------------------------------------------------------------------- + /** Registers a new post-process step. + * + * At the moment, there's a small limitation: new post processing + * steps are added to end of the list, or in other words, executed + * last, after all built-in steps. + * @param pImp Post-process step to be added. The Importer instance + * takes ownership of the pointer, so it will be automatically + * deleted with the Importer instance. + * @return AI_SUCCESS if the step has been added correctly. + */ + aiReturn RegisterPPStep(BaseProcess* pImp); + + // ------------------------------------------------------------------- + /** Unregisters a post-process step. + * + * @param pImp Step to be unregistered. + * @return AI_SUCCESS if the step has been removed. The function + * fails if the step is currently in use (this could happen + * if the #Importer instance is used by more than one thread) or + * if it has not yet been registered. + */ + aiReturn UnregisterPPStep(BaseProcess* pImp); + + // ------------------------------------------------------------------- + /** Set an integer configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX and are simple strings). + * @param iValue New value of the property + * @return true if the property was set before. The new value replaces + * the previous value in this case. + * @note Property of different types (float, int, string ..) are kept + * on different stacks, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + bool SetPropertyInteger(const char* szName, int iValue); + + // ------------------------------------------------------------------- + /** Set a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see SetPropertyInteger() + */ + bool SetPropertyBool(const char* szName, bool value) { + return SetPropertyInteger(szName,value); + } + + // ------------------------------------------------------------------- + /** Set a floating-point configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyFloat(const char* szName, ai_real fValue); + + // ------------------------------------------------------------------- + /** Set a string configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyString(const char* szName, const std::string& sValue); + + // ------------------------------------------------------------------- + /** Set a matrix configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyMatrix(const char* szName, const aiMatrix4x4& sValue); + + // ------------------------------------------------------------------- + /** Get a configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX). + * @param iErrorReturn Value that is returned if the property + * is not found. + * @return Current value of the property + * @note Property of different types (float, int, string ..) are kept + * on different lists, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + int GetPropertyInteger(const char* szName, + int iErrorReturn = 0xffffffff) const; + + // ------------------------------------------------------------------- + /** Get a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see GetPropertyInteger() + */ + bool GetPropertyBool(const char* szName, bool bErrorReturn = false) const { + return GetPropertyInteger(szName,bErrorReturn)!=0; + } + + // ------------------------------------------------------------------- + /** Get a floating-point configuration property + * @see GetPropertyInteger() + */ + ai_real GetPropertyFloat(const char* szName, + ai_real fErrorReturn = 10e10) const; + + // ------------------------------------------------------------------- + /** Get a string configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const std::string GetPropertyString(const char* szName, + const std::string& sErrorReturn = "") const; + + // ------------------------------------------------------------------- + /** Get a matrix configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const aiMatrix4x4 GetPropertyMatrix(const char* szName, + const aiMatrix4x4& sErrorReturn = aiMatrix4x4()) const; + + // ------------------------------------------------------------------- + /** Supplies a custom IO handler to the importer to use to open and + * access files. If you need the importer to use custom IO logic to + * access the files, you need to provide a custom implementation of + * IOSystem and IOFile to the importer. Then create an instance of + * your custom IOSystem implementation and supply it by this function. + * + * The Importer takes ownership of the object and will destroy it + * afterwards. The previously assigned handler will be deleted. + * Pass NULL to take again ownership of your IOSystem and reset Assimp + * to use its default implementation. + * + * @param pIOHandler The IO handler to be used in all file accesses + * of the Importer. + */ + void SetIOHandler( IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Retrieves the IO handler that is currently set. + * You can use #IsDefaultIOHandler() to check whether the returned + * interface is the default IO handler provided by ASSIMP. The default + * handler is active as long the application doesn't supply its own + * custom IO handler via #SetIOHandler(). + * @return A valid IOSystem interface, never NULL. + */ + IOSystem* GetIOHandler() const; + + // ------------------------------------------------------------------- + /** Checks whether a default IO handler is active + * A default handler is active as long the application doesn't + * supply its own custom IO handler via #SetIOHandler(). + * @return true by default + */ + bool IsDefaultIOHandler() const; + + // ------------------------------------------------------------------- + /** Supplies a custom progress handler to the importer. This + * interface exposes an #Update() callback, which is called + * more or less periodically (please don't sue us if it + * isn't as periodically as you'd like it to have ...). + * This can be used to implement progress bars and loading + * timeouts. + * @param pHandler Progress callback interface. Pass NULL to + * disable progress reporting. + * @note Progress handlers can be used to abort the loading + * at almost any time.*/ + void SetProgressHandler ( ProgressHandler* pHandler ); + + // ------------------------------------------------------------------- + /** Retrieves the progress handler that is currently set. + * You can use #IsDefaultProgressHandler() to check whether the returned + * interface is the default handler provided by ASSIMP. The default + * handler is active as long the application doesn't supply its own + * custom handler via #SetProgressHandler(). + * @return A valid ProgressHandler interface, never NULL. + */ + ProgressHandler* GetProgressHandler() const; + + // ------------------------------------------------------------------- + /** Checks whether a default progress handler is active + * A default handler is active as long the application doesn't + * supply its own custom progress handler via #SetProgressHandler(). + * @return true by default + */ + bool IsDefaultProgressHandler() const; + + // ------------------------------------------------------------------- + /** @brief Check whether a given set of post-processing flags + * is supported. + * + * Some flags are mutually exclusive, others are probably + * not available because your excluded them from your + * Assimp builds. Calling this function is recommended if + * you're unsure. + * + * @param pFlags Bitwise combination of the aiPostProcess flags. + * @return true if this flag combination is fine. + */ + bool ValidateFlags(unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Reads the given file and returns its contents if successful. + * + * If the call succeeds, the contents of the file are returned as a + * pointer to an aiScene object. The returned data is intended to be + * read-only, the importer object keeps ownership of the data and will + * destroy it upon destruction. If the import fails, NULL is returned. + * A human-readable error description can be retrieved by calling + * GetErrorString(). The previous scene will be deleted during this call. + * @param pFile Path and filename to the file to be imported. + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #ApplyPostProcessing(). + * @return A pointer to the imported data, NULL if the import failed. + * The pointer to the scene remains in possession of the Importer + * instance. Use GetOrphanedScene() to take ownership of it. + * + * @note Assimp is able to determine the file format of a file + * automatically. + */ + const aiScene* ReadFile( + const char* pFile, + unsigned int pFlags); + + // ------------------------------------------------------------------- + /** Reads the given file from a memory buffer and returns its + * contents if successful. + * + * If the call succeeds, the contents of the file are returned as a + * pointer to an aiScene object. The returned data is intended to be + * read-only, the importer object keeps ownership of the data and will + * destroy it upon destruction. If the import fails, NULL is returned. + * A human-readable error description can be retrieved by calling + * GetErrorString(). The previous scene will be deleted during this call. + * Calling this method doesn't affect the active IOSystem. + * @param pBuffer Pointer to the file data + * @param pLength Length of pBuffer, in bytes + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #ApplyPostProcessing(). + * @param pHint An additional hint to the library. If this is a non + * empty string, the library looks for a loader to support + * the file extension specified by pHint and passes the file to + * the first matching loader. If this loader is unable to completely + * the request, the library continues and tries to determine the + * file format on its own, a task that may or may not be successful. + * Check the return value, and you'll know ... + * @return A pointer to the imported data, NULL if the import failed. + * The pointer to the scene remains in possession of the Importer + * instance. Use GetOrphanedScene() to take ownership of it. + * + * @note This is a straightforward way to decode models from memory + * buffers, but it doesn't handle model formats that spread their + * data across multiple files or even directories. Examples include + * OBJ or MD3, which outsource parts of their material info into + * external scripts. If you need full functionality, provide + * a custom IOSystem to make Assimp find these files and use + * the regular ReadFile() API. + */ + const aiScene* ReadFileFromMemory( + const void* pBuffer, + size_t pLength, + unsigned int pFlags, + const char* pHint = ""); + + // ------------------------------------------------------------------- + /** Apply post-processing to an already-imported scene. + * + * This is strictly equivalent to calling #ReadFile() with the same + * flags. However, you can use this separate function to inspect + * the imported scene first to fine-tune your post-processing setup. + * @param pFlags Provide a bitwise combination of the + * #aiPostProcessSteps flags. + * @return A pointer to the post-processed data. This is still the + * same as the pointer returned by #ReadFile(). However, if + * post-processing fails, the scene could now be NULL. + * That's quite a rare case, post processing steps are not really + * designed to 'fail'. To be exact, the #aiProcess_ValidateDS + * flag is currently the only post processing step which can actually + * cause the scene to be reset to NULL. + * + * @note The method does nothing if no scene is currently bound + * to the #Importer instance. */ + const aiScene* ApplyPostProcessing(unsigned int pFlags); + + const aiScene* ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ); + + // ------------------------------------------------------------------- + /** @brief Reads the given file and returns its contents if successful. + * + * This function is provided for backward compatibility. + * See the const char* version for detailed docs. + * @see ReadFile(const char*, pFlags) */ + const aiScene* ReadFile( + const std::string& pFile, + unsigned int pFlags); + + // ------------------------------------------------------------------- + /** Frees the current scene. + * + * The function does nothing if no scene has previously been + * read via ReadFile(). FreeScene() is called automatically by the + * destructor and ReadFile() itself. */ + void FreeScene( ); + + // ------------------------------------------------------------------- + /** Returns an error description of an error that occurred in ReadFile(). + * + * Returns an empty string if no error occurred. + * @return A description of the last error, an empty string if no + * error occurred. The string is never NULL. + * + * @note The returned function remains valid until one of the + * following methods is called: #ReadFile(), #FreeScene(). */ + const char* GetErrorString() const; + + // ------------------------------------------------------------------- + /** Returns the scene loaded by the last successful call to ReadFile() + * + * @return Current scene or NULL if there is currently no scene loaded */ + const aiScene* GetScene() const; + + // ------------------------------------------------------------------- + /** Returns the scene loaded by the last successful call to ReadFile() + * and releases the scene from the ownership of the Importer + * instance. The application is now responsible for deleting the + * scene. Any further calls to GetScene() or GetOrphanedScene() + * will return NULL - until a new scene has been loaded via ReadFile(). + * + * @return Current scene or NULL if there is currently no scene loaded + * @note Use this method with maximal caution, and only if you have to. + * By design, aiScene's are exclusively maintained, allocated and + * deallocated by Assimp and no one else. The reasoning behind this + * is the golden rule that deallocations should always be done + * by the module that did the original allocation because heaps + * are not necessarily shared. GetOrphanedScene() enforces you + * to delete the returned scene by yourself, but this will only + * be fine if and only if you're using the same heap as assimp. + * On Windows, it's typically fine provided everything is linked + * against the multithreaded-dll version of the runtime library. + * It will work as well for static linkage with Assimp.*/ + aiScene* GetOrphanedScene(); + + // ------------------------------------------------------------------- + /** Returns whether a given file extension is supported by ASSIMP. + * + * @param szExtension Extension to be checked. + * Must include a trailing dot '.'. Example: ".3ds", ".md3". + * Cases-insensitive. + * @return true if the extension is supported, false otherwise */ + bool IsExtensionSupported(const char* szExtension) const; + + // ------------------------------------------------------------------- + /** @brief Returns whether a given file extension is supported by ASSIMP. + * + * This function is provided for backward compatibility. + * See the const char* version for detailed and up-to-date docs. + * @see IsExtensionSupported(const char*) */ + inline bool IsExtensionSupported(const std::string& szExtension) const; + + // ------------------------------------------------------------------- + /** Get a full list of all file extensions supported by ASSIMP. + * + * If a file extension is contained in the list this does of course not + * mean that ASSIMP is able to load all files with this extension --- + * it simply means there is an importer loaded which claims to handle + * files with this file extension. + * @param szOut String to receive the extension list. + * Format of the list: "*.3ds;*.obj;*.dae". This is useful for + * use with the WinAPI call GetOpenFileName(Ex). */ + void GetExtensionList(aiString& szOut) const; + + // ------------------------------------------------------------------- + /** @brief Get a full list of all file extensions supported by ASSIMP. + * + * This function is provided for backward compatibility. + * See the aiString version for detailed and up-to-date docs. + * @see GetExtensionList(aiString&)*/ + inline void GetExtensionList(std::string& szOut) const; + + // ------------------------------------------------------------------- + /** Get the number of importers currently registered with Assimp. */ + size_t GetImporterCount() const; + + // ------------------------------------------------------------------- + /** Get meta data for the importer corresponding to a specific index.. + * + * For the declaration of #aiImporterDesc, include <assimp/importerdesc.h>. + * @param index Index to query, must be within [0,GetImporterCount()) + * @return Importer meta data structure, NULL if the index does not + * exist or if the importer doesn't offer meta information ( + * importers may do this at the cost of being hated by their peers).*/ + const aiImporterDesc* GetImporterInfo(size_t index) const; + + // ------------------------------------------------------------------- + /** Find the importer corresponding to a specific index. + * + * @param index Index to query, must be within [0,GetImporterCount()) + * @return Importer instance. NULL if the index does not + * exist. */ + BaseImporter* GetImporter(size_t index) const; + + // ------------------------------------------------------------------- + /** Find the importer corresponding to a specific file extension. + * + * This is quite similar to #IsExtensionSupported except a + * BaseImporter instance is returned. + * @param szExtension Extension to check for. The following formats + * are recognized (BAH being the file extension): "BAH" (comparison + * is case-insensitive), ".bah", "*.bah" (wild card and dot + * characters at the beginning of the extension are skipped). + * @return NULL if no importer is found*/ + BaseImporter* GetImporter (const char* szExtension) const; + + // ------------------------------------------------------------------- + /** Find the importer index corresponding to a specific file extension. + * + * @param szExtension Extension to check for. The following formats + * are recognized (BAH being the file extension): "BAH" (comparison + * is case-insensitive), ".bah", "*.bah" (wild card and dot + * characters at the beginning of the extension are skipped). + * @return (size_t)-1 if no importer is found */ + size_t GetImporterIndex (const char* szExtension) const; + + // ------------------------------------------------------------------- + /** Returns the storage allocated by ASSIMP to hold the scene data + * in memory. + * + * This refers to the currently loaded file, see #ReadFile(). + * @param in Data structure to be filled. + * @note The returned memory statistics refer to the actual + * size of the use data of the aiScene. Heap-related overhead + * is (naturally) not included.*/ + void GetMemoryRequirements(aiMemoryInfo& in) const; + + // ------------------------------------------------------------------- + /** Enables "extra verbose" mode. + * + * 'Extra verbose' means the data structure is validated after *every* + * single post processing step to make sure everyone modifies the data + * structure in a well-defined manner. This is a debug feature and not + * intended for use in production environments. */ + void SetExtraVerbose(bool bDo); + + // ------------------------------------------------------------------- + /** Private, do not use. */ + ImporterPimpl* Pimpl() { return pimpl; } + const ImporterPimpl* Pimpl() const { return pimpl; } + +protected: + + // Just because we don't want you to know how we're hacking around. + ImporterPimpl* pimpl; +}; //! class Importer + + +// ---------------------------------------------------------------------------- +// For compatibility, the interface of some functions taking a std::string was +// changed to const char* to avoid crashes between binary incompatible STL +// versions. This code her is inlined, so it shouldn't cause any problems. +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE const aiScene* Importer::ReadFile( const std::string& pFile,unsigned int pFlags){ + return ReadFile(pFile.c_str(),pFlags); +} +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE void Importer::GetExtensionList(std::string& szOut) const { + aiString s; + GetExtensionList(s); + szOut = s.data; +} +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE bool Importer::IsExtensionSupported(const std::string& szExtension) const { + return IsExtensionSupported(szExtension.c_str()); +} + +} // !namespace Assimp + +#endif // AI_ASSIMP_HPP_INC diff --git a/thirdparty/assimp/include/assimp/LineSplitter.h b/thirdparty/assimp/include/assimp/LineSplitter.h new file mode 100644 index 0000000000..4afe45b92a --- /dev/null +++ b/thirdparty/assimp/include/assimp/LineSplitter.h @@ -0,0 +1,285 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file LineSplitter.h + * @brief LineSplitter, a helper class to iterate through all lines + * of a file easily. Works with StreamReader. + */ +#pragma once +#ifndef INCLUDED_LINE_SPLITTER_H +#define INCLUDED_LINE_SPLITTER_H + +#include <stdexcept> +#include "StreamReader.h" +#include "ParsingUtils.h" + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +/** Usage: +@code +for(LineSplitter splitter(stream);splitter;++splitter) { + + if (*splitter == "hi!") { + ... + } + else if (splitter->substr(0,5) == "hello") { + ... + // access the third token in the line (tokens are space-separated) + if (strtol(splitter[2]) > 5) { .. } + } + + std::cout << "Current line is: " << splitter.get_index() << std::endl; +} +@endcode +*/ +// ------------------------------------------------------------------------------------------------ +class LineSplitter { +public: + typedef size_t line_idx; + + // ----------------------------------------- + /** construct from existing stream reader + note: trim is *always* assumed true if skyp_empty_lines==true + */ + LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true); + + ~LineSplitter(); + + // ----------------------------------------- + /** pseudo-iterator increment */ + LineSplitter& operator++(); + + // ----------------------------------------- + LineSplitter& operator++(int); + + // ----------------------------------------- + /** get a pointer to the beginning of a particular token */ + const char* operator[] (size_t idx) const; + + // ----------------------------------------- + /** extract the start positions of N tokens from the current line*/ + template <size_t N> + void get_tokens(const char* (&tokens)[N]) const; + + // ----------------------------------------- + /** member access */ + const std::string* operator -> () const; + + std::string operator* () const; + + // ----------------------------------------- + /** boolean context */ + operator bool() const; + + // ----------------------------------------- + /** line indices are zero-based, empty lines are included */ + operator line_idx() const; + + line_idx get_index() const; + + // ----------------------------------------- + /** access the underlying stream object */ + StreamReaderLE& get_stream(); + + // ----------------------------------------- + /** !strcmp((*this)->substr(0,strlen(check)),check) */ + bool match_start(const char* check); + + // ----------------------------------------- + /** swallow the next call to ++, return the previous value. */ + void swallow_next_increment(); + + LineSplitter( const LineSplitter & ) = delete; + LineSplitter(LineSplitter &&) = delete; + LineSplitter &operator = ( const LineSplitter & ) = delete; + +private: + line_idx mIdx; + std::string mCur; + StreamReaderLE& mStream; + bool mSwallow, mSkip_empty_lines, mTrim; +}; + +inline +LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) +: mIdx(0) +, mCur() +, mStream(stream) +, mSwallow() +, mSkip_empty_lines(skip_empty_lines) +, mTrim(trim) { + mCur.reserve(1024); + operator++(); + mIdx = 0; +} + +inline +LineSplitter::~LineSplitter() { + // empty +} + +inline +LineSplitter& LineSplitter::operator++() { + if (mSwallow) { + mSwallow = false; + return *this; + } + + if (!*this) { + throw std::logic_error("End of file, no more lines to be retrieved."); + } + + char s; + mCur.clear(); + while (mStream.GetRemainingSize() && (s = mStream.GetI1(), 1)) { + if (s == '\n' || s == '\r') { + if (mSkip_empty_lines) { + while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\r' || s == '\n')); + if (mStream.GetRemainingSize()) { + mStream.IncPtr(-1); + } + } else { + // skip both potential line terminators but don't read past this line. + if (mStream.GetRemainingSize() && (s == '\r' && mStream.GetI1() != '\n')) { + mStream.IncPtr(-1); + } + if (mTrim) { + while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t')); + if (mStream.GetRemainingSize()) { + mStream.IncPtr(-1); + } + } + } + break; + } + mCur += s; + } + ++mIdx; + + return *this; +} + +inline +LineSplitter &LineSplitter::operator++(int) { + return ++(*this); +} + +inline +const char *LineSplitter::operator[] (size_t idx) const { + const char* s = operator->()->c_str(); + + SkipSpaces(&s); + for (size_t i = 0; i < idx; ++i) { + + for (; !IsSpace(*s); ++s) { + if (IsLineEnd(*s)) { + throw std::range_error("Token index out of range, EOL reached"); + } + } + SkipSpaces(&s); + } + return s; +} + +template <size_t N> +inline +void LineSplitter::get_tokens(const char* (&tokens)[N]) const { + const char* s = operator->()->c_str(); + + SkipSpaces(&s); + for (size_t i = 0; i < N; ++i) { + if (IsLineEnd(*s)) { + throw std::range_error("Token count out of range, EOL reached"); + } + tokens[i] = s; + + for (; *s && !IsSpace(*s); ++s); + SkipSpaces(&s); + } +} + +inline +const std::string* LineSplitter::operator -> () const { + return &mCur; +} + +inline +std::string LineSplitter::operator* () const { + return mCur; +} + +inline +LineSplitter::operator bool() const { + return mStream.GetRemainingSize() > 0; +} + +inline +LineSplitter::operator line_idx() const { + return mIdx; +} + +inline +LineSplitter::line_idx LineSplitter::get_index() const { + return mIdx; +} + +inline +StreamReaderLE &LineSplitter::get_stream() { + return mStream; +} + +inline +bool LineSplitter::match_start(const char* check) { + const size_t len = ::strlen(check); + + return len <= mCur.length() && std::equal(check, check + len, mCur.begin()); +} + +inline +void LineSplitter::swallow_next_increment() { + mSwallow = true; +} + +} // Namespace Assimp + +#endif // INCLUDED_LINE_SPLITTER_H diff --git a/thirdparty/assimp/include/assimp/LogAux.h b/thirdparty/assimp/include/assimp/LogAux.h new file mode 100644 index 0000000000..558485272e --- /dev/null +++ b/thirdparty/assimp/include/assimp/LogAux.h @@ -0,0 +1,131 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file LogAux.h + * @brief Common logging usage patterns for importer implementations + */ +#ifndef INCLUDED_AI_LOGAUX_H +#define INCLUDED_AI_LOGAUX_H + +#include <assimp/TinyFormatter.h> +#include <assimp/Exceptional.h> +#include <assimp/DefaultLogger.hpp> + +namespace Assimp { + +template<class TDeriving> +class LogFunctions { +public: + // ------------------------------------------------------------------------------------------------ + static void ThrowException(const std::string& msg) + { + throw DeadlyImportError(Prefix()+msg); + } + + // ------------------------------------------------------------------------------------------------ + static void LogWarn(const Formatter::format& message) { + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_WARN(Prefix()+(std::string)message); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogError(const Formatter::format& message) { + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_ERROR(Prefix()+(std::string)message); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogInfo(const Formatter::format& message) { + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_INFO(Prefix()+(std::string)message); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogDebug(const Formatter::format& message) { + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_DEBUG(Prefix()+(std::string)message); + } + } + + // https://sourceforge.net/tracker/?func=detail&atid=1067632&aid=3358562&group_id=226462 +#if !defined(__GNUC__) || !defined(__APPLE__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) + + // ------------------------------------------------------------------------------------------------ + static void LogWarn (const char* message) { + if (!DefaultLogger::isNullLogger()) { + LogWarn(Formatter::format(message)); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogError (const char* message) { + if (!DefaultLogger::isNullLogger()) { + LogError(Formatter::format(message)); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogInfo (const char* message) { + if (!DefaultLogger::isNullLogger()) { + LogInfo(Formatter::format(message)); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogDebug (const char* message) { + if (!DefaultLogger::isNullLogger()) { + LogDebug(Formatter::format(message)); + } + } + +#endif + +private: + static const char* Prefix(); + +}; +} // ! Assimp + +#endif diff --git a/thirdparty/assimp/include/assimp/LogStream.hpp b/thirdparty/assimp/include/assimp/LogStream.hpp new file mode 100644 index 0000000000..d0281e2d02 --- /dev/null +++ b/thirdparty/assimp/include/assimp/LogStream.hpp @@ -0,0 +1,111 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file LogStream.hpp + * @brief Abstract base class 'LogStream', representing an output log stream. + */ +#ifndef INCLUDED_AI_LOGSTREAM_H +#define INCLUDED_AI_LOGSTREAM_H + +#include "types.h" + +namespace Assimp { + +class IOSystem; + +// ------------------------------------------------------------------------------------ +/** @brief CPP-API: Abstract interface for log stream implementations. + * + * Several default implementations are provided, see #aiDefaultLogStream for more + * details. Writing your own implementation of LogStream is just necessary if these + * are not enough for your purpose. */ +class ASSIMP_API LogStream +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +protected: + /** @brief Default constructor */ + LogStream() AI_NO_EXCEPT; + +public: + /** @brief Virtual destructor */ + virtual ~LogStream(); + + // ------------------------------------------------------------------- + /** @brief Overwrite this for your own output methods + * + * Log messages *may* consist of multiple lines and you shouldn't + * expect a consistent formatting. If you want custom formatting + * (e.g. generate HTML), supply a custom instance of Logger to + * #DefaultLogger:set(). Usually you can *expect* that a log message + * is exactly one line and terminated with a single \n character. + * @param message Message to be written */ + virtual void write(const char* message) = 0; + + // ------------------------------------------------------------------- + /** @brief Creates a default log stream + * @param streams Type of the default stream + * @param name For aiDefaultLogStream_FILE: name of the output file + * @param io For aiDefaultLogStream_FILE: IOSystem to be used to open the output + * file. Pass NULL for the default implementation. + * @return New LogStream instance. */ + static LogStream* createDefaultStream(aiDefaultLogStream stream, + const char* name = "AssimpLog.txt", + IOSystem* io = nullptr ); + +}; // !class LogStream + +inline +LogStream::LogStream() AI_NO_EXCEPT { + // empty +} + +inline +LogStream::~LogStream() { + // empty +} + +// ------------------------------------------------------------------------------------ +} // Namespace Assimp + +#endif diff --git a/thirdparty/assimp/include/assimp/Logger.hpp b/thirdparty/assimp/include/assimp/Logger.hpp new file mode 100644 index 0000000000..89cade6c33 --- /dev/null +++ b/thirdparty/assimp/include/assimp/Logger.hpp @@ -0,0 +1,305 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Logger.hpp + * @brief Abstract base class 'Logger', base of the logging system. + */ +#ifndef INCLUDED_AI_LOGGER_H +#define INCLUDED_AI_LOGGER_H + +#include <assimp/types.h> +#include <assimp/TinyFormatter.h> + +namespace Assimp { + +class LogStream; + +// Maximum length of a log message. Longer messages are rejected. +#define MAX_LOG_MESSAGE_LENGTH 1024u + +// ---------------------------------------------------------------------------------- +/** @brief CPP-API: Abstract interface for logger implementations. + * Assimp provides a default implementation and uses it for almost all + * logging stuff ('DefaultLogger'). This class defines just basic logging + * behavior and is not of interest for you. Instead, take a look at #DefaultLogger. */ +class ASSIMP_API Logger +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +public: + + // ---------------------------------------------------------------------- + /** @enum LogSeverity + * @brief Log severity to describe the granularity of logging. + */ + enum LogSeverity { + NORMAL, //!< Normal granularity of logging + VERBOSE //!< Debug infos will be logged, too + }; + + // ---------------------------------------------------------------------- + /** @enum ErrorSeverity + * @brief Description for severity of a log message. + * + * Every LogStream has a bitwise combination of these flags. + * A LogStream doesn't receive any messages of a specific type + * if it doesn't specify the corresponding ErrorSeverity flag. + */ + enum ErrorSeverity { + Debugging = 1, //!< Debug log message + Info = 2, //!< Info log message + Warn = 4, //!< Warn log message + Err = 8 //!< Error log message + }; + +public: + + /** @brief Virtual destructor */ + virtual ~Logger(); + + // ---------------------------------------------------------------------- + /** @brief Writes a debug message + * @param message Debug message*/ + void debug(const char* message); + void debug(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Writes a info message + * @param message Info message*/ + void info(const char* message); + void info(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Writes a warning message + * @param message Warn message*/ + void warn(const char* message); + void warn(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Writes an error message + * @param message Error message*/ + void error(const char* message); + void error(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Set a new log severity. + * @param log_severity New severity for logging*/ + void setLogSeverity(LogSeverity log_severity); + + // ---------------------------------------------------------------------- + /** @brief Get the current log severity*/ + LogSeverity getLogSeverity() const; + + // ---------------------------------------------------------------------- + /** @brief Attach a new log-stream + * + * The logger takes ownership of the stream and is responsible + * for its destruction (which is done using ::delete when the logger + * itself is destroyed). Call detachStream to detach a stream and to + * gain ownership of it again. + * @param pStream Log-stream to attach + * @param severity Message filter, specified which types of log + * messages are dispatched to the stream. Provide a bitwise + * combination of the ErrorSeverity flags. + * @return true if the stream has been attached, false otherwise.*/ + virtual bool attachStream(LogStream *pStream, + unsigned int severity = Debugging | Err | Warn | Info) = 0; + + // ---------------------------------------------------------------------- + /** @brief Detach a still attached stream from the logger (or + * modify the filter flags bits) + * @param pStream Log-stream instance for detaching + * @param severity Provide a bitwise combination of the ErrorSeverity + * flags. This value is &~ed with the current flags of the stream, + * if the result is 0 the stream is detached from the Logger and + * the caller retakes the possession of the stream. + * @return true if the stream has been detached, false otherwise.*/ + virtual bool detatchStream(LogStream *pStream, + unsigned int severity = Debugging | Err | Warn | Info) = 0; + +protected: + /** + * Default constructor + */ + Logger() AI_NO_EXCEPT; + + /** + * Construction with a given log severity + */ + explicit Logger(LogSeverity severity); + + // ---------------------------------------------------------------------- + /** + * @brief Called as a request to write a specific debug message + * @param message Debug message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (excluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnDebug(const char* message)= 0; + + // ---------------------------------------------------------------------- + /** + * @brief Called as a request to write a specific info message + * @param message Info message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (ecxluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnInfo(const char* message) = 0; + + // ---------------------------------------------------------------------- + /** + * @brief Called as a request to write a specific warn message + * @param message Warn message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (exluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnWarn(const char* essage) = 0; + + // ---------------------------------------------------------------------- + /** + * @brief Called as a request to write a specific error message + * @param message Error message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (exluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnError(const char* message) = 0; + +protected: + LogSeverity m_Severity; +}; + +// ---------------------------------------------------------------------------------- +// Default constructor +inline +Logger::Logger() AI_NO_EXCEPT +: m_Severity(NORMAL) { + // empty +} + +// ---------------------------------------------------------------------------------- +// Virtual destructor +inline +Logger::~Logger() { + // empty +} + +// ---------------------------------------------------------------------------------- +// Construction with given logging severity +inline +Logger::Logger(LogSeverity severity) +: m_Severity(severity) { + // empty +} + +// ---------------------------------------------------------------------------------- +// Log severity setter +inline +void Logger::setLogSeverity(LogSeverity log_severity){ + m_Severity = log_severity; +} + +// ---------------------------------------------------------------------------------- +// Log severity getter +inline +Logger::LogSeverity Logger::getLogSeverity() const { + return m_Severity; +} + +// ---------------------------------------------------------------------------------- +inline +void Logger::debug(const std::string &message) { + return debug(message.c_str()); +} + +// ---------------------------------------------------------------------------------- +inline +void Logger::error(const std::string &message) { + return error(message.c_str()); +} + +// ---------------------------------------------------------------------------------- +inline +void Logger::warn(const std::string &message) { + return warn(message.c_str()); +} + +// ---------------------------------------------------------------------------------- +inline +void Logger::info(const std::string &message) { + return info(message.c_str()); +} + +// ------------------------------------------------------------------------------------------------ +#define ASSIMP_LOG_WARN_F(string,...)\ + DefaultLogger::get()->warn((Formatter::format(string),__VA_ARGS__)) + +#define ASSIMP_LOG_ERROR_F(string,...)\ + DefaultLogger::get()->error((Formatter::format(string),__VA_ARGS__)) + +#define ASSIMP_LOG_DEBUG_F(string,...)\ + DefaultLogger::get()->debug((Formatter::format(string),__VA_ARGS__)) + +#define ASSIMP_LOG_INFO_F(string,...)\ + DefaultLogger::get()->info((Formatter::format(string),__VA_ARGS__)) + + +#define ASSIMP_LOG_WARN(string)\ + DefaultLogger::get()->warn(string) + +#define ASSIMP_LOG_ERROR(string)\ + DefaultLogger::get()->error(string) + +#define ASSIMP_LOG_DEBUG(string)\ + DefaultLogger::get()->debug(string) + +#define ASSIMP_LOG_INFO(string)\ + DefaultLogger::get()->info(string) + + +} // Namespace Assimp + +#endif // !! INCLUDED_AI_LOGGER_H diff --git a/thirdparty/assimp/include/assimp/Macros.h b/thirdparty/assimp/include/assimp/Macros.h new file mode 100644 index 0000000000..6515303372 --- /dev/null +++ b/thirdparty/assimp/include/assimp/Macros.h @@ -0,0 +1,49 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/* Helper macro to set a pointer to NULL in debug builds + */ +#if (defined ASSIMP_BUILD_DEBUG) +# define AI_DEBUG_INVALIDATE_PTR(x) x = NULL; +#else +# define AI_DEBUG_INVALIDATE_PTR(x) +#endif + diff --git a/thirdparty/assimp/include/assimp/MathFunctions.h b/thirdparty/assimp/include/assimp/MathFunctions.h new file mode 100644 index 0000000000..cb3b696076 --- /dev/null +++ b/thirdparty/assimp/include/assimp/MathFunctions.h @@ -0,0 +1,77 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2016, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file MathFunctions.h + * @brief Implementation of the math functions (gcd and lcm) + * + * Copied from BoostWorkaround/math + */ + +namespace Assimp { +namespace Math { + +// TODO: use binary GCD for unsigned integers .... +template < typename IntegerType > +IntegerType gcd( IntegerType a, IntegerType b ) +{ + const IntegerType zero = (IntegerType)0; + while ( true ) + { + if ( a == zero ) + return b; + b %= a; + + if ( b == zero ) + return a; + a %= b; + } +} + +template < typename IntegerType > +IntegerType lcm( IntegerType a, IntegerType b ) +{ + const IntegerType t = gcd (a,b); + if (!t)return t; + return a / t * b; +} + +} +} diff --git a/thirdparty/assimp/include/assimp/MemoryIOWrapper.h b/thirdparty/assimp/include/assimp/MemoryIOWrapper.h new file mode 100644 index 0000000000..c522787184 --- /dev/null +++ b/thirdparty/assimp/include/assimp/MemoryIOWrapper.h @@ -0,0 +1,244 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file MemoryIOWrapper.h + * Handy IOStream/IOSystem implemetation to read directly from a memory buffer */ +#ifndef AI_MEMORYIOSTREAM_H_INC +#define AI_MEMORYIOSTREAM_H_INC + +#include <assimp/IOStream.hpp> +#include <assimp/IOSystem.hpp> +#include <assimp/ai_assert.h> +#include <stdint.h> + +namespace Assimp { + +#define AI_MEMORYIO_MAGIC_FILENAME "$$$___magic___$$$" +#define AI_MEMORYIO_MAGIC_FILENAME_LENGTH 17 + +// ---------------------------------------------------------------------------------- +/** Implementation of IOStream to read directly from a memory buffer */ +// ---------------------------------------------------------------------------------- +class MemoryIOStream : public IOStream { +public: + MemoryIOStream (const uint8_t* buff, size_t len, bool own = false) + : buffer (buff) + , length(len) + , pos((size_t)0) + , own(own) { + // empty + } + + ~MemoryIOStream () { + if(own) { + delete[] buffer; + } + } + + // ------------------------------------------------------------------- + // Read from stream + size_t Read(void* pvBuffer, size_t pSize, size_t pCount) { + ai_assert(nullptr != pvBuffer); + ai_assert(0 != pSize); + + const size_t cnt = std::min( pCount, (length-pos) / pSize); + const size_t ofs = pSize * cnt; + + ::memcpy(pvBuffer,buffer+pos,ofs); + pos += ofs; + + return cnt; + } + + // ------------------------------------------------------------------- + // Write to stream + size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/,size_t /*pCount*/) { + ai_assert(false); // won't be needed + return 0; + } + + // ------------------------------------------------------------------- + // Seek specific position + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) { + if (aiOrigin_SET == pOrigin) { + if (pOffset > length) { + return AI_FAILURE; + } + pos = pOffset; + } else if (aiOrigin_END == pOrigin) { + if (pOffset > length) { + return AI_FAILURE; + } + pos = length-pOffset; + } else { + if (pOffset+pos > length) { + return AI_FAILURE; + } + pos += pOffset; + } + return AI_SUCCESS; + } + + // ------------------------------------------------------------------- + // Get current seek position + size_t Tell() const { + return pos; + } + + // ------------------------------------------------------------------- + // Get size of file + size_t FileSize() const { + return length; + } + + // ------------------------------------------------------------------- + // Flush file contents + void Flush() { + ai_assert(false); // won't be needed + } + +private: + const uint8_t* buffer; + size_t length,pos; + bool own; +}; + +// --------------------------------------------------------------------------- +/** Dummy IO system to read from a memory buffer */ +class MemoryIOSystem : public IOSystem { +public: + /** Constructor. */ + MemoryIOSystem(const uint8_t* buff, size_t len, IOSystem* io) + : buffer(buff) + , length(len) + , existing_io(io) + , created_streams() { + // empty + } + + /** Destructor. */ + ~MemoryIOSystem() { + } + + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists(const char* pFile) const override { + if (0 == strncmp( pFile, AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH ) ) { + return true; + } + return existing_io ? existing_io->Exists(pFile) : false; + } + + // ------------------------------------------------------------------- + /** Returns the directory separator. */ + char getOsSeparator() const override { + return existing_io ? existing_io->getOsSeparator() + : '/'; // why not? it doesn't care + } + + // ------------------------------------------------------------------- + /** Open a new file with a given path. */ + IOStream* Open(const char* pFile, const char* pMode = "rb") override { + if ( 0 == strncmp( pFile, AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH ) ) { + created_streams.emplace_back(new MemoryIOStream(buffer, length)); + return created_streams.back(); + } + return existing_io ? existing_io->Open(pFile, pMode) : NULL; + } + + // ------------------------------------------------------------------- + /** Closes the given file and releases all resources associated with it. */ + void Close( IOStream* pFile) override { + auto it = std::find(created_streams.begin(), created_streams.end(), pFile); + if (it != created_streams.end()) { + delete pFile; + created_streams.erase(it); + } else if (existing_io) { + existing_io->Close(pFile); + } + } + + // ------------------------------------------------------------------- + /** Compare two paths */ + bool ComparePaths(const char* one, const char* second) const override { + return existing_io ? existing_io->ComparePaths(one, second) : false; + } + + bool PushDirectory( const std::string &path ) override { + return existing_io ? existing_io->PushDirectory(path) : false; + } + + const std::string &CurrentDirectory() const override { + static std::string empty; + return existing_io ? existing_io->CurrentDirectory() : empty; + } + + size_t StackSize() const override { + return existing_io ? existing_io->StackSize() : 0; + } + + bool PopDirectory() override { + return existing_io ? existing_io->PopDirectory() : false; + } + + bool CreateDirectory( const std::string &path ) override { + return existing_io ? existing_io->CreateDirectory(path) : false; + } + + bool ChangeDirectory( const std::string &path ) override { + return existing_io ? existing_io->ChangeDirectory(path) : false; + } + + bool DeleteFile( const std::string &file ) override { + return existing_io ? existing_io->DeleteFile(file) : false; + } + +private: + const uint8_t* buffer; + size_t length; + IOSystem* existing_io; + std::vector<IOStream*> created_streams; +}; + +} // end namespace Assimp + +#endif diff --git a/thirdparty/assimp/include/assimp/NullLogger.hpp b/thirdparty/assimp/include/assimp/NullLogger.hpp new file mode 100644 index 0000000000..c45d01bd48 --- /dev/null +++ b/thirdparty/assimp/include/assimp/NullLogger.hpp @@ -0,0 +1,99 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file NullLogger.hpp + * @brief Dummy logger +*/ + +#ifndef INCLUDED_AI_NULLLOGGER_H +#define INCLUDED_AI_NULLLOGGER_H + +#include "Logger.hpp" + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** @brief CPP-API: Empty logging implementation. + * + * Does nothing! Used by default if the application hasn't requested a + * custom logger via #DefaultLogger::set() or #DefaultLogger::create(); */ +class ASSIMP_API NullLogger + : public Logger { + +public: + + /** @brief Logs a debug message */ + void OnDebug(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Logs an info message */ + void OnInfo(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Logs a warning message */ + void OnWarn(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Logs an error message */ + void OnError(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Detach a still attached stream from logger */ + bool attachStream(LogStream *pStream, unsigned int severity) { + (void)pStream; (void)severity; //this avoids compiler warnings + return false; + } + + /** @brief Detach a still attached stream from logger */ + bool detatchStream(LogStream *pStream, unsigned int severity) { + (void)pStream; (void)severity; //this avoids compiler warnings + return false; + } + +private: +}; +} +#endif // !! AI_NULLLOGGER_H_INCLUDED diff --git a/thirdparty/assimp/include/assimp/ParsingUtils.h b/thirdparty/assimp/include/assimp/ParsingUtils.h new file mode 100644 index 0000000000..ca30ce13b0 --- /dev/null +++ b/thirdparty/assimp/include/assimp/ParsingUtils.h @@ -0,0 +1,260 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + + +/** @file ParsingUtils.h + * @brief Defines helper functions for text parsing + */ +#ifndef AI_PARSING_UTILS_H_INC +#define AI_PARSING_UTILS_H_INC + +#include "StringComparison.h" +#include "StringUtils.h" +#include <assimp/defs.h> + +namespace Assimp { + +// NOTE: the functions below are mostly intended as replacement for +// std::upper, std::lower, std::isupper, std::islower, std::isspace. +// we don't bother of locales. We don't want them. We want reliable +// (i.e. identical) results across all locales. + +// The functions below accept any character type, but know only +// about ASCII. However, UTF-32 is the only safe ASCII superset to +// use since it doesn't have multi-byte sequences. + +static const unsigned int BufferSize = 4096; + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +char_t ToLower( char_t in ) { + return (in >= (char_t)'A' && in <= (char_t)'Z') ? (char_t)(in+0x20) : in; +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +char_t ToUpper( char_t in) { + return (in >= (char_t)'a' && in <= (char_t)'z') ? (char_t)(in-0x20) : in; +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool IsUpper( char_t in) { + return (in >= (char_t)'A' && in <= (char_t)'Z'); +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool IsLower( char_t in) { + return (in >= (char_t)'a' && in <= (char_t)'z'); +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool IsSpace( char_t in) { + return (in == (char_t)' ' || in == (char_t)'\t'); +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool IsLineEnd( char_t in) { + return (in==(char_t)'\r'||in==(char_t)'\n'||in==(char_t)'\0'||in==(char_t)'\f'); +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool IsSpaceOrNewLine( char_t in) { + return IsSpace<char_t>(in) || IsLineEnd<char_t>(in); +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool SkipSpaces( const char_t* in, const char_t** out) { + while( *in == ( char_t )' ' || *in == ( char_t )'\t' ) { + ++in; + } + *out = in; + return !IsLineEnd<char_t>(*in); +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool SkipSpaces( const char_t** inout) { + return SkipSpaces<char_t>(*inout,inout); +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool SkipLine( const char_t* in, const char_t** out) { + while( *in != ( char_t )'\r' && *in != ( char_t )'\n' && *in != ( char_t )'\0' ) { + ++in; + } + + // files are opened in binary mode. Ergo there are both NL and CR + while( *in == ( char_t )'\r' || *in == ( char_t )'\n' ) { + ++in; + } + *out = in; + return *in != (char_t)'\0'; +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool SkipLine( const char_t** inout) { + return SkipLine<char_t>(*inout,inout); +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out) { + while( *in == ( char_t )' ' || *in == ( char_t )'\t' || *in == ( char_t )'\r' || *in == ( char_t )'\n' ) { + ++in; + } + *out = in; + return *in != '\0'; +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool SkipSpacesAndLineEnd( const char_t** inout) { + return SkipSpacesAndLineEnd<char_t>(*inout,inout); +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize ] ) { + if( ( char_t )'\0' == *buffer ) { + return false; + } + + char* _out = out; + char* const end = _out + BufferSize; + while( !IsLineEnd( *buffer ) && _out < end ) { + *_out++ = *buffer++; + } + *_out = (char_t)'\0'; + + while( IsLineEnd( *buffer ) && '\0' != *buffer ) { + ++buffer; + } + + return true; +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE bool IsNumeric( char_t in) +{ + return ( in >= '0' && in <= '9' ) || '-' == in || '+' == in; +} + +// --------------------------------------------------------------------------------- +template <class char_t> +AI_FORCE_INLINE +bool TokenMatch(char_t*& in, const char* token, unsigned int len) +{ + if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) { + if (in[len] != '\0') { + in += len+1; + } else { + // If EOF after the token make sure we don't go past end of buffer + in += len; + } + return true; + } + + return false; +} +// --------------------------------------------------------------------------------- +/** @brief Case-ignoring version of TokenMatch + * @param in Input + * @param token Token to check for + * @param len Number of characters to check + */ +AI_FORCE_INLINE +bool TokenMatchI(const char*& in, const char* token, unsigned int len) { + if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len])) { + in += len+1; + return true; + } + return false; +} + +// --------------------------------------------------------------------------------- +AI_FORCE_INLINE +void SkipToken(const char*& in) { + SkipSpaces(&in); + while ( !IsSpaceOrNewLine( *in ) ) { + ++in; + } +} + +// --------------------------------------------------------------------------------- +AI_FORCE_INLINE +std::string GetNextToken(const char*& in) { + SkipSpacesAndLineEnd(&in); + const char* cur = in; + while ( !IsSpaceOrNewLine( *in ) ) { + ++in; + } + return std::string(cur,(size_t)(in-cur)); +} + +// --------------------------------------------------------------------------------- + +} // ! namespace Assimp + +#endif // ! AI_PARSING_UTILS_H_INC diff --git a/thirdparty/assimp/include/assimp/Profiler.h b/thirdparty/assimp/include/assimp/Profiler.h new file mode 100644 index 0000000000..6ff9d41c0a --- /dev/null +++ b/thirdparty/assimp/include/assimp/Profiler.h @@ -0,0 +1,99 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Profiler.h + * @brief Utility to measure the respective runtime of each import step + */ +#ifndef INCLUDED_PROFILER_H +#define INCLUDED_PROFILER_H + +#include <chrono> +#include <assimp/DefaultLogger.hpp> +#include "TinyFormatter.h" + +#include <map> + +namespace Assimp { +namespace Profiling { + +using namespace Formatter; + +// ------------------------------------------------------------------------------------------------ +/** Simple wrapper around boost::timer to simplify reporting. Timings are automatically + * dumped to the log file. + */ +class Profiler { +public: + Profiler() { + // empty + } + +public: + + /** Start a named timer */ + void BeginRegion(const std::string& region) { + regions[region] = std::chrono::system_clock::now(); + ASSIMP_LOG_DEBUG((format("START `"),region,"`")); + } + + + /** End a specific named timer and write its end time to the log */ + void EndRegion(const std::string& region) { + RegionMap::const_iterator it = regions.find(region); + if (it == regions.end()) { + return; + } + + std::chrono::duration<double> elapsedSeconds = std::chrono::system_clock::now() - regions[region]; + ASSIMP_LOG_DEBUG((format("END `"),region,"`, dt= ", elapsedSeconds.count()," s")); + } + +private: + typedef std::map<std::string,std::chrono::time_point<std::chrono::system_clock>> RegionMap; + RegionMap regions; +}; + +} +} + +#endif + diff --git a/thirdparty/assimp/include/assimp/ProgressHandler.hpp b/thirdparty/assimp/include/assimp/ProgressHandler.hpp new file mode 100644 index 0000000000..4e47f1d0a6 --- /dev/null +++ b/thirdparty/assimp/include/assimp/ProgressHandler.hpp @@ -0,0 +1,145 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file ProgressHandler.hpp + * @brief Abstract base class 'ProgressHandler'. + */ +#pragma once +#ifndef AI_PROGRESSHANDLER_H_INC +#define AI_PROGRESSHANDLER_H_INC + +#include "types.h" + +namespace Assimp { + +// ------------------------------------------------------------------------------------ +/** @brief CPP-API: Abstract interface for custom progress report receivers. + * + * Each #Importer instance maintains its own #ProgressHandler. The default + * implementation provided by Assimp doesn't do anything at all. */ +class ASSIMP_API ProgressHandler +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +protected: + /// @brief Default constructor + ProgressHandler () AI_NO_EXCEPT { + // empty + } + +public: + /// @brief Virtual destructor. + virtual ~ProgressHandler () { + } + + // ------------------------------------------------------------------- + /** @brief Progress callback. + * @param percentage An estimate of the current loading progress, + * in percent. Or -1.f if such an estimate is not available. + * + * There are restriction on what you may do from within your + * implementation of this method: no exceptions may be thrown and no + * non-const #Importer methods may be called. It is + * not generally possible to predict the number of callbacks + * fired during a single import. + * + * @return Return false to abort loading at the next possible + * occasion (loaders and Assimp are generally allowed to perform + * all needed cleanup tasks prior to returning control to the + * caller). If the loading is aborted, #Importer::ReadFile() + * returns always NULL. + * */ + virtual bool Update(float percentage = -1.f) = 0; + + // ------------------------------------------------------------------- + /** @brief Progress callback for file loading steps + * @param numberOfSteps The number of total post-processing + * steps + * @param currentStep The index of the current post-processing + * step that will run, or equal to numberOfSteps if all of + * them has finished. This number is always strictly monotone + * increasing, although not necessarily linearly. + * + * @note This is currently only used at the start and the end + * of the file parsing. + * */ + virtual void UpdateFileRead(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) { + float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f; + Update( f * 0.5f ); + } + + // ------------------------------------------------------------------- + /** @brief Progress callback for post-processing steps + * @param numberOfSteps The number of total post-processing + * steps + * @param currentStep The index of the current post-processing + * step that will run, or equal to numberOfSteps if all of + * them has finished. This number is always strictly monotone + * increasing, although not necessarily linearly. + * */ + virtual void UpdatePostProcess(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) { + float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f; + Update( f * 0.5f + 0.5f ); + } + + + // ------------------------------------------------------------------- + /** @brief Progress callback for export steps. + * @param numberOfSteps The number of total processing + * steps + * @param currentStep The index of the current post-processing + * step that will run, or equal to numberOfSteps if all of + * them has finished. This number is always strictly monotone + * increasing, although not necessarily linearly. + * */ + virtual void UpdateFileWrite(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) { + float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f; + Update(f * 0.5f); + } +}; // !class ProgressHandler + +// ------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif // AI_PROGRESSHANDLER_H_INC diff --git a/thirdparty/assimp/include/assimp/RemoveComments.h b/thirdparty/assimp/include/assimp/RemoveComments.h new file mode 100644 index 0000000000..404b496719 --- /dev/null +++ b/thirdparty/assimp/include/assimp/RemoveComments.h @@ -0,0 +1,91 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Declares a helper class, "CommentRemover", which can be + * used to remove comments (single and multi line) from a text file. + */ +#ifndef AI_REMOVE_COMMENTS_H_INC +#define AI_REMOVE_COMMENTS_H_INC + + +#include <assimp/defs.h> + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** \brief Helper class to remove single and multi line comments from a file + * + * Some mesh formats like MD5 have comments that are quite similar + * to those in C or C++ so this code has been moved to a separate + * module. + */ +class ASSIMP_API CommentRemover +{ + // class cannot be instanced + CommentRemover() {} + +public: + + //! Remove single-line comments. The end of a line is + //! expected to be either NL or CR or NLCR. + //! \param szComment The start sequence of the comment, e.g. "//" + //! \param szBuffer Buffer to work with + //! \param chReplacement Character to be used as replacement + //! for commented lines. By default this is ' ' + static void RemoveLineComments(const char* szComment, + char* szBuffer, char chReplacement = ' '); + + //! Remove multi-line comments. The end of a line is + //! expected to be either NL or CR or NLCR. Multi-line comments + //! may not be nested (as in C). + //! \param szCommentStart The start sequence of the comment, e.g. "/*" + //! \param szCommentEnd The end sequence of the comment, e.g. "*/" + //! \param szBuffer Buffer to work with + //! \param chReplacement Character to be used as replacement + //! for commented lines. By default this is ' ' + static void RemoveMultiLineComments(const char* szCommentStart, + const char* szCommentEnd,char* szBuffer, + char chReplacement = ' '); +}; +} // ! Assimp + +#endif // !! AI_REMOVE_COMMENTS_H_INC diff --git a/thirdparty/assimp/include/assimp/SGSpatialSort.h b/thirdparty/assimp/include/assimp/SGSpatialSort.h new file mode 100644 index 0000000000..5b4f3f41f2 --- /dev/null +++ b/thirdparty/assimp/include/assimp/SGSpatialSort.h @@ -0,0 +1,150 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** Small helper classes to optimize finding vertices close to a given location + */ +#ifndef AI_D3DSSPATIALSORT_H_INC +#define AI_D3DSSPATIALSORT_H_INC + +#include <assimp/types.h> +#include <vector> +#include <stdint.h> + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** Specialized version of SpatialSort to support smoothing groups + * This is used in by the 3DS, ASE and LWO loaders. 3DS and ASE share their + * normal computation code in SmoothingGroups.inl, the LWO loader has its own + * implementation to handle all details of its file format correctly. + */ +// ---------------------------------------------------------------------------------- +class ASSIMP_API SGSpatialSort +{ +public: + + SGSpatialSort(); + + // ------------------------------------------------------------------- + /** Construction from a given face array, handling smoothing groups + * properly + */ + explicit SGSpatialSort(const std::vector<aiVector3D>& vPositions); + + // ------------------------------------------------------------------- + /** Add a vertex to the spatial sort + * @param vPosition Vertex position to be added + * @param index Index of the vrtex + * @param smoothingGroup SmoothingGroup for this vertex + */ + void Add(const aiVector3D& vPosition, unsigned int index, + unsigned int smoothingGroup); + + // ------------------------------------------------------------------- + /** Prepare the spatial sorter for use. This step runs in O(logn) + */ + void Prepare(); + + /** Destructor */ + ~SGSpatialSort(); + + // ------------------------------------------------------------------- + /** Returns an iterator for all positions close to the given position. + * @param pPosition The position to look for vertices. + * @param pSG Only included vertices with at least one shared smooth group + * @param pRadius Maximal distance from the position a vertex may have + * to be counted in. + * @param poResults The container to store the indices of the found + * positions. Will be emptied by the call so it may contain anything. + * @param exactMatch Specifies whether smoothing groups are bit masks + * (false) or integral values (true). In the latter case, a vertex + * cannot belong to more than one smoothing group. + * @return An iterator to iterate over all vertices in the given area. + */ + // ------------------------------------------------------------------- + void FindPositions( const aiVector3D& pPosition, uint32_t pSG, + float pRadius, std::vector<unsigned int>& poResults, + bool exactMatch = false) const; + +protected: + /** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */ + aiVector3D mPlaneNormal; + + // ------------------------------------------------------------------- + /** An entry in a spatially sorted position array. Consists of a + * vertex index, its position and its pre-calculated distance from + * the reference plane */ + // ------------------------------------------------------------------- + struct Entry { + unsigned int mIndex; ///< The vertex referred by this entry + aiVector3D mPosition; ///< Position + uint32_t mSmoothGroups; + float mDistance; ///< Distance of this vertex to the sorting plane + + Entry() AI_NO_EXCEPT + : mIndex(0) + , mPosition() + , mSmoothGroups(0) + , mDistance(0.0f) { + // empty + } + + Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance,uint32_t pSG) + : mIndex( pIndex) + , mPosition( pPosition) + , mSmoothGroups(pSG) + , mDistance( pDistance) { + // empty + } + + bool operator < (const Entry& e) const { + return mDistance < e.mDistance; + } + }; + + // all positions, sorted by distance to the sorting plane + std::vector<Entry> mPositions; +}; + +} // end of namespace Assimp + +#endif // AI_SPATIALSORT_H_INC diff --git a/thirdparty/assimp/include/assimp/SceneCombiner.h b/thirdparty/assimp/include/assimp/SceneCombiner.h new file mode 100644 index 0000000000..679a2acea4 --- /dev/null +++ b/thirdparty/assimp/include/assimp/SceneCombiner.h @@ -0,0 +1,401 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Declares a helper class, "SceneCombiner" providing various + * utilities to merge scenes. + */ +#ifndef AI_SCENE_COMBINER_H_INC +#define AI_SCENE_COMBINER_H_INC + +#include <assimp/ai_assert.h> +#include <assimp/types.h> +#include <assimp/Defines.h> +#include <stddef.h> +#include <set> +#include <list> +#include <stdint.h> + +#include <vector> + +struct aiScene; +struct aiNode; +struct aiMaterial; +struct aiTexture; +struct aiCamera; +struct aiLight; +struct aiMetadata; +struct aiBone; +struct aiMesh; +struct aiAnimation; +struct aiNodeAnim; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** \brief Helper data structure for SceneCombiner. + * + * Describes to which node a scene must be attached to. + */ +struct AttachmentInfo +{ + AttachmentInfo() + : scene (NULL) + , attachToNode (NULL) + {} + + AttachmentInfo(aiScene* _scene, aiNode* _attachToNode) + : scene (_scene) + , attachToNode (_attachToNode) + {} + + aiScene* scene; + aiNode* attachToNode; +}; + +// --------------------------------------------------------------------------- +struct NodeAttachmentInfo +{ + NodeAttachmentInfo() + : node (NULL) + , attachToNode (NULL) + , resolved (false) + , src_idx (SIZE_MAX) + {} + + NodeAttachmentInfo(aiNode* _scene, aiNode* _attachToNode,size_t idx) + : node (_scene) + , attachToNode (_attachToNode) + , resolved (false) + , src_idx (idx) + {} + + aiNode* node; + aiNode* attachToNode; + bool resolved; + size_t src_idx; +}; + +// --------------------------------------------------------------------------- +/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES + * Generate unique names for all named scene items + */ +#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES 0x1 + +/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES + * Generate unique names for materials, too. + * This is not absolutely required to pass the validation. + */ +#define AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES 0x2 + +/** @def AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY + * Use deep copies of duplicate scenes + */ +#define AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY 0x4 + +/** @def AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS + * If attachment nodes are not found in the given master scene, + * search the other imported scenes for them in an any order. + */ +#define AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS 0x8 + +/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY + * Can be combined with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES. + * Unique names are generated, but only if this is absolutely + * required to avoid name conflicts. + */ +#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY 0x10 + +typedef std::pair<aiBone*,unsigned int> BoneSrcIndex; + +// --------------------------------------------------------------------------- +/** @brief Helper data structure for SceneCombiner::MergeBones. + */ +struct BoneWithHash : public std::pair<uint32_t,aiString*> { + std::vector<BoneSrcIndex> pSrcBones; +}; + +// --------------------------------------------------------------------------- +/** @brief Utility for SceneCombiner + */ +struct SceneHelper +{ + SceneHelper () + : scene (NULL) + , idlen (0) + { + id[0] = 0; + } + + explicit SceneHelper (aiScene* _scene) + : scene (_scene) + , idlen (0) + { + id[0] = 0; + } + + AI_FORCE_INLINE aiScene* operator-> () const + { + return scene; + } + + // scene we're working on + aiScene* scene; + + // prefix to be added to all identifiers in the scene ... + char id [32]; + + // and its strlen() + unsigned int idlen; + + // hash table to quickly check whether a name is contained in the scene + std::set<unsigned int> hashes; +}; + +// --------------------------------------------------------------------------- +/** \brief Static helper class providing various utilities to merge two + * scenes. It is intended as internal utility and NOT for use by + * applications. + * + * The class is currently being used by various postprocessing steps + * and loaders (ie. LWS). + */ +class ASSIMP_API SceneCombiner { + // class cannot be instanced + SceneCombiner() { + // empty + } + + ~SceneCombiner() { + // empty + } + +public: + // ------------------------------------------------------------------- + /** Merges two or more scenes. + * + * @param dest Receives a pointer to the destination scene. If the + * pointer doesn't point to NULL when the function is called, the + * existing scene is cleared and refilled. + * @param src Non-empty list of scenes to be merged. The function + * deletes the input scenes afterwards. There may be duplicate scenes. + * @param flags Combination of the AI_INT_MERGE_SCENE flags defined above + */ + static void MergeScenes(aiScene** dest,std::vector<aiScene*>& src, + unsigned int flags = 0); + + // ------------------------------------------------------------------- + /** Merges two or more scenes and attaches all scenes to a specific + * position in the node graph of the master scene. + * + * @param dest Receives a pointer to the destination scene. If the + * pointer doesn't point to NULL when the function is called, the + * existing scene is cleared and refilled. + * @param master Master scene. It will be deleted afterwards. All + * other scenes will be inserted in its node graph. + * @param src Non-empty list of scenes to be merged along with their + * corresponding attachment points in the master scene. The function + * deletes the input scenes afterwards. There may be duplicate scenes. + * @param flags Combination of the AI_INT_MERGE_SCENE flags defined above + */ + static void MergeScenes(aiScene** dest, aiScene* master, + std::vector<AttachmentInfo>& src, + unsigned int flags = 0); + + // ------------------------------------------------------------------- + /** Merges two or more meshes + * + * The meshes should have equal vertex formats. Only components + * that are provided by ALL meshes will be present in the output mesh. + * An exception is made for VColors - they are set to black. The + * meshes should have the same material indices, too. The output + * material index is always the material index of the first mesh. + * + * @param dest Destination mesh. Must be empty. + * @param flags Currently no parameters + * @param begin First mesh to be processed + * @param end Points to the mesh after the last mesh to be processed + */ + static void MergeMeshes(aiMesh** dest,unsigned int flags, + std::vector<aiMesh*>::const_iterator begin, + std::vector<aiMesh*>::const_iterator end); + + // ------------------------------------------------------------------- + /** Merges two or more bones + * + * @param out Mesh to receive the output bone list + * @param flags Currently no parameters + * @param begin First mesh to be processed + * @param end Points to the mesh after the last mesh to be processed + */ + static void MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator it, + std::vector<aiMesh*>::const_iterator end); + + // ------------------------------------------------------------------- + /** Merges two or more materials + * + * The materials should be complementary as much as possible. In case + * of a property present in different materials, the first occurrence + * is used. + * + * @param dest Destination material. Must be empty. + * @param begin First material to be processed + * @param end Points to the material after the last material to be processed + */ + static void MergeMaterials(aiMaterial** dest, + std::vector<aiMaterial*>::const_iterator begin, + std::vector<aiMaterial*>::const_iterator end); + + // ------------------------------------------------------------------- + /** Builds a list of uniquely named bones in a mesh list + * + * @param asBones Receives the output list + * @param it First mesh to be processed + * @param end Last mesh to be processed + */ + static void BuildUniqueBoneList(std::list<BoneWithHash>& asBones, + std::vector<aiMesh*>::const_iterator it, + std::vector<aiMesh*>::const_iterator end); + + // ------------------------------------------------------------------- + /** Add a name prefix to all nodes in a scene. + * + * @param Current node. This function is called recursively. + * @param prefix Prefix to be added to all nodes + * @param len STring length + */ + static void AddNodePrefixes(aiNode* node, const char* prefix, + unsigned int len); + + // ------------------------------------------------------------------- + /** Add an offset to all mesh indices in a node graph + * + * @param Current node. This function is called recursively. + * @param offset Offset to be added to all mesh indices + */ + static void OffsetNodeMeshIndices (aiNode* node, unsigned int offset); + + // ------------------------------------------------------------------- + /** Attach a list of node graphs to well-defined nodes in a master + * graph. This is a helper for MergeScenes() + * + * @param master Master scene + * @param srcList List of source scenes along with their attachment + * points. If an attachment point is NULL (or does not exist in + * the master graph), a scene is attached to the root of the master + * graph (as an additional child node) + * @duplicates List of duplicates. If elem[n] == n the scene is not + * a duplicate. Otherwise elem[n] links scene n to its first occurrence. + */ + static void AttachToGraph ( aiScene* master, + std::vector<NodeAttachmentInfo>& srcList); + + static void AttachToGraph (aiNode* attach, + std::vector<NodeAttachmentInfo>& srcList); + + + // ------------------------------------------------------------------- + /** Get a deep copy of a scene + * + * @param dest Receives a pointer to the destination scene + * @param src Source scene - remains unmodified. + */ + static void CopyScene(aiScene** dest,const aiScene* source,bool allocate = true); + + + // ------------------------------------------------------------------- + /** Get a flat copy of a scene + * + * Only the first hierarchy layer is copied. All pointer members of + * aiScene are shared by source and destination scene. If the + * pointer doesn't point to NULL when the function is called, the + * existing scene is cleared and refilled. + * @param dest Receives a pointer to the destination scene + * @param src Source scene - remains unmodified. + */ + static void CopySceneFlat(aiScene** dest,const aiScene* source); + + + // ------------------------------------------------------------------- + /** Get a deep copy of a mesh + * + * @param dest Receives a pointer to the destination mesh + * @param src Source mesh - remains unmodified. + */ + static void Copy (aiMesh** dest, const aiMesh* src); + + // similar to Copy(): + static void Copy (aiMaterial** dest, const aiMaterial* src); + static void Copy (aiTexture** dest, const aiTexture* src); + static void Copy (aiAnimation** dest, const aiAnimation* src); + static void Copy (aiCamera** dest, const aiCamera* src); + static void Copy (aiBone** dest, const aiBone* src); + static void Copy (aiLight** dest, const aiLight* src); + static void Copy (aiNodeAnim** dest, const aiNodeAnim* src); + static void Copy (aiMetadata** dest, const aiMetadata* src); + + // recursive, of course + static void Copy (aiNode** dest, const aiNode* src); + + +private: + + // ------------------------------------------------------------------- + // Same as AddNodePrefixes, but with an additional check + static void AddNodePrefixesChecked(aiNode* node, const char* prefix, + unsigned int len, + std::vector<SceneHelper>& input, + unsigned int cur); + + // ------------------------------------------------------------------- + // Add node identifiers to a hashing set + static void AddNodeHashes(aiNode* node, std::set<unsigned int>& hashes); + + + // ------------------------------------------------------------------- + // Search for duplicate names + static bool FindNameMatch(const aiString& name, + std::vector<SceneHelper>& input, unsigned int cur); +}; + +} + +#endif // !! AI_SCENE_COMBINER_H_INC diff --git a/thirdparty/assimp/include/assimp/SkeletonMeshBuilder.h b/thirdparty/assimp/include/assimp/SkeletonMeshBuilder.h new file mode 100644 index 0000000000..f9b8d9f55c --- /dev/null +++ b/thirdparty/assimp/include/assimp/SkeletonMeshBuilder.h @@ -0,0 +1,125 @@ +/** Helper class to construct a dummy mesh for file formats containing only motion data */ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file SkeletonMeshBuilder.h + * Declares SkeletonMeshBuilder, a tiny utility to build dummy meshes + * for animation skeletons. + */ + +#ifndef AI_SKELETONMESHBUILDER_H_INC +#define AI_SKELETONMESHBUILDER_H_INC + +#include <vector> +#include <assimp/mesh.h> + +struct aiMaterial; +struct aiScene; +struct aiNode; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** + * This little helper class constructs a dummy mesh for a given scene + * the resembles the node hierarchy. This is useful for file formats + * that don't carry any mesh data but only animation data. + */ +class ASSIMP_API SkeletonMeshBuilder +{ +public: + + // ------------------------------------------------------------------- + /** The constructor processes the given scene and adds a mesh there. + * + * Does nothing if the scene already has mesh data. + * @param pScene The scene for which a skeleton mesh should be constructed. + * @param root The node to start with. NULL is the scene root + * @param bKnobsOnly Set this to true if you don't want the connectors + * between the knobs representing the nodes. + */ + SkeletonMeshBuilder( aiScene* pScene, aiNode* root = NULL, + bool bKnobsOnly = false); + +protected: + + // ------------------------------------------------------------------- + /** Recursively builds a simple mesh representation for the given node + * and also creates a joint for the node that affects this part of + * the mesh. + * @param pNode The node to build geometry for. + */ + void CreateGeometry( const aiNode* pNode); + + // ------------------------------------------------------------------- + /** Creates the mesh from the internally accumulated stuff and returns it. + */ + aiMesh* CreateMesh(); + + // ------------------------------------------------------------------- + /** Creates a dummy material and returns it. */ + aiMaterial* CreateMaterial(); + +protected: + /** space to assemble the mesh data: points */ + std::vector<aiVector3D> mVertices; + + /** faces */ + struct Face + { + unsigned int mIndices[3]; + Face(); + Face( unsigned int p0, unsigned int p1, unsigned int p2) + { mIndices[0] = p0; mIndices[1] = p1; mIndices[2] = p2; } + }; + std::vector<Face> mFaces; + + /** bones */ + std::vector<aiBone*> mBones; + + bool mKnobsOnly; +}; + +} // end of namespace Assimp + +#endif // AI_SKELETONMESHBUILDER_H_INC diff --git a/thirdparty/assimp/include/assimp/SmoothingGroups.h b/thirdparty/assimp/include/assimp/SmoothingGroups.h new file mode 100644 index 0000000000..92d65cea02 --- /dev/null +++ b/thirdparty/assimp/include/assimp/SmoothingGroups.h @@ -0,0 +1,108 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines the helper data structures for importing 3DS files. +http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */ + +#ifndef AI_SMOOTHINGGROUPS_H_INC +#define AI_SMOOTHINGGROUPS_H_INC + +#include <assimp/vector3.h> +#include <stdint.h> +#include <vector> + +// --------------------------------------------------------------------------- +/** Helper structure representing a face with smoothing groups assigned */ +struct FaceWithSmoothingGroup { + FaceWithSmoothingGroup() AI_NO_EXCEPT + : mIndices() + , iSmoothGroup(0) { + // in debug builds set all indices to a common magic value +#ifdef ASSIMP_BUILD_DEBUG + this->mIndices[0] = 0xffffffff; + this->mIndices[1] = 0xffffffff; + this->mIndices[2] = 0xffffffff; +#endif + } + + + //! Indices. .3ds is using uint16. However, after + //! an unique vertex set has been generated, + //! individual index values might exceed 2^16 + uint32_t mIndices[3]; + + //! specifies to which smoothing group the face belongs to + uint32_t iSmoothGroup; +}; + +// --------------------------------------------------------------------------- +/** Helper structure representing a mesh whose faces have smoothing + groups assigned. This allows us to reuse the code for normal computations + from smoothings groups for several loaders (3DS, ASE). All of them + use face structures which inherit from #FaceWithSmoothingGroup, + but as they add extra members and need to be copied by value we + need to use a template here. + */ +template <class T> +struct MeshWithSmoothingGroups +{ + //! Vertex positions + std::vector<aiVector3D> mPositions; + + //! Face lists + std::vector<T> mFaces; + + //! List of normal vectors + std::vector<aiVector3D> mNormals; +}; + +// --------------------------------------------------------------------------- +/** Computes normal vectors for the mesh + */ +template <class T> +void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh); + + +// include implementations +#include "SmoothingGroups.inl" + +#endif // !! AI_SMOOTHINGGROUPS_H_INC diff --git a/thirdparty/assimp/include/assimp/SmoothingGroups.inl b/thirdparty/assimp/include/assimp/SmoothingGroups.inl new file mode 100644 index 0000000000..84ea4a1b00 --- /dev/null +++ b/thirdparty/assimp/include/assimp/SmoothingGroups.inl @@ -0,0 +1,138 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Generation of normal vectors basing on smoothing groups */ + +#ifndef AI_SMOOTHINGGROUPS_INL_INCLUDED +#define AI_SMOOTHINGGROUPS_INL_INCLUDED + +// internal headers +#include <assimp/SGSpatialSort.h> + +// CRT header +#include <algorithm> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +template <class T> +void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh) +{ + // First generate face normals + sMesh.mNormals.resize(sMesh.mPositions.size(),aiVector3D()); + for( unsigned int a = 0; a < sMesh.mFaces.size(); a++) + { + T& face = sMesh.mFaces[a]; + + aiVector3D* pV1 = &sMesh.mPositions[face.mIndices[0]]; + aiVector3D* pV2 = &sMesh.mPositions[face.mIndices[1]]; + aiVector3D* pV3 = &sMesh.mPositions[face.mIndices[2]]; + + aiVector3D pDelta1 = *pV2 - *pV1; + aiVector3D pDelta2 = *pV3 - *pV1; + aiVector3D vNor = pDelta1 ^ pDelta2; + + for (unsigned int c = 0; c < 3;++c) + sMesh.mNormals[face.mIndices[c]] = vNor; + } + + // calculate the position bounds so we have a reliable epsilon to check position differences against + aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); + for( unsigned int a = 0; a < sMesh.mPositions.size(); a++) + { + minVec.x = std::min( minVec.x, sMesh.mPositions[a].x); + minVec.y = std::min( minVec.y, sMesh.mPositions[a].y); + minVec.z = std::min( minVec.z, sMesh.mPositions[a].z); + maxVec.x = std::max( maxVec.x, sMesh.mPositions[a].x); + maxVec.y = std::max( maxVec.y, sMesh.mPositions[a].y); + maxVec.z = std::max( maxVec.z, sMesh.mPositions[a].z); + } + const float posEpsilon = (maxVec - minVec).Length() * 1e-5f; + std::vector<aiVector3D> avNormals; + avNormals.resize(sMesh.mNormals.size()); + + // now generate the spatial sort tree + SGSpatialSort sSort; + for( typename std::vector<T>::iterator i = sMesh.mFaces.begin(); + i != sMesh.mFaces.end();++i) + { + for (unsigned int c = 0; c < 3;++c) + sSort.Add(sMesh.mPositions[(*i).mIndices[c]],(*i).mIndices[c],(*i).iSmoothGroup); + } + sSort.Prepare(); + + std::vector<bool> vertexDone(sMesh.mPositions.size(),false); + for( typename std::vector<T>::iterator i = sMesh.mFaces.begin(); + i != sMesh.mFaces.end();++i) + { + std::vector<unsigned int> poResult; + for (unsigned int c = 0; c < 3;++c) + { + unsigned int idx = (*i).mIndices[c]; + if (vertexDone[idx])continue; + + sSort.FindPositions(sMesh.mPositions[idx],(*i).iSmoothGroup, + posEpsilon,poResult); + + aiVector3D vNormals; + for (std::vector<unsigned int>::const_iterator + a = poResult.begin(); + a != poResult.end();++a) + { + vNormals += sMesh.mNormals[(*a)]; + } + vNormals.NormalizeSafe(); + + // write back into all affected normals + for (std::vector<unsigned int>::const_iterator + a = poResult.begin(); + a != poResult.end();++a) + { + idx = *a; + avNormals [idx] = vNormals; + vertexDone[idx] = true; + } + } + } + sMesh.mNormals = avNormals; +} + +#endif // !! AI_SMOOTHINGGROUPS_INL_INCLUDED diff --git a/thirdparty/assimp/include/assimp/SpatialSort.h b/thirdparty/assimp/include/assimp/SpatialSort.h new file mode 100644 index 0000000000..61b345bcbf --- /dev/null +++ b/thirdparty/assimp/include/assimp/SpatialSort.h @@ -0,0 +1,174 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** Small helper classes to optimise finding vertizes close to a given location */ +#ifndef AI_SPATIALSORT_H_INC +#define AI_SPATIALSORT_H_INC + +#include <vector> +#include <assimp/types.h> + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +/** A little helper class to quickly find all vertices in the epsilon environment of a given + * position. Construct an instance with an array of positions. The class stores the given positions + * by their indices and sorts them by their distance to an arbitrary chosen plane. + * You can then query the instance for all vertices close to a given position in an average O(log n) + * time, with O(n) worst case complexity when all vertices lay on the plane. The plane is chosen + * so that it avoids common planes in usual data sets. */ +// ------------------------------------------------------------------------------------------------ +class ASSIMP_API SpatialSort +{ +public: + + SpatialSort(); + + // ------------------------------------------------------------------------------------ + /** Constructs a spatially sorted representation from the given position array. + * Supply the positions in its layout in memory, the class will only refer to them + * by index. + * @param pPositions Pointer to the first position vector of the array. + * @param pNumPositions Number of vectors to expect in that array. + * @param pElementOffset Offset in bytes from the beginning of one vector in memory + * to the beginning of the next vector. */ + SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions, + unsigned int pElementOffset); + + /** Destructor */ + ~SpatialSort(); + +public: + + // ------------------------------------------------------------------------------------ + /** Sets the input data for the SpatialSort. This replaces existing data, if any. + * The new data receives new indices in ascending order. + * + * @param pPositions Pointer to the first position vector of the array. + * @param pNumPositions Number of vectors to expect in that array. + * @param pElementOffset Offset in bytes from the beginning of one vector in memory + * to the beginning of the next vector. + * @param pFinalize Specifies whether the SpatialSort's internal representation + * is finalized after the new data has been added. Finalization is + * required in order to use #FindPosition() or #GenerateMappingTable(). + * If you don't finalize yet, you can use #Append() to add data from + * other sources.*/ + void Fill( const aiVector3D* pPositions, unsigned int pNumPositions, + unsigned int pElementOffset, + bool pFinalize = true); + + + // ------------------------------------------------------------------------------------ + /** Same as #Fill(), except the method appends to existing data in the #SpatialSort. */ + void Append( const aiVector3D* pPositions, unsigned int pNumPositions, + unsigned int pElementOffset, + bool pFinalize = true); + + + // ------------------------------------------------------------------------------------ + /** Finalize the spatial hash data structure. This can be useful after + * multiple calls to #Append() with the pFinalize parameter set to false. + * This is finally required before one of #FindPositions() and #GenerateMappingTable() + * can be called to query the spatial sort.*/ + void Finalize(); + + // ------------------------------------------------------------------------------------ + /** Returns an iterator for all positions close to the given position. + * @param pPosition The position to look for vertices. + * @param pRadius Maximal distance from the position a vertex may have to be counted in. + * @param poResults The container to store the indices of the found positions. + * Will be emptied by the call so it may contain anything. + * @return An iterator to iterate over all vertices in the given area.*/ + void FindPositions( const aiVector3D& pPosition, ai_real pRadius, + std::vector<unsigned int>& poResults) const; + + // ------------------------------------------------------------------------------------ + /** Fills an array with indices of all positions identical to the given position. In + * opposite to FindPositions(), not an epsilon is used but a (very low) tolerance of + * four floating-point units. + * @param pPosition The position to look for vertices. + * @param poResults The container to store the indices of the found positions. + * Will be emptied by the call so it may contain anything.*/ + void FindIdenticalPositions( const aiVector3D& pPosition, + std::vector<unsigned int>& poResults) const; + + // ------------------------------------------------------------------------------------ + /** Compute a table that maps each vertex ID referring to a spatially close + * enough position to the same output ID. Output IDs are assigned in ascending order + * from 0...n. + * @param fill Will be filled with numPositions entries. + * @param pRadius Maximal distance from the position a vertex may have to + * be counted in. + * @return Number of unique vertices (n). */ + unsigned int GenerateMappingTable(std::vector<unsigned int>& fill, + ai_real pRadius) const; + +protected: + /** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */ + aiVector3D mPlaneNormal; + + /** An entry in a spatially sorted position array. Consists of a vertex index, + * its position and its pre-calculated distance from the reference plane */ + struct Entry { + unsigned int mIndex; ///< The vertex referred by this entry + aiVector3D mPosition; ///< Position + ai_real mDistance; ///< Distance of this vertex to the sorting plane + + Entry() AI_NO_EXCEPT + : mIndex( 999999999 ), mPosition(), mDistance( 99999. ) { + // empty + } + Entry( unsigned int pIndex, const aiVector3D& pPosition, ai_real pDistance) + : mIndex( pIndex), mPosition( pPosition), mDistance( pDistance) { + // empty + } + + bool operator < (const Entry& e) const { return mDistance < e.mDistance; } + }; + + // all positions, sorted by distance to the sorting plane + std::vector<Entry> mPositions; +}; + +} // end of namespace Assimp + +#endif // AI_SPATIALSORT_H_INC diff --git a/thirdparty/assimp/include/assimp/StandardShapes.h b/thirdparty/assimp/include/assimp/StandardShapes.h new file mode 100644 index 0000000000..3791569b83 --- /dev/null +++ b/thirdparty/assimp/include/assimp/StandardShapes.h @@ -0,0 +1,200 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Declares a helper class, "StandardShapes" which generates + * vertices for standard shapes, such as cylnders, cones, spheres .. + */ +#ifndef AI_STANDARD_SHAPES_H_INC +#define AI_STANDARD_SHAPES_H_INC + +#include <assimp/vector3.h> +#include <vector> + +struct aiMesh; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** \brief Helper class to generate vertex buffers for standard geometric + * shapes, such as cylinders, cones, boxes, spheres, elipsoids ... . + */ +class ASSIMP_API StandardShapes +{ + // class cannot be instanced + StandardShapes() {} + +public: + + + // ---------------------------------------------------------------- + /** Generates a mesh from an array of vertex positions. + * + * @param positions List of vertex positions + * @param numIndices Number of indices per primitive + * @return Output mesh + */ + static aiMesh* MakeMesh(const std::vector<aiVector3D>& positions, + unsigned int numIndices); + + + static aiMesh* MakeMesh ( unsigned int (*GenerateFunc) + (std::vector<aiVector3D>&)); + + static aiMesh* MakeMesh ( unsigned int (*GenerateFunc) + (std::vector<aiVector3D>&, bool)); + + static aiMesh* MakeMesh ( unsigned int n, void (*GenerateFunc) + (unsigned int,std::vector<aiVector3D>&)); + + // ---------------------------------------------------------------- + /** @brief Generates a hexahedron (cube) + * + * Hexahedrons can be scaled on all axes. + * @param positions Receives output triangles. + * @param polygons If you pass true here quads will be returned + * @return Number of vertices per face + */ + static unsigned int MakeHexahedron( + std::vector<aiVector3D>& positions, + bool polygons = false); + + // ---------------------------------------------------------------- + /** @brief Generates an icosahedron + * + * @param positions Receives output triangles. + * @return Number of vertices per face + */ + static unsigned int MakeIcosahedron( + std::vector<aiVector3D>& positions); + + + // ---------------------------------------------------------------- + /** @brief Generates a dodecahedron + * + * @param positions Receives output triangles + * @param polygons If you pass true here pentagons will be returned + * @return Number of vertices per face + */ + static unsigned int MakeDodecahedron( + std::vector<aiVector3D>& positions, + bool polygons = false); + + + // ---------------------------------------------------------------- + /** @brief Generates an octahedron + * + * @param positions Receives output triangles. + * @return Number of vertices per face + */ + static unsigned int MakeOctahedron( + std::vector<aiVector3D>& positions); + + + // ---------------------------------------------------------------- + /** @brief Generates a tetrahedron + * + * @param positions Receives output triangles. + * @return Number of vertices per face + */ + static unsigned int MakeTetrahedron( + std::vector<aiVector3D>& positions); + + + + // ---------------------------------------------------------------- + /** @brief Generates a sphere + * + * @param tess Number of subdivions - 0 generates a octahedron + * @param positions Receives output triangles. + */ + static void MakeSphere(unsigned int tess, + std::vector<aiVector3D>& positions); + + + // ---------------------------------------------------------------- + /** @brief Generates a cone or a cylinder, either open or closed. + * + * @code + * + * |-----| <- radius 1 + * + * __x__ <- ] ^ + * / \ | height | + * / \ | Y + * / \ | + * / \ | + * /______x______\ <- ] <- end cap + * + * |-------------| <- radius 2 + * + * @endcode + * + * @param height Height of the cone + * @param radius1 First radius + * @param radius2 Second radius + * @param tess Number of triangles. + * @param bOpened true for an open cone/cylinder. An open shape has + * no 'end caps' + * @param positions Receives output triangles + */ + static void MakeCone(ai_real height,ai_real radius1, + ai_real radius2,unsigned int tess, + std::vector<aiVector3D>& positions,bool bOpen= false); + + + // ---------------------------------------------------------------- + /** @brief Generates a flat circle + * + * The circle is constructed in the planned formed by the x,z + * axes of the cartesian coordinate system. + * + * @param radius Radius of the circle + * @param tess Number of segments. + * @param positions Receives output triangles. + */ + static void MakeCircle(ai_real radius, unsigned int tess, + std::vector<aiVector3D>& positions); + +}; +} // ! Assimp + +#endif // !! AI_STANDARD_SHAPES_H_INC diff --git a/thirdparty/assimp/include/assimp/StreamReader.h b/thirdparty/assimp/include/assimp/StreamReader.h new file mode 100644 index 0000000000..9116c14261 --- /dev/null +++ b/thirdparty/assimp/include/assimp/StreamReader.h @@ -0,0 +1,343 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Defines the StreamReader class which reads data from + * a binary stream with a well-defined endianness. + */ + +#ifndef AI_STREAMREADER_H_INCLUDED +#define AI_STREAMREADER_H_INCLUDED + +#include <assimp/IOStream.hpp> +#include <assimp/Defines.h> + +#include "ByteSwapper.h" +#include "Exceptional.h" +#include <memory> + +namespace Assimp { + +// -------------------------------------------------------------------------------------------- +/** Wrapper class around IOStream to allow for consistent reading of binary data in both + * little and big endian format. Don't attempt to instance the template directly. Use + * StreamReaderLE to read from a little-endian stream and StreamReaderBE to read from a + * BE stream. The class expects that the endianness of any input data is known at + * compile-time, which should usually be true (#BaseImporter::ConvertToUTF8 implements + * runtime endianness conversions for text files). + * + * XXX switch from unsigned int for size types to size_t? or ptrdiff_t?*/ +// -------------------------------------------------------------------------------------------- +template <bool SwapEndianess = false, bool RuntimeSwitch = false> +class StreamReader { +public: + // FIXME: use these data types throughout the whole library, + // then change them to 64 bit values :-) + using diff = int; + using pos = unsigned int; + + // --------------------------------------------------------------------- + /** Construction from a given stream with a well-defined endianness. + * + * The StreamReader holds a permanent strong reference to the + * stream, which is released upon destruction. + * @param stream Input stream. The stream is not restarted if + * its file pointer is not at 0. Instead, the stream reader + * reads from the current position to the end of the stream. + * @param le If @c RuntimeSwitch is true: specifies whether the + * stream is in little endian byte order. Otherwise the + * endianness information is contained in the @c SwapEndianess + * template parameter and this parameter is meaningless. */ + StreamReader(std::shared_ptr<IOStream> stream, bool le = false) + : stream(stream) + , le(le) + { + ai_assert(stream); + InternBegin(); + } + + // --------------------------------------------------------------------- + StreamReader(IOStream* stream, bool le = false) + : stream(std::shared_ptr<IOStream>(stream)) + , le(le) + { + ai_assert(stream); + InternBegin(); + } + + // --------------------------------------------------------------------- + ~StreamReader() { + delete[] buffer; + } + + // deprecated, use overloaded operator>> instead + + // --------------------------------------------------------------------- + /** Read a float from the stream */ + float GetF4() + { + return Get<float>(); + } + + // --------------------------------------------------------------------- + /** Read a double from the stream */ + double GetF8() { + return Get<double>(); + } + + // --------------------------------------------------------------------- + /** Read a signed 16 bit integer from the stream */ + int16_t GetI2() { + return Get<int16_t>(); + } + + // --------------------------------------------------------------------- + /** Read a signed 8 bit integer from the stream */ + int8_t GetI1() { + return Get<int8_t>(); + } + + // --------------------------------------------------------------------- + /** Read an signed 32 bit integer from the stream */ + int32_t GetI4() { + return Get<int32_t>(); + } + + // --------------------------------------------------------------------- + /** Read a signed 64 bit integer from the stream */ + int64_t GetI8() { + return Get<int64_t>(); + } + + // --------------------------------------------------------------------- + /** Read a unsigned 16 bit integer from the stream */ + uint16_t GetU2() { + return Get<uint16_t>(); + } + + // --------------------------------------------------------------------- + /** Read a unsigned 8 bit integer from the stream */ + uint8_t GetU1() { + return Get<uint8_t>(); + } + + // --------------------------------------------------------------------- + /** Read an unsigned 32 bit integer from the stream */ + uint32_t GetU4() { + return Get<uint32_t>(); + } + + // --------------------------------------------------------------------- + /** Read a unsigned 64 bit integer from the stream */ + uint64_t GetU8() { + return Get<uint64_t>(); + } + + // --------------------------------------------------------------------- + /** Get the remaining stream size (to the end of the stream) */ + unsigned int GetRemainingSize() const { + return (unsigned int)(end - current); + } + + // --------------------------------------------------------------------- + /** Get the remaining stream size (to the current read limit). The + * return value is the remaining size of the stream if no custom + * read limit has been set. */ + unsigned int GetRemainingSizeToLimit() const { + return (unsigned int)(limit - current); + } + + // --------------------------------------------------------------------- + /** Increase the file pointer (relative seeking) */ + void IncPtr(intptr_t plus) { + current += plus; + if (current > limit) { + throw DeadlyImportError("End of file or read limit was reached"); + } + } + + // --------------------------------------------------------------------- + /** Get the current file pointer */ + int8_t* GetPtr() const { + return current; + } + + // --------------------------------------------------------------------- + /** Set current file pointer (Get it from #GetPtr). This is if you + * prefer to do pointer arithmetics on your own or want to copy + * large chunks of data at once. + * @param p The new pointer, which is validated against the size + * limit and buffer boundaries. */ + void SetPtr(int8_t* p) { + current = p; + if (current > limit || current < buffer) { + throw DeadlyImportError("End of file or read limit was reached"); + } + } + + // --------------------------------------------------------------------- + /** Copy n bytes to an external buffer + * @param out Destination for copying + * @param bytes Number of bytes to copy */ + void CopyAndAdvance(void* out, size_t bytes) { + int8_t* ur = GetPtr(); + SetPtr(ur+bytes); // fire exception if eof + + ::memcpy(out,ur,bytes); + } + + // --------------------------------------------------------------------- + /** Get the current offset from the beginning of the file */ + int GetCurrentPos() const { + return (unsigned int)(current - buffer); + } + + void SetCurrentPos(size_t pos) { + SetPtr(buffer + pos); + } + + // --------------------------------------------------------------------- + /** Setup a temporary read limit + * + * @param limit Maximum number of bytes to be read from + * the beginning of the file. Specifying UINT_MAX + * resets the limit to the original end of the stream. + * Returns the previously set limit. */ + unsigned int SetReadLimit(unsigned int _limit) { + unsigned int prev = GetReadLimit(); + if (UINT_MAX == _limit) { + limit = end; + return prev; + } + + limit = buffer + _limit; + if (limit > end) { + throw DeadlyImportError("StreamReader: Invalid read limit"); + } + return prev; + } + + // --------------------------------------------------------------------- + /** Get the current read limit in bytes. Reading over this limit + * accidentally raises an exception. */ + unsigned int GetReadLimit() const { + return (unsigned int)(limit - buffer); + } + + // --------------------------------------------------------------------- + /** Skip to the read limit in bytes. Reading over this limit + * accidentally raises an exception. */ + void SkipToReadLimit() { + current = limit; + } + + // --------------------------------------------------------------------- + /** overload operator>> and allow chaining of >> ops. */ + template <typename T> + StreamReader& operator >> (T& f) { + f = Get<T>(); + return *this; + } + + // --------------------------------------------------------------------- + /** Generic read method. ByteSwap::Swap(T*) *must* be defined */ + template <typename T> + T Get() { + if ( current + sizeof(T) > limit) { + throw DeadlyImportError("End of file or stream limit was reached"); + } + + T f; + ::memcpy (&f, current, sizeof(T)); + Intern::Getter<SwapEndianess,T,RuntimeSwitch>() (&f,le); + current += sizeof(T); + + return f; + } + +private: + // --------------------------------------------------------------------- + void InternBegin() { + if (!stream) { + // in case someone wonders: StreamReader is frequently invoked with + // no prior validation whether the input stream is valid. Since + // no one bothers changing the error message, this message here + // is passed down to the caller and 'unable to open file' + // simply describes best what happened. + throw DeadlyImportError("StreamReader: Unable to open file"); + } + + const size_t s = stream->FileSize() - stream->Tell(); + if (!s) { + throw DeadlyImportError("StreamReader: File is empty or EOF is already reached"); + } + + current = buffer = new int8_t[s]; + const size_t read = stream->Read(current,1,s); + // (read < s) can only happen if the stream was opened in text mode, in which case FileSize() is not reliable + ai_assert(read <= s); + end = limit = &buffer[read-1] + 1; + } + +private: + std::shared_ptr<IOStream> stream; + int8_t *buffer, *current, *end, *limit; + bool le; +}; + +// -------------------------------------------------------------------------------------------- +// `static` StreamReaders. Their byte order is fixed and they might be a little bit faster. +#ifdef AI_BUILD_BIG_ENDIAN + typedef StreamReader<true> StreamReaderLE; + typedef StreamReader<false> StreamReaderBE; +#else + typedef StreamReader<true> StreamReaderBE; + typedef StreamReader<false> StreamReaderLE; +#endif + +// `dynamic` StreamReader. The byte order of the input data is specified in the +// c'tor. This involves runtime branching and might be a little bit slower. +typedef StreamReader<true,true> StreamReaderAny; + +} // end namespace Assimp + +#endif // !! AI_STREAMREADER_H_INCLUDED diff --git a/thirdparty/assimp/include/assimp/StreamWriter.h b/thirdparty/assimp/include/assimp/StreamWriter.h new file mode 100644 index 0000000000..c7cf6c0d74 --- /dev/null +++ b/thirdparty/assimp/include/assimp/StreamWriter.h @@ -0,0 +1,303 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Defines the StreamWriter class which writes data to + * a binary stream with a well-defined endianness. */ + +#ifndef AI_STREAMWRITER_H_INCLUDED +#define AI_STREAMWRITER_H_INCLUDED + +#include "ByteSwapper.h" +#include <assimp/IOStream.hpp> + +#include <memory> +#include <vector> + +namespace Assimp { + +// -------------------------------------------------------------------------------------------- +/** Wrapper class around IOStream to allow for consistent writing of binary data in both + * little and big endian format. Don't attempt to instance the template directly. Use + * StreamWriterLE to write to a little-endian stream and StreamWriterBE to write to a + * BE stream. Alternatively, there is StreamWriterAny if the endianness of the output + * stream is to be determined at runtime. + */ +// -------------------------------------------------------------------------------------------- +template <bool SwapEndianess = false, bool RuntimeSwitch = false> +class StreamWriter +{ + enum { + INITIAL_CAPACITY = 1024 + }; + +public: + + // --------------------------------------------------------------------- + /** Construction from a given stream with a well-defined endianness. + * + * The StreamReader holds a permanent strong reference to the + * stream, which is released upon destruction. + * @param stream Input stream. The stream is not re-seeked and writing + continues at the current position of the stream cursor. + * @param le If @c RuntimeSwitch is true: specifies whether the + * stream is in little endian byte order. Otherwise the + * endianness information is defined by the @c SwapEndianess + * template parameter and this parameter is meaningless. */ + StreamWriter(std::shared_ptr<IOStream> stream, bool le = false) + : stream(stream) + , le(le) + , cursor() + { + ai_assert(stream); + buffer.reserve(INITIAL_CAPACITY); + } + + // --------------------------------------------------------------------- + StreamWriter(IOStream* stream, bool le = false) + : stream(std::shared_ptr<IOStream>(stream)) + , le(le) + , cursor() + { + ai_assert(stream); + buffer.reserve(INITIAL_CAPACITY); + } + + // --------------------------------------------------------------------- + ~StreamWriter() { + stream->Write(buffer.data(), 1, buffer.size()); + stream->Flush(); + } + +public: + + // --------------------------------------------------------------------- + /** Flush the contents of the internal buffer, and the output IOStream */ + void Flush() + { + stream->Write(buffer.data(), 1, buffer.size()); + stream->Flush(); + buffer.clear(); + cursor = 0; + } + + // --------------------------------------------------------------------- + /** Seek to the given offset / origin in the output IOStream. + * + * Flushes the internal buffer and the output IOStream prior to seeking. */ + aiReturn Seek(size_t pOffset, aiOrigin pOrigin=aiOrigin_SET) + { + Flush(); + return stream->Seek(pOffset, pOrigin); + } + + // --------------------------------------------------------------------- + /** Tell the current position in the output IOStream. + * + * First flushes the internal buffer and the output IOStream. */ + size_t Tell() + { + Flush(); + return stream->Tell(); + } + +public: + + // --------------------------------------------------------------------- + /** Write a float to the stream */ + void PutF4(float f) + { + Put(f); + } + + // --------------------------------------------------------------------- + /** Write a double to the stream */ + void PutF8(double d) { + Put(d); + } + + // --------------------------------------------------------------------- + /** Write a signed 16 bit integer to the stream */ + void PutI2(int16_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a signed 8 bit integer to the stream */ + void PutI1(int8_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write an signed 32 bit integer to the stream */ + void PutI4(int32_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a signed 64 bit integer to the stream */ + void PutI8(int64_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a unsigned 16 bit integer to the stream */ + void PutU2(uint16_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a unsigned 8 bit integer to the stream */ + void PutU1(uint8_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write an unsigned 32 bit integer to the stream */ + void PutU4(uint32_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a unsigned 64 bit integer to the stream */ + void PutU8(uint64_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a single character to the stream */ + void PutChar(char c) { + Put(c); + } + + // --------------------------------------------------------------------- + /** Write an aiString to the stream */ + void PutString(const aiString& s) + { + // as Put(T f) below + if (cursor + s.length >= buffer.size()) { + buffer.resize(cursor + s.length); + } + void* dest = &buffer[cursor]; + ::memcpy(dest, s.C_Str(), s.length); + cursor += s.length; + } + + // --------------------------------------------------------------------- + /** Write a std::string to the stream */ + void PutString(const std::string& s) + { + // as Put(T f) below + if (cursor + s.size() >= buffer.size()) { + buffer.resize(cursor + s.size()); + } + void* dest = &buffer[cursor]; + ::memcpy(dest, s.c_str(), s.size()); + cursor += s.size(); + } + +public: + + // --------------------------------------------------------------------- + /** overload operator<< and allow chaining of MM ops. */ + template <typename T> + StreamWriter& operator << (T f) { + Put(f); + return *this; + } + + // --------------------------------------------------------------------- + std::size_t GetCurrentPos() const { + return cursor; + } + + // --------------------------------------------------------------------- + void SetCurrentPos(std::size_t new_cursor) { + cursor = new_cursor; + } + + // --------------------------------------------------------------------- + /** Generic write method. ByteSwap::Swap(T*) *must* be defined */ + template <typename T> + void Put(T f) { + Intern :: Getter<SwapEndianess,T,RuntimeSwitch>() (&f, le); + + if (cursor + sizeof(T) >= buffer.size()) { + buffer.resize(cursor + sizeof(T)); + } + + void* dest = &buffer[cursor]; + + // reinterpret_cast + assignment breaks strict aliasing rules + // and generally causes trouble on platforms such as ARM that + // do not silently ignore alignment faults. + ::memcpy(dest, &f, sizeof(T)); + cursor += sizeof(T); + } + +private: + + std::shared_ptr<IOStream> stream; + bool le; + + std::vector<uint8_t> buffer; + std::size_t cursor; +}; + + +// -------------------------------------------------------------------------------------------- +// `static` StreamWriter. Their byte order is fixed and they might be a little bit faster. +#ifdef AI_BUILD_BIG_ENDIAN + typedef StreamWriter<true> StreamWriterLE; + typedef StreamWriter<false> StreamWriterBE; +#else + typedef StreamWriter<true> StreamWriterBE; + typedef StreamWriter<false> StreamWriterLE; +#endif + +// `dynamic` StreamWriter. The byte order of the input data is specified in the +// c'tor. This involves runtime branching and might be a little bit slower. +typedef StreamWriter<true,true> StreamWriterAny; + +} // end namespace Assimp + +#endif // !! AI_STREAMWriter_H_INCLUDED diff --git a/thirdparty/assimp/include/assimp/StringComparison.h b/thirdparty/assimp/include/assimp/StringComparison.h new file mode 100644 index 0000000000..8acef277b9 --- /dev/null +++ b/thirdparty/assimp/include/assimp/StringComparison.h @@ -0,0 +1,233 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Definition of platform independent string workers: + + ASSIMP_itoa10 + ASSIMP_stricmp + ASSIMP_strincmp + + These functions are not consistently available on all platforms, + or the provided implementations behave too differently. +*/ +#ifndef INCLUDED_AI_STRING_WORKERS_H +#define INCLUDED_AI_STRING_WORKERS_H + +#include <assimp/ai_assert.h> +#include <assimp/defs.h> +#include "StringComparison.h" + +#include <string.h> +#include <stdint.h> +#include <string> + +namespace Assimp { + +// ------------------------------------------------------------------------------- +/** @brief itoa with a fixed base 10 + * 'itoa' is not consistently available on all platforms so it is quite useful + * to have a small replacement function here. No need to use a full sprintf() + * if we just want to print a number ... + * @param out Output buffer + * @param max Maximum number of characters to be written, including '\0'. + * This parameter may not be 0. + * @param number Number to be written + * @return Length of the output string, excluding the '\0' + */ +AI_FORCE_INLINE +unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number) { + ai_assert(NULL != out); + + // write the unary minus to indicate we have a negative number + unsigned int written = 1u; + if (number < 0 && written < max) { + *out++ = '-'; + ++written; + number = -number; + } + + // We begin with the largest number that is not zero. + int32_t cur = 1000000000; // 2147483648 + bool mustPrint = false; + while (written < max) { + + const unsigned int digit = number / cur; + if (mustPrint || digit > 0 || 1 == cur) { + // print all future zeroe's from now + mustPrint = true; + + *out++ = '0'+static_cast<char>(digit); + + ++written; + number -= digit*cur; + if (1 == cur) { + break; + } + } + cur /= 10; + } + + // append a terminal zero + *out++ = '\0'; + return written-1; +} + +// ------------------------------------------------------------------------------- +/** @brief itoa with a fixed base 10 (Secure template overload) + * The compiler should choose this function if he or she is able to determine the + * size of the array automatically. + */ +template <size_t length> +AI_FORCE_INLINE +unsigned int ASSIMP_itoa10( char(& out)[length], int32_t number) { + return ASSIMP_itoa10(out,length,number); +} + +// ------------------------------------------------------------------------------- +/** @brief Helper function to do platform independent string comparison. + * + * This is required since stricmp() is not consistently available on + * all platforms. Some platforms use the '_' prefix, others don't even + * have such a function. + * + * @param s1 First input string + * @param s2 Second input string + * @return 0 if the given strings are identical + */ +AI_FORCE_INLINE +int ASSIMP_stricmp(const char *s1, const char *s2) { + ai_assert( NULL != s1 ); + ai_assert( NULL != s2 ); + +#if (defined _MSC_VER) + + return ::_stricmp(s1,s2); +#elif defined( __GNUC__ ) + + return ::strcasecmp(s1,s2); +#else + + char c1, c2; + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } + while ( c1 && (c1 == c2) ); + return c1 - c2; +#endif +} + +// ------------------------------------------------------------------------------- +/** @brief Case independent comparison of two std::strings + * + * @param a First string + * @param b Second string + * @return 0 if a == b + */ +AI_FORCE_INLINE +int ASSIMP_stricmp(const std::string& a, const std::string& b) { + int i = (int)b.length()-(int)a.length(); + return (i ? i : ASSIMP_stricmp(a.c_str(),b.c_str())); +} + +// ------------------------------------------------------------------------------- +/** @brief Helper function to do platform independent string comparison. + * + * This is required since strincmp() is not consistently available on + * all platforms. Some platforms use the '_' prefix, others don't even + * have such a function. + * + * @param s1 First input string + * @param s2 Second input string + * @param n Macimum number of characters to compare + * @return 0 if the given strings are identical + */ +AI_FORCE_INLINE +int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) { + ai_assert( NULL != s1 ); + ai_assert( NULL != s2 ); + if ( !n ) { + return 0; + } + +#if (defined _MSC_VER) + + return ::_strnicmp(s1,s2,n); + +#elif defined( __GNUC__ ) + + return ::strncasecmp(s1,s2, n); + +#else + char c1, c2; + unsigned int p = 0; + do + { + if (p++ >= n)return 0; + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } + while ( c1 && (c1 == c2) ); + + return c1 - c2; +#endif +} + + +// ------------------------------------------------------------------------------- +/** @brief Evaluates an integer power + * + * todo: move somewhere where it fits better in than here + */ +AI_FORCE_INLINE +unsigned int integer_pow( unsigned int base, unsigned int power ) { + unsigned int res = 1; + for ( unsigned int i = 0; i < power; ++i ) { + res *= base; + } + + return res; +} + +} // end of namespace + +#endif // ! AI_STRINGCOMPARISON_H_INC diff --git a/thirdparty/assimp/include/assimp/StringUtils.h b/thirdparty/assimp/include/assimp/StringUtils.h new file mode 100644 index 0000000000..d68b7fa479 --- /dev/null +++ b/thirdparty/assimp/include/assimp/StringUtils.h @@ -0,0 +1,143 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef INCLUDED_AI_STRINGUTILS_H +#define INCLUDED_AI_STRINGUTILS_H + +#include <assimp/defs.h> + +#include <sstream> +#include <stdarg.h> +#include <cstdlib> + +/// @fn ai_snprintf +/// @brief The portable version of the function snprintf ( C99 standard ), which works on visual studio compilers 2013 and earlier. +/// @param outBuf The buffer to write in +/// @param size The buffer size +/// @param format The format string +/// @param ap The additional arguments. +/// @return The number of written characters if the buffer size was big enough. If an encoding error occurs, a negative number is returned. +#if defined(_MSC_VER) && _MSC_VER < 1900 + + AI_FORCE_INLINE + int c99_ai_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { + int count(-1); + if (0 != size) { + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + } + if (count == -1) { + count = _vscprintf(format, ap); + } + + return count; + } + + AI_FORCE_INLINE + int ai_snprintf(char *outBuf, size_t size, const char *format, ...) { + int count; + va_list ap; + + va_start(ap, format); + count = c99_ai_vsnprintf(outBuf, size, format, ap); + va_end(ap); + + return count; + } + +#else +# define ai_snprintf snprintf +#endif + +/// @fn to_string +/// @brief The portable version of to_string ( some gcc-versions on embedded devices are not supporting this). +/// @param value The value to write into the std::string. +/// @return The value as a std::string +template <typename T> +AI_FORCE_INLINE +std::string to_string( T value ) { + std::ostringstream os; + os << value; + + return os.str(); +} + +/// @fn ai_strtof +/// @brief The portable version of strtof. +/// @param begin The first character of the string. +/// @param end The last character +/// @return The float value, 0.0f in cas of an error. +AI_FORCE_INLINE +float ai_strtof( const char *begin, const char *end ) { + if ( nullptr == begin ) { + return 0.0f; + } + float val( 0.0f ); + if ( nullptr == end ) { + val = static_cast< float >( ::atof( begin ) ); + } else { + std::string::size_type len( end - begin ); + std::string token( begin, len ); + val = static_cast< float >( ::atof( token.c_str() ) ); + } + + return val; +} + +/// @fn DecimalToHexa +/// @brief The portable to convert a decimal value into a hexadecimal string. +/// @param toConvert Value to convert +/// @return The hexadecimal string, is empty in case of an error. +template<class T> +AI_FORCE_INLINE +std::string DecimalToHexa( T toConvert ) { + std::string result; + std::stringstream ss; + ss << std::hex << toConvert; + ss >> result; + + for ( size_t i = 0; i < result.size(); ++i ) { + result[ i ] = toupper( result[ i ] ); + } + + return result; +} + +#endif // INCLUDED_AI_STRINGUTILS_H diff --git a/thirdparty/assimp/include/assimp/Subdivision.h b/thirdparty/assimp/include/assimp/Subdivision.h new file mode 100644 index 0000000000..43feb73b30 --- /dev/null +++ b/thirdparty/assimp/include/assimp/Subdivision.h @@ -0,0 +1,131 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a helper class to evaluate subdivision surfaces.*/ +#pragma once +#ifndef AI_SUBDISIVION_H_INC +#define AI_SUBDISIVION_H_INC + +#include <cstddef> +#include <assimp/types.h> + +struct aiMesh; + +namespace Assimp { + +// ------------------------------------------------------------------------------ +/** Helper class to evaluate subdivision surfaces. Different algorithms + * are provided for choice. */ +// ------------------------------------------------------------------------------ +class ASSIMP_API Subdivider { +public: + + /** Enumerates all supported subvidision algorithms */ + enum Algorithm { + CATMULL_CLARKE = 0x1 + }; + + virtual ~Subdivider(); + + // --------------------------------------------------------------- + /** Create a subdivider of a specific type + * + * @param algo Algorithm to be used for subdivision + * @return Subdivider instance. */ + static Subdivider* Create (Algorithm algo); + + // --------------------------------------------------------------- + /** Subdivide a mesh using the selected algorithm + * + * @param mesh First mesh to be subdivided. Must be in verbose + * format. + * @param out Receives the output mesh, allocated by me. + * @param num Number of subdivisions to perform. + * @param discard_input If true is passed, the input mesh is + * deleted after the subdivision is complete. This can + * improve performance because it allows the optimization + * to reuse the existing mesh for intermediate results. + * @pre out!=mesh*/ + virtual void Subdivide ( aiMesh* mesh, + aiMesh*& out, unsigned int num, + bool discard_input = false) = 0; + + // --------------------------------------------------------------- + /** Subdivide multiple meshes using the selected algorithm. This + * avoids erroneous smoothing on objects consisting of multiple + * per-material meshes. Usually, most 3d modellers smooth on a + * per-object base, regardless the materials assigned to the + * meshes. + * + * @param smesh Array of meshes to be subdivided. Must be in + * verbose format. + * @param nmesh Number of meshes in smesh. + * @param out Receives the output meshes. The array must be + * sufficiently large (at least @c nmesh elements) and may not + * overlap the input array. Output meshes map one-to-one to + * their corresponding input meshes. The meshes are allocated + * by the function. + * @param discard_input If true is passed, input meshes are + * deleted after the subdivision is complete. This can + * improve performance because it allows the optimization + * of reusing existing meshes for intermediate results. + * @param num Number of subdivisions to perform. + * @pre nmesh != 0, smesh and out may not overlap*/ + virtual void Subdivide ( + aiMesh** smesh, + size_t nmesh, + aiMesh** out, + unsigned int num, + bool discard_input = false) = 0; + +}; + +inline +Subdivider::~Subdivider() { + // empty +} + +} // end namespace Assimp + + +#endif // !! AI_SUBDISIVION_H_INC + diff --git a/thirdparty/assimp/include/assimp/TinyFormatter.h b/thirdparty/assimp/include/assimp/TinyFormatter.h new file mode 100644 index 0000000000..1226b482e6 --- /dev/null +++ b/thirdparty/assimp/include/assimp/TinyFormatter.h @@ -0,0 +1,166 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file TinyFormatter.h + * @brief Utility to format log messages more easily. Introduced + * to get rid of the boost::format dependency. Much slinker, + * basically just extends stringstream. + */ +#ifndef INCLUDED_TINY_FORMATTER_H +#define INCLUDED_TINY_FORMATTER_H + +#include <sstream> + +namespace Assimp { +namespace Formatter { + +// ------------------------------------------------------------------------------------------------ +/** stringstream utility. Usage: + * @code + * void writelog(const std::string&s); + * void writelog(const std::wstring&s); + * ... + * writelog(format()<< "hi! this is a number: " << 4); + * writelog(wformat()<< L"hi! this is a number: " << 4); + * + * @endcode */ +template < typename T, + typename CharTraits = std::char_traits<T>, + typename Allocator = std::allocator<T> +> +class basic_formatter +{ + +public: + + typedef class std::basic_string< + T,CharTraits,Allocator + > string; + + typedef class std::basic_ostringstream< + T,CharTraits,Allocator + > stringstream; + +public: + + basic_formatter() {} + + /* Allow basic_formatter<T>'s to be used almost interchangeably + * with std::(w)string or const (w)char* arguments because the + * conversion c'tor is called implicitly. */ + template <typename TT> + basic_formatter(const TT& sin) { + underlying << sin; + } + + + // The problem described here: + // https://sourceforge.net/tracker/?func=detail&atid=1067632&aid=3358562&group_id=226462 + // can also cause trouble here. Apparently, older gcc versions sometimes copy temporaries + // being bound to const ref& function parameters. Copying streams is not permitted, though. + // This workaround avoids this by manually specifying a copy ctor. +#if !defined(__GNUC__) || !defined(__APPLE__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) + explicit basic_formatter(const basic_formatter& other) { + underlying << (string)other; + } +#endif + + +public: + + operator string () const { + return underlying.str(); + } + + + /* note - this is declared const because binding temporaries does only + * work for const references, so many function prototypes will + * include const basic_formatter<T>& s but might still want to + * modify the formatted string without the need for a full copy.*/ + template <typename TToken> + const basic_formatter& operator << (const TToken& s) const { + underlying << s; + return *this; + } + + template <typename TToken> + basic_formatter& operator << (const TToken& s) { + underlying << s; + return *this; + } + + + // comma operator overloaded as well, choose your preferred way. + template <typename TToken> + const basic_formatter& operator, (const TToken& s) const { + underlying << s; + return *this; + } + + template <typename TToken> + basic_formatter& operator, (const TToken& s) { + underlying << s; + return *this; + } + + // Fix for MSVC8 + // See https://sourceforge.net/projects/assimp/forums/forum/817654/topic/4372824 + template <typename TToken> + basic_formatter& operator, (TToken& s) { + underlying << s; + return *this; + } + + +private: + mutable stringstream underlying; +}; + + +typedef basic_formatter< char > format; +typedef basic_formatter< wchar_t > wformat; + +} // ! namespace Formatter + +} // ! namespace Assimp + +#endif diff --git a/thirdparty/assimp/include/assimp/Vertex.h b/thirdparty/assimp/include/assimp/Vertex.h new file mode 100644 index 0000000000..2a7f0256ad --- /dev/null +++ b/thirdparty/assimp/include/assimp/Vertex.h @@ -0,0 +1,348 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/** @file Defines a helper class to represent an interleaved vertex + along with arithmetic operations to support vertex operations + such as subdivision, smoothing etc. + + While the code is kept as general as possible, arithmetic operations + that are not currently well-defined (and would cause compile errors + due to missing operators in the math library), are commented. + */ +#ifndef AI_VERTEX_H_INC +#define AI_VERTEX_H_INC + +#include <assimp/vector3.h> +#include <assimp/mesh.h> +#include <assimp/ai_assert.h> +#include <functional> + +namespace Assimp { + + /////////////////////////////////////////////////////////////////////////// + // std::plus-family operates on operands with identical types - we need to + // support all the (vectype op float) combinations in vector maths. + // Providing T(float) would open the way to endless implicit conversions. + /////////////////////////////////////////////////////////////////////////// + namespace Intern { + template <typename T0, typename T1, typename TRES = T0> struct plus { + TRES operator() (const T0& t0, const T1& t1) const { + return t0+t1; + } + }; + template <typename T0, typename T1, typename TRES = T0> struct minus { + TRES operator() (const T0& t0, const T1& t1) const { + return t0-t1; + } + }; + template <typename T0, typename T1, typename TRES = T0> struct multiplies { + TRES operator() (const T0& t0, const T1& t1) const { + return t0*t1; + } + }; + template <typename T0, typename T1, typename TRES = T0> struct divides { + TRES operator() (const T0& t0, const T1& t1) const { + return t0/t1; + } + }; + } + +// ------------------------------------------------------------------------------------------------ +/** Intermediate description a vertex with all possible components. Defines a full set of + * operators, so you may use such a 'Vertex' in basic arithmetics. All operators are applied + * to *all* vertex components equally. This is useful for stuff like interpolation + * or subdivision, but won't work if special handling is required for some vertex components. */ +// ------------------------------------------------------------------------------------------------ +class Vertex +{ + friend Vertex operator + (const Vertex&,const Vertex&); + friend Vertex operator - (const Vertex&,const Vertex&); + +// friend Vertex operator + (const Vertex&,ai_real); +// friend Vertex operator - (const Vertex&,ai_real); + friend Vertex operator * (const Vertex&,ai_real); + friend Vertex operator / (const Vertex&,ai_real); + +// friend Vertex operator + (ai_real, const Vertex&); +// friend Vertex operator - (ai_real, const Vertex&); + friend Vertex operator * (ai_real, const Vertex&); +// friend Vertex operator / (ai_real, const Vertex&); + +public: + + Vertex() {} + + // ---------------------------------------------------------------------------- + /** Extract a particular vertex from a mesh and interleave all components */ + explicit Vertex(const aiMesh* msh, unsigned int idx) { + ai_assert(idx < msh->mNumVertices); + position = msh->mVertices[idx]; + + if (msh->HasNormals()) { + normal = msh->mNormals[idx]; + } + + if (msh->HasTangentsAndBitangents()) { + tangent = msh->mTangents[idx]; + bitangent = msh->mBitangents[idx]; + } + + for (unsigned int i = 0; msh->HasTextureCoords(i); ++i) { + texcoords[i] = msh->mTextureCoords[i][idx]; + } + + for (unsigned int i = 0; msh->HasVertexColors(i); ++i) { + colors[i] = msh->mColors[i][idx]; + } + } + + // ---------------------------------------------------------------------------- + /** Extract a particular vertex from a anim mesh and interleave all components */ + explicit Vertex(const aiAnimMesh* msh, unsigned int idx) { + ai_assert(idx < msh->mNumVertices); + position = msh->mVertices[idx]; + + if (msh->HasNormals()) { + normal = msh->mNormals[idx]; + } + + if (msh->HasTangentsAndBitangents()) { + tangent = msh->mTangents[idx]; + bitangent = msh->mBitangents[idx]; + } + + for (unsigned int i = 0; msh->HasTextureCoords(i); ++i) { + texcoords[i] = msh->mTextureCoords[i][idx]; + } + + for (unsigned int i = 0; msh->HasVertexColors(i); ++i) { + colors[i] = msh->mColors[i][idx]; + } + } + +public: + + Vertex& operator += (const Vertex& v) { + *this = *this+v; + return *this; + } + + Vertex& operator -= (const Vertex& v) { + *this = *this-v; + return *this; + } + + +/* + Vertex& operator += (ai_real v) { + *this = *this+v; + return *this; + } + + Vertex& operator -= (ai_real v) { + *this = *this-v; + return *this; + } +*/ + Vertex& operator *= (ai_real v) { + *this = *this*v; + return *this; + } + + Vertex& operator /= (ai_real v) { + *this = *this/v; + return *this; + } + +public: + + // ---------------------------------------------------------------------------- + /** Convert back to non-interleaved storage */ + void SortBack(aiMesh* out, unsigned int idx) const { + + ai_assert(idx<out->mNumVertices); + out->mVertices[idx] = position; + + if (out->HasNormals()) { + out->mNormals[idx] = normal; + } + + if (out->HasTangentsAndBitangents()) { + out->mTangents[idx] = tangent; + out->mBitangents[idx] = bitangent; + } + + for(unsigned int i = 0; out->HasTextureCoords(i); ++i) { + out->mTextureCoords[i][idx] = texcoords[i]; + } + + for(unsigned int i = 0; out->HasVertexColors(i); ++i) { + out->mColors[i][idx] = colors[i]; + } + } + +private: + + // ---------------------------------------------------------------------------- + /** Construct from two operands and a binary operation to combine them */ + template <template <typename t> class op> static Vertex BinaryOp(const Vertex& v0, const Vertex& v1) { + // this is a heavy task for the compiler to optimize ... *pray* + + Vertex res; + res.position = op<aiVector3D>()(v0.position,v1.position); + res.normal = op<aiVector3D>()(v0.normal,v1.normal); + res.tangent = op<aiVector3D>()(v0.tangent,v1.tangent); + res.bitangent = op<aiVector3D>()(v0.bitangent,v1.bitangent); + + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + res.texcoords[i] = op<aiVector3D>()(v0.texcoords[i],v1.texcoords[i]); + } + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { + res.colors[i] = op<aiColor4D>()(v0.colors[i],v1.colors[i]); + } + return res; + } + + // ---------------------------------------------------------------------------- + /** This time binary arithmetics of v0 with a floating-point number */ + template <template <typename, typename, typename> class op> static Vertex BinaryOp(const Vertex& v0, ai_real f) { + // this is a heavy task for the compiler to optimize ... *pray* + + Vertex res; + res.position = op<aiVector3D,ai_real,aiVector3D>()(v0.position,f); + res.normal = op<aiVector3D,ai_real,aiVector3D>()(v0.normal,f); + res.tangent = op<aiVector3D,ai_real,aiVector3D>()(v0.tangent,f); + res.bitangent = op<aiVector3D,ai_real,aiVector3D>()(v0.bitangent,f); + + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + res.texcoords[i] = op<aiVector3D,ai_real,aiVector3D>()(v0.texcoords[i],f); + } + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { + res.colors[i] = op<aiColor4D,ai_real,aiColor4D>()(v0.colors[i],f); + } + return res; + } + + // ---------------------------------------------------------------------------- + /** This time binary arithmetics of v0 with a floating-point number */ + template <template <typename, typename, typename> class op> static Vertex BinaryOp(ai_real f, const Vertex& v0) { + // this is a heavy task for the compiler to optimize ... *pray* + + Vertex res; + res.position = op<ai_real,aiVector3D,aiVector3D>()(f,v0.position); + res.normal = op<ai_real,aiVector3D,aiVector3D>()(f,v0.normal); + res.tangent = op<ai_real,aiVector3D,aiVector3D>()(f,v0.tangent); + res.bitangent = op<ai_real,aiVector3D,aiVector3D>()(f,v0.bitangent); + + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + res.texcoords[i] = op<ai_real,aiVector3D,aiVector3D>()(f,v0.texcoords[i]); + } + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) { + res.colors[i] = op<ai_real,aiColor4D,aiColor4D>()(f,v0.colors[i]); + } + return res; + } + +public: + + aiVector3D position; + aiVector3D normal; + aiVector3D tangent, bitangent; + + aiVector3D texcoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + aiColor4D colors[AI_MAX_NUMBER_OF_COLOR_SETS]; +}; + + + +// ------------------------------------------------------------------------------------------------ +AI_FORCE_INLINE Vertex operator + (const Vertex& v0,const Vertex& v1) { + return Vertex::BinaryOp<std::plus>(v0,v1); +} + +AI_FORCE_INLINE Vertex operator - (const Vertex& v0,const Vertex& v1) { + return Vertex::BinaryOp<std::minus>(v0,v1); +} + + +// ------------------------------------------------------------------------------------------------ +/* +AI_FORCE_INLINE Vertex operator + (const Vertex& v0,ai_real f) { + return Vertex::BinaryOp<Intern::plus>(v0,f); +} + +AI_FORCE_INLINE Vertex operator - (const Vertex& v0,ai_real f) { + return Vertex::BinaryOp<Intern::minus>(v0,f); +} + +*/ + +AI_FORCE_INLINE Vertex operator * (const Vertex& v0,ai_real f) { + return Vertex::BinaryOp<Intern::multiplies>(v0,f); +} + +AI_FORCE_INLINE Vertex operator / (const Vertex& v0,ai_real f) { + return Vertex::BinaryOp<Intern::multiplies>(v0,1.f/f); +} + +// ------------------------------------------------------------------------------------------------ +/* +AI_FORCE_INLINE Vertex operator + (ai_real f,const Vertex& v0) { + return Vertex::BinaryOp<Intern::plus>(f,v0); +} + +AI_FORCE_INLINE Vertex operator - (ai_real f,const Vertex& v0) { + return Vertex::BinaryOp<Intern::minus>(f,v0); +} +*/ + +AI_FORCE_INLINE Vertex operator * (ai_real f,const Vertex& v0) { + return Vertex::BinaryOp<Intern::multiplies>(f,v0); +} + +/* +AI_FORCE_INLINE Vertex operator / (ai_real f,const Vertex& v0) { + return Vertex::BinaryOp<Intern::divides>(f,v0); +} +*/ + +} +#endif diff --git a/thirdparty/assimp/include/assimp/XMLTools.h b/thirdparty/assimp/include/assimp/XMLTools.h new file mode 100644 index 0000000000..b0d3276873 --- /dev/null +++ b/thirdparty/assimp/include/assimp/XMLTools.h @@ -0,0 +1,83 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef INCLUDED_ASSIMP_XML_TOOLS_H +#define INCLUDED_ASSIMP_XML_TOOLS_H + +#include <string> + +namespace Assimp { + // XML escape the 5 XML special characters (",',<,> and &) in |data| + // Based on http://stackoverflow.com/questions/5665231 + std::string XMLEscape(const std::string& data) { + std::string buffer; + + const size_t size = data.size(); + buffer.reserve(size + size / 8); + for(size_t i = 0; i < size; ++i) { + const char c = data[i]; + switch(c) { + case '&' : + buffer.append("&"); + break; + case '\"': + buffer.append("""); + break; + case '\'': + buffer.append("'"); + break; + case '<' : + buffer.append("<"); + break; + case '>' : + buffer.append(">"); + break; + default: + buffer.append(&c, 1); + break; + } + } + return buffer; + } +} + +#endif // INCLUDED_ASSIMP_XML_TOOLS_H diff --git a/thirdparty/assimp/include/assimp/ai_assert.h b/thirdparty/assimp/include/assimp/ai_assert.h new file mode 100644 index 0000000000..e5de5d3f36 --- /dev/null +++ b/thirdparty/assimp/include/assimp/ai_assert.h @@ -0,0 +1,57 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#pragma once +#ifndef AI_ASSERT_H_INC +#define AI_ASSERT_H_INC + +#ifdef ASSIMP_BUILD_DEBUG +# include <assert.h> +# define ai_assert(expression) assert( expression ) +# define ai_assert_entry() assert( false ) +#else +# define ai_assert(expression) +# define ai_assert_entry() +#endif // ASSIMP_BUILD_DEBUG + +#endif // AI_ASSERT_H_INC + diff --git a/thirdparty/assimp/include/assimp/anim.h b/thirdparty/assimp/include/assimp/anim.h new file mode 100644 index 0000000000..02e92739ec --- /dev/null +++ b/thirdparty/assimp/include/assimp/anim.h @@ -0,0 +1,577 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** + * @file anim.h + * @brief Defines the data structures in which the imported animations + * are returned. + */ +#pragma once +#ifndef AI_ANIM_H_INC +#define AI_ANIM_H_INC + +#include <assimp/types.h> +#include <assimp/quaternion.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** A time-value pair specifying a certain 3D vector for the given time. */ +struct aiVectorKey +{ + /** The time of this key */ + double mTime; + + /** The value of this key */ + C_STRUCT aiVector3D mValue; + +#ifdef __cplusplus + + /// @brief The default constructor. + aiVectorKey() AI_NO_EXCEPT + : mTime( 0.0 ) + , mValue() { + // empty + } + + /// @brief Construction from a given time and key value. + + aiVectorKey(double time, const aiVector3D& value) + : mTime( time ) + , mValue( value ) { + // empty + } + + typedef aiVector3D elem_type; + + // Comparison operators. For use with std::find(); + bool operator == (const aiVectorKey& rhs) const { + return rhs.mValue == this->mValue; + } + bool operator != (const aiVectorKey& rhs ) const { + return rhs.mValue != this->mValue; + } + + // Relational operators. For use with std::sort(); + bool operator < (const aiVectorKey& rhs ) const { + return mTime < rhs.mTime; + } + bool operator > (const aiVectorKey& rhs ) const { + return mTime > rhs.mTime; + } +#endif // __cplusplus +}; + +// --------------------------------------------------------------------------- +/** A time-value pair specifying a rotation for the given time. + * Rotations are expressed with quaternions. */ +struct aiQuatKey +{ + /** The time of this key */ + double mTime; + + /** The value of this key */ + C_STRUCT aiQuaternion mValue; + +#ifdef __cplusplus + aiQuatKey() AI_NO_EXCEPT + : mTime( 0.0 ) + , mValue() { + // empty + } + + /** Construction from a given time and key value */ + aiQuatKey(double time, const aiQuaternion& value) + : mTime (time) + , mValue (value) + {} + + typedef aiQuaternion elem_type; + + // Comparison operators. For use with std::find(); + bool operator == (const aiQuatKey& rhs ) const { + return rhs.mValue == this->mValue; + } + bool operator != (const aiQuatKey& rhs ) const { + return rhs.mValue != this->mValue; + } + + // Relational operators. For use with std::sort(); + bool operator < (const aiQuatKey& rhs ) const { + return mTime < rhs.mTime; + } + bool operator > (const aiQuatKey& rhs ) const { + return mTime > rhs.mTime; + } +#endif +}; + +// --------------------------------------------------------------------------- +/** Binds a anim-mesh to a specific point in time. */ +struct aiMeshKey +{ + /** The time of this key */ + double mTime; + + /** Index into the aiMesh::mAnimMeshes array of the + * mesh corresponding to the #aiMeshAnim hosting this + * key frame. The referenced anim mesh is evaluated + * according to the rules defined in the docs for #aiAnimMesh.*/ + unsigned int mValue; + +#ifdef __cplusplus + + aiMeshKey() AI_NO_EXCEPT + : mTime(0.0) + , mValue(0) + { + } + + /** Construction from a given time and key value */ + aiMeshKey(double time, const unsigned int value) + : mTime (time) + , mValue (value) + {} + + typedef unsigned int elem_type; + + // Comparison operators. For use with std::find(); + bool operator == (const aiMeshKey& o) const { + return o.mValue == this->mValue; + } + bool operator != (const aiMeshKey& o) const { + return o.mValue != this->mValue; + } + + // Relational operators. For use with std::sort(); + bool operator < (const aiMeshKey& o) const { + return mTime < o.mTime; + } + bool operator > (const aiMeshKey& o) const { + return mTime > o.mTime; + } + +#endif +}; + +// --------------------------------------------------------------------------- +/** Binds a morph anim mesh to a specific point in time. */ +struct aiMeshMorphKey +{ + /** The time of this key */ + double mTime; + + /** The values and weights at the time of this key */ + unsigned int *mValues; + double *mWeights; + + /** The number of values and weights */ + unsigned int mNumValuesAndWeights; +#ifdef __cplusplus + aiMeshMorphKey() AI_NO_EXCEPT + : mTime(0.0) + , mValues(nullptr) + , mWeights(nullptr) + , mNumValuesAndWeights(0) + { + + } + + ~aiMeshMorphKey() + { + if (mNumValuesAndWeights && mValues && mWeights) { + delete [] mValues; + delete [] mWeights; + } + } +#endif +}; + +// --------------------------------------------------------------------------- +/** Defines how an animation channel behaves outside the defined time + * range. This corresponds to aiNodeAnim::mPreState and + * aiNodeAnim::mPostState.*/ +enum aiAnimBehaviour +{ + /** The value from the default node transformation is taken*/ + aiAnimBehaviour_DEFAULT = 0x0, + + /** The nearest key value is used without interpolation */ + aiAnimBehaviour_CONSTANT = 0x1, + + /** The value of the nearest two keys is linearly + * extrapolated for the current time value.*/ + aiAnimBehaviour_LINEAR = 0x2, + + /** The animation is repeated. + * + * If the animation key go from n to m and the current + * time is t, use the value at (t-n) % (|m-n|).*/ + aiAnimBehaviour_REPEAT = 0x3, + + /** This value is not used, it is just here to force the + * the compiler to map this enum to a 32 Bit integer */ +#ifndef SWIG + _aiAnimBehaviour_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** Describes the animation of a single node. The name specifies the + * bone/node which is affected by this animation channel. The keyframes + * are given in three separate series of values, one each for position, + * rotation and scaling. The transformation matrix computed from these + * values replaces the node's original transformation matrix at a + * specific time. + * This means all keys are absolute and not relative to the bone default pose. + * The order in which the transformations are applied is + * - as usual - scaling, rotation, translation. + * + * @note All keys are returned in their correct, chronological order. + * Duplicate keys don't pass the validation step. Most likely there + * will be no negative time values, but they are not forbidden also ( so + * implementations need to cope with them! ) */ +struct aiNodeAnim { + /** The name of the node affected by this animation. The node + * must exist and it must be unique.*/ + C_STRUCT aiString mNodeName; + + /** The number of position keys */ + unsigned int mNumPositionKeys; + + /** The position keys of this animation channel. Positions are + * specified as 3D vector. The array is mNumPositionKeys in size. + * + * If there are position keys, there will also be at least one + * scaling and one rotation key.*/ + C_STRUCT aiVectorKey* mPositionKeys; + + /** The number of rotation keys */ + unsigned int mNumRotationKeys; + + /** The rotation keys of this animation channel. Rotations are + * given as quaternions, which are 4D vectors. The array is + * mNumRotationKeys in size. + * + * If there are rotation keys, there will also be at least one + * scaling and one position key. */ + C_STRUCT aiQuatKey* mRotationKeys; + + /** The number of scaling keys */ + unsigned int mNumScalingKeys; + + /** The scaling keys of this animation channel. Scalings are + * specified as 3D vector. The array is mNumScalingKeys in size. + * + * If there are scaling keys, there will also be at least one + * position and one rotation key.*/ + C_STRUCT aiVectorKey* mScalingKeys; + + /** Defines how the animation behaves before the first + * key is encountered. + * + * The default value is aiAnimBehaviour_DEFAULT (the original + * transformation matrix of the affected node is used).*/ + C_ENUM aiAnimBehaviour mPreState; + + /** Defines how the animation behaves after the last + * key was processed. + * + * The default value is aiAnimBehaviour_DEFAULT (the original + * transformation matrix of the affected node is taken).*/ + C_ENUM aiAnimBehaviour mPostState; + +#ifdef __cplusplus + aiNodeAnim() AI_NO_EXCEPT + : mNumPositionKeys( 0 ) + , mPositionKeys( nullptr ) + , mNumRotationKeys( 0 ) + , mRotationKeys( nullptr ) + , mNumScalingKeys( 0 ) + , mScalingKeys( nullptr ) + , mPreState( aiAnimBehaviour_DEFAULT ) + , mPostState( aiAnimBehaviour_DEFAULT ) { + // empty + } + + ~aiNodeAnim() { + delete [] mPositionKeys; + delete [] mRotationKeys; + delete [] mScalingKeys; + } +#endif // __cplusplus +}; + +// --------------------------------------------------------------------------- +/** Describes vertex-based animations for a single mesh or a group of + * meshes. Meshes carry the animation data for each frame in their + * aiMesh::mAnimMeshes array. The purpose of aiMeshAnim is to + * define keyframes linking each mesh attachment to a particular + * point in time. */ +struct aiMeshAnim +{ + /** Name of the mesh to be animated. An empty string is not allowed, + * animated meshes need to be named (not necessarily uniquely, + * the name can basically serve as wild-card to select a group + * of meshes with similar animation setup)*/ + C_STRUCT aiString mName; + + /** Size of the #mKeys array. Must be 1, at least. */ + unsigned int mNumKeys; + + /** Key frames of the animation. May not be NULL. */ + C_STRUCT aiMeshKey* mKeys; + +#ifdef __cplusplus + + aiMeshAnim() AI_NO_EXCEPT + : mNumKeys() + , mKeys() + {} + + ~aiMeshAnim() + { + delete[] mKeys; + } + +#endif +}; + +// --------------------------------------------------------------------------- +/** Describes a morphing animation of a given mesh. */ +struct aiMeshMorphAnim +{ + /** Name of the mesh to be animated. An empty string is not allowed, + * animated meshes need to be named (not necessarily uniquely, + * the name can basically serve as wildcard to select a group + * of meshes with similar animation setup)*/ + C_STRUCT aiString mName; + + /** Size of the #mKeys array. Must be 1, at least. */ + unsigned int mNumKeys; + + /** Key frames of the animation. May not be NULL. */ + C_STRUCT aiMeshMorphKey* mKeys; + +#ifdef __cplusplus + + aiMeshMorphAnim() AI_NO_EXCEPT + : mNumKeys() + , mKeys() + {} + + ~aiMeshMorphAnim() + { + delete[] mKeys; + } + +#endif +}; + +// --------------------------------------------------------------------------- +/** An animation consists of key-frame data for a number of nodes. For + * each node affected by the animation a separate series of data is given.*/ +struct aiAnimation { + /** The name of the animation. If the modeling package this data was + * exported from does support only a single animation channel, this + * name is usually empty (length is zero). */ + C_STRUCT aiString mName; + + /** Duration of the animation in ticks. */ + double mDuration; + + /** Ticks per second. 0 if not specified in the imported file */ + double mTicksPerSecond; + + /** The number of bone animation channels. Each channel affects + * a single node. */ + unsigned int mNumChannels; + + /** The node animation channels. Each channel affects a single node. + * The array is mNumChannels in size. */ + C_STRUCT aiNodeAnim** mChannels; + + + /** The number of mesh animation channels. Each channel affects + * a single mesh and defines vertex-based animation. */ + unsigned int mNumMeshChannels; + + /** The mesh animation channels. Each channel affects a single mesh. + * The array is mNumMeshChannels in size. */ + C_STRUCT aiMeshAnim** mMeshChannels; + + /** The number of mesh animation channels. Each channel affects + * a single mesh and defines morphing animation. */ + unsigned int mNumMorphMeshChannels; + + /** The morph mesh animation channels. Each channel affects a single mesh. + * The array is mNumMorphMeshChannels in size. */ + C_STRUCT aiMeshMorphAnim **mMorphMeshChannels; + +#ifdef __cplusplus + aiAnimation() AI_NO_EXCEPT + : mDuration(-1.) + , mTicksPerSecond(0.) + , mNumChannels(0) + , mChannels(nullptr) + , mNumMeshChannels(0) + , mMeshChannels(nullptr) + , mNumMorphMeshChannels(0) + , mMorphMeshChannels(nullptr) { + // empty + } + + ~aiAnimation() { + // DO NOT REMOVE THIS ADDITIONAL CHECK + if ( mNumChannels && mChannels ) { + for( unsigned int a = 0; a < mNumChannels; a++) { + delete mChannels[ a ]; + } + + delete [] mChannels; + } + if (mNumMeshChannels && mMeshChannels) { + for( unsigned int a = 0; a < mNumMeshChannels; a++) { + delete mMeshChannels[a]; + } + + delete [] mMeshChannels; + } + if (mNumMorphMeshChannels && mMorphMeshChannels) { + for( unsigned int a = 0; a < mNumMorphMeshChannels; a++) { + delete mMorphMeshChannels[a]; + } + + delete [] mMorphMeshChannels; + } + } +#endif // __cplusplus +}; + +#ifdef __cplusplus + +} + +/// @brief Some C++ utilities for inter- and extrapolation +namespace Assimp { + +// --------------------------------------------------------------------------- +/** + * @brief CPP-API: Utility class to simplify interpolations of various data types. + * + * The type of interpolation is chosen automatically depending on the + * types of the arguments. + */ +template <typename T> +struct Interpolator +{ + // ------------------------------------------------------------------ + /** @brief Get the result of the interpolation between a,b. + * + * The interpolation algorithm depends on the type of the operands. + * aiQuaternion's and aiQuatKey's SLERP, the rest does a simple + * linear interpolation. */ + void operator () (T& out,const T& a, const T& b, ai_real d) const { + out = a + (b-a)*d; + } +}; // ! Interpolator <T> + +//! @cond Never + +template <> +struct Interpolator <aiQuaternion> { + void operator () (aiQuaternion& out,const aiQuaternion& a, + const aiQuaternion& b, ai_real d) const + { + aiQuaternion::Interpolate(out,a,b,d); + } +}; // ! Interpolator <aiQuaternion> + +template <> +struct Interpolator <unsigned int> { + void operator () (unsigned int& out,unsigned int a, + unsigned int b, ai_real d) const + { + out = d>0.5f ? b : a; + } +}; // ! Interpolator <aiQuaternion> + +template <> +struct Interpolator<aiVectorKey> { + void operator () (aiVector3D& out,const aiVectorKey& a, + const aiVectorKey& b, ai_real d) const + { + Interpolator<aiVector3D> ipl; + ipl(out,a.mValue,b.mValue,d); + } +}; // ! Interpolator <aiVectorKey> + +template <> +struct Interpolator<aiQuatKey> { + void operator () (aiQuaternion& out, const aiQuatKey& a, + const aiQuatKey& b, ai_real d) const + { + Interpolator<aiQuaternion> ipl; + ipl(out,a.mValue,b.mValue,d); + } +}; // ! Interpolator <aiQuatKey> + +template <> +struct Interpolator<aiMeshKey> { + void operator () (unsigned int& out, const aiMeshKey& a, + const aiMeshKey& b, ai_real d) const + { + Interpolator<unsigned int> ipl; + ipl(out,a.mValue,b.mValue,d); + } +}; // ! Interpolator <aiQuatKey> + +//! @endcond + +} // ! end namespace Assimp + +#endif // __cplusplus + +#endif // AI_ANIM_H_INC diff --git a/thirdparty/assimp/include/assimp/camera.h b/thirdparty/assimp/include/assimp/camera.h new file mode 100644 index 0000000000..99daf69934 --- /dev/null +++ b/thirdparty/assimp/include/assimp/camera.h @@ -0,0 +1,226 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file camera.h + * @brief Defines the aiCamera data structure + */ + +#pragma once +#ifndef AI_CAMERA_H_INC +#define AI_CAMERA_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** Helper structure to describe a virtual camera. + * + * Cameras have a representation in the node graph and can be animated. + * An important aspect is that the camera itself is also part of the + * scenegraph. This means, any values such as the look-at vector are not + * *absolute*, they're <b>relative</b> to the coordinate system defined + * by the node which corresponds to the camera. This allows for camera + * animations. For static cameras parameters like the 'look-at' or 'up' vectors + * are usually specified directly in aiCamera, but beware, they could also + * be encoded in the node transformation. The following (pseudo)code sample + * shows how to do it: <br><br> + * @code + * // Get the camera matrix for a camera at a specific time + * // if the node hierarchy for the camera does not contain + * // at least one animated node this is a static computation + * get-camera-matrix (node sceneRoot, camera cam) : matrix + * { + * node cnd = find-node-for-camera(cam) + * matrix cmt = identity() + * + * // as usual - get the absolute camera transformation for this frame + * for each node nd in hierarchy from sceneRoot to cnd + * matrix cur + * if (is-animated(nd)) + * cur = eval-animation(nd) + * else cur = nd->mTransformation; + * cmt = mult-matrices( cmt, cur ) + * end for + * + * // now multiply with the camera's own local transform + * cam = mult-matrices (cam, get-camera-matrix(cmt) ) + * } + * @endcode + * + * @note some file formats (such as 3DS, ASE) export a "target point" - + * the point the camera is looking at (it can even be animated). Assimp + * writes the target point as a subnode of the camera's main node, + * called "<camName>.Target". However this is just additional information + * then the transformation tracks of the camera main node make the + * camera already look in the right direction. + * +*/ +struct aiCamera +{ + /** The name of the camera. + * + * There must be a node in the scenegraph with the same name. + * This node specifies the position of the camera in the scene + * hierarchy and can be animated. + */ + C_STRUCT aiString mName; + + /** Position of the camera relative to the coordinate space + * defined by the corresponding node. + * + * The default value is 0|0|0. + */ + C_STRUCT aiVector3D mPosition; + + + /** 'Up' - vector of the camera coordinate system relative to + * the coordinate space defined by the corresponding node. + * + * The 'right' vector of the camera coordinate system is + * the cross product of the up and lookAt vectors. + * The default value is 0|1|0. The vector + * may be normalized, but it needn't. + */ + C_STRUCT aiVector3D mUp; + + + /** 'LookAt' - vector of the camera coordinate system relative to + * the coordinate space defined by the corresponding node. + * + * This is the viewing direction of the user. + * The default value is 0|0|1. The vector + * may be normalized, but it needn't. + */ + C_STRUCT aiVector3D mLookAt; + + + /** Half horizontal field of view angle, in radians. + * + * The field of view angle is the angle between the center + * line of the screen and the left or right border. + * The default value is 1/4PI. + */ + float mHorizontalFOV; + + /** Distance of the near clipping plane from the camera. + * + * The value may not be 0.f (for arithmetic reasons to prevent + * a division through zero). The default value is 0.1f. + */ + float mClipPlaneNear; + + /** Distance of the far clipping plane from the camera. + * + * The far clipping plane must, of course, be further away than the + * near clipping plane. The default value is 1000.f. The ratio + * between the near and the far plane should not be too + * large (between 1000-10000 should be ok) to avoid floating-point + * inaccuracies which could lead to z-fighting. + */ + float mClipPlaneFar; + + + /** Screen aspect ratio. + * + * This is the ration between the width and the height of the + * screen. Typical values are 4/3, 1/2 or 1/1. This value is + * 0 if the aspect ratio is not defined in the source file. + * 0 is also the default value. + */ + float mAspect; + +#ifdef __cplusplus + + aiCamera() AI_NO_EXCEPT + : mUp (0.f,1.f,0.f) + , mLookAt (0.f,0.f,1.f) + , mHorizontalFOV (0.25f * (float)AI_MATH_PI) + , mClipPlaneNear (0.1f) + , mClipPlaneFar (1000.f) + , mAspect (0.f) + {} + + /** @brief Get a *right-handed* camera matrix from me + * @param out Camera matrix to be filled + */ + void GetCameraMatrix (aiMatrix4x4& out) const + { + /** todo: test ... should work, but i'm not absolutely sure */ + + /** We don't know whether these vectors are already normalized ...*/ + aiVector3D zaxis = mLookAt; zaxis.Normalize(); + aiVector3D yaxis = mUp; yaxis.Normalize(); + aiVector3D xaxis = mUp^mLookAt; xaxis.Normalize(); + + out.a4 = -(xaxis * mPosition); + out.b4 = -(yaxis * mPosition); + out.c4 = -(zaxis * mPosition); + + out.a1 = xaxis.x; + out.a2 = xaxis.y; + out.a3 = xaxis.z; + + out.b1 = yaxis.x; + out.b2 = yaxis.y; + out.b3 = yaxis.z; + + out.c1 = zaxis.x; + out.c2 = zaxis.y; + out.c3 = zaxis.z; + + out.d1 = out.d2 = out.d3 = 0.f; + out.d4 = 1.f; + } + +#endif +}; + + +#ifdef __cplusplus +} +#endif + +#endif // AI_CAMERA_H_INC diff --git a/thirdparty/assimp/include/assimp/cexport.h b/thirdparty/assimp/include/assimp/cexport.h new file mode 100644 index 0000000000..1d62dc26b3 --- /dev/null +++ b/thirdparty/assimp/include/assimp/cexport.h @@ -0,0 +1,261 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2011, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file cexport.h +* @brief Defines the C-API for the Assimp export interface +*/ +#pragma once +#ifndef AI_EXPORT_H_INC +#define AI_EXPORT_H_INC + +#ifndef ASSIMP_BUILD_NO_EXPORT + +// Public ASSIMP data structures +#include <assimp/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct aiScene; // aiScene.h +struct aiFileIO; // aiFileIO.h + +// -------------------------------------------------------------------------------- +/** Describes an file format which Assimp can export to. Use #aiGetExportFormatCount() to +* learn how many export formats the current Assimp build supports and #aiGetExportFormatDescription() +* to retrieve a description of an export format option. +*/ +struct aiExportFormatDesc +{ + /// a short string ID to uniquely identify the export format. Use this ID string to + /// specify which file format you want to export to when calling #aiExportScene(). + /// Example: "dae" or "obj" + const char* id; + + /// A short description of the file format to present to users. Useful if you want + /// to allow the user to select an export format. + const char* description; + + /// Recommended file extension for the exported file in lower case. + const char* fileExtension; +}; + + +// -------------------------------------------------------------------------------- +/** Returns the number of export file formats available in the current Assimp build. + * Use aiGetExportFormatDescription() to retrieve infos of a specific export format. + */ +ASSIMP_API size_t aiGetExportFormatCount(void); + +// -------------------------------------------------------------------------------- +/** Returns a description of the nth export file format. Use #aiGetExportFormatCount() + * to learn how many export formats are supported. The description must be released by + * calling aiReleaseExportFormatDescription afterwards. + * @param pIndex Index of the export format to retrieve information for. Valid range is + * 0 to #aiGetExportFormatCount() + * @return A description of that specific export format. NULL if pIndex is out of range. + */ +ASSIMP_API const C_STRUCT aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex); + +// -------------------------------------------------------------------------------- +/** Release a description of the nth export file format. Must be returned by +* aiGetExportFormatDescription +* @param desc Pointer to the description +*/ +ASSIMP_API void aiReleaseExportFormatDescription( const C_STRUCT aiExportFormatDesc *desc ); + +// -------------------------------------------------------------------------------- +/** Create a modifiable copy of a scene. + * This is useful to import files via Assimp, change their topology and + * export them again. Since the scene returned by the various importer functions + * is const, a modifiable copy is needed. + * @param pIn Valid scene to be copied + * @param pOut Receives a modifyable copy of the scene. Use aiFreeScene() to + * delete it again. + */ +ASSIMP_API void aiCopyScene(const C_STRUCT aiScene* pIn, + C_STRUCT aiScene** pOut); + + +// -------------------------------------------------------------------------------- +/** Frees a scene copy created using aiCopyScene() */ +ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn); + +// -------------------------------------------------------------------------------- +/** Exports the given scene to a chosen file format and writes the result file(s) to disk. +* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function. +* The scene is expected to conform to Assimp's Importer output format as specified +* in the @link data Data Structures Page @endlink. In short, this means the model data +* should use a right-handed coordinate systems, face winding should be counter-clockwise +* and the UV coordinate origin is assumed to be in the upper left. If your input data +* uses different conventions, have a look at the last parameter. +* @param pFormatId ID string to specify to which format you want to export to. Use +* aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available. +* @param pFileName Output file to write +* @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated +* flags, but in reality only a subset of them makes sense here. Specifying +* 'preprocessing' flags is useful if the input scene does not conform to +* Assimp's default conventions as specified in the @link data Data Structures Page @endlink. +* In short, this means the geometry data should use a right-handed coordinate systems, face +* winding should be counter-clockwise and the UV coordinate origin is assumed to be in +* the upper left. The #aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and +* #aiProcess_FlipWindingOrder flags are used in the import side to allow users +* to have those defaults automatically adapted to their conventions. Specifying those flags +* for exporting has the opposite effect, respectively. Some other of the +* #aiPostProcessSteps enumerated values may be useful as well, but you'll need +* to try out what their effect on the exported file is. Many formats impose +* their own restrictions on the structure of the geometry stored therein, +* so some preprocessing may have little or no effect at all, or may be +* redundant as exporters would apply them anyhow. A good example +* is triangulation - whilst you can enforce it by specifying +* the #aiProcess_Triangulate flag, most export formats support only +* triangulate data so they would run the step anyway. +* +* If assimp detects that the input scene was directly taken from the importer side of +* the library (i.e. not copied using aiCopyScene and potetially modified afterwards), +* any postprocessing steps already applied to the scene will not be applied again, unless +* they show non-idempotent behaviour (#aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and +* #aiProcess_FlipWindingOrder). +* @return a status code indicating the result of the export +* @note Use aiCopyScene() to get a modifiable copy of a previously +* imported scene. +*/ +ASSIMP_API aiReturn aiExportScene( const C_STRUCT aiScene* pScene, + const char* pFormatId, + const char* pFileName, + unsigned int pPreprocessing); + + +// -------------------------------------------------------------------------------- +/** Exports the given scene to a chosen file format using custom IO logic supplied by you. +* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function. +* @param pFormatId ID string to specify to which format you want to export to. Use +* aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available. +* @param pFileName Output file to write +* @param pIO custom IO implementation to be used. Use this if you use your own storage methods. +* If none is supplied, a default implementation using standard file IO is used. Note that +* #aiExportSceneToBlob is provided as convenience function to export to memory buffers. +* @param pPreprocessing Please see the documentation for #aiExportScene +* @return a status code indicating the result of the export +* @note Include <aiFileIO.h> for the definition of #aiFileIO. +* @note Use aiCopyScene() to get a modifiable copy of a previously +* imported scene. +*/ +ASSIMP_API aiReturn aiExportSceneEx( const C_STRUCT aiScene* pScene, + const char* pFormatId, + const char* pFileName, + C_STRUCT aiFileIO* pIO, + unsigned int pPreprocessing ); + +// -------------------------------------------------------------------------------- +/** Describes a blob of exported scene data. Use #aiExportSceneToBlob() to create a blob containing an +* exported scene. The memory referred by this structure is owned by Assimp. +* to free its resources. Don't try to free the memory on your side - it will crash for most build configurations +* due to conflicting heaps. +* +* Blobs can be nested - each blob may reference another blob, which may in turn reference another blob and so on. +* This is used when exporters write more than one output file for a given #aiScene. See the remarks for +* #aiExportDataBlob::name for more information. +*/ +struct aiExportDataBlob +{ + /// Size of the data in bytes + size_t size; + + /// The data. + void* data; + + /** Name of the blob. An empty string always + indicates the first (and primary) blob, + which contains the actual file data. + Any other blobs are auxiliary files produced + by exporters (i.e. material files). Existence + of such files depends on the file format. Most + formats don't split assets across multiple files. + + If used, blob names usually contain the file + extension that should be used when writing + the data to disc. + */ + C_STRUCT aiString name; + + /** Pointer to the next blob in the chain or NULL if there is none. */ + C_STRUCT aiExportDataBlob * next; + +#ifdef __cplusplus + /// Default constructor + aiExportDataBlob() { size = 0; data = next = NULL; } + /// Releases the data + ~aiExportDataBlob() { delete [] static_cast<unsigned char*>( data ); delete next; } + +private: + // no copying + aiExportDataBlob(const aiExportDataBlob& ); + aiExportDataBlob& operator= (const aiExportDataBlob& ); +#endif // __cplusplus +}; + +// -------------------------------------------------------------------------------- +/** Exports the given scene to a chosen file format. Returns the exported data as a binary blob which +* you can write into a file or something. When you're done with the data, use #aiReleaseExportBlob() +* to free the resources associated with the export. +* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function. +* @param pFormatId ID string to specify to which format you want to export to. Use +* #aiGetExportFormatCount() / #aiGetExportFormatDescription() to learn which export formats are available. +* @param pPreprocessing Please see the documentation for #aiExportScene +* @return the exported data or NULL in case of error +*/ +ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const C_STRUCT aiScene* pScene, const char* pFormatId, + unsigned int pPreprocessing ); + +// -------------------------------------------------------------------------------- +/** Releases the memory associated with the given exported data. Use this function to free a data blob +* returned by aiExportScene(). +* @param pData the data blob returned by #aiExportSceneToBlob +*/ +ASSIMP_API void aiReleaseExportBlob( const C_STRUCT aiExportDataBlob* pData ); + +#ifdef __cplusplus +} +#endif + +#endif // ASSIMP_BUILD_NO_EXPORT +#endif // AI_EXPORT_H_INC diff --git a/thirdparty/assimp/include/assimp/cfileio.h b/thirdparty/assimp/include/assimp/cfileio.h new file mode 100644 index 0000000000..8f7ca45469 --- /dev/null +++ b/thirdparty/assimp/include/assimp/cfileio.h @@ -0,0 +1,138 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file cfileio.h + * @brief Defines generic C routines to access memory-mapped files + */ +#pragma once +#ifndef AI_FILEIO_H_INC +#define AI_FILEIO_H_INC + +#include <assimp/types.h> +#ifdef __cplusplus +extern "C" { +#endif +struct aiFileIO; +struct aiFile; + +// aiFile callbacks +typedef size_t (*aiFileWriteProc) (C_STRUCT aiFile*, const char*, size_t, size_t); +typedef size_t (*aiFileReadProc) (C_STRUCT aiFile*, char*, size_t,size_t); +typedef size_t (*aiFileTellProc) (C_STRUCT aiFile*); +typedef void (*aiFileFlushProc) (C_STRUCT aiFile*); +typedef C_ENUM aiReturn (*aiFileSeek) (C_STRUCT aiFile*, size_t, C_ENUM aiOrigin); + +// aiFileIO callbacks +typedef C_STRUCT aiFile* (*aiFileOpenProc) (C_STRUCT aiFileIO*, const char*, const char*); +typedef void (*aiFileCloseProc) (C_STRUCT aiFileIO*, C_STRUCT aiFile*); + +// Represents user-defined data +typedef char* aiUserData; + +// ---------------------------------------------------------------------------------- +/** @brief C-API: File system callbacks + * + * Provided are functions to open and close files. Supply a custom structure to + * the import function. If you don't, a default implementation is used. Use custom + * file systems to enable reading from other sources, such as ZIPs + * or memory locations. */ +struct aiFileIO +{ + /** Function used to open a new file + */ + aiFileOpenProc OpenProc; + + /** Function used to close an existing file + */ + aiFileCloseProc CloseProc; + + /** User-defined, opaque data */ + aiUserData UserData; +}; + +// ---------------------------------------------------------------------------------- +/** @brief C-API: File callbacks + * + * Actually, it's a data structure to wrap a set of fXXXX (e.g fopen) + * replacement functions. + * + * The default implementation of the functions utilizes the fXXX functions from + * the CRT. However, you can supply a custom implementation to Assimp by + * delivering a custom aiFileIO. Use this to enable reading from other sources, + * such as ZIP archives or memory locations. */ +struct aiFile +{ + /** Callback to read from a file */ + aiFileReadProc ReadProc; + + /** Callback to write to a file */ + aiFileWriteProc WriteProc; + + /** Callback to retrieve the current position of + * the file cursor (ftell()) + */ + aiFileTellProc TellProc; + + /** Callback to retrieve the size of the file, + * in bytes + */ + aiFileTellProc FileSizeProc; + + /** Callback to set the current position + * of the file cursor (fseek()) + */ + aiFileSeek SeekProc; + + /** Callback to flush the file contents + */ + aiFileFlushProc FlushProc; + + /** User-defined, opaque data + */ + aiUserData UserData; +}; + +#ifdef __cplusplus +} +#endif +#endif // AI_FILEIO_H_INC diff --git a/thirdparty/assimp/include/assimp/cimport.h b/thirdparty/assimp/include/assimp/cimport.h new file mode 100644 index 0000000000..dbd10f1370 --- /dev/null +++ b/thirdparty/assimp/include/assimp/cimport.h @@ -0,0 +1,565 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file cimport.h + * @brief Defines the C-API to the Open Asset Import Library. + */ +#pragma once +#ifndef AI_ASSIMP_H_INC +#define AI_ASSIMP_H_INC + +#include <assimp/types.h> +#include "importerdesc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct aiScene; // aiScene.h +struct aiFileIO; // aiFileIO.h +typedef void (*aiLogStreamCallback)(const char* /* message */, char* /* user */); + +// -------------------------------------------------------------------------------- +/** C-API: Represents a log stream. A log stream receives all log messages and + * streams them _somewhere_. + * @see aiGetPredefinedLogStream + * @see aiAttachLogStream + * @see aiDetachLogStream */ +// -------------------------------------------------------------------------------- +struct aiLogStream +{ + /** callback to be called */ + aiLogStreamCallback callback; + + /** user data to be passed to the callback */ + char* user; +}; + + +// -------------------------------------------------------------------------------- +/** C-API: Represents an opaque set of settings to be used during importing. + * @see aiCreatePropertyStore + * @see aiReleasePropertyStore + * @see aiImportFileExWithProperties + * @see aiSetPropertyInteger + * @see aiSetPropertyFloat + * @see aiSetPropertyString + * @see aiSetPropertyMatrix + */ +// -------------------------------------------------------------------------------- +struct aiPropertyStore { char sentinel; }; + +/** Our own C boolean type */ +typedef int aiBool; + +#define AI_FALSE 0 +#define AI_TRUE 1 + +// -------------------------------------------------------------------------------- +/** Reads the given file and returns its content. + * + * If the call succeeds, the imported data is returned in an aiScene structure. + * The data is intended to be read-only, it stays property of the ASSIMP + * library and will be stable until aiReleaseImport() is called. After you're + * done with it, call aiReleaseImport() to free the resources associated with + * this file. If the import fails, NULL is returned instead. Call + * aiGetErrorString() to retrieve a human-readable error text. + * @param pFile Path and filename of the file to be imported, + * expected to be a null-terminated c-string. NULL is not a valid value. + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. + * @return Pointer to the imported data or NULL if the import failed. + */ +ASSIMP_API const C_STRUCT aiScene* aiImportFile( + const char* pFile, + unsigned int pFlags); + +// -------------------------------------------------------------------------------- +/** Reads the given file using user-defined I/O functions and returns + * its content. + * + * If the call succeeds, the imported data is returned in an aiScene structure. + * The data is intended to be read-only, it stays property of the ASSIMP + * library and will be stable until aiReleaseImport() is called. After you're + * done with it, call aiReleaseImport() to free the resources associated with + * this file. If the import fails, NULL is returned instead. Call + * aiGetErrorString() to retrieve a human-readable error text. + * @param pFile Path and filename of the file to be imported, + * expected to be a null-terminated c-string. NULL is not a valid value. + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. + * @param pFS aiFileIO structure. Will be used to open the model file itself + * and any other files the loader needs to open. Pass NULL to use the default + * implementation. + * @return Pointer to the imported data or NULL if the import failed. + * @note Include <aiFileIO.h> for the definition of #aiFileIO. + */ +ASSIMP_API const C_STRUCT aiScene* aiImportFileEx( + const char* pFile, + unsigned int pFlags, + C_STRUCT aiFileIO* pFS); + +// -------------------------------------------------------------------------------- +/** Same as #aiImportFileEx, but adds an extra parameter containing importer settings. + * + * @param pFile Path and filename of the file to be imported, + * expected to be a null-terminated c-string. NULL is not a valid value. + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. + * @param pFS aiFileIO structure. Will be used to open the model file itself + * and any other files the loader needs to open. Pass NULL to use the default + * implementation. + * @param pProps #aiPropertyStore instance containing import settings. + * @return Pointer to the imported data or NULL if the import failed. + * @note Include <aiFileIO.h> for the definition of #aiFileIO. + * @see aiImportFileEx + */ +ASSIMP_API const C_STRUCT aiScene* aiImportFileExWithProperties( + const char* pFile, + unsigned int pFlags, + C_STRUCT aiFileIO* pFS, + const C_STRUCT aiPropertyStore* pProps); + +// -------------------------------------------------------------------------------- +/** Reads the given file from a given memory buffer, + * + * If the call succeeds, the contents of the file are returned as a pointer to an + * aiScene object. The returned data is intended to be read-only, the importer keeps + * ownership of the data and will destroy it upon destruction. If the import fails, + * NULL is returned. + * A human-readable error description can be retrieved by calling aiGetErrorString(). + * @param pBuffer Pointer to the file data + * @param pLength Length of pBuffer, in bytes + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #aiApplyPostProcessing(). + * @param pHint An additional hint to the library. If this is a non empty string, + * the library looks for a loader to support the file extension specified by pHint + * and passes the file to the first matching loader. If this loader is unable to + * completely the request, the library continues and tries to determine the file + * format on its own, a task that may or may not be successful. + * Check the return value, and you'll know ... + * @return A pointer to the imported data, NULL if the import failed. + * + * @note This is a straightforward way to decode models from memory + * buffers, but it doesn't handle model formats that spread their + * data across multiple files or even directories. Examples include + * OBJ or MD3, which outsource parts of their material info into + * external scripts. If you need full functionality, provide + * a custom IOSystem to make Assimp find these files and use + * the regular aiImportFileEx()/aiImportFileExWithProperties() API. + */ +ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemory( + const char* pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char* pHint); + +// -------------------------------------------------------------------------------- +/** Same as #aiImportFileFromMemory, but adds an extra parameter containing importer settings. + * + * @param pBuffer Pointer to the file data + * @param pLength Length of pBuffer, in bytes + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #aiApplyPostProcessing(). + * @param pHint An additional hint to the library. If this is a non empty string, + * the library looks for a loader to support the file extension specified by pHint + * and passes the file to the first matching loader. If this loader is unable to + * completely the request, the library continues and tries to determine the file + * format on its own, a task that may or may not be successful. + * Check the return value, and you'll know ... + * @param pProps #aiPropertyStore instance containing import settings. + * @return A pointer to the imported data, NULL if the import failed. + * + * @note This is a straightforward way to decode models from memory + * buffers, but it doesn't handle model formats that spread their + * data across multiple files or even directories. Examples include + * OBJ or MD3, which outsource parts of their material info into + * external scripts. If you need full functionality, provide + * a custom IOSystem to make Assimp find these files and use + * the regular aiImportFileEx()/aiImportFileExWithProperties() API. + * @see aiImportFileFromMemory + */ +ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemoryWithProperties( + const char* pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char* pHint, + const C_STRUCT aiPropertyStore* pProps); + +// -------------------------------------------------------------------------------- +/** Apply post-processing to an already-imported scene. + * + * This is strictly equivalent to calling #aiImportFile()/#aiImportFileEx with the + * same flags. However, you can use this separate function to inspect the imported + * scene first to fine-tune your post-processing setup. + * @param pScene Scene to work on. + * @param pFlags Provide a bitwise combination of the #aiPostProcessSteps flags. + * @return A pointer to the post-processed data. Post processing is done in-place, + * meaning this is still the same #aiScene which you passed for pScene. However, + * _if_ post-processing failed, the scene could now be NULL. That's quite a rare + * case, post processing steps are not really designed to 'fail'. To be exact, + * the #aiProcess_ValidateDataStructure flag is currently the only post processing step + * which can actually cause the scene to be reset to NULL. + */ +ASSIMP_API const C_STRUCT aiScene* aiApplyPostProcessing( + const C_STRUCT aiScene* pScene, + unsigned int pFlags); + +// -------------------------------------------------------------------------------- +/** Get one of the predefine log streams. This is the quick'n'easy solution to + * access Assimp's log system. Attaching a log stream can slightly reduce Assimp's + * overall import performance. + * + * Usage is rather simple (this will stream the log to a file, named log.txt, and + * the stdout stream of the process: + * @code + * struct aiLogStream c; + * c = aiGetPredefinedLogStream(aiDefaultLogStream_FILE,"log.txt"); + * aiAttachLogStream(&c); + * c = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT,NULL); + * aiAttachLogStream(&c); + * @endcode + * + * @param pStreams One of the #aiDefaultLogStream enumerated values. + * @param file Solely for the #aiDefaultLogStream_FILE flag: specifies the file to write to. + * Pass NULL for all other flags. + * @return The log stream. callback is set to NULL if something went wrong. + */ +ASSIMP_API C_STRUCT aiLogStream aiGetPredefinedLogStream( + C_ENUM aiDefaultLogStream pStreams, + const char* file); + +// -------------------------------------------------------------------------------- +/** Attach a custom log stream to the libraries' logging system. + * + * Attaching a log stream can slightly reduce Assimp's overall import + * performance. Multiple log-streams can be attached. + * @param stream Describes the new log stream. + * @note To ensure proper destruction of the logging system, you need to manually + * call aiDetachLogStream() on every single log stream you attach. + * Alternatively (for the lazy folks) #aiDetachAllLogStreams is provided. + */ +ASSIMP_API void aiAttachLogStream( + const C_STRUCT aiLogStream* stream); + +// -------------------------------------------------------------------------------- +/** Enable verbose logging. Verbose logging includes debug-related stuff and + * detailed import statistics. This can have severe impact on import performance + * and memory consumption. However, it might be useful to find out why a file + * didn't read correctly. + * @param d AI_TRUE or AI_FALSE, your decision. + */ +ASSIMP_API void aiEnableVerboseLogging(aiBool d); + +// -------------------------------------------------------------------------------- +/** Detach a custom log stream from the libraries' logging system. + * + * This is the counterpart of #aiAttachLogStream. If you attached a stream, + * don't forget to detach it again. + * @param stream The log stream to be detached. + * @return AI_SUCCESS if the log stream has been detached successfully. + * @see aiDetachAllLogStreams + */ +ASSIMP_API C_ENUM aiReturn aiDetachLogStream( + const C_STRUCT aiLogStream* stream); + +// -------------------------------------------------------------------------------- +/** Detach all active log streams from the libraries' logging system. + * This ensures that the logging system is terminated properly and all + * resources allocated by it are actually freed. If you attached a stream, + * don't forget to detach it again. + * @see aiAttachLogStream + * @see aiDetachLogStream + */ +ASSIMP_API void aiDetachAllLogStreams(void); + +// -------------------------------------------------------------------------------- +/** Releases all resources associated with the given import process. + * + * Call this function after you're done with the imported data. + * @param pScene The imported data to release. NULL is a valid value. + */ +ASSIMP_API void aiReleaseImport( + const C_STRUCT aiScene* pScene); + +// -------------------------------------------------------------------------------- +/** Returns the error text of the last failed import process. + * + * @return A textual description of the error that occurred at the last + * import process. NULL if there was no error. There can't be an error if you + * got a non-NULL #aiScene from #aiImportFile/#aiImportFileEx/#aiApplyPostProcessing. + */ +ASSIMP_API const char* aiGetErrorString(void); + +// -------------------------------------------------------------------------------- +/** Returns whether a given file extension is supported by ASSIMP + * + * @param szExtension Extension for which the function queries support for. + * Must include a leading dot '.'. Example: ".3ds", ".md3" + * @return AI_TRUE if the file extension is supported. + */ +ASSIMP_API aiBool aiIsExtensionSupported( + const char* szExtension); + +// -------------------------------------------------------------------------------- +/** Get a list of all file extensions supported by ASSIMP. + * + * If a file extension is contained in the list this does, of course, not + * mean that ASSIMP is able to load all files with this extension. + * @param szOut String to receive the extension list. + * Format of the list: "*.3ds;*.obj;*.dae". NULL is not a valid parameter. + */ +ASSIMP_API void aiGetExtensionList( + C_STRUCT aiString* szOut); + +// -------------------------------------------------------------------------------- +/** Get the approximated storage required by an imported asset + * @param pIn Input asset. + * @param in Data structure to be filled. + */ +ASSIMP_API void aiGetMemoryRequirements( + const C_STRUCT aiScene* pIn, + C_STRUCT aiMemoryInfo* in); + + + +// -------------------------------------------------------------------------------- +/** Create an empty property store. Property stores are used to collect import + * settings. + * @return New property store. Property stores need to be manually destroyed using + * the #aiReleasePropertyStore API function. + */ +ASSIMP_API C_STRUCT aiPropertyStore* aiCreatePropertyStore(void); + +// -------------------------------------------------------------------------------- +/** Delete a property store. + * @param p Property store to be deleted. + */ +ASSIMP_API void aiReleasePropertyStore(C_STRUCT aiPropertyStore* p); + +// -------------------------------------------------------------------------------- +/** Set an integer property. + * + * This is the C-version of #Assimp::Importer::SetPropertyInteger(). In the C + * interface, properties are always shared by all imports. It is not possible to + * specify them per import. + * + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. + * @param szName Name of the configuration property to be set. All supported + * public properties are defined in the config.h header file (AI_CONFIG_XXX). + * @param value New value for the property + */ +ASSIMP_API void aiSetImportPropertyInteger( + C_STRUCT aiPropertyStore* store, + const char* szName, + int value); + +// -------------------------------------------------------------------------------- +/** Set a floating-point property. + * + * This is the C-version of #Assimp::Importer::SetPropertyFloat(). In the C + * interface, properties are always shared by all imports. It is not possible to + * specify them per import. + * + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. + * @param szName Name of the configuration property to be set. All supported + * public properties are defined in the config.h header file (AI_CONFIG_XXX). + * @param value New value for the property + */ +ASSIMP_API void aiSetImportPropertyFloat( + C_STRUCT aiPropertyStore* store, + const char* szName, + ai_real value); + +// -------------------------------------------------------------------------------- +/** Set a string property. + * + * This is the C-version of #Assimp::Importer::SetPropertyString(). In the C + * interface, properties are always shared by all imports. It is not possible to + * specify them per import. + * + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. + * @param szName Name of the configuration property to be set. All supported + * public properties are defined in the config.h header file (AI_CONFIG_XXX). + * @param st New value for the property + */ +ASSIMP_API void aiSetImportPropertyString( + C_STRUCT aiPropertyStore* store, + const char* szName, + const C_STRUCT aiString* st); + +// -------------------------------------------------------------------------------- +/** Set a matrix property. + * + * This is the C-version of #Assimp::Importer::SetPropertyMatrix(). In the C + * interface, properties are always shared by all imports. It is not possible to + * specify them per import. + * + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. + * @param szName Name of the configuration property to be set. All supported + * public properties are defined in the config.h header file (AI_CONFIG_XXX). + * @param mat New value for the property + */ +ASSIMP_API void aiSetImportPropertyMatrix( + C_STRUCT aiPropertyStore* store, + const char* szName, + const C_STRUCT aiMatrix4x4* mat); + +// -------------------------------------------------------------------------------- +/** Construct a quaternion from a 3x3 rotation matrix. + * @param quat Receives the output quaternion. + * @param mat Matrix to 'quaternionize'. + * @see aiQuaternion(const aiMatrix3x3& pRotMatrix) + */ +ASSIMP_API void aiCreateQuaternionFromMatrix( + C_STRUCT aiQuaternion* quat, + const C_STRUCT aiMatrix3x3* mat); + +// -------------------------------------------------------------------------------- +/** Decompose a transformation matrix into its rotational, translational and + * scaling components. + * + * @param mat Matrix to decompose + * @param scaling Receives the scaling component + * @param rotation Receives the rotational component + * @param position Receives the translational component. + * @see aiMatrix4x4::Decompose (aiVector3D&, aiQuaternion&, aiVector3D&) const; + */ +ASSIMP_API void aiDecomposeMatrix( + const C_STRUCT aiMatrix4x4* mat, + C_STRUCT aiVector3D* scaling, + C_STRUCT aiQuaternion* rotation, + C_STRUCT aiVector3D* position); + +// -------------------------------------------------------------------------------- +/** Transpose a 4x4 matrix. + * @param mat Pointer to the matrix to be transposed + */ +ASSIMP_API void aiTransposeMatrix4( + C_STRUCT aiMatrix4x4* mat); + +// -------------------------------------------------------------------------------- +/** Transpose a 3x3 matrix. + * @param mat Pointer to the matrix to be transposed + */ +ASSIMP_API void aiTransposeMatrix3( + C_STRUCT aiMatrix3x3* mat); + +// -------------------------------------------------------------------------------- +/** Transform a vector by a 3x3 matrix + * @param vec Vector to be transformed. + * @param mat Matrix to transform the vector with. + */ +ASSIMP_API void aiTransformVecByMatrix3( + C_STRUCT aiVector3D* vec, + const C_STRUCT aiMatrix3x3* mat); + +// -------------------------------------------------------------------------------- +/** Transform a vector by a 4x4 matrix + * @param vec Vector to be transformed. + * @param mat Matrix to transform the vector with. + */ +ASSIMP_API void aiTransformVecByMatrix4( + C_STRUCT aiVector3D* vec, + const C_STRUCT aiMatrix4x4* mat); + +// -------------------------------------------------------------------------------- +/** Multiply two 4x4 matrices. + * @param dst First factor, receives result. + * @param src Matrix to be multiplied with 'dst'. + */ +ASSIMP_API void aiMultiplyMatrix4( + C_STRUCT aiMatrix4x4* dst, + const C_STRUCT aiMatrix4x4* src); + +// -------------------------------------------------------------------------------- +/** Multiply two 3x3 matrices. + * @param dst First factor, receives result. + * @param src Matrix to be multiplied with 'dst'. + */ +ASSIMP_API void aiMultiplyMatrix3( + C_STRUCT aiMatrix3x3* dst, + const C_STRUCT aiMatrix3x3* src); + +// -------------------------------------------------------------------------------- +/** Get a 3x3 identity matrix. + * @param mat Matrix to receive its personal identity + */ +ASSIMP_API void aiIdentityMatrix3( + C_STRUCT aiMatrix3x3* mat); + +// -------------------------------------------------------------------------------- +/** Get a 4x4 identity matrix. + * @param mat Matrix to receive its personal identity + */ +ASSIMP_API void aiIdentityMatrix4( + C_STRUCT aiMatrix4x4* mat); + +// -------------------------------------------------------------------------------- +/** Returns the number of import file formats available in the current Assimp build. + * Use aiGetImportFormatDescription() to retrieve infos of a specific import format. + */ +ASSIMP_API size_t aiGetImportFormatCount(void); + +// -------------------------------------------------------------------------------- +/** Returns a description of the nth import file format. Use #aiGetImportFormatCount() + * to learn how many import formats are supported. + * @param pIndex Index of the import format to retrieve information for. Valid range is + * 0 to #aiGetImportFormatCount() + * @return A description of that specific import format. NULL if pIndex is out of range. + */ +ASSIMP_API const C_STRUCT aiImporterDesc* aiGetImportFormatDescription( size_t pIndex); +#ifdef __cplusplus +} +#endif + +#endif // AI_ASSIMP_H_INC diff --git a/thirdparty/assimp/include/assimp/color4.h b/thirdparty/assimp/include/assimp/color4.h new file mode 100644 index 0000000000..3c97c8eda2 --- /dev/null +++ b/thirdparty/assimp/include/assimp/color4.h @@ -0,0 +1,104 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file color4.h + * @brief RGBA color structure, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_COLOR4D_H_INC +#define AI_COLOR4D_H_INC + +#include "defs.h" + +#ifdef __cplusplus + +// ---------------------------------------------------------------------------------- +/** Represents a color in Red-Green-Blue space including an +* alpha component. Color values range from 0 to 1. */ +// ---------------------------------------------------------------------------------- +template <typename TReal> +class aiColor4t +{ +public: + aiColor4t() AI_NO_EXCEPT : r(), g(), b(), a() {} + aiColor4t (TReal _r, TReal _g, TReal _b, TReal _a) + : r(_r), g(_g), b(_b), a(_a) {} + explicit aiColor4t (TReal _r) : r(_r), g(_r), b(_r), a(_r) {} + aiColor4t (const aiColor4t& o) = default; + +public: + // combined operators + const aiColor4t& operator += (const aiColor4t& o); + const aiColor4t& operator -= (const aiColor4t& o); + const aiColor4t& operator *= (TReal f); + const aiColor4t& operator /= (TReal f); + +public: + // comparison + bool operator == (const aiColor4t& other) const; + bool operator != (const aiColor4t& other) const; + bool operator < (const aiColor4t& other) const; + + // color tuple access, rgba order + inline TReal operator[](unsigned int i) const; + inline TReal& operator[](unsigned int i); + + /** check whether a color is (close to) black */ + inline bool IsBlack() const; + +public: + + // Red, green, blue and alpha color values + TReal r, g, b, a; +}; // !struct aiColor4D + +typedef aiColor4t<ai_real> aiColor4D; + +#else + +struct aiColor4D { + ai_real r, g, b, a; +}; + +#endif // __cplusplus + +#endif // AI_COLOR4D_H_INC diff --git a/thirdparty/assimp/include/assimp/color4.inl b/thirdparty/assimp/include/assimp/color4.inl new file mode 100644 index 0000000000..3192d55f39 --- /dev/null +++ b/thirdparty/assimp/include/assimp/color4.inl @@ -0,0 +1,205 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file color4.inl + * @brief Inline implementation of aiColor4t<TReal> operators + */ +#pragma once +#ifndef AI_COLOR4D_INL_INC +#define AI_COLOR4D_INL_INC + +#ifdef __cplusplus +#include "color4.h" + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE const aiColor4t<TReal>& aiColor4t<TReal>::operator += (const aiColor4t<TReal>& o) { + r += o.r; g += o.g; b += o.b; a += o.a; + return *this; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE const aiColor4t<TReal>& aiColor4t<TReal>::operator -= (const aiColor4t<TReal>& o) { + r -= o.r; g -= o.g; b -= o.b; a -= o.a; + return *this; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE const aiColor4t<TReal>& aiColor4t<TReal>::operator *= (TReal f) { + r *= f; g *= f; b *= f; a *= f; + return *this; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE const aiColor4t<TReal>& aiColor4t<TReal>::operator /= (TReal f) { + r /= f; g /= f; b /= f; a /= f; + return *this; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE TReal aiColor4t<TReal>::operator[](unsigned int i) const { + switch ( i ) { + case 0: + return r; + case 1: + return g; + case 2: + return b; + default: + break; + } + return r; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE TReal& aiColor4t<TReal>::operator[](unsigned int i) { + switch ( i ) { + case 0: + return r; + case 1: + return g; + case 2: + return b; + default: + break; + } + return r; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE bool aiColor4t<TReal>::operator== (const aiColor4t<TReal>& other) const { + return r == other.r && g == other.g && b == other.b && a == other.a; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE bool aiColor4t<TReal>::operator!= (const aiColor4t<TReal>& other) const { + return r != other.r || g != other.g || b != other.b || a != other.a; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE bool aiColor4t<TReal>::operator< (const aiColor4t<TReal>& other) const { + return r < other.r || ( + r == other.r && ( + g < other.g || ( + g == other.g && ( + b < other.b || ( + b == other.b && ( + a < other.a + ) + ) + ) + ) + ) + ); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator + (const aiColor4t<TReal>& v1, const aiColor4t<TReal>& v2) { + return aiColor4t<TReal>( v1.r + v2.r, v1.g + v2.g, v1.b + v2.b, v1.a + v2.a); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator - (const aiColor4t<TReal>& v1, const aiColor4t<TReal>& v2) { + return aiColor4t<TReal>( v1.r - v2.r, v1.g - v2.g, v1.b - v2.b, v1.a - v2.a); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator * (const aiColor4t<TReal>& v1, const aiColor4t<TReal>& v2) { + return aiColor4t<TReal>( v1.r * v2.r, v1.g * v2.g, v1.b * v2.b, v1.a * v2.a); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator / (const aiColor4t<TReal>& v1, const aiColor4t<TReal>& v2) { + return aiColor4t<TReal>( v1.r / v2.r, v1.g / v2.g, v1.b / v2.b, v1.a / v2.a); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator * ( TReal f, const aiColor4t<TReal>& v) { + return aiColor4t<TReal>( f*v.r, f*v.g, f*v.b, f*v.a); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator * ( const aiColor4t<TReal>& v, TReal f) { + return aiColor4t<TReal>( f*v.r, f*v.g, f*v.b, f*v.a); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator / ( const aiColor4t<TReal>& v, TReal f) { + return v * (1/f); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator / ( TReal f,const aiColor4t<TReal>& v) { + return aiColor4t<TReal>(f,f,f,f)/v; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator + ( const aiColor4t<TReal>& v, TReal f) { + return aiColor4t<TReal>( f+v.r, f+v.g, f+v.b, f+v.a); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator - ( const aiColor4t<TReal>& v, TReal f) { + return aiColor4t<TReal>( v.r-f, v.g-f, v.b-f, v.a-f); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator + ( TReal f, const aiColor4t<TReal>& v) { + return aiColor4t<TReal>( f+v.r, f+v.g, f+v.b, f+v.a); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE aiColor4t<TReal> operator - ( TReal f, const aiColor4t<TReal>& v) { + return aiColor4t<TReal>( f-v.r, f-v.g, f-v.b, f-v.a); +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline bool aiColor4t<TReal> :: IsBlack() const { + // The alpha component doesn't care here. black is black. + static const TReal epsilon = 10e-3f; + return std::fabs( r ) < epsilon && std::fabs( g ) < epsilon && std::fabs( b ) < epsilon; +} + +#endif // __cplusplus +#endif // AI_VECTOR3D_INL_INC diff --git a/thirdparty/assimp/include/assimp/config.h.in b/thirdparty/assimp/include/assimp/config.h.in new file mode 100644 index 0000000000..a37ff0b8c8 --- /dev/null +++ b/thirdparty/assimp/include/assimp/config.h.in @@ -0,0 +1,992 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file config.h + * @brief Defines constants for configurable properties for the library + * + * Typically these properties are set via + * #Assimp::Importer::SetPropertyFloat, + * #Assimp::Importer::SetPropertyInteger or + * #Assimp::Importer::SetPropertyString, + * depending on the data type of a property. All properties have a + * default value. See the doc for the mentioned methods for more details. + * + * <br><br> + * The corresponding functions for use with the plain-c API are: + * #aiSetImportPropertyInteger, + * #aiSetImportPropertyFloat, + * #aiSetImportPropertyString + */ +#pragma once +#ifndef AI_CONFIG_H_INC +#define AI_CONFIG_H_INC + + +// ########################################################################### +// LIBRARY SETTINGS +// General, global settings +// ########################################################################### + +// --------------------------------------------------------------------------- +/** @brief Enables time measurements. + * + * If enabled, measures the time needed for each part of the loading + * process (i.e. IO time, importing, postprocessing, ..) and dumps + * these timings to the DefaultLogger. See the @link perf Performance + * Page@endlink for more information on this topic. + * + * Property type: bool. Default value: false. + */ +#define AI_CONFIG_GLOB_MEASURE_TIME \ + "GLOB_MEASURE_TIME" + + +// --------------------------------------------------------------------------- +/** @brief Global setting to disable generation of skeleton dummy meshes + * + * Skeleton dummy meshes are generated as a visualization aid in cases which + * the input data contains no geometry, but only animation data. + * Property data type: bool. Default value: false + */ +// --------------------------------------------------------------------------- +#define AI_CONFIG_IMPORT_NO_SKELETON_MESHES \ + "IMPORT_NO_SKELETON_MESHES" + + + +# if 0 // not implemented yet +// --------------------------------------------------------------------------- +/** @brief Set Assimp's multithreading policy. + * + * This setting is ignored if Assimp was built without boost.thread + * support (ASSIMP_BUILD_NO_THREADING, which is implied by ASSIMP_BUILD_BOOST_WORKAROUND). + * Possible values are: -1 to let Assimp decide what to do, 0 to disable + * multithreading entirely and any number larger than 0 to force a specific + * number of threads. Assimp is always free to ignore this settings, which is + * merely a hint. Usually, the default value (-1) will be fine. However, if + * Assimp is used concurrently from multiple user threads, it might be useful + * to limit each Importer instance to a specific number of cores. + * + * For more information, see the @link threading Threading page@endlink. + * Property type: int, default value: -1. + */ +#define AI_CONFIG_GLOB_MULTITHREADING \ + "GLOB_MULTITHREADING" +#endif + +// ########################################################################### +// POST PROCESSING SETTINGS +// Various stuff to fine-tune the behavior of a specific post processing step. +// ########################################################################### + + +// --------------------------------------------------------------------------- +/** @brief Maximum bone count per mesh for the SplitbyBoneCount step. + * + * Meshes are split until the maximum number of bones is reached. The default + * value is AI_SBBC_DEFAULT_MAX_BONES, which may be altered at + * compile-time. + * Property data type: integer. + */ +// --------------------------------------------------------------------------- +#define AI_CONFIG_PP_SBBC_MAX_BONES \ + "PP_SBBC_MAX_BONES" + + +// default limit for bone count +#if (!defined AI_SBBC_DEFAULT_MAX_BONES) +# define AI_SBBC_DEFAULT_MAX_BONES 60 +#endif + + +// --------------------------------------------------------------------------- +/** @brief Specifies the maximum angle that may be between two vertex tangents + * that their tangents and bi-tangents are smoothed. + * + * This applies to the CalcTangentSpace-Step. The angle is specified + * in degrees. The maximum value is 175. + * Property type: float. Default value: 45 degrees + */ +#define AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE \ + "PP_CT_MAX_SMOOTHING_ANGLE" + +// --------------------------------------------------------------------------- +/** @brief Source UV channel for tangent space computation. + * + * The specified channel must exist or an error will be raised. + * Property type: integer. Default value: 0 + */ +// --------------------------------------------------------------------------- +#define AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX \ + "PP_CT_TEXTURE_CHANNEL_INDEX" + +// --------------------------------------------------------------------------- +/** @brief Specifies the maximum angle that may be between two face normals + * at the same vertex position that their are smoothed together. + * + * Sometimes referred to as 'crease angle'. + * This applies to the GenSmoothNormals-Step. The angle is specified + * in degrees, so 180 is PI. The default value is 175 degrees (all vertex + * normals are smoothed). The maximum value is 175, too. Property type: float. + * Warning: setting this option may cause a severe loss of performance. The + * performance is unaffected if the #AI_CONFIG_FAVOUR_SPEED flag is set but + * the output quality may be reduced. + */ +#define AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE \ + "PP_GSN_MAX_SMOOTHING_ANGLE" + + +// --------------------------------------------------------------------------- +/** @brief Sets the colormap (= palette) to be used to decode embedded + * textures in MDL (Quake or 3DGS) files. + * + * This must be a valid path to a file. The file is 768 (256*3) bytes + * large and contains RGB triplets for each of the 256 palette entries. + * The default value is colormap.lmp. If the file is not found, + * a default palette (from Quake 1) is used. + * Property type: string. + */ +#define AI_CONFIG_IMPORT_MDL_COLORMAP \ + "IMPORT_MDL_COLORMAP" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_RemoveRedundantMaterials step to + * keep materials matching a name in a given list. + * + * This is a list of 1 to n strings, ' ' serves as delimiter character. + * Identifiers containing whitespaces must be enclosed in *single* + * quotation marks. For example:<tt> + * "keep-me and_me_to anotherMaterialToBeKept \'name with whitespace\'"</tt>. + * If a material matches on of these names, it will not be modified or + * removed by the postprocessing step nor will other materials be replaced + * by a reference to it. <br> + * This option might be useful if you are using some magic material names + * to pass additional semantics through the content pipeline. This ensures + * they won't be optimized away, but a general optimization is still + * performed for materials not contained in the list. + * Property type: String. Default value: n/a + * @note Linefeeds, tabs or carriage returns are treated as whitespace. + * Material names are case sensitive. + */ +#define AI_CONFIG_PP_RRM_EXCLUDE_LIST \ + "PP_RRM_EXCLUDE_LIST" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_PreTransformVertices step to + * keep the scene hierarchy. Meshes are moved to worldspace, but + * no optimization is performed (read: meshes with equal materials are not + * joined. The total number of meshes won't change). + * + * This option could be of use for you if the scene hierarchy contains + * important additional information which you intend to parse. + * For rendering, you can still render all meshes in the scene without + * any transformations. + * Property type: bool. Default value: false. + */ +#define AI_CONFIG_PP_PTV_KEEP_HIERARCHY \ + "PP_PTV_KEEP_HIERARCHY" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_PreTransformVertices step to normalize + * all vertex components into the [-1,1] range. That is, a bounding box + * for the whole scene is computed, the maximum component is taken and all + * meshes are scaled appropriately (uniformly of course!). + * This might be useful if you don't know the spatial dimension of the input + * data*/ +#define AI_CONFIG_PP_PTV_NORMALIZE \ + "PP_PTV_NORMALIZE" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_PreTransformVertices step to use + * a users defined matrix as the scene root node transformation before + * transforming vertices. + * Property type: bool. Default value: false. + */ +#define AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION \ + "PP_PTV_ADD_ROOT_TRANSFORMATION" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_PreTransformVertices step to use + * a users defined matrix as the scene root node transformation before + * transforming vertices. This property correspond to the 'a1' component + * of the transformation matrix. + * Property type: aiMatrix4x4. + */ +#define AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION \ + "PP_PTV_ROOT_TRANSFORMATION" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_FindDegenerates step to + * remove degenerated primitives from the import - immediately. + * + * The default behaviour converts degenerated triangles to lines and + * degenerated lines to points. See the documentation to the + * #aiProcess_FindDegenerates step for a detailed example of the various ways + * to get rid of these lines and points if you don't want them. + * Property type: bool. Default value: false. + */ +#define AI_CONFIG_PP_FD_REMOVE \ + "PP_FD_REMOVE" + +// --------------------------------------------------------------------------- +/** + * @brief Configures the #aiProcess_FindDegenerates to check the area of a + * trinagle to be greates than e-6. If this is not the case the triangle will + * be removed if #AI_CONFIG_PP_FD_REMOVE is set to true. + */ +#define AI_CONFIG_PP_FD_CHECKAREA \ + "PP_FD_CHECKAREA" + +// --------------------------------------------------------------------------- +/** @brief Configures the #aiProcess_OptimizeGraph step to preserve nodes + * matching a name in a given list. + * + * This is a list of 1 to n strings, ' ' serves as delimiter character. + * Identifiers containing whitespaces must be enclosed in *single* + * quotation marks. For example:<tt> + * "keep-me and_me_to anotherNodeToBeKept \'name with whitespace\'"</tt>. + * If a node matches on of these names, it will not be modified or + * removed by the postprocessing step.<br> + * This option might be useful if you are using some magic node names + * to pass additional semantics through the content pipeline. This ensures + * they won't be optimized away, but a general optimization is still + * performed for nodes not contained in the list. + * Property type: String. Default value: n/a + * @note Linefeeds, tabs or carriage returns are treated as whitespace. + * Node names are case sensitive. + */ +#define AI_CONFIG_PP_OG_EXCLUDE_LIST \ + "PP_OG_EXCLUDE_LIST" + +// --------------------------------------------------------------------------- +/** @brief Set the maximum number of triangles in a mesh. + * + * This is used by the "SplitLargeMeshes" PostProcess-Step to determine + * whether a mesh must be split or not. + * @note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES + * Property type: integer. + */ +#define AI_CONFIG_PP_SLM_TRIANGLE_LIMIT \ + "PP_SLM_TRIANGLE_LIMIT" + +// default value for AI_CONFIG_PP_SLM_TRIANGLE_LIMIT +#if (!defined AI_SLM_DEFAULT_MAX_TRIANGLES) +# define AI_SLM_DEFAULT_MAX_TRIANGLES 1000000 +#endif + +// --------------------------------------------------------------------------- +/** @brief Set the maximum number of vertices in a mesh. + * + * This is used by the "SplitLargeMeshes" PostProcess-Step to determine + * whether a mesh must be split or not. + * @note The default value is AI_SLM_DEFAULT_MAX_VERTICES + * Property type: integer. + */ +#define AI_CONFIG_PP_SLM_VERTEX_LIMIT \ + "PP_SLM_VERTEX_LIMIT" + +// default value for AI_CONFIG_PP_SLM_VERTEX_LIMIT +#if (!defined AI_SLM_DEFAULT_MAX_VERTICES) +# define AI_SLM_DEFAULT_MAX_VERTICES 1000000 +#endif + +// --------------------------------------------------------------------------- +/** @brief Set the maximum number of bones affecting a single vertex + * + * This is used by the #aiProcess_LimitBoneWeights PostProcess-Step. + * @note The default value is AI_LMW_MAX_WEIGHTS + * Property type: integer.*/ +#define AI_CONFIG_PP_LBW_MAX_WEIGHTS \ + "PP_LBW_MAX_WEIGHTS" + +// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS +#if (!defined AI_LMW_MAX_WEIGHTS) +# define AI_LMW_MAX_WEIGHTS 0x4 +#endif // !! AI_LMW_MAX_WEIGHTS + +// --------------------------------------------------------------------------- +/** @brief Lower the deboning threshold in order to remove more bones. + * + * This is used by the #aiProcess_Debone PostProcess-Step. + * @note The default value is AI_DEBONE_THRESHOLD + * Property type: float.*/ +#define AI_CONFIG_PP_DB_THRESHOLD \ + "PP_DB_THRESHOLD" + +// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS +#if (!defined AI_DEBONE_THRESHOLD) +# define AI_DEBONE_THRESHOLD 1.0f +#endif // !! AI_DEBONE_THRESHOLD + +// --------------------------------------------------------------------------- +/** @brief Require all bones qualify for deboning before removing any + * + * This is used by the #aiProcess_Debone PostProcess-Step. + * @note The default value is 0 + * Property type: bool.*/ +#define AI_CONFIG_PP_DB_ALL_OR_NONE \ + "PP_DB_ALL_OR_NONE" + +/** @brief Default value for the #AI_CONFIG_PP_ICL_PTCACHE_SIZE property + */ +#ifndef PP_ICL_PTCACHE_SIZE +# define PP_ICL_PTCACHE_SIZE 12 +#endif + +// --------------------------------------------------------------------------- +/** @brief Set the size of the post-transform vertex cache to optimize the + * vertices for. This configures the #aiProcess_ImproveCacheLocality step. + * + * The size is given in vertices. Of course you can't know how the vertex + * format will exactly look like after the import returns, but you can still + * guess what your meshes will probably have. + * @note The default value is #PP_ICL_PTCACHE_SIZE. That results in slight + * performance improvements for most nVidia/AMD cards since 2002. + * Property type: integer. + */ +#define AI_CONFIG_PP_ICL_PTCACHE_SIZE "PP_ICL_PTCACHE_SIZE" + +// --------------------------------------------------------------------------- +/** @brief Enumerates components of the aiScene and aiMesh data structures + * that can be excluded from the import using the #aiProcess_RemoveComponent step. + * + * See the documentation to #aiProcess_RemoveComponent for more details. + */ +enum aiComponent +{ + /** Normal vectors */ +#ifdef SWIG + aiComponent_NORMALS = 0x2, +#else + aiComponent_NORMALS = 0x2u, +#endif + + /** Tangents and bitangents go always together ... */ +#ifdef SWIG + aiComponent_TANGENTS_AND_BITANGENTS = 0x4, +#else + aiComponent_TANGENTS_AND_BITANGENTS = 0x4u, +#endif + + /** ALL color sets + * Use aiComponent_COLORn(N) to specify the N'th set */ + aiComponent_COLORS = 0x8, + + /** ALL texture UV sets + * aiComponent_TEXCOORDn(N) to specify the N'th set */ + aiComponent_TEXCOORDS = 0x10, + + /** Removes all bone weights from all meshes. + * The scenegraph nodes corresponding to the bones are NOT removed. + * use the #aiProcess_OptimizeGraph step to do this */ + aiComponent_BONEWEIGHTS = 0x20, + + /** Removes all node animations (aiScene::mAnimations). + * The corresponding scenegraph nodes are NOT removed. + * use the #aiProcess_OptimizeGraph step to do this */ + aiComponent_ANIMATIONS = 0x40, + + /** Removes all embedded textures (aiScene::mTextures) */ + aiComponent_TEXTURES = 0x80, + + /** Removes all light sources (aiScene::mLights). + * The corresponding scenegraph nodes are NOT removed. + * use the #aiProcess_OptimizeGraph step to do this */ + aiComponent_LIGHTS = 0x100, + + /** Removes all cameras (aiScene::mCameras). + * The corresponding scenegraph nodes are NOT removed. + * use the #aiProcess_OptimizeGraph step to do this */ + aiComponent_CAMERAS = 0x200, + + /** Removes all meshes (aiScene::mMeshes). */ + aiComponent_MESHES = 0x400, + + /** Removes all materials. One default material will + * be generated, so aiScene::mNumMaterials will be 1. */ + aiComponent_MATERIALS = 0x800, + + + /** This value is not used. It is just there to force the + * compiler to map this enum to a 32 Bit integer. */ +#ifndef SWIG + _aiComponent_Force32Bit = 0x9fffffff +#endif +}; + +// Remove a specific color channel 'n' +#define aiComponent_COLORSn(n) (1u << (n+20u)) + +// Remove a specific UV channel 'n' +#define aiComponent_TEXCOORDSn(n) (1u << (n+25u)) + +// --------------------------------------------------------------------------- +/** @brief Input parameter to the #aiProcess_RemoveComponent step: + * Specifies the parts of the data structure to be removed. + * + * See the documentation to this step for further details. The property + * is expected to be an integer, a bitwise combination of the + * #aiComponent flags defined above in this header. The default + * value is 0. Important: if no valid mesh is remaining after the + * step has been executed (e.g you thought it was funny to specify ALL + * of the flags defined above) the import FAILS. Mainly because there is + * no data to work on anymore ... + */ +#define AI_CONFIG_PP_RVC_FLAGS \ + "PP_RVC_FLAGS" + +// --------------------------------------------------------------------------- +/** @brief Input parameter to the #aiProcess_SortByPType step: + * Specifies which primitive types are removed by the step. + * + * This is a bitwise combination of the aiPrimitiveType flags. + * Specifying all of them is illegal, of course. A typical use would + * be to exclude all line and point meshes from the import. This + * is an integer property, its default value is 0. + */ +#define AI_CONFIG_PP_SBP_REMOVE \ + "PP_SBP_REMOVE" + +// --------------------------------------------------------------------------- +/** @brief Input parameter to the #aiProcess_FindInvalidData step: + * Specifies the floating-point accuracy for animation values. The step + * checks for animation tracks where all frame values are absolutely equal + * and removes them. This tweakable controls the epsilon for floating-point + * comparisons - two keys are considered equal if the invariant + * abs(n0-n1)>epsilon holds true for all vector respectively quaternion + * components. The default value is 0.f - comparisons are exact then. + */ +#define AI_CONFIG_PP_FID_ANIM_ACCURACY \ + "PP_FID_ANIM_ACCURACY" + +// --------------------------------------------------------------------------- +/** @brief Input parameter to the #aiProcess_FindInvalidData step: + * Set to true to ignore texture coordinates. This may be useful if you have + * to assign different kind of textures like one for the summer or one for the winter. + */ +#define AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS \ + "PP_FID_IGNORE_TEXTURECOORDS" + +// TransformUVCoords evaluates UV scalings +#define AI_UVTRAFO_SCALING 0x1 + +// TransformUVCoords evaluates UV rotations +#define AI_UVTRAFO_ROTATION 0x2 + +// TransformUVCoords evaluates UV translation +#define AI_UVTRAFO_TRANSLATION 0x4 + +// Everything baked together -> default value +#define AI_UVTRAFO_ALL (AI_UVTRAFO_SCALING | AI_UVTRAFO_ROTATION | AI_UVTRAFO_TRANSLATION) + +// --------------------------------------------------------------------------- +/** @brief Input parameter to the #aiProcess_TransformUVCoords step: + * Specifies which UV transformations are evaluated. + * + * This is a bitwise combination of the AI_UVTRAFO_XXX flags (integer + * property, of course). By default all transformations are enabled + * (AI_UVTRAFO_ALL). + */ +#define AI_CONFIG_PP_TUV_EVALUATE \ + "PP_TUV_EVALUATE" + +// --------------------------------------------------------------------------- +/** @brief A hint to assimp to favour speed against import quality. + * + * Enabling this option may result in faster loading, but it needn't. + * It represents just a hint to loaders and post-processing steps to use + * faster code paths, if possible. + * This property is expected to be an integer, != 0 stands for true. + * The default value is 0. + */ +#define AI_CONFIG_FAVOUR_SPEED \ + "FAVOUR_SPEED" + + +// ########################################################################### +// IMPORTER SETTINGS +// Various stuff to fine-tune the behaviour of specific importer plugins. +// ########################################################################### + + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will merge all geometry layers present + * in the source file or take only the first. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS \ + "IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read all materials present in the + * source file or take only the referenced materials. + * + * This is void unless IMPORT_FBX_READ_MATERIALS=1. + * + * The default value is false (0) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS \ + "IMPORT_FBX_READ_ALL_MATERIALS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read materials. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_MATERIALS \ + "IMPORT_FBX_READ_MATERIALS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read embedded textures. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_TEXTURES \ + "IMPORT_FBX_READ_TEXTURES" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read cameras. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_CAMERAS \ + "IMPORT_FBX_READ_CAMERAS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read light sources. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_LIGHTS \ + "IMPORT_FBX_READ_LIGHTS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will read animations. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS \ + "IMPORT_FBX_READ_ANIMATIONS" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will act in strict mode in which only + * FBX 2013 is supported and any other sub formats are rejected. FBX 2013 + * is the primary target for the importer, so this format is best + * supported and well-tested. + * + * The default value is false (0) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_STRICT_MODE \ + "IMPORT_FBX_STRICT_MODE" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will preserve pivot points for + * transformations (as extra nodes). If set to false, pivots and offsets + * will be evaluated whenever possible. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS \ + "IMPORT_FBX_PRESERVE_PIVOTS" + +// --------------------------------------------------------------------------- +/** @brief Specifies whether the importer will drop empty animation curves or + * animation curves which match the bind pose transformation over their + * entire defined range. + * + * The default value is true (1) + * Property type: bool + */ +#define AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES \ + "IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES" + +// --------------------------------------------------------------------------- +/** @brief Set whether the fbx importer will use the legacy embedded texture naming. +* +* The default value is false (0) +* Property type: bool +*/ +#define AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING \ + "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING" + +// --------------------------------------------------------------------------- +/** @brief Set the vertex animation keyframe to be imported + * + * ASSIMP does not support vertex keyframes (only bone animation is supported). + * The library reads only one frame of models with vertex animations. + * By default this is the first frame. + * \note The default value is 0. This option applies to all importers. + * However, it is also possible to override the global setting + * for a specific loader. You can use the AI_CONFIG_IMPORT_XXX_KEYFRAME + * options (where XXX is a placeholder for the file format for which you + * want to override the global setting). + * Property type: integer. + */ +#define AI_CONFIG_IMPORT_GLOBAL_KEYFRAME "IMPORT_GLOBAL_KEYFRAME" + +#define AI_CONFIG_IMPORT_MD3_KEYFRAME "IMPORT_MD3_KEYFRAME" +#define AI_CONFIG_IMPORT_MD2_KEYFRAME "IMPORT_MD2_KEYFRAME" +#define AI_CONFIG_IMPORT_MDL_KEYFRAME "IMPORT_MDL_KEYFRAME" +#define AI_CONFIG_IMPORT_MDC_KEYFRAME "IMPORT_MDC_KEYFRAME" +#define AI_CONFIG_IMPORT_SMD_KEYFRAME "IMPORT_SMD_KEYFRAME" +#define AI_CONFIG_IMPORT_UNREAL_KEYFRAME "IMPORT_UNREAL_KEYFRAME" + +// --------------------------------------------------------------------------- +/** Smd load multiple animations + * + * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_SMD_LOAD_ANIMATION_LIST "IMPORT_SMD_LOAD_ANIMATION_LIST" + +// --------------------------------------------------------------------------- +/** @brief Configures the AC loader to collect all surfaces which have the + * "Backface cull" flag set in separate meshes. + * + * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL \ + "IMPORT_AC_SEPARATE_BFCULL" + +// --------------------------------------------------------------------------- +/** @brief Configures whether the AC loader evaluates subdivision surfaces ( + * indicated by the presence of the 'subdiv' attribute in the file). By + * default, Assimp performs the subdivision using the standard + * Catmull-Clark algorithm + * + * * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION \ + "IMPORT_AC_EVAL_SUBDIVISION" + +// --------------------------------------------------------------------------- +/** @brief Configures the UNREAL 3D loader to separate faces with different + * surface flags (e.g. two-sided vs. single-sided). + * + * * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS \ + "UNREAL_HANDLE_FLAGS" + +// --------------------------------------------------------------------------- +/** @brief Configures the terragen import plugin to compute uv's for + * terrains, if not given. Furthermore a default texture is assigned. + * + * UV coordinates for terrains are so simple to compute that you'll usually + * want to compute them on your own, if you need them. This option is intended + * for model viewers which want to offer an easy way to apply textures to + * terrains. + * * Property type: bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_TER_MAKE_UVS \ + "IMPORT_TER_MAKE_UVS" + +// --------------------------------------------------------------------------- +/** @brief Configures the ASE loader to always reconstruct normal vectors + * basing on the smoothing groups loaded from the file. + * + * Some ASE files have carry invalid normals, other don't. + * * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS \ + "IMPORT_ASE_RECONSTRUCT_NORMALS" + +// --------------------------------------------------------------------------- +/** @brief Configures the M3D loader to detect and process multi-part + * Quake player models. + * + * These models usually consist of 3 files, lower.md3, upper.md3 and + * head.md3. If this property is set to true, Assimp will try to load and + * combine all three files if one of them is loaded. + * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART \ + "IMPORT_MD3_HANDLE_MULTIPART" + +// --------------------------------------------------------------------------- +/** @brief Tells the MD3 loader which skin files to load. + * + * When loading MD3 files, Assimp checks whether a file + * [md3_file_name]_[skin_name].skin is existing. These files are used by + * Quake III to be able to assign different skins (e.g. red and blue team) + * to models. 'default', 'red', 'blue' are typical skin names. + * Property type: String. Default value: "default". + */ +#define AI_CONFIG_IMPORT_MD3_SKIN_NAME \ + "IMPORT_MD3_SKIN_NAME" + +// --------------------------------------------------------------------------- +/** @brief Specify the Quake 3 shader file to be used for a particular + * MD3 file. This can also be a search path. + * + * By default Assimp's behaviour is as follows: If a MD3 file + * <tt>any_path/models/any_q3_subdir/model_name/file_name.md3</tt> is + * loaded, the library tries to locate the corresponding shader file in + * <tt>any_path/scripts/model_name.shader</tt>. This property overrides this + * behaviour. It can either specify a full path to the shader to be loaded + * or alternatively the path (relative or absolute) to the directory where + * the shaders for all MD3s to be loaded reside. Assimp attempts to open + * <tt>IMPORT_MD3_SHADER_SRC/model_name.shader</tt> first, <tt>IMPORT_MD3_SHADER_SRC/file_name.shader</tt> + * is the fallback file. Note that IMPORT_MD3_SHADER_SRC should have a terminal (back)slash. + * Property type: String. Default value: n/a. + */ +#define AI_CONFIG_IMPORT_MD3_SHADER_SRC \ + "IMPORT_MD3_SHADER_SRC" + +// --------------------------------------------------------------------------- +/** @brief Configures the LWO loader to load just one layer from the model. + * + * LWO files consist of layers and in some cases it could be useful to load + * only one of them. This property can be either a string - which specifies + * the name of the layer - or an integer - the index of the layer. If the + * property is not set the whole LWO model is loaded. Loading fails if the + * requested layer is not available. The layer index is zero-based and the + * layer name may not be empty.<br> + * Property type: Integer. Default value: all layers are loaded. + */ +#define AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY \ + "IMPORT_LWO_ONE_LAYER_ONLY" + +// --------------------------------------------------------------------------- +/** @brief Configures the MD5 loader to not load the MD5ANIM file for + * a MD5MESH file automatically. + * + * The default strategy is to look for a file with the same name but the + * MD5ANIM extension in the same directory. If it is found, it is loaded + * and combined with the MD5MESH file. This configuration option can be + * used to disable this behaviour. + * + * * Property type: bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD \ + "IMPORT_MD5_NO_ANIM_AUTOLOAD" + +// --------------------------------------------------------------------------- +/** @brief Defines the begin of the time range for which the LWS loader + * evaluates animations and computes aiNodeAnim's. + * + * Assimp provides full conversion of LightWave's envelope system, including + * pre and post conditions. The loader computes linearly subsampled animation + * chanels with the frame rate given in the LWS file. This property defines + * the start time. Note: animation channels are only generated if a node + * has at least one envelope with more tan one key assigned. This property. + * is given in frames, '0' is the first frame. By default, if this property + * is not set, the importer takes the animation start from the input LWS + * file ('FirstFrame' line)<br> + * Property type: Integer. Default value: taken from file. + * + * @see AI_CONFIG_IMPORT_LWS_ANIM_END - end of the imported time range + */ +#define AI_CONFIG_IMPORT_LWS_ANIM_START \ + "IMPORT_LWS_ANIM_START" +#define AI_CONFIG_IMPORT_LWS_ANIM_END \ + "IMPORT_LWS_ANIM_END" + +// --------------------------------------------------------------------------- +/** @brief Defines the output frame rate of the IRR loader. + * + * IRR animations are difficult to convert for Assimp and there will + * always be a loss of quality. This setting defines how many keys per second + * are returned by the converter.<br> + * Property type: integer. Default value: 100 + */ +#define AI_CONFIG_IMPORT_IRR_ANIM_FPS \ + "IMPORT_IRR_ANIM_FPS" + +// --------------------------------------------------------------------------- +/** @brief Ogre Importer will try to find referenced materials from this file. + * + * Ogre meshes reference with material names, this does not tell Assimp the file + * where it is located in. Assimp will try to find the source file in the following + * order: <material-name>.material, <mesh-filename-base>.material and + * lastly the material name defined by this config property. + * <br> + * Property type: String. Default value: Scene.material. + */ +#define AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE \ + "IMPORT_OGRE_MATERIAL_FILE" + +// --------------------------------------------------------------------------- +/** @brief Ogre Importer detect the texture usage from its filename. + * + * Ogre material texture units do not define texture type, the textures usage + * depends on the used shader or Ogre's fixed pipeline. If this config property + * is true Assimp will try to detect the type from the textures filename postfix: + * _n, _nrm, _nrml, _normal, _normals and _normalmap for normal map, _s, _spec, + * _specular and _specularmap for specular map, _l, _light, _lightmap, _occ + * and _occlusion for light map, _disp and _displacement for displacement map. + * The matching is case insensitive. Post fix is taken between the last + * underscore and the last period. + * Default behavior is to detect type from lower cased texture unit name by + * matching against: normalmap, specularmap, lightmap and displacementmap. + * For both cases if no match is found aiTextureType_DIFFUSE is used. + * <br> + * Property type: Bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME \ + "IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME" + + /** @brief Specifies whether the Android JNI asset extraction is supported. + * + * Turn on this option if you want to manage assets in native + * Android application without having to keep the internal directory and asset + * manager pointer. + */ + #define AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT "AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT" + +// --------------------------------------------------------------------------- +/** @brief Specifies whether the IFC loader skips over IfcSpace elements. + * + * IfcSpace elements (and their geometric representations) are used to + * represent, well, free space in a building storey.<br> + * Property type: Bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS "IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS" + +// --------------------------------------------------------------------------- +/** @brief Specifies whether the IFC loader will use its own, custom triangulation + * algorithm to triangulate wall and floor meshes. + * + * If this property is set to false, walls will be either triangulated by + * #aiProcess_Triangulate or will be passed through as huge polygons with + * faked holes (i.e. holes that are connected with the outer boundary using + * a dummy edge). It is highly recommended to set this property to true + * if you want triangulated data because #aiProcess_Triangulate is known to + * have problems with the kind of polygons that the IFC loader spits out for + * complicated meshes. + * Property type: Bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_IFC_CUSTOM_TRIANGULATION "IMPORT_IFC_CUSTOM_TRIANGULATION" + +// --------------------------------------------------------------------------- +/** @brief Set the tessellation conic angle for IFC smoothing curves. + * + * This is used by the IFC importer to determine the tessellation parameter + * for smoothing curves. + * @note The default value is AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE and the + * accepted values are in range [5.0, 120.0]. + * Property type: Float. + */ +#define AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE "IMPORT_IFC_SMOOTHING_ANGLE" + +// default value for AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE +#if (!defined AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE) +# define AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE 10.0f +#endif + +// --------------------------------------------------------------------------- +/** @brief Set the tessellation for IFC cylindrical shapes. + * + * This is used by the IFC importer to determine the tessellation parameter + * for cylindrical shapes, i.e. the number of segments used to approximate a circle. + * @note The default value is AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION and the + * accepted values are in range [3, 180]. + * Property type: Integer. + */ +#define AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION "IMPORT_IFC_CYLINDRICAL_TESSELLATION" + +// default value for AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION +#if (!defined AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION) +# define AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION 32 +#endif + +// --------------------------------------------------------------------------- +/** @brief Specifies whether the Collada loader will ignore the provided up direction. + * + * If this property is set to true, the up direction provided in the file header will + * be ignored and the file will be loaded as is. + * Property type: Bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION "IMPORT_COLLADA_IGNORE_UP_DIRECTION" + +// --------------------------------------------------------------------------- +/** @brief Specifies whether the Collada loader should use Collada names as node names. + * + * If this property is set to true, the Collada names will be used as the + * node name. The default is to use the id tag (resp. sid tag, if no id tag is present) + * instead. + * Property type: Bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES "IMPORT_COLLADA_USE_COLLADA_NAMES" + +// ---------- All the Export defines ------------ + +/** @brief Specifies the xfile use double for real values of float + * + * Property type: Bool. Default value: false. + */ + +#define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT" + +/** + * + */ +#define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS" + +/** + * @brief Specifies a gobal key factor for scale, float value + */ +#define AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY "GLOBAL_SCALE_FACTOR" + +#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT) +# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f +#endif // !! AI_DEBONE_THRESHOLD + +// ---------- All the Build/Compile-time defines ------------ + +/** @brief Specifies if double precision is supported inside assimp + * + * Property type: Bool. Default value: undefined. + */ + +#cmakedefine ASSIMP_DOUBLE_PRECISION 1 + +#endif // !! AI_CONFIG_H_INC diff --git a/thirdparty/assimp/include/assimp/defs.h b/thirdparty/assimp/include/assimp/defs.h new file mode 100644 index 0000000000..4a177e3c3e --- /dev/null +++ b/thirdparty/assimp/include/assimp/defs.h @@ -0,0 +1,303 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file defs.h + * @brief Assimp build configuration setup. See the notes in the comment + * blocks to find out how to customize _your_ Assimp build. + */ + +#pragma once +#ifndef AI_DEFINES_H_INC +#define AI_DEFINES_H_INC + +#include <assimp/config.h> + +////////////////////////////////////////////////////////////////////////// +/* Define ASSIMP_BUILD_NO_XX_IMPORTER to disable a specific + * file format loader. The loader is be excluded from the + * build in this case. 'XX' stands for the most common file + * extension of the file format. E.g.: + * ASSIMP_BUILD_NO_X_IMPORTER disables the X loader. + * + * If you're unsure about that, take a look at the implementation of the + * import plugin you wish to disable. You'll find the right define in the + * first lines of the corresponding unit. + * + * Other (mixed) configuration switches are listed here: + * ASSIMP_BUILD_NO_COMPRESSED_X + * - Disable support for compressed X files (zip) + * ASSIMP_BUILD_NO_COMPRESSED_BLEND + * - Disable support for compressed Blender files (zip) + * ASSIMP_BUILD_NO_COMPRESSED_IFC + * - Disable support for IFCZIP files (unzip) + */ +////////////////////////////////////////////////////////////////////////// + +#ifndef ASSIMP_BUILD_NO_COMPRESSED_X +# define ASSIMP_BUILD_NEED_Z_INFLATE +#endif + +#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND +# define ASSIMP_BUILD_NEED_Z_INFLATE +#endif + +#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC +# define ASSIMP_BUILD_NEED_Z_INFLATE +# define ASSIMP_BUILD_NEED_UNZIP +#endif + +#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER +# define ASSIMP_BUILD_NEED_Z_INFLATE +# define ASSIMP_BUILD_NEED_UNZIP +#endif + +////////////////////////////////////////////////////////////////////////// +/* Define ASSIMP_BUILD_NO_XX_PROCESS to disable a specific + * post processing step. This is the current list of process names ('XX'): + * CALCTANGENTS + * JOINVERTICES + * TRIANGULATE + * DROPFACENORMALS + * GENFACENORMALS + * GENVERTEXNORMALS + * REMOVEVC + * SPLITLARGEMESHES + * PRETRANSFORMVERTICES + * LIMITBONEWEIGHTS + * VALIDATEDS + * IMPROVECACHELOCALITY + * FIXINFACINGNORMALS + * REMOVE_REDUNDANTMATERIALS + * OPTIMIZEGRAPH + * SORTBYPTYPE + * FINDINVALIDDATA + * TRANSFORMTEXCOORDS + * GENUVCOORDS + * ENTITYMESHBUILDER + * EMBEDTEXTURES + * MAKELEFTHANDED + * FLIPUVS + * FLIPWINDINGORDER + * OPTIMIZEMESHES + * OPTIMIZEANIMS + * OPTIMIZEGRAPH + * GENENTITYMESHES + * FIXTEXTUREPATHS */ +////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +# undef ASSIMP_API + + ////////////////////////////////////////////////////////////////////////// + /* Define 'ASSIMP_BUILD_DLL_EXPORT' to build a DLL of the library */ + ////////////////////////////////////////////////////////////////////////// +# ifdef ASSIMP_BUILD_DLL_EXPORT +# define ASSIMP_API __declspec(dllexport) +# define ASSIMP_API_WINONLY __declspec(dllexport) +# pragma warning (disable : 4251) + + ////////////////////////////////////////////////////////////////////////// + /* Define 'ASSIMP_DLL' before including Assimp to link to ASSIMP in + * an external DLL under Windows. Default is static linkage. */ + ////////////////////////////////////////////////////////////////////////// +# elif (defined ASSIMP_DLL) +# define ASSIMP_API __declspec(dllimport) +# define ASSIMP_API_WINONLY __declspec(dllimport) +# else +# define ASSIMP_API +# define ASSIMP_API_WINONLY +# endif + + /* Force the compiler to inline a function, if possible + */ +# define AI_FORCE_INLINE __forceinline + + /* Tells the compiler that a function never returns. Used in code analysis + * to skip dead paths (e.g. after an assertion evaluated to false). */ +# define AI_WONT_RETURN __declspec(noreturn) + +#elif defined(SWIG) + + /* Do nothing, the relevant defines are all in AssimpSwigPort.i */ + +#else + +# define AI_WONT_RETURN + +# define ASSIMP_API __attribute__ ((visibility("default"))) +# define ASSIMP_API_WINONLY +# define AI_FORCE_INLINE inline +#endif // (defined _MSC_VER) + +#ifdef __GNUC__ +# define AI_WONT_RETURN_SUFFIX __attribute__((noreturn)) +#else +# define AI_WONT_RETURN_SUFFIX +#endif // (defined __clang__) + +#ifdef __cplusplus + /* No explicit 'struct' and 'enum' tags for C++, this keeps showing up + * in doxydocs. + */ +# define C_STRUCT +# define C_ENUM +#else + ////////////////////////////////////////////////////////////////////////// + /* To build the documentation, make sure ASSIMP_DOXYGEN_BUILD + * is defined by Doxygen's preprocessor. The corresponding + * entries in the DOXYFILE are: */ + ////////////////////////////////////////////////////////////////////////// +#if 0 + ENABLE_PREPROCESSING = YES + MACRO_EXPANSION = YES + EXPAND_ONLY_PREDEF = YES + SEARCH_INCLUDES = YES + INCLUDE_PATH = + INCLUDE_FILE_PATTERNS = + PREDEFINED = ASSIMP_DOXYGEN_BUILD=1 + EXPAND_AS_DEFINED = C_STRUCT C_ENUM + SKIP_FUNCTION_MACROS = YES +#endif + ////////////////////////////////////////////////////////////////////////// + /* Doxygen gets confused if we use c-struct typedefs to avoid + * the explicit 'struct' notation. This trick here has the same + * effect as the TYPEDEF_HIDES_STRUCT option, but we don't need + * to typedef all structs/enums. */ + ////////////////////////////////////////////////////////////////////////// +# if (defined ASSIMP_DOXYGEN_BUILD) +# define C_STRUCT +# define C_ENUM +# else +# define C_STRUCT struct +# define C_ENUM enum +# endif +#endif + +#if (defined(__BORLANDC__) || defined (__BCPLUSPLUS__)) +#error Currently, Borland is unsupported. Feel free to port Assimp. + +// "W8059 Packgröße der Struktur geändert" + +#endif + + + ////////////////////////////////////////////////////////////////////////// + /* Define ASSIMP_BUILD_SINGLETHREADED to compile assimp + * without threading support. The library doesn't utilize + * threads then and is itself not threadsafe. */ + ////////////////////////////////////////////////////////////////////////// +#ifndef ASSIMP_BUILD_SINGLETHREADED +# define ASSIMP_BUILD_SINGLETHREADED +#endif + +#if defined(_DEBUG) || ! defined(NDEBUG) +# define ASSIMP_BUILD_DEBUG +#endif + + ////////////////////////////////////////////////////////////////////////// + /* Define ASSIMP_DOUBLE_PRECISION to compile assimp + * with double precision support (64-bit). */ + ////////////////////////////////////////////////////////////////////////// + +#ifdef ASSIMP_DOUBLE_PRECISION + typedef double ai_real; + typedef signed long long int ai_int; + typedef unsigned long long int ai_uint; +#else // ASSIMP_DOUBLE_PRECISION + typedef float ai_real; + typedef signed int ai_int; + typedef unsigned int ai_uint; +#endif // ASSIMP_DOUBLE_PRECISION + + ////////////////////////////////////////////////////////////////////////// + /* Useful constants */ + ////////////////////////////////////////////////////////////////////////// + +/* This is PI. Hi PI. */ +#define AI_MATH_PI (3.141592653589793238462643383279 ) +#define AI_MATH_TWO_PI (AI_MATH_PI * 2.0) +#define AI_MATH_HALF_PI (AI_MATH_PI * 0.5) + +/* And this is to avoid endless casts to float */ +#define AI_MATH_PI_F (3.1415926538f) +#define AI_MATH_TWO_PI_F (AI_MATH_PI_F * 2.0f) +#define AI_MATH_HALF_PI_F (AI_MATH_PI_F * 0.5f) + +/* Tiny macro to convert from radians to degrees and back */ +#define AI_DEG_TO_RAD(x) ((x)*(ai_real)0.0174532925) +#define AI_RAD_TO_DEG(x) ((x)*(ai_real)57.2957795) + +/* Support for big-endian builds */ +#if defined(__BYTE_ORDER__) +# if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +# if !defined(__BIG_ENDIAN__) +# define __BIG_ENDIAN__ +# endif +# else /* little endian */ +# if defined (__BIG_ENDIAN__) +# undef __BIG_ENDIAN__ +# endif +# endif +#endif +#if defined(__BIG_ENDIAN__) +# define AI_BUILD_BIG_ENDIAN +#endif + + +/* To avoid running out of memory + * This can be adjusted for specific use cases + * It's NOT a total limit, just a limit for individual allocations + */ +#define AI_MAX_ALLOC(type) ((256U * 1024 * 1024) / sizeof(type)) + +#ifndef _MSC_VER +# define AI_NO_EXCEPT noexcept +#else +# if (_MSC_VER == 1915 ) +# define AI_NO_EXCEPT noexcept +# else +# define AI_NO_EXCEPT +# endif +#endif + +#endif // !! AI_DEFINES_H_INC diff --git a/thirdparty/assimp/include/assimp/fast_atof.h b/thirdparty/assimp/include/assimp/fast_atof.h new file mode 100644 index 0000000000..62ea969e97 --- /dev/null +++ b/thirdparty/assimp/include/assimp/fast_atof.h @@ -0,0 +1,373 @@ +#pragma once + +// Copyright (C) 2002-2007 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine" and the "irrXML" project. +// For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h + +// ------------------------------------------------------------------------------------ +// Original description: (Schrompf) +// Adapted to the ASSIMP library because the builtin atof indeed takes AGES to parse a +// float inside a large string. Before parsing, it does a strlen on the given point. +// Changes: +// 22nd October 08 (Aramis_acg): Added temporary cast to double, added strtoul10_64 +// to ensure long numbers are handled correctly +// ------------------------------------------------------------------------------------ + + +#ifndef FAST_A_TO_F_H_INCLUDED +#define FAST_A_TO_F_H_INCLUDED + +#include <cmath> +#include <limits> +#include <stdint.h> +#include <stdexcept> +#include <assimp/defs.h> + +#include "StringComparison.h" +#include <assimp/DefaultLogger.hpp> + +#ifdef _MSC_VER +# include <stdint.h> +#else +# include <assimp/Compiler/pstdint.h> +#endif + +namespace Assimp { + +const double fast_atof_table[16] = { // we write [16] here instead of [] to work around a swig bug + 0.0, + 0.1, + 0.01, + 0.001, + 0.0001, + 0.00001, + 0.000001, + 0.0000001, + 0.00000001, + 0.000000001, + 0.0000000001, + 0.00000000001, + 0.000000000001, + 0.0000000000001, + 0.00000000000001, + 0.000000000000001 +}; + + +// ------------------------------------------------------------------------------------ +// Convert a string in decimal format to a number +// ------------------------------------------------------------------------------------ +inline +unsigned int strtoul10( const char* in, const char** out=0) { + unsigned int value = 0; + + for ( ;; ) { + if ( *in < '0' || *in > '9' ) { + break; + } + + value = ( value * 10 ) + ( *in - '0' ); + ++in; + } + if ( out ) { + *out = in; + } + return value; +} + +// ------------------------------------------------------------------------------------ +// Convert a string in octal format to a number +// ------------------------------------------------------------------------------------ +inline +unsigned int strtoul8( const char* in, const char** out=0) { + unsigned int value( 0 ); + for ( ;; ) { + if ( *in < '0' || *in > '7' ) { + break; + } + + value = ( value << 3 ) + ( *in - '0' ); + ++in; + } + if ( out ) { + *out = in; + } + return value; +} + +// ------------------------------------------------------------------------------------ +// Convert a string in hex format to a number +// ------------------------------------------------------------------------------------ +inline +unsigned int strtoul16( const char* in, const char** out=0) { + unsigned int value( 0 ); + for ( ;; ) { + if ( *in >= '0' && *in <= '9' ) { + value = ( value << 4u ) + ( *in - '0' ); + } else if (*in >= 'A' && *in <= 'F') { + value = ( value << 4u ) + ( *in - 'A' ) + 10; + } else if (*in >= 'a' && *in <= 'f') { + value = ( value << 4u ) + ( *in - 'a' ) + 10; + } else { + break; + } + ++in; + } + if ( out ) { + *out = in; + } + return value; +} + +// ------------------------------------------------------------------------------------ +// Convert just one hex digit +// Return value is UINT_MAX if the input character is not a hex digit. +// ------------------------------------------------------------------------------------ +inline +unsigned int HexDigitToDecimal(char in) { + unsigned int out( UINT_MAX ); + if ( in >= '0' && in <= '9' ) { + out = in - '0'; + } else if ( in >= 'a' && in <= 'f' ) { + out = 10u + in - 'a'; + } else if ( in >= 'A' && in <= 'F' ) { + out = 10u + in - 'A'; + } + + // return value is UINT_MAX if the input is not a hex digit + return out; +} + +// ------------------------------------------------------------------------------------ +// Convert a hex-encoded octet (2 characters, i.e. df or 1a). +// ------------------------------------------------------------------------------------ +inline +uint8_t HexOctetToDecimal(const char* in) { + return ((uint8_t)HexDigitToDecimal(in[0])<<4)+(uint8_t)HexDigitToDecimal(in[1]); +} + +// ------------------------------------------------------------------------------------ +// signed variant of strtoul10 +// ------------------------------------------------------------------------------------ +inline +int strtol10( const char* in, const char** out=0) { + bool inv = (*in=='-'); + if ( inv || *in == '+' ) { + ++in; + } + + int value = strtoul10(in,out); + if (inv) { + value = -value; + } + return value; +} + +// ------------------------------------------------------------------------------------ +// Parse a C++-like integer literal - hex and oct prefixes. +// 0xNNNN - hex +// 0NNN - oct +// NNN - dec +// ------------------------------------------------------------------------------------ +inline +unsigned int strtoul_cppstyle( const char* in, const char** out=0) { + if ('0' == in[0]) { + return 'x' == in[1] ? strtoul16(in+2,out) : strtoul8(in+1,out); + } + return strtoul10(in, out); +} + +// ------------------------------------------------------------------------------------ +// Special version of the function, providing higher accuracy and safety +// It is mainly used by fast_atof to prevent ugly and unwanted integer overflows. +// ------------------------------------------------------------------------------------ +inline +uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_inout=0) { + unsigned int cur = 0; + uint64_t value = 0; + + if ( *in < '0' || *in > '9' ) { + throw std::invalid_argument( std::string( "The string \"" ) + in + "\" cannot be converted into a value." ); + } + + for ( ;; ) { + if ( *in < '0' || *in > '9' ) { + break; + } + + const uint64_t new_value = ( value * (uint64_t) 10 ) + ( (uint64_t) ( *in - '0' ) ); + + // numeric overflow, we rely on you + if ( new_value < value ) { + ASSIMP_LOG_WARN_F( "Converting the string \"", in, "\" into a value resulted in overflow." ); + return 0; + } + + value = new_value; + + ++in; + ++cur; + + if (max_inout && *max_inout == cur) { + if (out) { /* skip to end */ + while ( *in >= '0' && *in <= '9' ) { + ++in; + } + *out = in; + } + + return value; + } + } + if ( out ) { + *out = in; + } + + if ( max_inout ) { + *max_inout = cur; + } + + return value; +} + +// ------------------------------------------------------------------------------------ +// signed variant of strtoul10_64 +// ------------------------------------------------------------------------------------ +inline +int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) { + bool inv = (*in == '-'); + if ( inv || *in == '+' ) { + ++in; + } + + int64_t value = strtoul10_64(in, out, max_inout); + if (inv) { + value = -value; + } + return value; +} + +// Number of relevant decimals for floating-point parsing. +#define AI_FAST_ATOF_RELAVANT_DECIMALS 15 + +// ------------------------------------------------------------------------------------ +//! Provides a fast function for converting a string into a float, +//! about 6 times faster than atof in win32. +// If you find any bugs, please send them to me, niko (at) irrlicht3d.org. +// ------------------------------------------------------------------------------------ +template<typename Real> +inline +const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) { + Real f = 0; + + bool inv = (*c == '-'); + if (inv || *c == '+') { + ++c; + } + + if ((c[0] == 'N' || c[0] == 'n') && ASSIMP_strincmp(c, "nan", 3) == 0) { + out = std::numeric_limits<Real>::quiet_NaN(); + c += 3; + return c; + } + + if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inf", 3) == 0) { + out = std::numeric_limits<Real>::infinity(); + if (inv) { + out = -out; + } + c += 3; + if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inity", 5) == 0) { + c += 5; + } + return c; + } + + if (!(c[0] >= '0' && c[0] <= '9') && + !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) { + throw std::invalid_argument("Cannot parse string " + "as real number: does not start with digit " + "or decimal point followed by digit."); + } + + if (*c != '.' && (! check_comma || c[0] != ',')) { + f = static_cast<Real>( strtoul10_64 ( c, &c) ); + } + + if ((*c == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9') { + ++c; + + // NOTE: The original implementation is highly inaccurate here. The precision of a single + // IEEE 754 float is not high enough, everything behind the 6th digit tends to be more + // inaccurate than it would need to be. Casting to double seems to solve the problem. + // strtol_64 is used to prevent integer overflow. + + // Another fix: this tends to become 0 for long numbers if we don't limit the maximum + // number of digits to be read. AI_FAST_ATOF_RELAVANT_DECIMALS can be a value between + // 1 and 15. + unsigned int diff = AI_FAST_ATOF_RELAVANT_DECIMALS; + double pl = static_cast<double>( strtoul10_64 ( c, &c, &diff )); + + pl *= fast_atof_table[diff]; + f += static_cast<Real>( pl ); + } + // For backwards compatibility: eat trailing dots, but not trailing commas. + else if (*c == '.') { + ++c; + } + + // A major 'E' must be allowed. Necessary for proper reading of some DXF files. + // Thanks to Zhao Lei to point out that this if() must be outside the if (*c == '.' ..) + if (*c == 'e' || *c == 'E') { + ++c; + const bool einv = (*c=='-'); + if (einv || *c=='+') { + ++c; + } + + // The reason float constants are used here is that we've seen cases where compilers + // would perform such casts on compile-time constants at runtime, which would be + // bad considering how frequently fast_atoreal_move<float> is called in Assimp. + Real exp = static_cast<Real>( strtoul10_64(c, &c) ); + if (einv) { + exp = -exp; + } + f *= std::pow(static_cast<Real>(10.0), exp); + } + + if (inv) { + f = -f; + } + out = f; + return c; +} + +// ------------------------------------------------------------------------------------ +// The same but more human. +inline +ai_real fast_atof(const char* c) { + ai_real ret(0.0); + fast_atoreal_move<ai_real>(c, ret); + + return ret; +} + +inline +ai_real fast_atof( const char* c, const char** cout) { + ai_real ret(0.0); + *cout = fast_atoreal_move<ai_real>(c, ret); + + return ret; +} + +inline +ai_real fast_atof( const char** inout) { + ai_real ret(0.0); + *inout = fast_atoreal_move<ai_real>(*inout, ret); + + return ret; +} + +} //! namespace Assimp + +#endif // FAST_A_TO_F_H_INCLUDED diff --git a/thirdparty/assimp/include/assimp/importerdesc.h b/thirdparty/assimp/include/assimp/importerdesc.h new file mode 100644 index 0000000000..36e387f011 --- /dev/null +++ b/thirdparty/assimp/include/assimp/importerdesc.h @@ -0,0 +1,146 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file importerdesc.h + * @brief #aiImporterFlags, aiImporterDesc implementation. + */ +#pragma once +#ifndef AI_IMPORTER_DESC_H_INC +#define AI_IMPORTER_DESC_H_INC + + +/** Mixed set of flags for #aiImporterDesc, indicating some features + * common to many importers*/ +enum aiImporterFlags +{ + /** Indicates that there is a textual encoding of the + * file format; and that it is supported.*/ + aiImporterFlags_SupportTextFlavour = 0x1, + + /** Indicates that there is a binary encoding of the + * file format; and that it is supported.*/ + aiImporterFlags_SupportBinaryFlavour = 0x2, + + /** Indicates that there is a compressed encoding of the + * file format; and that it is supported.*/ + aiImporterFlags_SupportCompressedFlavour = 0x4, + + /** Indicates that the importer reads only a very particular + * subset of the file format. This happens commonly for + * declarative or procedural formats which cannot easily + * be mapped to #aiScene */ + aiImporterFlags_LimitedSupport = 0x8, + + /** Indicates that the importer is highly experimental and + * should be used with care. This only happens for trunk + * (i.e. SVN) versions, experimental code is not included + * in releases. */ + aiImporterFlags_Experimental = 0x10 +}; + + +/** Meta information about a particular importer. Importers need to fill + * this structure, but they can freely decide how talkative they are. + * A common use case for loader meta info is a user interface + * in which the user can choose between various import/export file + * formats. Building such an UI by hand means a lot of maintenance + * as importers/exporters are added to Assimp, so it might be useful + * to have a common mechanism to query some rough importer + * characteristics. */ +struct aiImporterDesc +{ + /** Full name of the importer (i.e. Blender3D importer)*/ + const char* mName; + + /** Original author (left blank if unknown or whole assimp team) */ + const char* mAuthor; + + /** Current maintainer, left blank if the author maintains */ + const char* mMaintainer; + + /** Implementation comments, i.e. unimplemented features*/ + const char* mComments; + + /** These flags indicate some characteristics common to many + importers. */ + unsigned int mFlags; + + /** Minimum format version that can be loaded im major.minor format, + both are set to 0 if there is either no version scheme + or if the loader doesn't care. */ + unsigned int mMinMajor; + unsigned int mMinMinor; + + /** Maximum format version that can be loaded im major.minor format, + both are set to 0 if there is either no version scheme + or if the loader doesn't care. Loaders that expect to be + forward-compatible to potential future format versions should + indicate zero, otherwise they should specify the current + maximum version.*/ + unsigned int mMaxMajor; + unsigned int mMaxMinor; + + /** List of file extensions this importer can handle. + List entries are separated by space characters. + All entries are lower case without a leading dot (i.e. + "xml dae" would be a valid value. Note that multiple + importers may respond to the same file extension - + assimp calls all importers in the order in which they + are registered and each importer gets the opportunity + to load the file until one importer "claims" the file. Apart + from file extension checks, importers typically use + other methods to quickly reject files (i.e. magic + words) so this does not mean that common or generic + file extensions such as XML would be tediously slow. */ + const char* mFileExtensions; +}; + +/** \brief Returns the Importer description for a given extension. + +Will return a NULL-pointer if no assigned importer desc. was found for the given extension + \param extension [in] The extension to look for + \return A pointer showing to the ImporterDesc, \see aiImporterDesc. +*/ +ASSIMP_API const C_STRUCT aiImporterDesc* aiGetImporterDesc( const char *extension ); + +#endif // AI_IMPORTER_DESC_H_INC diff --git a/thirdparty/assimp/include/assimp/irrXMLWrapper.h b/thirdparty/assimp/include/assimp/irrXMLWrapper.h new file mode 100644 index 0000000000..ec8ee7c76e --- /dev/null +++ b/thirdparty/assimp/include/assimp/irrXMLWrapper.h @@ -0,0 +1,144 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef INCLUDED_AI_IRRXML_WRAPPER +#define INCLUDED_AI_IRRXML_WRAPPER + +// some long includes .... +#include <irrXML.h> +#include "IOStream.hpp" +#include "BaseImporter.h" +#include <vector> + +namespace Assimp { + +// --------------------------------------------------------------------------------- +/** @brief Utility class to make IrrXML work together with our custom IO system + * See the IrrXML docs for more details. + * + * Construct IrrXML-Reader in BaseImporter::InternReadFile(): + * @code + * // open the file + * std::unique_ptr<IOStream> file( pIOHandler->Open( pFile)); + * if( file.get() == NULL) { + * throw DeadlyImportError( "Failed to open file " + pFile + "."); + * } + * + * // generate a XML reader for it + * std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper( new CIrrXML_IOStreamReader( file.get())); + * mReader = irr::io::createIrrXMLReader( mIOWrapper.get()); + * if( !mReader) { + * ThrowException( "xxxx: Unable to open file."); + * } + * @endcode + **/ +class CIrrXML_IOStreamReader : public irr::io::IFileReadCallBack { +public: + + // ---------------------------------------------------------------------------------- + //! Construction from an existing IOStream + explicit CIrrXML_IOStreamReader(IOStream* _stream) + : stream (_stream) + , t (0) + { + + // Map the buffer into memory and convert it to UTF8. IrrXML provides its + // own conversion, which is merely a cast from uintNN_t to uint8_t. Thus, + // it is not suitable for our purposes and we have to do it BEFORE IrrXML + // gets the buffer. Sadly, this forces us to map the whole file into + // memory. + + data.resize(stream->FileSize()); + stream->Read(&data[0],data.size(),1); + + // Remove null characters from the input sequence otherwise the parsing will utterly fail + unsigned int size = 0; + unsigned int size_max = static_cast<unsigned int>(data.size()); + for(unsigned int i = 0; i < size_max; i++) { + if(data[i] != '\0') { + data[size++] = data[i]; + } + } + data.resize(size); + + BaseImporter::ConvertToUTF8(data); + } + + // ---------------------------------------------------------------------------------- + //! Virtual destructor + virtual ~CIrrXML_IOStreamReader() {} + + // ---------------------------------------------------------------------------------- + //! Reads an amount of bytes from the file. + /** @param buffer: Pointer to output buffer. + * @param sizeToRead: Amount of bytes to read + * @return Returns how much bytes were read. */ + virtual int read(void* buffer, int sizeToRead) { + if(sizeToRead<0) { + return 0; + } + if(t+sizeToRead>data.size()) { + sizeToRead = static_cast<int>(data.size()-t); + } + + memcpy(buffer,&data.front()+t,sizeToRead); + + t += sizeToRead; + return sizeToRead; + } + + // ---------------------------------------------------------------------------------- + //! Returns size of file in bytes + virtual int getSize() { + return (int)data.size(); + } + +private: + IOStream* stream; + std::vector<char> data; + size_t t; + +}; // ! class CIrrXML_IOStreamReader + +} // ! Assimp + +#endif // !! INCLUDED_AI_IRRXML_WRAPPER diff --git a/thirdparty/assimp/include/assimp/light.h b/thirdparty/assimp/include/assimp/light.h new file mode 100644 index 0000000000..1667cfb8c1 --- /dev/null +++ b/thirdparty/assimp/include/assimp/light.h @@ -0,0 +1,259 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file light.h + * @brief Defines the aiLight data structure + */ + +#pragma once +#ifndef AI_LIGHT_H_INC +#define AI_LIGHT_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** Enumerates all supported types of light sources. + */ +enum aiLightSourceType +{ + aiLightSource_UNDEFINED = 0x0, + + //! A directional light source has a well-defined direction + //! but is infinitely far away. That's quite a good + //! approximation for sun light. + aiLightSource_DIRECTIONAL = 0x1, + + //! A point light source has a well-defined position + //! in space but no direction - it emits light in all + //! directions. A normal bulb is a point light. + aiLightSource_POINT = 0x2, + + //! A spot light source emits light in a specific + //! angle. It has a position and a direction it is pointing to. + //! A good example for a spot light is a light spot in + //! sport arenas. + aiLightSource_SPOT = 0x3, + + //! The generic light level of the world, including the bounces + //! of all other light sources. + //! Typically, there's at most one ambient light in a scene. + //! This light type doesn't have a valid position, direction, or + //! other properties, just a color. + aiLightSource_AMBIENT = 0x4, + + //! An area light is a rectangle with predefined size that uniformly + //! emits light from one of its sides. The position is center of the + //! rectangle and direction is its normal vector. + aiLightSource_AREA = 0x5, + + /** This value is not used. It is just there to force the + * compiler to map this enum to a 32 Bit integer. + */ +#ifndef SWIG + _aiLightSource_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** Helper structure to describe a light source. + * + * Assimp supports multiple sorts of light sources, including + * directional, point and spot lights. All of them are defined with just + * a single structure and distinguished by their parameters. + * Note - some file formats (such as 3DS, ASE) export a "target point" - + * the point a spot light is looking at (it can even be animated). Assimp + * writes the target point as a subnode of a spotlights's main node, + * called "<spotName>.Target". However, this is just additional information + * then, the transformation tracks of the main node make the + * spot light already point in the right direction. +*/ +struct aiLight +{ + /** The name of the light source. + * + * There must be a node in the scenegraph with the same name. + * This node specifies the position of the light in the scene + * hierarchy and can be animated. + */ + C_STRUCT aiString mName; + + /** The type of the light source. + * + * aiLightSource_UNDEFINED is not a valid value for this member. + */ + C_ENUM aiLightSourceType mType; + + /** Position of the light source in space. Relative to the + * transformation of the node corresponding to the light. + * + * The position is undefined for directional lights. + */ + C_STRUCT aiVector3D mPosition; + + /** Direction of the light source in space. Relative to the + * transformation of the node corresponding to the light. + * + * The direction is undefined for point lights. The vector + * may be normalized, but it needn't. + */ + C_STRUCT aiVector3D mDirection; + + /** Up direction of the light source in space. Relative to the + * transformation of the node corresponding to the light. + * + * The direction is undefined for point lights. The vector + * may be normalized, but it needn't. + */ + C_STRUCT aiVector3D mUp; + + /** Constant light attenuation factor. + * + * The intensity of the light source at a given distance 'd' from + * the light's position is + * @code + * Atten = 1/( att0 + att1 * d + att2 * d*d) + * @endcode + * This member corresponds to the att0 variable in the equation. + * Naturally undefined for directional lights. + */ + float mAttenuationConstant; + + /** Linear light attenuation factor. + * + * The intensity of the light source at a given distance 'd' from + * the light's position is + * @code + * Atten = 1/( att0 + att1 * d + att2 * d*d) + * @endcode + * This member corresponds to the att1 variable in the equation. + * Naturally undefined for directional lights. + */ + float mAttenuationLinear; + + /** Quadratic light attenuation factor. + * + * The intensity of the light source at a given distance 'd' from + * the light's position is + * @code + * Atten = 1/( att0 + att1 * d + att2 * d*d) + * @endcode + * This member corresponds to the att2 variable in the equation. + * Naturally undefined for directional lights. + */ + float mAttenuationQuadratic; + + /** Diffuse color of the light source + * + * The diffuse light color is multiplied with the diffuse + * material color to obtain the final color that contributes + * to the diffuse shading term. + */ + C_STRUCT aiColor3D mColorDiffuse; + + /** Specular color of the light source + * + * The specular light color is multiplied with the specular + * material color to obtain the final color that contributes + * to the specular shading term. + */ + C_STRUCT aiColor3D mColorSpecular; + + /** Ambient color of the light source + * + * The ambient light color is multiplied with the ambient + * material color to obtain the final color that contributes + * to the ambient shading term. Most renderers will ignore + * this value it, is just a remaining of the fixed-function pipeline + * that is still supported by quite many file formats. + */ + C_STRUCT aiColor3D mColorAmbient; + + /** Inner angle of a spot light's light cone. + * + * The spot light has maximum influence on objects inside this + * angle. The angle is given in radians. It is 2PI for point + * lights and undefined for directional lights. + */ + float mAngleInnerCone; + + /** Outer angle of a spot light's light cone. + * + * The spot light does not affect objects outside this angle. + * The angle is given in radians. It is 2PI for point lights and + * undefined for directional lights. The outer angle must be + * greater than or equal to the inner angle. + * It is assumed that the application uses a smooth + * interpolation between the inner and the outer cone of the + * spot light. + */ + float mAngleOuterCone; + + /** Size of area light source. */ + C_STRUCT aiVector2D mSize; + +#ifdef __cplusplus + + aiLight() AI_NO_EXCEPT + : mType (aiLightSource_UNDEFINED) + , mAttenuationConstant (0.f) + , mAttenuationLinear (1.f) + , mAttenuationQuadratic (0.f) + , mAngleInnerCone ((float)AI_MATH_TWO_PI) + , mAngleOuterCone ((float)AI_MATH_TWO_PI) + , mSize (0.f, 0.f) + { + } + +#endif +}; + +#ifdef __cplusplus +} +#endif + + +#endif // !! AI_LIGHT_H_INC diff --git a/thirdparty/assimp/include/assimp/material.h b/thirdparty/assimp/include/assimp/material.h new file mode 100644 index 0000000000..b882bbc72c --- /dev/null +++ b/thirdparty/assimp/include/assimp/material.h @@ -0,0 +1,1579 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file material.h + * @brief Defines the material system of the library + */ +#pragma once +#ifndef AI_MATERIAL_H_INC +#define AI_MATERIAL_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Name for default materials (2nd is used if meshes have UV coords) +#define AI_DEFAULT_MATERIAL_NAME "DefaultMaterial" + +// --------------------------------------------------------------------------- +/** @brief Defines how the Nth texture of a specific type is combined with + * the result of all previous layers. + * + * Example (left: key, right: value): <br> + * @code + * DiffColor0 - gray + * DiffTextureOp0 - aiTextureOpMultiply + * DiffTexture0 - tex1.png + * DiffTextureOp0 - aiTextureOpAdd + * DiffTexture1 - tex2.png + * @endcode + * Written as equation, the final diffuse term for a specific pixel would be: + * @code + * diffFinal = DiffColor0 * sampleTex(DiffTexture0,UV0) + + * sampleTex(DiffTexture1,UV0) * diffContrib; + * @endcode + * where 'diffContrib' is the intensity of the incoming light for that pixel. + */ +enum aiTextureOp +{ + /** T = T1 * T2 */ + aiTextureOp_Multiply = 0x0, + + /** T = T1 + T2 */ + aiTextureOp_Add = 0x1, + + /** T = T1 - T2 */ + aiTextureOp_Subtract = 0x2, + + /** T = T1 / T2 */ + aiTextureOp_Divide = 0x3, + + /** T = (T1 + T2) - (T1 * T2) */ + aiTextureOp_SmoothAdd = 0x4, + + /** T = T1 + (T2-0.5) */ + aiTextureOp_SignedAdd = 0x5, + + +#ifndef SWIG + _aiTextureOp_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** @brief Defines how UV coordinates outside the [0...1] range are handled. + * + * Commonly referred to as 'wrapping mode'. + */ +enum aiTextureMapMode +{ + /** A texture coordinate u|v is translated to u%1|v%1 + */ + aiTextureMapMode_Wrap = 0x0, + + /** Texture coordinates outside [0...1] + * are clamped to the nearest valid value. + */ + aiTextureMapMode_Clamp = 0x1, + + /** If the texture coordinates for a pixel are outside [0...1] + * the texture is not applied to that pixel + */ + aiTextureMapMode_Decal = 0x3, + + /** A texture coordinate u|v becomes u%1|v%1 if (u-(u%1))%2 is zero and + * 1-(u%1)|1-(v%1) otherwise + */ + aiTextureMapMode_Mirror = 0x2, + +#ifndef SWIG + _aiTextureMapMode_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** @brief Defines how the mapping coords for a texture are generated. + * + * Real-time applications typically require full UV coordinates, so the use of + * the aiProcess_GenUVCoords step is highly recommended. It generates proper + * UV channels for non-UV mapped objects, as long as an accurate description + * how the mapping should look like (e.g spherical) is given. + * See the #AI_MATKEY_MAPPING property for more details. + */ +enum aiTextureMapping +{ + /** The mapping coordinates are taken from an UV channel. + * + * The #AI_MATKEY_UVWSRC key specifies from which UV channel + * the texture coordinates are to be taken from (remember, + * meshes can have more than one UV channel). + */ + aiTextureMapping_UV = 0x0, + + /** Spherical mapping */ + aiTextureMapping_SPHERE = 0x1, + + /** Cylindrical mapping */ + aiTextureMapping_CYLINDER = 0x2, + + /** Cubic mapping */ + aiTextureMapping_BOX = 0x3, + + /** Planar mapping */ + aiTextureMapping_PLANE = 0x4, + + /** Undefined mapping. Have fun. */ + aiTextureMapping_OTHER = 0x5, + + +#ifndef SWIG + _aiTextureMapping_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** @brief Defines the purpose of a texture + * + * This is a very difficult topic. Different 3D packages support different + * kinds of textures. For very common texture types, such as bumpmaps, the + * rendering results depend on implementation details in the rendering + * pipelines of these applications. Assimp loads all texture references from + * the model file and tries to determine which of the predefined texture + * types below is the best choice to match the original use of the texture + * as closely as possible.<br> + * + * In content pipelines you'll usually define how textures have to be handled, + * and the artists working on models have to conform to this specification, + * regardless which 3D tool they're using. + */ +enum aiTextureType +{ + /** Dummy value. + * + * No texture, but the value to be used as 'texture semantic' + * (#aiMaterialProperty::mSemantic) for all material properties + * *not* related to textures. + */ + aiTextureType_NONE = 0x0, + + /** The texture is combined with the result of the diffuse + * lighting equation. + */ + aiTextureType_DIFFUSE = 0x1, + + /** The texture is combined with the result of the specular + * lighting equation. + */ + aiTextureType_SPECULAR = 0x2, + + /** The texture is combined with the result of the ambient + * lighting equation. + */ + aiTextureType_AMBIENT = 0x3, + + /** The texture is added to the result of the lighting + * calculation. It isn't influenced by incoming light. + */ + aiTextureType_EMISSIVE = 0x4, + + /** The texture is a height map. + * + * By convention, higher gray-scale values stand for + * higher elevations from the base height. + */ + aiTextureType_HEIGHT = 0x5, + + /** The texture is a (tangent space) normal-map. + * + * Again, there are several conventions for tangent-space + * normal maps. Assimp does (intentionally) not + * distinguish here. + */ + aiTextureType_NORMALS = 0x6, + + /** The texture defines the glossiness of the material. + * + * The glossiness is in fact the exponent of the specular + * (phong) lighting equation. Usually there is a conversion + * function defined to map the linear color values in the + * texture to a suitable exponent. Have fun. + */ + aiTextureType_SHININESS = 0x7, + + /** The texture defines per-pixel opacity. + * + * Usually 'white' means opaque and 'black' means + * 'transparency'. Or quite the opposite. Have fun. + */ + aiTextureType_OPACITY = 0x8, + + /** Displacement texture + * + * The exact purpose and format is application-dependent. + * Higher color values stand for higher vertex displacements. + */ + aiTextureType_DISPLACEMENT = 0x9, + + /** Lightmap texture (aka Ambient Occlusion) + * + * Both 'Lightmaps' and dedicated 'ambient occlusion maps' are + * covered by this material property. The texture contains a + * scaling value for the final color value of a pixel. Its + * intensity is not affected by incoming light. + */ + aiTextureType_LIGHTMAP = 0xA, + + /** Reflection texture + * + * Contains the color of a perfect mirror reflection. + * Rarely used, almost never for real-time applications. + */ + aiTextureType_REFLECTION = 0xB, + + /** Unknown texture + * + * A texture reference that does not match any of the definitions + * above is considered to be 'unknown'. It is still imported, + * but is excluded from any further post-processing. + */ + aiTextureType_UNKNOWN = 0xC, + + +#ifndef SWIG + _aiTextureType_Force32Bit = INT_MAX +#endif +}; + +#define AI_TEXTURE_TYPE_MAX aiTextureType_UNKNOWN + +// --------------------------------------------------------------------------- +/** @brief Defines all shading models supported by the library + * + * The list of shading modes has been taken from Blender. + * See Blender documentation for more information. The API does + * not distinguish between "specular" and "diffuse" shaders (thus the + * specular term for diffuse shading models like Oren-Nayar remains + * undefined). <br> + * Again, this value is just a hint. Assimp tries to select the shader whose + * most common implementation matches the original rendering results of the + * 3D modeller which wrote a particular model as closely as possible. + */ +enum aiShadingMode +{ + /** Flat shading. Shading is done on per-face base, + * diffuse only. Also known as 'faceted shading'. + */ + aiShadingMode_Flat = 0x1, + + /** Simple Gouraud shading. + */ + aiShadingMode_Gouraud = 0x2, + + /** Phong-Shading - + */ + aiShadingMode_Phong = 0x3, + + /** Phong-Blinn-Shading + */ + aiShadingMode_Blinn = 0x4, + + /** Toon-Shading per pixel + * + * Also known as 'comic' shader. + */ + aiShadingMode_Toon = 0x5, + + /** OrenNayar-Shading per pixel + * + * Extension to standard Lambertian shading, taking the + * roughness of the material into account + */ + aiShadingMode_OrenNayar = 0x6, + + /** Minnaert-Shading per pixel + * + * Extension to standard Lambertian shading, taking the + * "darkness" of the material into account + */ + aiShadingMode_Minnaert = 0x7, + + /** CookTorrance-Shading per pixel + * + * Special shader for metallic surfaces. + */ + aiShadingMode_CookTorrance = 0x8, + + /** No shading at all. Constant light influence of 1.0. + */ + aiShadingMode_NoShading = 0x9, + + /** Fresnel shading + */ + aiShadingMode_Fresnel = 0xa, + + +#ifndef SWIG + _aiShadingMode_Force32Bit = INT_MAX +#endif +}; + + +// --------------------------------------------------------------------------- +/** @brief Defines some mixed flags for a particular texture. + * + * Usually you'll instruct your cg artists how textures have to look like ... + * and how they will be processed in your application. However, if you use + * Assimp for completely generic loading purposes you might also need to + * process these flags in order to display as many 'unknown' 3D models as + * possible correctly. + * + * This corresponds to the #AI_MATKEY_TEXFLAGS property. +*/ +enum aiTextureFlags +{ + /** The texture's color values have to be inverted (component-wise 1-n) + */ + aiTextureFlags_Invert = 0x1, + + /** Explicit request to the application to process the alpha channel + * of the texture. + * + * Mutually exclusive with #aiTextureFlags_IgnoreAlpha. These + * flags are set if the library can say for sure that the alpha + * channel is used/is not used. If the model format does not + * define this, it is left to the application to decide whether + * the texture alpha channel - if any - is evaluated or not. + */ + aiTextureFlags_UseAlpha = 0x2, + + /** Explicit request to the application to ignore the alpha channel + * of the texture. + * + * Mutually exclusive with #aiTextureFlags_UseAlpha. + */ + aiTextureFlags_IgnoreAlpha = 0x4, + +#ifndef SWIG + _aiTextureFlags_Force32Bit = INT_MAX +#endif +}; + + +// --------------------------------------------------------------------------- +/** @brief Defines alpha-blend flags. + * + * If you're familiar with OpenGL or D3D, these flags aren't new to you. + * They define *how* the final color value of a pixel is computed, basing + * on the previous color at that pixel and the new color value from the + * material. + * The blend formula is: + * @code + * SourceColor * SourceBlend + DestColor * DestBlend + * @endcode + * where DestColor is the previous color in the framebuffer at this + * position and SourceColor is the material color before the transparency + * calculation.<br> + * This corresponds to the #AI_MATKEY_BLEND_FUNC property. +*/ +enum aiBlendMode +{ + /** + * Formula: + * @code + * SourceColor*SourceAlpha + DestColor*(1-SourceAlpha) + * @endcode + */ + aiBlendMode_Default = 0x0, + + /** Additive blending + * + * Formula: + * @code + * SourceColor*1 + DestColor*1 + * @endcode + */ + aiBlendMode_Additive = 0x1, + + // we don't need more for the moment, but we might need them + // in future versions ... + +#ifndef SWIG + _aiBlendMode_Force32Bit = INT_MAX +#endif +}; + + +#include "./Compiler/pushpack1.h" + +// --------------------------------------------------------------------------- +/** @brief Defines how an UV channel is transformed. + * + * This is just a helper structure for the #AI_MATKEY_UVTRANSFORM key. + * See its documentation for more details. + * + * Typically you'll want to build a matrix of this information. However, + * we keep separate scaling/translation/rotation values to make it + * easier to process and optimize UV transformations internally. + */ +struct aiUVTransform +{ + /** Translation on the u and v axes. + * + * The default value is (0|0). + */ + C_STRUCT aiVector2D mTranslation; + + /** Scaling on the u and v axes. + * + * The default value is (1|1). + */ + C_STRUCT aiVector2D mScaling; + + /** Rotation - in counter-clockwise direction. + * + * The rotation angle is specified in radians. The + * rotation center is 0.5f|0.5f. The default value + * 0.f. + */ + ai_real mRotation; + + +#ifdef __cplusplus + aiUVTransform() AI_NO_EXCEPT + : mTranslation (0.0,0.0) + , mScaling (1.0,1.0) + , mRotation (0.0) + { + // nothing to be done here ... + } +#endif + +}; + +#include "./Compiler/poppack1.h" + +//! @cond AI_DOX_INCLUDE_INTERNAL +// --------------------------------------------------------------------------- +/** @brief A very primitive RTTI system for the contents of material + * properties. + */ +enum aiPropertyTypeInfo +{ + /** Array of single-precision (32 Bit) floats + * + * It is possible to use aiGetMaterialInteger[Array]() (or the C++-API + * aiMaterial::Get()) to query properties stored in floating-point format. + * The material system performs the type conversion automatically. + */ + aiPTI_Float = 0x1, + + /** Array of double-precision (64 Bit) floats + * + * It is possible to use aiGetMaterialInteger[Array]() (or the C++-API + * aiMaterial::Get()) to query properties stored in floating-point format. + * The material system performs the type conversion automatically. + */ + aiPTI_Double = 0x2, + + /** The material property is an aiString. + * + * Arrays of strings aren't possible, aiGetMaterialString() (or the + * C++-API aiMaterial::Get()) *must* be used to query a string property. + */ + aiPTI_String = 0x3, + + /** Array of (32 Bit) integers + * + * It is possible to use aiGetMaterialFloat[Array]() (or the C++-API + * aiMaterial::Get()) to query properties stored in integer format. + * The material system performs the type conversion automatically. + */ + aiPTI_Integer = 0x4, + + + /** Simple binary buffer, content undefined. Not convertible to anything. + */ + aiPTI_Buffer = 0x5, + + + /** This value is not used. It is just there to force the + * compiler to map this enum to a 32 Bit integer. + */ +#ifndef SWIG + _aiPTI_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** @brief Data structure for a single material property + * + * As an user, you'll probably never need to deal with this data structure. + * Just use the provided aiGetMaterialXXX() or aiMaterial::Get() family + * of functions to query material properties easily. Processing them + * manually is faster, but it is not the recommended way. It isn't worth + * the effort. <br> + * Material property names follow a simple scheme: + * @code + * $<name> + * ?<name> + * A public property, there must be corresponding AI_MATKEY_XXX define + * 2nd: Public, but ignored by the #aiProcess_RemoveRedundantMaterials + * post-processing step. + * ~<name> + * A temporary property for internal use. + * @endcode + * @see aiMaterial + */ +struct aiMaterialProperty +{ + /** Specifies the name of the property (key) + * Keys are generally case insensitive. + */ + C_STRUCT aiString mKey; + + /** Textures: Specifies their exact usage semantic. + * For non-texture properties, this member is always 0 + * (or, better-said, #aiTextureType_NONE). + */ + unsigned int mSemantic; + + /** Textures: Specifies the index of the texture. + * For non-texture properties, this member is always 0. + */ + unsigned int mIndex; + + /** Size of the buffer mData is pointing to, in bytes. + * This value may not be 0. + */ + unsigned int mDataLength; + + /** Type information for the property. + * + * Defines the data layout inside the data buffer. This is used + * by the library internally to perform debug checks and to + * utilize proper type conversions. + * (It's probably a hacky solution, but it works.) + */ + C_ENUM aiPropertyTypeInfo mType; + + /** Binary buffer to hold the property's value. + * The size of the buffer is always mDataLength. + */ + char* mData; + +#ifdef __cplusplus + + aiMaterialProperty() AI_NO_EXCEPT + : mSemantic( 0 ) + , mIndex( 0 ) + , mDataLength( 0 ) + , mType( aiPTI_Float ) + , mData(nullptr) { + // empty + } + + ~aiMaterialProperty() { + delete[] mData; + mData = nullptr; + } + +#endif +}; +//! @endcond + +#ifdef __cplusplus +} // We need to leave the "C" block here to allow template member functions +#endif + +// --------------------------------------------------------------------------- +/** @brief Data structure for a material +* +* Material data is stored using a key-value structure. A single key-value +* pair is called a 'material property'. C++ users should use the provided +* member functions of aiMaterial to process material properties, C users +* have to stick with the aiMaterialGetXXX family of unbound functions. +* The library defines a set of standard keys (AI_MATKEY_XXX). +*/ +#ifdef __cplusplus +struct ASSIMP_API aiMaterial +#else +struct aiMaterial +#endif +{ + +#ifdef __cplusplus + +public: + + aiMaterial(); + ~aiMaterial(); + + // ------------------------------------------------------------------- + /** + * @brief Returns the name of the material. + * @return The name of the material. + */ + // ------------------------------------------------------------------- + aiString GetName(); + + // ------------------------------------------------------------------- + /** @brief Retrieve an array of Type values with a specific key + * from the material + * + * @param pKey Key to search for. One of the AI_MATKEY_XXX constants. + * @param type .. set by AI_MATKEY_XXX + * @param idx .. set by AI_MATKEY_XXX + * @param pOut Pointer to a buffer to receive the result. + * @param pMax Specifies the size of the given buffer, in Type's. + * Receives the number of values (not bytes!) read. + * NULL is a valid value for this parameter. + */ + template <typename Type> + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, Type* pOut, unsigned int* pMax) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, int* pOut, unsigned int* pMax) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, ai_real* pOut, unsigned int* pMax) const; + + // ------------------------------------------------------------------- + /** @brief Retrieve a Type value with a specific key + * from the material + * + * @param pKey Key to search for. One of the AI_MATKEY_XXX constants. + * @param type Specifies the type of the texture to be retrieved ( + * e.g. diffuse, specular, height map ...) + * @param idx Index of the texture to be retrieved. + * @param pOut Reference to receive the output value + */ + template <typename Type> + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx,Type& pOut) const; + + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, int& pOut) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, ai_real& pOut) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, aiString& pOut) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, aiColor3D& pOut) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, aiColor4D& pOut) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, aiUVTransform& pOut) const; + + // ------------------------------------------------------------------- + /** Get the number of textures for a particular texture type. + * @param type Texture type to check for + * @return Number of textures for this type. + * @note A texture can be easily queried using #GetTexture() */ + unsigned int GetTextureCount(aiTextureType type) const; + + // ------------------------------------------------------------------- + /** Helper function to get all parameters pertaining to a + * particular texture slot from a material. + * + * This function is provided just for convenience, you could also + * read the single material properties manually. + * @param type Specifies the type of the texture to be retrieved ( + * e.g. diffuse, specular, height map ...) + * @param index Index of the texture to be retrieved. The function fails + * if there is no texture of that type with this index. + * #GetTextureCount() can be used to determine the number of textures + * per texture type. + * @param path Receives the path to the texture. + * If the texture is embedded, receives a '*' followed by the id of + * the texture (for the textures stored in the corresponding scene) which + * can be converted to an int using a function like atoi. + * NULL is a valid value. + * @param mapping The texture mapping. + * NULL is allowed as value. + * @param uvindex Receives the UV index of the texture. + * NULL is a valid value. + * @param blend Receives the blend factor for the texture + * NULL is a valid value. + * @param op Receives the texture operation to be performed between + * this texture and the previous texture. NULL is allowed as value. + * @param mapmode Receives the mapping modes to be used for the texture. + * The parameter may be NULL but if it is a valid pointer it MUST + * point to an array of 3 aiTextureMapMode's (one for each + * axis: UVW order (=XYZ)). + */ + // ------------------------------------------------------------------- + aiReturn GetTexture(aiTextureType type, + unsigned int index, + C_STRUCT aiString* path, + aiTextureMapping* mapping = NULL, + unsigned int* uvindex = NULL, + ai_real* blend = NULL, + aiTextureOp* op = NULL, + aiTextureMapMode* mapmode = NULL) const; + + + // Setters + + + // ------------------------------------------------------------------------------ + /** @brief Add a property with a given key and type info to the material + * structure + * + * @param pInput Pointer to input data + * @param pSizeInBytes Size of input data + * @param pKey Key/Usage of the property (AI_MATKEY_XXX) + * @param type Set by the AI_MATKEY_XXX macro + * @param index Set by the AI_MATKEY_XXX macro + * @param pType Type information hint */ + aiReturn AddBinaryProperty (const void* pInput, + unsigned int pSizeInBytes, + const char* pKey, + unsigned int type , + unsigned int index , + aiPropertyTypeInfo pType); + + // ------------------------------------------------------------------------------ + /** @brief Add a string property with a given key and type info to the + * material structure + * + * @param pInput Input string + * @param pKey Key/Usage of the property (AI_MATKEY_XXX) + * @param type Set by the AI_MATKEY_XXX macro + * @param index Set by the AI_MATKEY_XXX macro */ + aiReturn AddProperty (const aiString* pInput, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + // ------------------------------------------------------------------------------ + /** @brief Add a property with a given key to the material structure + * @param pInput Pointer to the input data + * @param pNumValues Number of values in the array + * @param pKey Key/Usage of the property (AI_MATKEY_XXX) + * @param type Set by the AI_MATKEY_XXX macro + * @param index Set by the AI_MATKEY_XXX macro */ + template<class TYPE> + aiReturn AddProperty (const TYPE* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const aiVector3D* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const aiColor3D* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const aiColor4D* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const int* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const float* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const double* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const aiUVTransform* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + // ------------------------------------------------------------------------------ + /** @brief Remove a given key from the list. + * + * The function fails if the key isn't found + * @param pKey Key to be deleted + * @param type Set by the AI_MATKEY_XXX macro + * @param index Set by the AI_MATKEY_XXX macro */ + aiReturn RemoveProperty (const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + // ------------------------------------------------------------------------------ + /** @brief Removes all properties from the material. + * + * The data array remains allocated so adding new properties is quite fast. */ + void Clear(); + + // ------------------------------------------------------------------------------ + /** Copy the property list of a material + * @param pcDest Destination material + * @param pcSrc Source material + */ + static void CopyPropertyList(aiMaterial* pcDest, + const aiMaterial* pcSrc); + + +#endif + + /** List of all material properties loaded. */ + C_STRUCT aiMaterialProperty** mProperties; + + /** Number of properties in the data base */ + unsigned int mNumProperties; + + /** Storage allocated */ + unsigned int mNumAllocated; +}; + +// Go back to extern "C" again +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +#define AI_MATKEY_NAME "?mat.name",0,0 +#define AI_MATKEY_TWOSIDED "$mat.twosided",0,0 +#define AI_MATKEY_SHADING_MODEL "$mat.shadingm",0,0 +#define AI_MATKEY_ENABLE_WIREFRAME "$mat.wireframe",0,0 +#define AI_MATKEY_BLEND_FUNC "$mat.blend",0,0 +#define AI_MATKEY_OPACITY "$mat.opacity",0,0 +#define AI_MATKEY_BUMPSCALING "$mat.bumpscaling",0,0 +#define AI_MATKEY_SHININESS "$mat.shininess",0,0 +#define AI_MATKEY_REFLECTIVITY "$mat.reflectivity",0,0 +#define AI_MATKEY_SHININESS_STRENGTH "$mat.shinpercent",0,0 +#define AI_MATKEY_REFRACTI "$mat.refracti",0,0 +#define AI_MATKEY_COLOR_DIFFUSE "$clr.diffuse",0,0 +#define AI_MATKEY_COLOR_AMBIENT "$clr.ambient",0,0 +#define AI_MATKEY_COLOR_SPECULAR "$clr.specular",0,0 +#define AI_MATKEY_COLOR_EMISSIVE "$clr.emissive",0,0 +#define AI_MATKEY_COLOR_TRANSPARENT "$clr.transparent",0,0 +#define AI_MATKEY_COLOR_REFLECTIVE "$clr.reflective",0,0 +#define AI_MATKEY_GLOBAL_BACKGROUND_IMAGE "?bg.global",0,0 +#define AI_MATKEY_GLOBAL_SHADERLANG "?sh.lang",0,0 +#define AI_MATKEY_SHADER_VERTEX "?sh.vs",0,0 +#define AI_MATKEY_SHADER_FRAGMENT "?sh.fs",0,0 +#define AI_MATKEY_SHADER_GEO "?sh.gs",0,0 +#define AI_MATKEY_SHADER_TESSELATION "?sh.ts",0,0 +#define AI_MATKEY_SHADER_PRIMITIVE "?sh.ps",0,0 +#define AI_MATKEY_SHADER_COMPUTE "?sh.cs",0,0 + +// --------------------------------------------------------------------------- +// Pure key names for all texture-related properties +//! @cond MATS_DOC_FULL +#define _AI_MATKEY_TEXTURE_BASE "$tex.file" +#define _AI_MATKEY_UVWSRC_BASE "$tex.uvwsrc" +#define _AI_MATKEY_TEXOP_BASE "$tex.op" +#define _AI_MATKEY_MAPPING_BASE "$tex.mapping" +#define _AI_MATKEY_TEXBLEND_BASE "$tex.blend" +#define _AI_MATKEY_MAPPINGMODE_U_BASE "$tex.mapmodeu" +#define _AI_MATKEY_MAPPINGMODE_V_BASE "$tex.mapmodev" +#define _AI_MATKEY_TEXMAP_AXIS_BASE "$tex.mapaxis" +#define _AI_MATKEY_UVTRANSFORM_BASE "$tex.uvtrafo" +#define _AI_MATKEY_TEXFLAGS_BASE "$tex.flags" +//! @endcond + +// --------------------------------------------------------------------------- +#define AI_MATKEY_TEXTURE(type, N) _AI_MATKEY_TEXTURE_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_TEXTURE_DIFFUSE(N) \ + AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_TEXTURE_SPECULAR(N) \ + AI_MATKEY_TEXTURE(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_TEXTURE_AMBIENT(N) \ + AI_MATKEY_TEXTURE(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_TEXTURE_EMISSIVE(N) \ + AI_MATKEY_TEXTURE(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_TEXTURE_NORMALS(N) \ + AI_MATKEY_TEXTURE(aiTextureType_NORMALS,N) + +#define AI_MATKEY_TEXTURE_HEIGHT(N) \ + AI_MATKEY_TEXTURE(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_TEXTURE_SHININESS(N) \ + AI_MATKEY_TEXTURE(aiTextureType_SHININESS,N) + +#define AI_MATKEY_TEXTURE_OPACITY(N) \ + AI_MATKEY_TEXTURE(aiTextureType_OPACITY,N) + +#define AI_MATKEY_TEXTURE_DISPLACEMENT(N) \ + AI_MATKEY_TEXTURE(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_TEXTURE_LIGHTMAP(N) \ + AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_TEXTURE_REFLECTION(N) \ + AI_MATKEY_TEXTURE(aiTextureType_REFLECTION,N) + +//! @endcond + +// --------------------------------------------------------------------------- +#define AI_MATKEY_UVWSRC(type, N) _AI_MATKEY_UVWSRC_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_UVWSRC_DIFFUSE(N) \ + AI_MATKEY_UVWSRC(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_UVWSRC_SPECULAR(N) \ + AI_MATKEY_UVWSRC(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_UVWSRC_AMBIENT(N) \ + AI_MATKEY_UVWSRC(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_UVWSRC_EMISSIVE(N) \ + AI_MATKEY_UVWSRC(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_UVWSRC_NORMALS(N) \ + AI_MATKEY_UVWSRC(aiTextureType_NORMALS,N) + +#define AI_MATKEY_UVWSRC_HEIGHT(N) \ + AI_MATKEY_UVWSRC(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_UVWSRC_SHININESS(N) \ + AI_MATKEY_UVWSRC(aiTextureType_SHININESS,N) + +#define AI_MATKEY_UVWSRC_OPACITY(N) \ + AI_MATKEY_UVWSRC(aiTextureType_OPACITY,N) + +#define AI_MATKEY_UVWSRC_DISPLACEMENT(N) \ + AI_MATKEY_UVWSRC(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_UVWSRC_LIGHTMAP(N) \ + AI_MATKEY_UVWSRC(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_UVWSRC_REFLECTION(N) \ + AI_MATKEY_UVWSRC(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_TEXOP(type, N) _AI_MATKEY_TEXOP_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_TEXOP_DIFFUSE(N) \ + AI_MATKEY_TEXOP(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_TEXOP_SPECULAR(N) \ + AI_MATKEY_TEXOP(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_TEXOP_AMBIENT(N) \ + AI_MATKEY_TEXOP(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_TEXOP_EMISSIVE(N) \ + AI_MATKEY_TEXOP(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_TEXOP_NORMALS(N) \ + AI_MATKEY_TEXOP(aiTextureType_NORMALS,N) + +#define AI_MATKEY_TEXOP_HEIGHT(N) \ + AI_MATKEY_TEXOP(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_TEXOP_SHININESS(N) \ + AI_MATKEY_TEXOP(aiTextureType_SHININESS,N) + +#define AI_MATKEY_TEXOP_OPACITY(N) \ + AI_MATKEY_TEXOP(aiTextureType_OPACITY,N) + +#define AI_MATKEY_TEXOP_DISPLACEMENT(N) \ + AI_MATKEY_TEXOP(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_TEXOP_LIGHTMAP(N) \ + AI_MATKEY_TEXOP(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_TEXOP_REFLECTION(N) \ + AI_MATKEY_TEXOP(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_MAPPING(type, N) _AI_MATKEY_MAPPING_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_MAPPING_DIFFUSE(N) \ + AI_MATKEY_MAPPING(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_MAPPING_SPECULAR(N) \ + AI_MATKEY_MAPPING(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_MAPPING_AMBIENT(N) \ + AI_MATKEY_MAPPING(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_MAPPING_EMISSIVE(N) \ + AI_MATKEY_MAPPING(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_MAPPING_NORMALS(N) \ + AI_MATKEY_MAPPING(aiTextureType_NORMALS,N) + +#define AI_MATKEY_MAPPING_HEIGHT(N) \ + AI_MATKEY_MAPPING(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_MAPPING_SHININESS(N) \ + AI_MATKEY_MAPPING(aiTextureType_SHININESS,N) + +#define AI_MATKEY_MAPPING_OPACITY(N) \ + AI_MATKEY_MAPPING(aiTextureType_OPACITY,N) + +#define AI_MATKEY_MAPPING_DISPLACEMENT(N) \ + AI_MATKEY_MAPPING(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_MAPPING_LIGHTMAP(N) \ + AI_MATKEY_MAPPING(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_MAPPING_REFLECTION(N) \ + AI_MATKEY_MAPPING(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_TEXBLEND(type, N) _AI_MATKEY_TEXBLEND_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_TEXBLEND_DIFFUSE(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_TEXBLEND_SPECULAR(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_TEXBLEND_AMBIENT(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_TEXBLEND_EMISSIVE(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_TEXBLEND_NORMALS(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_NORMALS,N) + +#define AI_MATKEY_TEXBLEND_HEIGHT(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_TEXBLEND_SHININESS(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_SHININESS,N) + +#define AI_MATKEY_TEXBLEND_OPACITY(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_OPACITY,N) + +#define AI_MATKEY_TEXBLEND_DISPLACEMENT(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_TEXBLEND_LIGHTMAP(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_TEXBLEND_REFLECTION(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_MAPPINGMODE_U(type, N) _AI_MATKEY_MAPPINGMODE_U_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_MAPPINGMODE_U_DIFFUSE(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_MAPPINGMODE_U_SPECULAR(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_MAPPINGMODE_U_AMBIENT(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_MAPPINGMODE_U_EMISSIVE(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_MAPPINGMODE_U_NORMALS(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_NORMALS,N) + +#define AI_MATKEY_MAPPINGMODE_U_HEIGHT(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_MAPPINGMODE_U_SHININESS(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_SHININESS,N) + +#define AI_MATKEY_MAPPINGMODE_U_OPACITY(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_OPACITY,N) + +#define AI_MATKEY_MAPPINGMODE_U_DISPLACEMENT(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_MAPPINGMODE_U_LIGHTMAP(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_MAPPINGMODE_U_REFLECTION(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_MAPPINGMODE_V(type, N) _AI_MATKEY_MAPPINGMODE_V_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_MAPPINGMODE_V_DIFFUSE(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_MAPPINGMODE_V_SPECULAR(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_MAPPINGMODE_V_AMBIENT(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_MAPPINGMODE_V_EMISSIVE(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_MAPPINGMODE_V_NORMALS(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_NORMALS,N) + +#define AI_MATKEY_MAPPINGMODE_V_HEIGHT(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_MAPPINGMODE_V_SHININESS(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_SHININESS,N) + +#define AI_MATKEY_MAPPINGMODE_V_OPACITY(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_OPACITY,N) + +#define AI_MATKEY_MAPPINGMODE_V_DISPLACEMENT(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_MAPPINGMODE_V_LIGHTMAP(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_MAPPINGMODE_V_REFLECTION(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_TEXMAP_AXIS(type, N) _AI_MATKEY_TEXMAP_AXIS_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_TEXMAP_AXIS_DIFFUSE(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_TEXMAP_AXIS_SPECULAR(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_TEXMAP_AXIS_AMBIENT(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_TEXMAP_AXIS_EMISSIVE(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_TEXMAP_AXIS_NORMALS(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_NORMALS,N) + +#define AI_MATKEY_TEXMAP_AXIS_HEIGHT(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_TEXMAP_AXIS_SHININESS(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_SHININESS,N) + +#define AI_MATKEY_TEXMAP_AXIS_OPACITY(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_OPACITY,N) + +#define AI_MATKEY_TEXMAP_AXIS_DISPLACEMENT(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_TEXMAP_AXIS_LIGHTMAP(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_TEXMAP_AXIS_REFLECTION(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_UVTRANSFORM(type, N) _AI_MATKEY_UVTRANSFORM_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_UVTRANSFORM_DIFFUSE(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_UVTRANSFORM_SPECULAR(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_UVTRANSFORM_AMBIENT(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_UVTRANSFORM_EMISSIVE(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_UVTRANSFORM_NORMALS(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_NORMALS,N) + +#define AI_MATKEY_UVTRANSFORM_HEIGHT(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_UVTRANSFORM_SHININESS(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_SHININESS,N) + +#define AI_MATKEY_UVTRANSFORM_OPACITY(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_OPACITY,N) + +#define AI_MATKEY_UVTRANSFORM_DISPLACEMENT(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_UVTRANSFORM_LIGHTMAP(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_UVTRANSFORM_REFLECTION(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_REFLECTION,N) + +#define AI_MATKEY_UVTRANSFORM_UNKNOWN(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_UNKNOWN,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_TEXFLAGS(type, N) _AI_MATKEY_TEXFLAGS_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_TEXFLAGS_DIFFUSE(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_TEXFLAGS_SPECULAR(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_TEXFLAGS_AMBIENT(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_TEXFLAGS_EMISSIVE(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_TEXFLAGS_NORMALS(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_NORMALS,N) + +#define AI_MATKEY_TEXFLAGS_HEIGHT(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_TEXFLAGS_SHININESS(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_SHININESS,N) + +#define AI_MATKEY_TEXFLAGS_OPACITY(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_OPACITY,N) + +#define AI_MATKEY_TEXFLAGS_DISPLACEMENT(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_TEXFLAGS_LIGHTMAP(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_TEXFLAGS_REFLECTION(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_REFLECTION,N) + +#define AI_MATKEY_TEXFLAGS_UNKNOWN(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_UNKNOWN,N) + +//! @endcond +//! +// --------------------------------------------------------------------------- +/** @brief Retrieve a material property with a specific key from the material + * + * @param pMat Pointer to the input material. May not be NULL + * @param pKey Key to search for. One of the AI_MATKEY_XXX constants. + * @param type Specifies the type of the texture to be retrieved ( + * e.g. diffuse, specular, height map ...) + * @param index Index of the texture to be retrieved. + * @param pPropOut Pointer to receive a pointer to a valid aiMaterialProperty + * structure or NULL if the key has not been found. */ +// --------------------------------------------------------------------------- +ASSIMP_API C_ENUM aiReturn aiGetMaterialProperty( + const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + const C_STRUCT aiMaterialProperty** pPropOut); + +// --------------------------------------------------------------------------- +/** @brief Retrieve an array of float values with a specific key + * from the material + * + * Pass one of the AI_MATKEY_XXX constants for the last three parameters (the + * example reads the #AI_MATKEY_UVTRANSFORM property of the first diffuse texture) + * @code + * aiUVTransform trafo; + * unsigned int max = sizeof(aiUVTransform); + * if (AI_SUCCESS != aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE,0), + * (float*)&trafo, &max) || sizeof(aiUVTransform) != max) + * { + * // error handling + * } + * @endcode + * + * @param pMat Pointer to the input material. May not be NULL + * @param pKey Key to search for. One of the AI_MATKEY_XXX constants. + * @param pOut Pointer to a buffer to receive the result. + * @param pMax Specifies the size of the given buffer, in float's. + * Receives the number of values (not bytes!) read. + * @param type (see the code sample above) + * @param index (see the code sample above) + * @return Specifies whether the key has been found. If not, the output + * arrays remains unmodified and pMax is set to 0.*/ +// --------------------------------------------------------------------------- +ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( + const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + ai_real* pOut, + unsigned int* pMax); + + +#ifdef __cplusplus + +// --------------------------------------------------------------------------- +/** @brief Retrieve a single float property with a specific key from the material. +* +* Pass one of the AI_MATKEY_XXX constants for the last three parameters (the +* example reads the #AI_MATKEY_SHININESS_STRENGTH property of the first diffuse texture) +* @code +* float specStrength = 1.f; // default value, remains unmodified if we fail. +* aiGetMaterialFloat(mat, AI_MATKEY_SHININESS_STRENGTH, +* (float*)&specStrength); +* @endcode +* +* @param pMat Pointer to the input material. May not be NULL +* @param pKey Key to search for. One of the AI_MATKEY_XXX constants. +* @param pOut Receives the output float. +* @param type (see the code sample above) +* @param index (see the code sample above) +* @return Specifies whether the key has been found. If not, the output +* float remains unmodified.*/ +// --------------------------------------------------------------------------- +inline aiReturn aiGetMaterialFloat(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + ai_real* pOut) +{ + return aiGetMaterialFloatArray(pMat,pKey,type,index,pOut,(unsigned int*)0x0); +} + +#else + +// Use our friend, the C preprocessor +#define aiGetMaterialFloat (pMat, type, index, pKey, pOut) \ + aiGetMaterialFloatArray(pMat, type, index, pKey, pOut, NULL) + +#endif //!__cplusplus + + +// --------------------------------------------------------------------------- +/** @brief Retrieve an array of integer values with a specific key + * from a material + * + * See the sample for aiGetMaterialFloatArray for more information.*/ +ASSIMP_API C_ENUM aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + int* pOut, + unsigned int* pMax); + + +#ifdef __cplusplus + +// --------------------------------------------------------------------------- +/** @brief Retrieve an integer property with a specific key from a material + * + * See the sample for aiGetMaterialFloat for more information.*/ +// --------------------------------------------------------------------------- +inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + int* pOut) +{ + return aiGetMaterialIntegerArray(pMat,pKey,type,index,pOut,(unsigned int*)0x0); +} + +#else + +// use our friend, the C preprocessor +#define aiGetMaterialInteger (pMat, type, index, pKey, pOut) \ + aiGetMaterialIntegerArray(pMat, type, index, pKey, pOut, NULL) + +#endif //!__cplusplus + +// --------------------------------------------------------------------------- +/** @brief Retrieve a color value from the material property table +* +* See the sample for aiGetMaterialFloat for more information*/ +// --------------------------------------------------------------------------- +ASSIMP_API C_ENUM aiReturn aiGetMaterialColor(const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + C_STRUCT aiColor4D* pOut); + + +// --------------------------------------------------------------------------- +/** @brief Retrieve a aiUVTransform value from the material property table +* +* See the sample for aiGetMaterialFloat for more information*/ +// --------------------------------------------------------------------------- +ASSIMP_API C_ENUM aiReturn aiGetMaterialUVTransform(const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + C_STRUCT aiUVTransform* pOut); + + +// --------------------------------------------------------------------------- +/** @brief Retrieve a string from the material property table +* +* See the sample for aiGetMaterialFloat for more information.*/ +// --------------------------------------------------------------------------- +ASSIMP_API C_ENUM aiReturn aiGetMaterialString(const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + C_STRUCT aiString* pOut); + +// --------------------------------------------------------------------------- +/** Get the number of textures for a particular texture type. + * @param[in] pMat Pointer to the input material. May not be NULL + * @param type Texture type to check for + * @return Number of textures for this type. + * @note A texture can be easily queried using #aiGetMaterialTexture() */ +// --------------------------------------------------------------------------- +ASSIMP_API unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial* pMat, + C_ENUM aiTextureType type); + +// --------------------------------------------------------------------------- +/** @brief Helper function to get all values pertaining to a particular + * texture slot from a material structure. + * + * This function is provided just for convenience. You could also read the + * texture by parsing all of its properties manually. This function bundles + * all of them in a huge function monster. + * + * @param[in] mat Pointer to the input material. May not be NULL + * @param[in] type Specifies the texture stack to read from (e.g. diffuse, + * specular, height map ...). + * @param[in] index Index of the texture. The function fails if the + * requested index is not available for this texture type. + * #aiGetMaterialTextureCount() can be used to determine the number of + * textures in a particular texture stack. + * @param[out] path Receives the output path + * If the texture is embedded, receives a '*' followed by the id of + * the texture (for the textures stored in the corresponding scene) which + * can be converted to an int using a function like atoi. + * This parameter must be non-null. + * @param mapping The texture mapping mode to be used. + * Pass NULL if you're not interested in this information. + * @param[out] uvindex For UV-mapped textures: receives the index of the UV + * source channel. Unmodified otherwise. + * Pass NULL if you're not interested in this information. + * @param[out] blend Receives the blend factor for the texture + * Pass NULL if you're not interested in this information. + * @param[out] op Receives the texture blend operation to be perform between + * this texture and the previous texture. + * Pass NULL if you're not interested in this information. + * @param[out] mapmode Receives the mapping modes to be used for the texture. + * Pass NULL if you're not interested in this information. Otherwise, + * pass a pointer to an array of two aiTextureMapMode's (one for each + * axis, UV order). + * @param[out] flags Receives the the texture flags. + * @return AI_SUCCESS on success, otherwise something else. Have fun.*/ +// --------------------------------------------------------------------------- +#ifdef __cplusplus +ASSIMP_API aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat, + aiTextureType type, + unsigned int index, + aiString* path, + aiTextureMapping* mapping = NULL, + unsigned int* uvindex = NULL, + ai_real* blend = NULL, + aiTextureOp* op = NULL, + aiTextureMapMode* mapmode = NULL, + unsigned int* flags = NULL); +#else +C_ENUM aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat, + C_ENUM aiTextureType type, + unsigned int index, + C_STRUCT aiString* path, + C_ENUM aiTextureMapping* mapping /*= NULL*/, + unsigned int* uvindex /*= NULL*/, + ai_real* blend /*= NULL*/, + C_ENUM aiTextureOp* op /*= NULL*/, + C_ENUM aiTextureMapMode* mapmode /*= NULL*/, + unsigned int* flags /*= NULL*/); +#endif // !#ifdef __cplusplus + + +#ifdef __cplusplus +} + +#include "material.inl" + +#endif //!__cplusplus + +#endif //!!AI_MATERIAL_H_INC diff --git a/thirdparty/assimp/include/assimp/material.inl b/thirdparty/assimp/include/assimp/material.inl new file mode 100644 index 0000000000..b05d6af6c3 --- /dev/null +++ b/thirdparty/assimp/include/assimp/material.inl @@ -0,0 +1,390 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file material.inl + * @brief Defines the C++ getters for the material system + */ + +#pragma once +#ifndef AI_MATERIAL_INL_INC +#define AI_MATERIAL_INL_INC + +// --------------------------------------------------------------------------- +inline aiPropertyTypeInfo ai_real_to_property_type_info(float) +{ + return aiPTI_Float; +} + +inline aiPropertyTypeInfo ai_real_to_property_type_info(double) +{ + return aiPTI_Double; +} +// --------------------------------------------------------------------------- + +//! @cond never + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::GetTexture( aiTextureType type, + unsigned int index, + C_STRUCT aiString* path, + aiTextureMapping* mapping /*= NULL*/, + unsigned int* uvindex /*= NULL*/, + ai_real* blend /*= NULL*/, + aiTextureOp* op /*= NULL*/, + aiTextureMapMode* mapmode /*= NULL*/) const +{ + return ::aiGetMaterialTexture(this,type,index,path,mapping,uvindex,blend,op,mapmode); +} + +// --------------------------------------------------------------------------- +inline unsigned int aiMaterial::GetTextureCount(aiTextureType type) const +{ + return ::aiGetMaterialTextureCount(this,type); +} + +// --------------------------------------------------------------------------- +template <typename Type> +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx, Type* pOut, + unsigned int* pMax) const +{ + unsigned int iNum = pMax ? *pMax : 1; + + const aiMaterialProperty* prop; + const aiReturn ret = ::aiGetMaterialProperty(this,pKey,type,idx, + (const aiMaterialProperty**)&prop); + if ( AI_SUCCESS == ret ) { + + if (prop->mDataLength < sizeof(Type)*iNum) { + return AI_FAILURE; + } + + if (prop->mType != aiPTI_Buffer) { + return AI_FAILURE; + } + + iNum = std::min((size_t)iNum,prop->mDataLength / sizeof(Type)); + ::memcpy(pOut,prop->mData,iNum * sizeof(Type)); + if (pMax) { + *pMax = iNum; + } + } + return ret; +} + +// --------------------------------------------------------------------------- +template <typename Type> +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,Type& pOut) const +{ + const aiMaterialProperty* prop; + const aiReturn ret = ::aiGetMaterialProperty(this,pKey,type,idx, + (const aiMaterialProperty**)&prop); + if ( AI_SUCCESS == ret ) { + + if (prop->mDataLength < sizeof(Type)) { + return AI_FAILURE; + } + + if (prop->mType != aiPTI_Buffer) { + return AI_FAILURE; + } + + ::memcpy( &pOut, prop->mData, sizeof( Type ) ); + } + return ret; +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,ai_real* pOut, + unsigned int* pMax) const +{ + return ::aiGetMaterialFloatArray(this,pKey,type,idx,pOut,pMax); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,int* pOut, + unsigned int* pMax) const +{ + return ::aiGetMaterialIntegerArray(this,pKey,type,idx,pOut,pMax); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,ai_real& pOut) const +{ + return aiGetMaterialFloat(this,pKey,type,idx,&pOut); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,int& pOut) const +{ + return aiGetMaterialInteger(this,pKey,type,idx,&pOut); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,aiColor4D& pOut) const +{ + return aiGetMaterialColor(this,pKey,type,idx,&pOut); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,aiColor3D& pOut) const +{ + aiColor4D c; + const aiReturn ret = aiGetMaterialColor(this,pKey,type,idx,&c); + pOut = aiColor3D(c.r,c.g,c.b); + return ret; +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,aiString& pOut) const +{ + return aiGetMaterialString(this,pKey,type,idx,&pOut); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,aiUVTransform& pOut) const +{ + return aiGetMaterialUVTransform(this,pKey,type,idx,&pOut); +} + + +// --------------------------------------------------------------------------- +template<class TYPE> +aiReturn aiMaterial::AddProperty (const TYPE* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(TYPE), + pKey,type,index,aiPTI_Buffer); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const float* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(float), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const double* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(double), + pKey,type,index,aiPTI_Double); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const aiUVTransform* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiUVTransform), + pKey,type,index,ai_real_to_property_type_info(pInput->mRotation)); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const aiColor4D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiColor4D), + pKey,type,index,ai_real_to_property_type_info(pInput->a)); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const aiColor3D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiColor3D), + pKey,type,index,ai_real_to_property_type_info(pInput->b)); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const aiVector3D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiVector3D), + pKey,type,index,ai_real_to_property_type_info(pInput->x)); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const int* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(int), + pKey,type,index,aiPTI_Integer); +} + + +// --------------------------------------------------------------------------- +// The template specializations below are for backwards compatibility. +// The recommended way to add material properties is using the non-template +// overloads. +// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty<float>(const float* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(float), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty<double>(const double* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(double), + pKey,type,index,aiPTI_Double); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty<aiUVTransform>(const aiUVTransform* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiUVTransform), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty<aiColor4D>(const aiColor4D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiColor4D), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty<aiColor3D>(const aiColor3D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiColor3D), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty<aiVector3D>(const aiVector3D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiVector3D), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty<int>(const int* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(int), + pKey,type,index,aiPTI_Integer); +} + +//! @endcond + +#endif //! AI_MATERIAL_INL_INC diff --git a/thirdparty/assimp/include/assimp/matrix3x3.h b/thirdparty/assimp/include/assimp/matrix3x3.h new file mode 100644 index 0000000000..22b69561ff --- /dev/null +++ b/thirdparty/assimp/include/assimp/matrix3x3.h @@ -0,0 +1,183 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file matrix3x3.h + * @brief Definition of a 3x3 matrix, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_MATRIX3X3_H_INC +#define AI_MATRIX3X3_H_INC + +#include "defs.h" + +#ifdef __cplusplus + +template <typename T> class aiMatrix4x4t; +template <typename T> class aiVector2t; + +// --------------------------------------------------------------------------- +/** @brief Represents a row-major 3x3 matrix + * + * There's much confusion about matrix layouts (column vs. row order). + * This is *always* a row-major matrix. Not even with the + * #aiProcess_ConvertToLeftHanded flag, which absolutely does not affect + * matrix order - it just affects the handedness of the coordinate system + * defined thereby. + */ +template <typename TReal> +class aiMatrix3x3t +{ +public: + + aiMatrix3x3t() AI_NO_EXCEPT : + a1(static_cast<TReal>(1.0f)), a2(), a3(), + b1(), b2(static_cast<TReal>(1.0f)), b3(), + c1(), c2(), c3(static_cast<TReal>(1.0f)) {} + + aiMatrix3x3t ( TReal _a1, TReal _a2, TReal _a3, + TReal _b1, TReal _b2, TReal _b3, + TReal _c1, TReal _c2, TReal _c3) : + a1(_a1), a2(_a2), a3(_a3), + b1(_b1), b2(_b2), b3(_b3), + c1(_c1), c2(_c2), c3(_c3) + {} + +public: + + // matrix multiplication. + aiMatrix3x3t& operator *= (const aiMatrix3x3t& m); + aiMatrix3x3t operator * (const aiMatrix3x3t& m) const; + + // array access operators + TReal* operator[] (unsigned int p_iIndex); + const TReal* operator[] (unsigned int p_iIndex) const; + + // comparison operators + bool operator== (const aiMatrix4x4t<TReal>& m) const; + bool operator!= (const aiMatrix4x4t<TReal>& m) const; + + bool Equal(const aiMatrix4x4t<TReal>& m, TReal epsilon = 1e-6) const; + + template <typename TOther> + operator aiMatrix3x3t<TOther> () const; + +public: + + // ------------------------------------------------------------------- + /** @brief Construction from a 4x4 matrix. The remaining parts + * of the matrix are ignored. + */ + explicit aiMatrix3x3t( const aiMatrix4x4t<TReal>& pMatrix); + + // ------------------------------------------------------------------- + /** @brief Transpose the matrix + */ + aiMatrix3x3t& Transpose(); + + // ------------------------------------------------------------------- + /** @brief Invert the matrix. + * If the matrix is not invertible all elements are set to qnan. + * Beware, use (f != f) to check whether a TReal f is qnan. + */ + aiMatrix3x3t& Inverse(); + TReal Determinant() const; + +public: + // ------------------------------------------------------------------- + /** @brief Returns a rotation matrix for a rotation around z + * @param a Rotation angle, in radians + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix3x3t& RotationZ(TReal a, aiMatrix3x3t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a rotation matrix for a rotation around + * an arbitrary axis. + * + * @param a Rotation angle, in radians + * @param axis Axis to rotate around + * @param out To be filled + */ + static aiMatrix3x3t& Rotation( TReal a, + const aiVector3t<TReal>& axis, aiMatrix3x3t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a translation matrix + * @param v Translation vector + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix3x3t& Translation( const aiVector2t<TReal>& v, aiMatrix3x3t& out); + + // ------------------------------------------------------------------- + /** @brief A function for creating a rotation matrix that rotates a + * vector called "from" into another vector called "to". + * Input : from[3], to[3] which both must be *normalized* non-zero vectors + * Output: mtx[3][3] -- a 3x3 matrix in column-major form + * Authors: Tomas Möller, John Hughes + * "Efficiently Building a Matrix to Rotate One Vector to Another" + * Journal of Graphics Tools, 4(4):1-4, 1999 + */ + static aiMatrix3x3t& FromToMatrix(const aiVector3t<TReal>& from, + const aiVector3t<TReal>& to, aiMatrix3x3t& out); + +public: + TReal a1, a2, a3; + TReal b1, b2, b3; + TReal c1, c2, c3; +}; + +typedef aiMatrix3x3t<ai_real> aiMatrix3x3; + +#else + +struct aiMatrix3x3 { + ai_real a1, a2, a3; + ai_real b1, b2, b3; + ai_real c1, c2, c3; +}; + +#endif // __cplusplus + +#endif // AI_MATRIX3X3_H_INC diff --git a/thirdparty/assimp/include/assimp/matrix3x3.inl b/thirdparty/assimp/include/assimp/matrix3x3.inl new file mode 100644 index 0000000000..d9d45a3e92 --- /dev/null +++ b/thirdparty/assimp/include/assimp/matrix3x3.inl @@ -0,0 +1,357 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file matrix3x3.inl + * @brief Inline implementation of the 3x3 matrix operators + */ +#pragma once +#ifndef AI_MATRIX3X3_INL_INC +#define AI_MATRIX3X3_INL_INC + +#ifdef __cplusplus +#include "matrix3x3.h" + +#include "matrix4x4.h" +#include <algorithm> +#include <cmath> +#include <limits> + +// ------------------------------------------------------------------------------------------------ +// Construction from a 4x4 matrix. The remaining parts of the matrix are ignored. +template <typename TReal> +inline aiMatrix3x3t<TReal>::aiMatrix3x3t( const aiMatrix4x4t<TReal>& pMatrix) +{ + a1 = pMatrix.a1; a2 = pMatrix.a2; a3 = pMatrix.a3; + b1 = pMatrix.b1; b2 = pMatrix.b2; b3 = pMatrix.b3; + c1 = pMatrix.c1; c2 = pMatrix.c2; c3 = pMatrix.c3; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::operator *= (const aiMatrix3x3t<TReal>& m) +{ + *this = aiMatrix3x3t<TReal>(m.a1 * a1 + m.b1 * a2 + m.c1 * a3, + m.a2 * a1 + m.b2 * a2 + m.c2 * a3, + m.a3 * a1 + m.b3 * a2 + m.c3 * a3, + m.a1 * b1 + m.b1 * b2 + m.c1 * b3, + m.a2 * b1 + m.b2 * b2 + m.c2 * b3, + m.a3 * b1 + m.b3 * b2 + m.c3 * b3, + m.a1 * c1 + m.b1 * c2 + m.c1 * c3, + m.a2 * c1 + m.b2 * c2 + m.c2 * c3, + m.a3 * c1 + m.b3 * c2 + m.c3 * c3); + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +template <typename TOther> +aiMatrix3x3t<TReal>::operator aiMatrix3x3t<TOther> () const +{ + return aiMatrix3x3t<TOther>(static_cast<TOther>(a1),static_cast<TOther>(a2),static_cast<TOther>(a3), + static_cast<TOther>(b1),static_cast<TOther>(b2),static_cast<TOther>(b3), + static_cast<TOther>(c1),static_cast<TOther>(c2),static_cast<TOther>(c3)); +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline aiMatrix3x3t<TReal> aiMatrix3x3t<TReal>::operator* (const aiMatrix3x3t<TReal>& m) const +{ + aiMatrix3x3t<TReal> temp( *this); + temp *= m; + return temp; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline TReal* aiMatrix3x3t<TReal>::operator[] (unsigned int p_iIndex) { + switch ( p_iIndex ) { + case 0: + return &a1; + case 1: + return &b1; + case 2: + return &c1; + default: + break; + } + return &a1; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline const TReal* aiMatrix3x3t<TReal>::operator[] (unsigned int p_iIndex) const { + switch ( p_iIndex ) { + case 0: + return &a1; + case 1: + return &b1; + case 2: + return &c1; + default: + break; + } + return &a1; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline bool aiMatrix3x3t<TReal>::operator== (const aiMatrix4x4t<TReal>& m) const +{ + return a1 == m.a1 && a2 == m.a2 && a3 == m.a3 && + b1 == m.b1 && b2 == m.b2 && b3 == m.b3 && + c1 == m.c1 && c2 == m.c2 && c3 == m.c3; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline bool aiMatrix3x3t<TReal>::operator!= (const aiMatrix4x4t<TReal>& m) const +{ + return !(*this == m); +} + +// --------------------------------------------------------------------------- +template<typename TReal> +inline bool aiMatrix3x3t<TReal>::Equal(const aiMatrix4x4t<TReal>& m, TReal epsilon) const { + return + std::abs(a1 - m.a1) <= epsilon && + std::abs(a2 - m.a2) <= epsilon && + std::abs(a3 - m.a3) <= epsilon && + std::abs(b1 - m.b1) <= epsilon && + std::abs(b2 - m.b2) <= epsilon && + std::abs(b3 - m.b3) <= epsilon && + std::abs(c1 - m.c1) <= epsilon && + std::abs(c2 - m.c2) <= epsilon && + std::abs(c3 - m.c3) <= epsilon; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::Transpose() +{ + // (TReal&) don't remove, GCC complains cause of packed fields + std::swap( (TReal&)a2, (TReal&)b1); + std::swap( (TReal&)a3, (TReal&)c1); + std::swap( (TReal&)b3, (TReal&)c2); + return *this; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline TReal aiMatrix3x3t<TReal>::Determinant() const +{ + return a1*b2*c3 - a1*b3*c2 + a2*b3*c1 - a2*b1*c3 + a3*b1*c2 - a3*b2*c1; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::Inverse() +{ + // Compute the reciprocal determinant + TReal det = Determinant(); + if(det == static_cast<TReal>(0.0)) + { + // Matrix not invertible. Setting all elements to nan is not really + // correct in a mathematical sense; but at least qnans are easy to + // spot. XXX we might throw an exception instead, which would + // be even much better to spot :/. + const TReal nan = std::numeric_limits<TReal>::quiet_NaN(); + *this = aiMatrix3x3t<TReal>( nan,nan,nan,nan,nan,nan,nan,nan,nan); + + return *this; + } + + TReal invdet = static_cast<TReal>(1.0) / det; + + aiMatrix3x3t<TReal> res; + res.a1 = invdet * (b2 * c3 - b3 * c2); + res.a2 = -invdet * (a2 * c3 - a3 * c2); + res.a3 = invdet * (a2 * b3 - a3 * b2); + res.b1 = -invdet * (b1 * c3 - b3 * c1); + res.b2 = invdet * (a1 * c3 - a3 * c1); + res.b3 = -invdet * (a1 * b3 - a3 * b1); + res.c1 = invdet * (b1 * c2 - b2 * c1); + res.c2 = -invdet * (a1 * c2 - a2 * c1); + res.c3 = invdet * (a1 * b2 - a2 * b1); + *this = res; + + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::RotationZ(TReal a, aiMatrix3x3t<TReal>& out) +{ + out.a1 = out.b2 = std::cos(a); + out.b1 = std::sin(a); + out.a2 = - out.b1; + + out.a3 = out.b3 = out.c1 = out.c2 = 0.f; + out.c3 = 1.f; + + return out; +} + +// ------------------------------------------------------------------------------------------------ +// Returns a rotation matrix for a rotation around an arbitrary axis. +template <typename TReal> +inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::Rotation( TReal a, const aiVector3t<TReal>& axis, aiMatrix3x3t<TReal>& out) +{ + TReal c = std::cos( a), s = std::sin( a), t = 1 - c; + TReal x = axis.x, y = axis.y, z = axis.z; + + // Many thanks to MathWorld and Wikipedia + out.a1 = t*x*x + c; out.a2 = t*x*y - s*z; out.a3 = t*x*z + s*y; + out.b1 = t*x*y + s*z; out.b2 = t*y*y + c; out.b3 = t*y*z - s*x; + out.c1 = t*x*z - s*y; out.c2 = t*y*z + s*x; out.c3 = t*z*z + c; + + return out; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::Translation( const aiVector2t<TReal>& v, aiMatrix3x3t<TReal>& out) +{ + out = aiMatrix3x3t<TReal>(); + out.a3 = v.x; + out.b3 = v.y; + return out; +} + +// ---------------------------------------------------------------------------------------- +/** A function for creating a rotation matrix that rotates a vector called + * "from" into another vector called "to". + * Input : from[3], to[3] which both must be *normalized* non-zero vectors + * Output: mtx[3][3] -- a 3x3 matrix in colum-major form + * Authors: Tomas Möller, John Hughes + * "Efficiently Building a Matrix to Rotate One Vector to Another" + * Journal of Graphics Tools, 4(4):1-4, 1999 + */ +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::FromToMatrix(const aiVector3t<TReal>& from, + const aiVector3t<TReal>& to, aiMatrix3x3t<TReal>& mtx) +{ + const TReal e = from * to; + const TReal f = (e < 0)? -e:e; + + if (f > static_cast<TReal>(1.0) - static_cast<TReal>(0.00001)) /* "from" and "to"-vector almost parallel */ + { + aiVector3D u,v; /* temporary storage vectors */ + aiVector3D x; /* vector most nearly orthogonal to "from" */ + + x.x = (from.x > 0.0)? from.x : -from.x; + x.y = (from.y > 0.0)? from.y : -from.y; + x.z = (from.z > 0.0)? from.z : -from.z; + + if (x.x < x.y) + { + if (x.x < x.z) + { + x.x = static_cast<TReal>(1.0); + x.y = x.z = static_cast<TReal>(0.0); + } + else + { + x.z = static_cast<TReal>(1.0); + x.x = x.y = static_cast<TReal>(0.0); + } + } + else + { + if (x.y < x.z) + { + x.y = static_cast<TReal>(1.0); + x.x = x.z = static_cast<TReal>(0.0); + } + else + { + x.z = static_cast<TReal>(1.0); + x.x = x.y = static_cast<TReal>(0.0); + } + } + + u.x = x.x - from.x; u.y = x.y - from.y; u.z = x.z - from.z; + v.x = x.x - to.x; v.y = x.y - to.y; v.z = x.z - to.z; + + const TReal c1_ = static_cast<TReal>(2.0) / (u * u); + const TReal c2_ = static_cast<TReal>(2.0) / (v * v); + const TReal c3_ = c1_ * c2_ * (u * v); + + for (unsigned int i = 0; i < 3; i++) + { + for (unsigned int j = 0; j < 3; j++) + { + mtx[i][j] = - c1_ * u[i] * u[j] - c2_ * v[i] * v[j] + + c3_ * v[i] * u[j]; + } + mtx[i][i] += static_cast<TReal>(1.0); + } + } + else /* the most common case, unless "from"="to", or "from"=-"to" */ + { + const aiVector3D v = from ^ to; + /* ... use this hand optimized version (9 mults less) */ + const TReal h = static_cast<TReal>(1.0)/(static_cast<TReal>(1.0) + e); /* optimization by Gottfried Chen */ + const TReal hvx = h * v.x; + const TReal hvz = h * v.z; + const TReal hvxy = hvx * v.y; + const TReal hvxz = hvx * v.z; + const TReal hvyz = hvz * v.y; + mtx[0][0] = e + hvx * v.x; + mtx[0][1] = hvxy - v.z; + mtx[0][2] = hvxz + v.y; + + mtx[1][0] = hvxy + v.z; + mtx[1][1] = e + h * v.y * v.y; + mtx[1][2] = hvyz - v.x; + + mtx[2][0] = hvxz - v.y; + mtx[2][1] = hvyz + v.x; + mtx[2][2] = e + hvz * v.z; + } + return mtx; +} + + +#endif // __cplusplus +#endif // AI_MATRIX3X3_INL_INC diff --git a/thirdparty/assimp/include/assimp/matrix4x4.h b/thirdparty/assimp/include/assimp/matrix4x4.h new file mode 100644 index 0000000000..046bb535f2 --- /dev/null +++ b/thirdparty/assimp/include/assimp/matrix4x4.h @@ -0,0 +1,280 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file matrix4x4.h + * @brief 4x4 matrix structure, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_MATRIX4X4_H_INC +#define AI_MATRIX4X4_H_INC + +#include "vector3.h" +#include "defs.h" + +#ifdef __cplusplus + +template<typename TReal> class aiMatrix3x3t; +template<typename TReal> class aiQuaterniont; + +// --------------------------------------------------------------------------- +/** @brief Represents a row-major 4x4 matrix, use this for homogeneous + * coordinates. + * + * There's much confusion about matrix layouts (column vs. row order). + * This is *always* a row-major matrix. Not even with the + * #aiProcess_ConvertToLeftHanded flag, which absolutely does not affect + * matrix order - it just affects the handedness of the coordinate system + * defined thereby. + */ +template<typename TReal> +class aiMatrix4x4t +{ +public: + + /** set to identity */ + aiMatrix4x4t() AI_NO_EXCEPT; + + /** construction from single values */ + aiMatrix4x4t ( TReal _a1, TReal _a2, TReal _a3, TReal _a4, + TReal _b1, TReal _b2, TReal _b3, TReal _b4, + TReal _c1, TReal _c2, TReal _c3, TReal _c4, + TReal _d1, TReal _d2, TReal _d3, TReal _d4); + + + /** construction from 3x3 matrix, remaining elements are set to identity */ + explicit aiMatrix4x4t( const aiMatrix3x3t<TReal>& m); + + /** construction from position, rotation and scaling components + * @param scaling The scaling for the x,y,z axes + * @param rotation The rotation as a hamilton quaternion + * @param position The position for the x,y,z axes + */ + aiMatrix4x4t(const aiVector3t<TReal>& scaling, const aiQuaterniont<TReal>& rotation, + const aiVector3t<TReal>& position); + +public: + + // array access operators + /** @fn TReal* operator[] (unsigned int p_iIndex) + * @param [in] p_iIndex - index of the row. + * @return pointer to pointed row. + */ + TReal* operator[] (unsigned int p_iIndex); + + /** @fn const TReal* operator[] (unsigned int p_iIndex) const + * @overload TReal* operator[] (unsigned int p_iIndex) + */ + const TReal* operator[] (unsigned int p_iIndex) const; + + // comparison operators + bool operator== (const aiMatrix4x4t& m) const; + bool operator!= (const aiMatrix4x4t& m) const; + + bool Equal(const aiMatrix4x4t& m, TReal epsilon = 1e-6) const; + + // matrix multiplication. + aiMatrix4x4t& operator *= (const aiMatrix4x4t& m); + aiMatrix4x4t operator * (const aiMatrix4x4t& m) const; + aiMatrix4x4t operator * (const TReal& aFloat) const; + aiMatrix4x4t operator + (const aiMatrix4x4t& aMatrix) const; + + template <typename TOther> + operator aiMatrix4x4t<TOther> () const; + +public: + + // ------------------------------------------------------------------- + /** @brief Transpose the matrix */ + aiMatrix4x4t& Transpose(); + + // ------------------------------------------------------------------- + /** @brief Invert the matrix. + * If the matrix is not invertible all elements are set to qnan. + * Beware, use (f != f) to check whether a TReal f is qnan. + */ + aiMatrix4x4t& Inverse(); + TReal Determinant() const; + + + // ------------------------------------------------------------------- + /** @brief Returns true of the matrix is the identity matrix. + * The check is performed against a not so small epsilon. + */ + inline bool IsIdentity() const; + + // ------------------------------------------------------------------- + /** @brief Decompose a trafo matrix into its original components + * @param scaling Receives the output scaling for the x,y,z axes + * @param rotation Receives the output rotation as a hamilton + * quaternion + * @param position Receives the output position for the x,y,z axes + */ + void Decompose (aiVector3t<TReal>& scaling, aiQuaterniont<TReal>& rotation, + aiVector3t<TReal>& position) const; + + // ------------------------------------------------------------------- + /** @fn void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const + * @brief Decompose a trafo matrix into its original components. + * Thx to good FAQ at http://www.gamedev.ru/code/articles/faq_matrix_quat + * @param [out] pScaling - Receives the output scaling for the x,y,z axes. + * @param [out] pRotation - Receives the output rotation as a Euler angles. + * @param [out] pPosition - Receives the output position for the x,y,z axes. + */ + void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const; + + // ------------------------------------------------------------------- + /** @fn void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotationAxis, TReal& pRotationAngle, aiVector3t<TReal>& pPosition) const + * @brief Decompose a trafo matrix into its original components + * Thx to good FAQ at http://www.gamedev.ru/code/articles/faq_matrix_quat + * @param [out] pScaling - Receives the output scaling for the x,y,z axes. + * @param [out] pRotationAxis - Receives the output rotation axis. + * @param [out] pRotationAngle - Receives the output rotation angle for @ref pRotationAxis. + * @param [out] pPosition - Receives the output position for the x,y,z axes. + */ + void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotationAxis, TReal& pRotationAngle, aiVector3t<TReal>& pPosition) const; + + // ------------------------------------------------------------------- + /** @brief Decompose a trafo matrix with no scaling into its + * original components + * @param rotation Receives the output rotation as a hamilton + * quaternion + * @param position Receives the output position for the x,y,z axes + */ + void DecomposeNoScaling (aiQuaterniont<TReal>& rotation, + aiVector3t<TReal>& position) const; + + + // ------------------------------------------------------------------- + /** @brief Creates a trafo matrix from a set of euler angles + * @param x Rotation angle for the x-axis, in radians + * @param y Rotation angle for the y-axis, in radians + * @param z Rotation angle for the z-axis, in radians + */ + aiMatrix4x4t& FromEulerAnglesXYZ(TReal x, TReal y, TReal z); + aiMatrix4x4t& FromEulerAnglesXYZ(const aiVector3t<TReal>& blubb); + +public: + // ------------------------------------------------------------------- + /** @brief Returns a rotation matrix for a rotation around the x axis + * @param a Rotation angle, in radians + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& RotationX(TReal a, aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a rotation matrix for a rotation around the y axis + * @param a Rotation angle, in radians + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& RotationY(TReal a, aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a rotation matrix for a rotation around the z axis + * @param a Rotation angle, in radians + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& RotationZ(TReal a, aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** Returns a rotation matrix for a rotation around an arbitrary axis. + * @param a Rotation angle, in radians + * @param axis Rotation axis, should be a normalized vector. + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& Rotation(TReal a, const aiVector3t<TReal>& axis, + aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a translation matrix + * @param v Translation vector + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& Translation( const aiVector3t<TReal>& v, + aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a scaling matrix + * @param v Scaling vector + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& Scaling( const aiVector3t<TReal>& v, aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** @brief A function for creating a rotation matrix that rotates a + * vector called "from" into another vector called "to". + * Input : from[3], to[3] which both must be *normalized* non-zero vectors + * Output: mtx[3][3] -- a 3x3 matrix in column-major form + * Authors: Tomas Mueller, John Hughes + * "Efficiently Building a Matrix to Rotate One Vector to Another" + * Journal of Graphics Tools, 4(4):1-4, 1999 + */ + static aiMatrix4x4t& FromToMatrix(const aiVector3t<TReal>& from, + const aiVector3t<TReal>& to, aiMatrix4x4t& out); + +public: + TReal a1, a2, a3, a4; + TReal b1, b2, b3, b4; + TReal c1, c2, c3, c4; + TReal d1, d2, d3, d4; +}; + +typedef aiMatrix4x4t<ai_real> aiMatrix4x4; + +#else + +struct aiMatrix4x4 { + ai_real a1, a2, a3, a4; + ai_real b1, b2, b3, b4; + ai_real c1, c2, c3, c4; + ai_real d1, d2, d3, d4; +}; + + +#endif // __cplusplus + +#endif // AI_MATRIX4X4_H_INC diff --git a/thirdparty/assimp/include/assimp/matrix4x4.inl b/thirdparty/assimp/include/assimp/matrix4x4.inl new file mode 100644 index 0000000000..ebc67a06ec --- /dev/null +++ b/thirdparty/assimp/include/assimp/matrix4x4.inl @@ -0,0 +1,686 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file matrix4x4.inl + * @brief Inline implementation of the 4x4 matrix operators + */ +#pragma once +#ifndef AI_MATRIX4X4_INL_INC +#define AI_MATRIX4X4_INL_INC + +#ifdef __cplusplus + +#include "matrix4x4.h" +#include "matrix3x3.h" +#include "quaternion.h" + +#include <algorithm> +#include <limits> +#include <cmath> + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +aiMatrix4x4t<TReal>::aiMatrix4x4t() AI_NO_EXCEPT : + a1(1.0f), a2(), a3(), a4(), + b1(), b2(1.0f), b3(), b4(), + c1(), c2(), c3(1.0f), c4(), + d1(), d2(), d3(), d4(1.0f) +{ + +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +aiMatrix4x4t<TReal>::aiMatrix4x4t (TReal _a1, TReal _a2, TReal _a3, TReal _a4, + TReal _b1, TReal _b2, TReal _b3, TReal _b4, + TReal _c1, TReal _c2, TReal _c3, TReal _c4, + TReal _d1, TReal _d2, TReal _d3, TReal _d4) : + a1(_a1), a2(_a2), a3(_a3), a4(_a4), + b1(_b1), b2(_b2), b3(_b3), b4(_b4), + c1(_c1), c2(_c2), c3(_c3), c4(_c4), + d1(_d1), d2(_d2), d3(_d3), d4(_d4) +{ + +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +template <typename TOther> +aiMatrix4x4t<TReal>::operator aiMatrix4x4t<TOther> () const +{ + return aiMatrix4x4t<TOther>(static_cast<TOther>(a1),static_cast<TOther>(a2),static_cast<TOther>(a3),static_cast<TOther>(a4), + static_cast<TOther>(b1),static_cast<TOther>(b2),static_cast<TOther>(b3),static_cast<TOther>(b4), + static_cast<TOther>(c1),static_cast<TOther>(c2),static_cast<TOther>(c3),static_cast<TOther>(c4), + static_cast<TOther>(d1),static_cast<TOther>(d2),static_cast<TOther>(d3),static_cast<TOther>(d4)); +} + + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>::aiMatrix4x4t (const aiMatrix3x3t<TReal>& m) +{ + a1 = m.a1; a2 = m.a2; a3 = m.a3; a4 = static_cast<TReal>(0.0); + b1 = m.b1; b2 = m.b2; b3 = m.b3; b4 = static_cast<TReal>(0.0); + c1 = m.c1; c2 = m.c2; c3 = m.c3; c4 = static_cast<TReal>(0.0); + d1 = static_cast<TReal>(0.0); d2 = static_cast<TReal>(0.0); d3 = static_cast<TReal>(0.0); d4 = static_cast<TReal>(1.0); +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>::aiMatrix4x4t (const aiVector3t<TReal>& scaling, const aiQuaterniont<TReal>& rotation, const aiVector3t<TReal>& position) +{ + // build a 3x3 rotation matrix + aiMatrix3x3t<TReal> m = rotation.GetMatrix(); + + a1 = m.a1 * scaling.x; + a2 = m.a2 * scaling.x; + a3 = m.a3 * scaling.x; + a4 = position.x; + + b1 = m.b1 * scaling.y; + b2 = m.b2 * scaling.y; + b3 = m.b3 * scaling.y; + b4 = position.y; + + c1 = m.c1 * scaling.z; + c2 = m.c2 * scaling.z; + c3 = m.c3 * scaling.z; + c4= position.z; + + d1 = static_cast<TReal>(0.0); + d2 = static_cast<TReal>(0.0); + d3 = static_cast<TReal>(0.0); + d4 = static_cast<TReal>(1.0); +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::operator *= (const aiMatrix4x4t<TReal>& m) +{ + *this = aiMatrix4x4t<TReal>( + m.a1 * a1 + m.b1 * a2 + m.c1 * a3 + m.d1 * a4, + m.a2 * a1 + m.b2 * a2 + m.c2 * a3 + m.d2 * a4, + m.a3 * a1 + m.b3 * a2 + m.c3 * a3 + m.d3 * a4, + m.a4 * a1 + m.b4 * a2 + m.c4 * a3 + m.d4 * a4, + m.a1 * b1 + m.b1 * b2 + m.c1 * b3 + m.d1 * b4, + m.a2 * b1 + m.b2 * b2 + m.c2 * b3 + m.d2 * b4, + m.a3 * b1 + m.b3 * b2 + m.c3 * b3 + m.d3 * b4, + m.a4 * b1 + m.b4 * b2 + m.c4 * b3 + m.d4 * b4, + m.a1 * c1 + m.b1 * c2 + m.c1 * c3 + m.d1 * c4, + m.a2 * c1 + m.b2 * c2 + m.c2 * c3 + m.d2 * c4, + m.a3 * c1 + m.b3 * c2 + m.c3 * c3 + m.d3 * c4, + m.a4 * c1 + m.b4 * c2 + m.c4 * c3 + m.d4 * c4, + m.a1 * d1 + m.b1 * d2 + m.c1 * d3 + m.d1 * d4, + m.a2 * d1 + m.b2 * d2 + m.c2 * d3 + m.d2 * d4, + m.a3 * d1 + m.b3 * d2 + m.c3 * d3 + m.d3 * d4, + m.a4 * d1 + m.b4 * d2 + m.c4 * d3 + m.d4 * d4); + return *this; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal> aiMatrix4x4t<TReal>::operator* (const TReal& aFloat) const +{ + aiMatrix4x4t<TReal> temp( + a1 * aFloat, + a2 * aFloat, + a3 * aFloat, + a4 * aFloat, + b1 * aFloat, + b2 * aFloat, + b3 * aFloat, + b4 * aFloat, + c1 * aFloat, + c2 * aFloat, + c3 * aFloat, + c4 * aFloat, + d1 * aFloat, + d2 * aFloat, + d3 * aFloat, + d4 * aFloat); + return temp; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal> aiMatrix4x4t<TReal>::operator+ (const aiMatrix4x4t<TReal>& m) const +{ + aiMatrix4x4t<TReal> temp( + m.a1 + a1, + m.a2 + a2, + m.a3 + a3, + m.a4 + a4, + m.b1 + b1, + m.b2 + b2, + m.b3 + b3, + m.b4 + b4, + m.c1 + c1, + m.c2 + c2, + m.c3 + c3, + m.c4 + c4, + m.d1 + d1, + m.d2 + d2, + m.d3 + d3, + m.d4 + d4); + return temp; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal> aiMatrix4x4t<TReal>::operator* (const aiMatrix4x4t<TReal>& m) const +{ + aiMatrix4x4t<TReal> temp( *this); + temp *= m; + return temp; +} + + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::Transpose() +{ + // (TReal&) don't remove, GCC complains cause of packed fields + std::swap( (TReal&)b1, (TReal&)a2); + std::swap( (TReal&)c1, (TReal&)a3); + std::swap( (TReal&)c2, (TReal&)b3); + std::swap( (TReal&)d1, (TReal&)a4); + std::swap( (TReal&)d2, (TReal&)b4); + std::swap( (TReal&)d3, (TReal&)c4); + return *this; +} + + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline TReal aiMatrix4x4t<TReal>::Determinant() const +{ + return a1*b2*c3*d4 - a1*b2*c4*d3 + a1*b3*c4*d2 - a1*b3*c2*d4 + + a1*b4*c2*d3 - a1*b4*c3*d2 - a2*b3*c4*d1 + a2*b3*c1*d4 + - a2*b4*c1*d3 + a2*b4*c3*d1 - a2*b1*c3*d4 + a2*b1*c4*d3 + + a3*b4*c1*d2 - a3*b4*c2*d1 + a3*b1*c2*d4 - a3*b1*c4*d2 + + a3*b2*c4*d1 - a3*b2*c1*d4 - a4*b1*c2*d3 + a4*b1*c3*d2 + - a4*b2*c3*d1 + a4*b2*c1*d3 - a4*b3*c1*d2 + a4*b3*c2*d1; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::Inverse() +{ + // Compute the reciprocal determinant + const TReal det = Determinant(); + if(det == static_cast<TReal>(0.0)) + { + // Matrix not invertible. Setting all elements to nan is not really + // correct in a mathematical sense but it is easy to debug for the + // programmer. + const TReal nan = std::numeric_limits<TReal>::quiet_NaN(); + *this = aiMatrix4x4t<TReal>( + nan,nan,nan,nan, + nan,nan,nan,nan, + nan,nan,nan,nan, + nan,nan,nan,nan); + + return *this; + } + + const TReal invdet = static_cast<TReal>(1.0) / det; + + aiMatrix4x4t<TReal> res; + res.a1 = invdet * (b2 * (c3 * d4 - c4 * d3) + b3 * (c4 * d2 - c2 * d4) + b4 * (c2 * d3 - c3 * d2)); + res.a2 = -invdet * (a2 * (c3 * d4 - c4 * d3) + a3 * (c4 * d2 - c2 * d4) + a4 * (c2 * d3 - c3 * d2)); + res.a3 = invdet * (a2 * (b3 * d4 - b4 * d3) + a3 * (b4 * d2 - b2 * d4) + a4 * (b2 * d3 - b3 * d2)); + res.a4 = -invdet * (a2 * (b3 * c4 - b4 * c3) + a3 * (b4 * c2 - b2 * c4) + a4 * (b2 * c3 - b3 * c2)); + res.b1 = -invdet * (b1 * (c3 * d4 - c4 * d3) + b3 * (c4 * d1 - c1 * d4) + b4 * (c1 * d3 - c3 * d1)); + res.b2 = invdet * (a1 * (c3 * d4 - c4 * d3) + a3 * (c4 * d1 - c1 * d4) + a4 * (c1 * d3 - c3 * d1)); + res.b3 = -invdet * (a1 * (b3 * d4 - b4 * d3) + a3 * (b4 * d1 - b1 * d4) + a4 * (b1 * d3 - b3 * d1)); + res.b4 = invdet * (a1 * (b3 * c4 - b4 * c3) + a3 * (b4 * c1 - b1 * c4) + a4 * (b1 * c3 - b3 * c1)); + res.c1 = invdet * (b1 * (c2 * d4 - c4 * d2) + b2 * (c4 * d1 - c1 * d4) + b4 * (c1 * d2 - c2 * d1)); + res.c2 = -invdet * (a1 * (c2 * d4 - c4 * d2) + a2 * (c4 * d1 - c1 * d4) + a4 * (c1 * d2 - c2 * d1)); + res.c3 = invdet * (a1 * (b2 * d4 - b4 * d2) + a2 * (b4 * d1 - b1 * d4) + a4 * (b1 * d2 - b2 * d1)); + res.c4 = -invdet * (a1 * (b2 * c4 - b4 * c2) + a2 * (b4 * c1 - b1 * c4) + a4 * (b1 * c2 - b2 * c1)); + res.d1 = -invdet * (b1 * (c2 * d3 - c3 * d2) + b2 * (c3 * d1 - c1 * d3) + b3 * (c1 * d2 - c2 * d1)); + res.d2 = invdet * (a1 * (c2 * d3 - c3 * d2) + a2 * (c3 * d1 - c1 * d3) + a3 * (c1 * d2 - c2 * d1)); + res.d3 = -invdet * (a1 * (b2 * d3 - b3 * d2) + a2 * (b3 * d1 - b1 * d3) + a3 * (b1 * d2 - b2 * d1)); + res.d4 = invdet * (a1 * (b2 * c3 - b3 * c2) + a2 * (b3 * c1 - b1 * c3) + a3 * (b1 * c2 - b2 * c1)); + *this = res; + + return *this; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline TReal* aiMatrix4x4t<TReal>::operator[](unsigned int p_iIndex) { + if (p_iIndex > 3) { + return NULL; + } + switch ( p_iIndex ) { + case 0: + return &a1; + case 1: + return &b1; + case 2: + return &c1; + case 3: + return &d1; + default: + break; + } + return &a1; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline const TReal* aiMatrix4x4t<TReal>::operator[](unsigned int p_iIndex) const { + if (p_iIndex > 3) { + return NULL; + } + + switch ( p_iIndex ) { + case 0: + return &a1; + case 1: + return &b1; + case 2: + return &c1; + case 3: + return &d1; + default: + break; + } + return &a1; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline bool aiMatrix4x4t<TReal>::operator== (const aiMatrix4x4t<TReal>& m) const +{ + return (a1 == m.a1 && a2 == m.a2 && a3 == m.a3 && a4 == m.a4 && + b1 == m.b1 && b2 == m.b2 && b3 == m.b3 && b4 == m.b4 && + c1 == m.c1 && c2 == m.c2 && c3 == m.c3 && c4 == m.c4 && + d1 == m.d1 && d2 == m.d2 && d3 == m.d3 && d4 == m.d4); +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline bool aiMatrix4x4t<TReal>::operator!= (const aiMatrix4x4t<TReal>& m) const +{ + return !(*this == m); +} + +// --------------------------------------------------------------------------- +template<typename TReal> +inline bool aiMatrix4x4t<TReal>::Equal(const aiMatrix4x4t<TReal>& m, TReal epsilon) const { + return + std::abs(a1 - m.a1) <= epsilon && + std::abs(a2 - m.a2) <= epsilon && + std::abs(a3 - m.a3) <= epsilon && + std::abs(a4 - m.a4) <= epsilon && + std::abs(b1 - m.b1) <= epsilon && + std::abs(b2 - m.b2) <= epsilon && + std::abs(b3 - m.b3) <= epsilon && + std::abs(b4 - m.b4) <= epsilon && + std::abs(c1 - m.c1) <= epsilon && + std::abs(c2 - m.c2) <= epsilon && + std::abs(c3 - m.c3) <= epsilon && + std::abs(c4 - m.c4) <= epsilon && + std::abs(d1 - m.d1) <= epsilon && + std::abs(d2 - m.d2) <= epsilon && + std::abs(d3 - m.d3) <= epsilon && + std::abs(d4 - m.d4) <= epsilon; +} + +// ---------------------------------------------------------------------------------------- + +#define ASSIMP_MATRIX4_4_DECOMPOSE_PART \ + const aiMatrix4x4t<TReal>& _this = *this;/* Create alias for conveniance. */ \ + \ + /* extract translation */ \ + pPosition.x = _this[0][3]; \ + pPosition.y = _this[1][3]; \ + pPosition.z = _this[2][3]; \ + \ + /* extract the columns of the matrix. */ \ + aiVector3t<TReal> vCols[3] = { \ + aiVector3t<TReal>(_this[0][0],_this[1][0],_this[2][0]), \ + aiVector3t<TReal>(_this[0][1],_this[1][1],_this[2][1]), \ + aiVector3t<TReal>(_this[0][2],_this[1][2],_this[2][2]) \ + }; \ + \ + /* extract the scaling factors */ \ + pScaling.x = vCols[0].Length(); \ + pScaling.y = vCols[1].Length(); \ + pScaling.z = vCols[2].Length(); \ + \ + /* and the sign of the scaling */ \ + if (Determinant() < 0) pScaling = -pScaling; \ + \ + /* and remove all scaling from the matrix */ \ + if(pScaling.x) vCols[0] /= pScaling.x; \ + if(pScaling.y) vCols[1] /= pScaling.y; \ + if(pScaling.z) vCols[2] /= pScaling.z; \ + \ + do {} while(false) + + + + +template <typename TReal> +inline void aiMatrix4x4t<TReal>::Decompose (aiVector3t<TReal>& pScaling, aiQuaterniont<TReal>& pRotation, + aiVector3t<TReal>& pPosition) const +{ + ASSIMP_MATRIX4_4_DECOMPOSE_PART; + + // build a 3x3 rotation matrix + aiMatrix3x3t<TReal> m(vCols[0].x,vCols[1].x,vCols[2].x, + vCols[0].y,vCols[1].y,vCols[2].y, + vCols[0].z,vCols[1].z,vCols[2].z); + + // and generate the rotation quaternion from it + pRotation = aiQuaterniont<TReal>(m); +} + +template <typename TReal> +inline void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const +{ + ASSIMP_MATRIX4_4_DECOMPOSE_PART; + + /* + assuming a right-handed coordinate system + and post-multiplication of column vectors, + the rotation matrix for an euler XYZ rotation is M = Rz * Ry * Rx. + combining gives: + + | CE BDE-AF ADE+BF 0 | + M = | CF BDF+AE ADF-BE 0 | + | -D CB AC 0 | + | 0 0 0 1 | + + where + A = cos(angle_x), B = sin(angle_x); + C = cos(angle_y), D = sin(angle_y); + E = cos(angle_z), F = sin(angle_z); + */ + + // Use a small epsilon to solve floating-point inaccuracies + const TReal epsilon = 10e-3f; + + pRotation.y = std::asin(-vCols[0].z);// D. Angle around oY. + + TReal C = std::cos(pRotation.y); + + if(std::fabs(C) > epsilon) + { + // Finding angle around oX. + TReal tan_x = vCols[2].z / C;// A + TReal tan_y = vCols[1].z / C;// B + + pRotation.x = std::atan2(tan_y, tan_x); + // Finding angle around oZ. + tan_x = vCols[0].x / C;// E + tan_y = vCols[0].y / C;// F + pRotation.z = std::atan2(tan_y, tan_x); + } + else + {// oY is fixed. + pRotation.x = 0;// Set angle around oX to 0. => A == 1, B == 0, C == 0, D == 1. + + // And finding angle around oZ. + TReal tan_x = vCols[1].y;// BDF+AE => E + TReal tan_y = -vCols[1].x;// BDE-AF => F + + pRotation.z = std::atan2(tan_y, tan_x); + } +} + +#undef ASSIMP_MATRIX4_4_DECOMPOSE_PART + +template <typename TReal> +inline void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotationAxis, TReal& pRotationAngle, + aiVector3t<TReal>& pPosition) const +{ +aiQuaterniont<TReal> pRotation; + + Decompose(pScaling, pRotation, pPosition); + pRotation.Normalize(); + + TReal angle_cos = pRotation.w; + TReal angle_sin = std::sqrt(1.0f - angle_cos * angle_cos); + + pRotationAngle = std::acos(angle_cos) * 2; + + // Use a small epsilon to solve floating-point inaccuracies + const TReal epsilon = 10e-3f; + + if(std::fabs(angle_sin) < epsilon) angle_sin = 1; + + pRotationAxis.x = pRotation.x / angle_sin; + pRotationAxis.y = pRotation.y / angle_sin; + pRotationAxis.z = pRotation.z / angle_sin; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline void aiMatrix4x4t<TReal>::DecomposeNoScaling (aiQuaterniont<TReal>& rotation, + aiVector3t<TReal>& position) const +{ + const aiMatrix4x4t<TReal>& _this = *this; + + // extract translation + position.x = _this[0][3]; + position.y = _this[1][3]; + position.z = _this[2][3]; + + // extract rotation + rotation = aiQuaterniont<TReal>((aiMatrix3x3t<TReal>)_this); +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::FromEulerAnglesXYZ(const aiVector3t<TReal>& blubb) +{ + return FromEulerAnglesXYZ(blubb.x,blubb.y,blubb.z); +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::FromEulerAnglesXYZ(TReal x, TReal y, TReal z) +{ + aiMatrix4x4t<TReal>& _this = *this; + + TReal cx = std::cos(x); + TReal sx = std::sin(x); + TReal cy = std::cos(y); + TReal sy = std::sin(y); + TReal cz = std::cos(z); + TReal sz = std::sin(z); + + // mz*my*mx + _this.a1 = cz * cy; + _this.a2 = cz * sy * sx - sz * cx; + _this.a3 = sz * sx + cz * sy * cx; + + _this.b1 = sz * cy; + _this.b2 = cz * cx + sz * sy * sx; + _this.b3 = sz * sy * cx - cz * sx; + + _this.c1 = -sy; + _this.c2 = cy * sx; + _this.c3 = cy * cx; + + return *this; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline bool aiMatrix4x4t<TReal>::IsIdentity() const +{ + // Use a small epsilon to solve floating-point inaccuracies + const static TReal epsilon = 10e-3f; + + return (a2 <= epsilon && a2 >= -epsilon && + a3 <= epsilon && a3 >= -epsilon && + a4 <= epsilon && a4 >= -epsilon && + b1 <= epsilon && b1 >= -epsilon && + b3 <= epsilon && b3 >= -epsilon && + b4 <= epsilon && b4 >= -epsilon && + c1 <= epsilon && c1 >= -epsilon && + c2 <= epsilon && c2 >= -epsilon && + c4 <= epsilon && c4 >= -epsilon && + d1 <= epsilon && d1 >= -epsilon && + d2 <= epsilon && d2 >= -epsilon && + d3 <= epsilon && d3 >= -epsilon && + a1 <= 1.f+epsilon && a1 >= 1.f-epsilon && + b2 <= 1.f+epsilon && b2 >= 1.f-epsilon && + c3 <= 1.f+epsilon && c3 >= 1.f-epsilon && + d4 <= 1.f+epsilon && d4 >= 1.f-epsilon); +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::RotationX(TReal a, aiMatrix4x4t<TReal>& out) +{ + /* + | 1 0 0 0 | + M = | 0 cos(A) -sin(A) 0 | + | 0 sin(A) cos(A) 0 | + | 0 0 0 1 | */ + out = aiMatrix4x4t<TReal>(); + out.b2 = out.c3 = std::cos(a); + out.b3 = -(out.c2 = std::sin(a)); + return out; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::RotationY(TReal a, aiMatrix4x4t<TReal>& out) +{ + /* + | cos(A) 0 sin(A) 0 | + M = | 0 1 0 0 | + | -sin(A) 0 cos(A) 0 | + | 0 0 0 1 | + */ + out = aiMatrix4x4t<TReal>(); + out.a1 = out.c3 = std::cos(a); + out.c1 = -(out.a3 = std::sin(a)); + return out; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::RotationZ(TReal a, aiMatrix4x4t<TReal>& out) +{ + /* + | cos(A) -sin(A) 0 0 | + M = | sin(A) cos(A) 0 0 | + | 0 0 1 0 | + | 0 0 0 1 | */ + out = aiMatrix4x4t<TReal>(); + out.a1 = out.b2 = std::cos(a); + out.a2 = -(out.b1 = std::sin(a)); + return out; +} + +// ---------------------------------------------------------------------------------------- +// Returns a rotation matrix for a rotation around an arbitrary axis. +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::Rotation( TReal a, const aiVector3t<TReal>& axis, aiMatrix4x4t<TReal>& out) +{ + TReal c = std::cos( a), s = std::sin( a), t = 1 - c; + TReal x = axis.x, y = axis.y, z = axis.z; + + // Many thanks to MathWorld and Wikipedia + out.a1 = t*x*x + c; out.a2 = t*x*y - s*z; out.a3 = t*x*z + s*y; + out.b1 = t*x*y + s*z; out.b2 = t*y*y + c; out.b3 = t*y*z - s*x; + out.c1 = t*x*z - s*y; out.c2 = t*y*z + s*x; out.c3 = t*z*z + c; + out.a4 = out.b4 = out.c4 = static_cast<TReal>(0.0); + out.d1 = out.d2 = out.d3 = static_cast<TReal>(0.0); + out.d4 = static_cast<TReal>(1.0); + + return out; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::Translation( const aiVector3t<TReal>& v, aiMatrix4x4t<TReal>& out) +{ + out = aiMatrix4x4t<TReal>(); + out.a4 = v.x; + out.b4 = v.y; + out.c4 = v.z; + return out; +} + +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::Scaling( const aiVector3t<TReal>& v, aiMatrix4x4t<TReal>& out) +{ + out = aiMatrix4x4t<TReal>(); + out.a1 = v.x; + out.b2 = v.y; + out.c3 = v.z; + return out; +} + +// ---------------------------------------------------------------------------------------- +/** A function for creating a rotation matrix that rotates a vector called + * "from" into another vector called "to". + * Input : from[3], to[3] which both must be *normalized* non-zero vectors + * Output: mtx[3][3] -- a 3x3 matrix in colum-major form + * Authors: Tomas Möller, John Hughes + * "Efficiently Building a Matrix to Rotate One Vector to Another" + * Journal of Graphics Tools, 4(4):1-4, 1999 + */ +// ---------------------------------------------------------------------------------------- +template <typename TReal> +inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::FromToMatrix(const aiVector3t<TReal>& from, + const aiVector3t<TReal>& to, aiMatrix4x4t<TReal>& mtx) +{ + aiMatrix3x3t<TReal> m3; + aiMatrix3x3t<TReal>::FromToMatrix(from,to,m3); + mtx = aiMatrix4x4t<TReal>(m3); + return mtx; +} + +#endif // __cplusplus +#endif // AI_MATRIX4X4_INL_INC diff --git a/thirdparty/assimp/include/assimp/mesh.h b/thirdparty/assimp/include/assimp/mesh.h new file mode 100644 index 0000000000..36f3ed2afd --- /dev/null +++ b/thirdparty/assimp/include/assimp/mesh.h @@ -0,0 +1,852 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file mesh.h + * @brief Declares the data structures in which the imported geometry is + returned by ASSIMP: aiMesh, aiFace and aiBone data structures. + */ +#pragma once +#ifndef AI_MESH_H_INC +#define AI_MESH_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +// Limits. These values are required to match the settings Assimp was +// compiled against. Therefore, do not redefine them unless you build the +// library from source using the same definitions. +// --------------------------------------------------------------------------- + +/** @def AI_MAX_FACE_INDICES + * Maximum number of indices per face (polygon). */ + +#ifndef AI_MAX_FACE_INDICES +# define AI_MAX_FACE_INDICES 0x7fff +#endif + +/** @def AI_MAX_BONE_WEIGHTS + * Maximum number of indices per face (polygon). */ + +#ifndef AI_MAX_BONE_WEIGHTS +# define AI_MAX_BONE_WEIGHTS 0x7fffffff +#endif + +/** @def AI_MAX_VERTICES + * Maximum number of vertices per mesh. */ + +#ifndef AI_MAX_VERTICES +# define AI_MAX_VERTICES 0x7fffffff +#endif + +/** @def AI_MAX_FACES + * Maximum number of faces per mesh. */ + +#ifndef AI_MAX_FACES +# define AI_MAX_FACES 0x7fffffff +#endif + +/** @def AI_MAX_NUMBER_OF_COLOR_SETS + * Supported number of vertex color sets per mesh. */ + +#ifndef AI_MAX_NUMBER_OF_COLOR_SETS +# define AI_MAX_NUMBER_OF_COLOR_SETS 0x8 +#endif // !! AI_MAX_NUMBER_OF_COLOR_SETS + +/** @def AI_MAX_NUMBER_OF_TEXTURECOORDS + * Supported number of texture coord sets (UV(W) channels) per mesh */ + +#ifndef AI_MAX_NUMBER_OF_TEXTURECOORDS +# define AI_MAX_NUMBER_OF_TEXTURECOORDS 0x8 +#endif // !! AI_MAX_NUMBER_OF_TEXTURECOORDS + +// --------------------------------------------------------------------------- +/** @brief A single face in a mesh, referring to multiple vertices. + * + * If mNumIndices is 3, we call the face 'triangle', for mNumIndices > 3 + * it's called 'polygon' (hey, that's just a definition!). + * <br> + * aiMesh::mPrimitiveTypes can be queried to quickly examine which types of + * primitive are actually present in a mesh. The #aiProcess_SortByPType flag + * executes a special post-processing algorithm which splits meshes with + * *different* primitive types mixed up (e.g. lines and triangles) in several + * 'clean' submeshes. Furthermore there is a configuration option ( + * #AI_CONFIG_PP_SBP_REMOVE) to force #aiProcess_SortByPType to remove + * specific kinds of primitives from the imported scene, completely and forever. + * In many cases you'll probably want to set this setting to + * @code + * aiPrimitiveType_LINE|aiPrimitiveType_POINT + * @endcode + * Together with the #aiProcess_Triangulate flag you can then be sure that + * #aiFace::mNumIndices is always 3. + * @note Take a look at the @link data Data Structures page @endlink for + * more information on the layout and winding order of a face. + */ +struct aiFace +{ + //! Number of indices defining this face. + //! The maximum value for this member is #AI_MAX_FACE_INDICES. + unsigned int mNumIndices; + + //! Pointer to the indices array. Size of the array is given in numIndices. + unsigned int* mIndices; + +#ifdef __cplusplus + + //! Default constructor + aiFace() AI_NO_EXCEPT + : mNumIndices( 0 ) + , mIndices( nullptr ) { + // empty + } + + //! Default destructor. Delete the index array + ~aiFace() + { + delete [] mIndices; + } + + //! Copy constructor. Copy the index array + aiFace( const aiFace& o) + : mNumIndices(0) + , mIndices( nullptr ) { + *this = o; + } + + //! Assignment operator. Copy the index array + aiFace& operator = ( const aiFace& o) { + if (&o == this) { + return *this; + } + + delete[] mIndices; + mNumIndices = o.mNumIndices; + if (mNumIndices) { + mIndices = new unsigned int[mNumIndices]; + ::memcpy( mIndices, o.mIndices, mNumIndices * sizeof( unsigned int)); + } else { + mIndices = nullptr; + } + + return *this; + } + + //! Comparison operator. Checks whether the index array + //! of two faces is identical + bool operator== (const aiFace& o) const { + if (mIndices == o.mIndices) { + return true; + } + + if (nullptr != mIndices && mNumIndices != o.mNumIndices) { + return false; + } + + if (nullptr == mIndices) { + return false; + } + + for (unsigned int i = 0; i < this->mNumIndices; ++i) { + if (mIndices[i] != o.mIndices[i]) { + return false; + } + } + + return true; + } + + //! Inverse comparison operator. Checks whether the index + //! array of two faces is NOT identical + bool operator != (const aiFace& o) const { + return !(*this == o); + } +#endif // __cplusplus +}; // struct aiFace + + +// --------------------------------------------------------------------------- +/** @brief A single influence of a bone on a vertex. + */ +struct aiVertexWeight { + //! Index of the vertex which is influenced by the bone. + unsigned int mVertexId; + + //! The strength of the influence in the range (0...1). + //! The influence from all bones at one vertex amounts to 1. + float mWeight; + +#ifdef __cplusplus + + //! Default constructor + aiVertexWeight() AI_NO_EXCEPT + : mVertexId(0) + , mWeight(0.0f) { + // empty + } + + //! Initialization from a given index and vertex weight factor + //! \param pID ID + //! \param pWeight Vertex weight factor + aiVertexWeight( unsigned int pID, float pWeight ) + : mVertexId( pID ) + , mWeight( pWeight ) { + // empty + } + + bool operator == ( const aiVertexWeight &rhs ) const { + return ( mVertexId == rhs.mVertexId && mWeight == rhs.mWeight ); + } + + bool operator != ( const aiVertexWeight &rhs ) const { + return ( *this == rhs ); + } + +#endif // __cplusplus +}; + + +// --------------------------------------------------------------------------- +/** @brief A single bone of a mesh. + * + * A bone has a name by which it can be found in the frame hierarchy and by + * which it can be addressed by animations. In addition it has a number of + * influences on vertices, and a matrix relating the mesh position to the + * position of the bone at the time of binding. + */ +struct aiBone { + //! The name of the bone. + C_STRUCT aiString mName; + + //! The number of vertices affected by this bone. + //! The maximum value for this member is #AI_MAX_BONE_WEIGHTS. + unsigned int mNumWeights; + + //! The influence weights of this bone, by vertex index. + C_STRUCT aiVertexWeight* mWeights; + + /** Matrix that transforms from bone space to mesh space in bind pose. + * + * This matrix describes the position of the mesh + * in the local space of this bone when the skeleton was bound. + * Thus it can be used directly to determine a desired vertex position, + * given the world-space transform of the bone when animated, + * and the position of the vertex in mesh space. + * + * It is sometimes called an inverse-bind matrix, + * or inverse bind pose matrix. + */ + C_STRUCT aiMatrix4x4 mOffsetMatrix; + +#ifdef __cplusplus + + //! Default constructor + aiBone() AI_NO_EXCEPT + : mName() + , mNumWeights( 0 ) + , mWeights( nullptr ) + , mOffsetMatrix() { + // empty + } + + //! Copy constructor + aiBone(const aiBone& other) + : mName( other.mName ) + , mNumWeights( other.mNumWeights ) + , mWeights(nullptr) + , mOffsetMatrix( other.mOffsetMatrix ) { + if (other.mWeights && other.mNumWeights) { + mWeights = new aiVertexWeight[mNumWeights]; + ::memcpy(mWeights,other.mWeights,mNumWeights * sizeof(aiVertexWeight)); + } + } + + + //! Assignment operator + aiBone &operator=(const aiBone& other) { + if (this == &other) { + return *this; + } + + mName = other.mName; + mNumWeights = other.mNumWeights; + mOffsetMatrix = other.mOffsetMatrix; + + if (other.mWeights && other.mNumWeights) + { + if (mWeights) { + delete[] mWeights; + } + + mWeights = new aiVertexWeight[mNumWeights]; + ::memcpy(mWeights,other.mWeights,mNumWeights * sizeof(aiVertexWeight)); + } + + return *this; + } + + bool operator == ( const aiBone &rhs ) const { + if ( mName != rhs.mName || mNumWeights != rhs.mNumWeights ) { + return false; + } + + for ( size_t i = 0; i < mNumWeights; ++i ) { + if ( mWeights[ i ] != rhs.mWeights[ i ] ) { + return false; + } + } + + return true; + } + //! Destructor - deletes the array of vertex weights + ~aiBone() { + delete [] mWeights; + } +#endif // __cplusplus +}; + + +// --------------------------------------------------------------------------- +/** @brief Enumerates the types of geometric primitives supported by Assimp. + * + * @see aiFace Face data structure + * @see aiProcess_SortByPType Per-primitive sorting of meshes + * @see aiProcess_Triangulate Automatic triangulation + * @see AI_CONFIG_PP_SBP_REMOVE Removal of specific primitive types. + */ +enum aiPrimitiveType +{ + /** A point primitive. + * + * This is just a single vertex in the virtual world, + * #aiFace contains just one index for such a primitive. + */ + aiPrimitiveType_POINT = 0x1, + + /** A line primitive. + * + * This is a line defined through a start and an end position. + * #aiFace contains exactly two indices for such a primitive. + */ + aiPrimitiveType_LINE = 0x2, + + /** A triangular primitive. + * + * A triangle consists of three indices. + */ + aiPrimitiveType_TRIANGLE = 0x4, + + /** A higher-level polygon with more than 3 edges. + * + * A triangle is a polygon, but polygon in this context means + * "all polygons that are not triangles". The "Triangulate"-Step + * is provided for your convenience, it splits all polygons in + * triangles (which are much easier to handle). + */ + aiPrimitiveType_POLYGON = 0x8, + + + /** This value is not used. It is just here to force the + * compiler to map this enum to a 32 Bit integer. + */ +#ifndef SWIG + _aiPrimitiveType_Force32Bit = INT_MAX +#endif +}; //! enum aiPrimitiveType + +// Get the #aiPrimitiveType flag for a specific number of face indices +#define AI_PRIMITIVE_TYPE_FOR_N_INDICES(n) \ + ((n) > 3 ? aiPrimitiveType_POLYGON : (aiPrimitiveType)(1u << ((n)-1))) + + + +// --------------------------------------------------------------------------- +/** @brief An AnimMesh is an attachment to an #aiMesh stores per-vertex + * animations for a particular frame. + * + * You may think of an #aiAnimMesh as a `patch` for the host mesh, which + * replaces only certain vertex data streams at a particular time. + * Each mesh stores n attached attached meshes (#aiMesh::mAnimMeshes). + * The actual relationship between the time line and anim meshes is + * established by #aiMeshAnim, which references singular mesh attachments + * by their ID and binds them to a time offset. +*/ +struct aiAnimMesh +{ + /**Anim Mesh name */ + C_STRUCT aiString mName; + + /** Replacement for aiMesh::mVertices. If this array is non-NULL, + * it *must* contain mNumVertices entries. The corresponding + * array in the host mesh must be non-NULL as well - animation + * meshes may neither add or nor remove vertex components (if + * a replacement array is NULL and the corresponding source + * array is not, the source data is taken instead)*/ + C_STRUCT aiVector3D* mVertices; + + /** Replacement for aiMesh::mNormals. */ + C_STRUCT aiVector3D* mNormals; + + /** Replacement for aiMesh::mTangents. */ + C_STRUCT aiVector3D* mTangents; + + /** Replacement for aiMesh::mBitangents. */ + C_STRUCT aiVector3D* mBitangents; + + /** Replacement for aiMesh::mColors */ + C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; + + /** Replacement for aiMesh::mTextureCoords */ + C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + /** The number of vertices in the aiAnimMesh, and thus the length of all + * the member arrays. + * + * This has always the same value as the mNumVertices property in the + * corresponding aiMesh. It is duplicated here merely to make the length + * of the member arrays accessible even if the aiMesh is not known, e.g. + * from language bindings. + */ + unsigned int mNumVertices; + + /** + * Weight of the AnimMesh. + */ + float mWeight; + +#ifdef __cplusplus + + aiAnimMesh() AI_NO_EXCEPT + : mVertices( nullptr ) + , mNormals(nullptr) + , mTangents(nullptr) + , mBitangents(nullptr) + , mColors() + , mTextureCoords() + , mNumVertices( 0 ) + , mWeight( 0.0f ) + { + // fixme consider moving this to the ctor initializer list as well + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++){ + mTextureCoords[a] = nullptr; + } + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) { + mColors[a] = nullptr; + } + } + + ~aiAnimMesh() + { + delete [] mVertices; + delete [] mNormals; + delete [] mTangents; + delete [] mBitangents; + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) { + delete [] mTextureCoords[a]; + } + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) { + delete [] mColors[a]; + } + } + + /** Check whether the anim mesh overrides the vertex positions + * of its host mesh*/ + bool HasPositions() const { + return mVertices != nullptr; + } + + /** Check whether the anim mesh overrides the vertex normals + * of its host mesh*/ + bool HasNormals() const { + return mNormals != nullptr; + } + + /** Check whether the anim mesh overrides the vertex tangents + * and bitangents of its host mesh. As for aiMesh, + * tangents and bitangents always go together. */ + bool HasTangentsAndBitangents() const { + return mTangents != nullptr; + } + + /** Check whether the anim mesh overrides a particular + * set of vertex colors on his host mesh. + * @param pIndex 0<index<AI_MAX_NUMBER_OF_COLOR_SETS */ + bool HasVertexColors( unsigned int pIndex) const { + return pIndex >= AI_MAX_NUMBER_OF_COLOR_SETS ? false : mColors[pIndex] != nullptr; + } + + /** Check whether the anim mesh overrides a particular + * set of texture coordinates on his host mesh. + * @param pIndex 0<index<AI_MAX_NUMBER_OF_TEXTURECOORDS */ + bool HasTextureCoords( unsigned int pIndex) const { + return pIndex >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? false : mTextureCoords[pIndex] != nullptr; + } + +#endif +}; + +// --------------------------------------------------------------------------- +/** @brief Enumerates the methods of mesh morphing supported by Assimp. + */ +enum aiMorphingMethod +{ + /** Interpolation between morph targets */ + aiMorphingMethod_VERTEX_BLEND = 0x1, + + /** Normalized morphing between morph targets */ + aiMorphingMethod_MORPH_NORMALIZED = 0x2, + + /** Relative morphing between morph targets */ + aiMorphingMethod_MORPH_RELATIVE = 0x3, + + /** This value is not used. It is just here to force the + * compiler to map this enum to a 32 Bit integer. + */ +#ifndef SWIG + _aiMorphingMethod_Force32Bit = INT_MAX +#endif +}; //! enum aiMorphingMethod + +// --------------------------------------------------------------------------- +/** @brief A mesh represents a geometry or model with a single material. +* +* It usually consists of a number of vertices and a series of primitives/faces +* referencing the vertices. In addition there might be a series of bones, each +* of them addressing a number of vertices with a certain weight. Vertex data +* is presented in channels with each channel containing a single per-vertex +* information such as a set of texture coords or a normal vector. +* If a data pointer is non-null, the corresponding data stream is present. +* From C++-programs you can also use the comfort functions Has*() to +* test for the presence of various data streams. +* +* A Mesh uses only a single material which is referenced by a material ID. +* @note The mPositions member is usually not optional. However, vertex positions +* *could* be missing if the #AI_SCENE_FLAGS_INCOMPLETE flag is set in +* @code +* aiScene::mFlags +* @endcode +*/ +struct aiMesh +{ + /** Bitwise combination of the members of the #aiPrimitiveType enum. + * This specifies which types of primitives are present in the mesh. + * The "SortByPrimitiveType"-Step can be used to make sure the + * output meshes consist of one primitive type each. + */ + unsigned int mPrimitiveTypes; + + /** The number of vertices in this mesh. + * This is also the size of all of the per-vertex data arrays. + * The maximum value for this member is #AI_MAX_VERTICES. + */ + unsigned int mNumVertices; + + /** The number of primitives (triangles, polygons, lines) in this mesh. + * This is also the size of the mFaces array. + * The maximum value for this member is #AI_MAX_FACES. + */ + unsigned int mNumFaces; + + /** Vertex positions. + * This array is always present in a mesh. The array is + * mNumVertices in size. + */ + C_STRUCT aiVector3D* mVertices; + + /** Vertex normals. + * The array contains normalized vectors, NULL if not present. + * The array is mNumVertices in size. Normals are undefined for + * point and line primitives. A mesh consisting of points and + * lines only may not have normal vectors. Meshes with mixed + * primitive types (i.e. lines and triangles) may have normals, + * but the normals for vertices that are only referenced by + * point or line primitives are undefined and set to QNaN (WARN: + * qNaN compares to inequal to *everything*, even to qNaN itself. + * Using code like this to check whether a field is qnan is: + * @code + * #define IS_QNAN(f) (f != f) + * @endcode + * still dangerous because even 1.f == 1.f could evaluate to false! ( + * remember the subtleties of IEEE754 artithmetics). Use stuff like + * @c fpclassify instead. + * @note Normal vectors computed by Assimp are always unit-length. + * However, this needn't apply for normals that have been taken + * directly from the model file. + */ + C_STRUCT aiVector3D* mNormals; + + /** Vertex tangents. + * The tangent of a vertex points in the direction of the positive + * X texture axis. The array contains normalized vectors, NULL if + * not present. The array is mNumVertices in size. A mesh consisting + * of points and lines only may not have normal vectors. Meshes with + * mixed primitive types (i.e. lines and triangles) may have + * normals, but the normals for vertices that are only referenced by + * point or line primitives are undefined and set to qNaN. See + * the #mNormals member for a detailed discussion of qNaNs. + * @note If the mesh contains tangents, it automatically also + * contains bitangents. + */ + C_STRUCT aiVector3D* mTangents; + + /** Vertex bitangents. + * The bitangent of a vertex points in the direction of the positive + * Y texture axis. The array contains normalized vectors, NULL if not + * present. The array is mNumVertices in size. + * @note If the mesh contains tangents, it automatically also contains + * bitangents. + */ + C_STRUCT aiVector3D* mBitangents; + + /** Vertex color sets. + * A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex + * colors per vertex. NULL if not present. Each array is + * mNumVertices in size if present. + */ + C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; + + /** Vertex texture coords, also known as UV channels. + * A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per + * vertex. NULL if not present. The array is mNumVertices in size. + */ + C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + /** Specifies the number of components for a given UV channel. + * Up to three channels are supported (UVW, for accessing volume + * or cube maps). If the value is 2 for a given channel n, the + * component p.z of mTextureCoords[n][p] is set to 0.0f. + * If the value is 1 for a given channel, p.y is set to 0.0f, too. + * @note 4D coords are not supported + */ + unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + /** The faces the mesh is constructed from. + * Each face refers to a number of vertices by their indices. + * This array is always present in a mesh, its size is given + * in mNumFaces. If the #AI_SCENE_FLAGS_NON_VERBOSE_FORMAT + * is NOT set each face references an unique set of vertices. + */ + C_STRUCT aiFace* mFaces; + + /** The number of bones this mesh contains. + * Can be 0, in which case the mBones array is NULL. + */ + unsigned int mNumBones; + + /** The bones of this mesh. + * A bone consists of a name by which it can be found in the + * frame hierarchy and a set of vertex weights. + */ + C_STRUCT aiBone** mBones; + + /** The material used by this mesh. + * A mesh uses only a single material. If an imported model uses + * multiple materials, the import splits up the mesh. Use this value + * as index into the scene's material list. + */ + unsigned int mMaterialIndex; + + /** Name of the mesh. Meshes can be named, but this is not a + * requirement and leaving this field empty is totally fine. + * There are mainly three uses for mesh names: + * - some formats name nodes and meshes independently. + * - importers tend to split meshes up to meet the + * one-material-per-mesh requirement. Assigning + * the same (dummy) name to each of the result meshes + * aids the caller at recovering the original mesh + * partitioning. + * - Vertex animations refer to meshes by their names. + **/ + C_STRUCT aiString mName; + + + /** The number of attachment meshes. Note! Currently only works with Collada loader. */ + unsigned int mNumAnimMeshes; + + /** Attachment meshes for this mesh, for vertex-based animation. + * Attachment meshes carry replacement data for some of the + * mesh'es vertex components (usually positions, normals). + * Note! Currently only works with Collada loader.*/ + C_STRUCT aiAnimMesh** mAnimMeshes; + + /** + * Method of morphing when animeshes are specified. + */ + unsigned int mMethod; + +#ifdef __cplusplus + + //! Default constructor. Initializes all members to 0 + aiMesh() AI_NO_EXCEPT + : mPrimitiveTypes( 0 ) + , mNumVertices( 0 ) + , mNumFaces( 0 ) + , mVertices( nullptr ) + , mNormals(nullptr) + , mTangents(nullptr) + , mBitangents(nullptr) + , mColors() + , mTextureCoords() + , mNumUVComponents() + , mFaces(nullptr) + , mNumBones( 0 ) + , mBones(nullptr) + , mMaterialIndex( 0 ) + , mNumAnimMeshes( 0 ) + , mAnimMeshes(nullptr) + , mMethod( 0 ) { + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) { + mNumUVComponents[a] = 0; + mTextureCoords[a] = nullptr; + } + + for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) { + mColors[a] = nullptr; + } + } + + //! Deletes all storage allocated for the mesh + ~aiMesh() { + delete [] mVertices; + delete [] mNormals; + delete [] mTangents; + delete [] mBitangents; + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) { + delete [] mTextureCoords[a]; + } + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) { + delete [] mColors[a]; + } + + // DO NOT REMOVE THIS ADDITIONAL CHECK + if (mNumBones && mBones) { + for( unsigned int a = 0; a < mNumBones; a++) { + delete mBones[a]; + } + delete [] mBones; + } + + if (mNumAnimMeshes && mAnimMeshes) { + for( unsigned int a = 0; a < mNumAnimMeshes; a++) { + delete mAnimMeshes[a]; + } + delete [] mAnimMeshes; + } + + delete [] mFaces; + } + + //! Check whether the mesh contains positions. Provided no special + //! scene flags are set, this will always be true + bool HasPositions() const + { return mVertices != nullptr && mNumVertices > 0; } + + //! Check whether the mesh contains faces. If no special scene flags + //! are set this should always return true + bool HasFaces() const + { return mFaces != nullptr && mNumFaces > 0; } + + //! Check whether the mesh contains normal vectors + bool HasNormals() const + { return mNormals != nullptr && mNumVertices > 0; } + + //! Check whether the mesh contains tangent and bitangent vectors + //! It is not possible that it contains tangents and no bitangents + //! (or the other way round). The existence of one of them + //! implies that the second is there, too. + bool HasTangentsAndBitangents() const + { return mTangents != nullptr && mBitangents != nullptr && mNumVertices > 0; } + + //! Check whether the mesh contains a vertex color set + //! \param pIndex Index of the vertex color set + bool HasVertexColors( unsigned int pIndex) const { + if (pIndex >= AI_MAX_NUMBER_OF_COLOR_SETS) { + return false; + } else { + return mColors[pIndex] != nullptr && mNumVertices > 0; + } + } + + //! Check whether the mesh contains a texture coordinate set + //! \param pIndex Index of the texture coordinates set + bool HasTextureCoords( unsigned int pIndex) const { + if (pIndex >= AI_MAX_NUMBER_OF_TEXTURECOORDS) { + return false; + } else { + return mTextureCoords[pIndex] != nullptr && mNumVertices > 0; + } + } + + //! Get the number of UV channels the mesh contains + unsigned int GetNumUVChannels() const { + unsigned int n( 0 ); + while (n < AI_MAX_NUMBER_OF_TEXTURECOORDS && mTextureCoords[n]) { + ++n; + } + + return n; + } + + //! Get the number of vertex color channels the mesh contains + unsigned int GetNumColorChannels() const { + unsigned int n(0); + while (n < AI_MAX_NUMBER_OF_COLOR_SETS && mColors[n]) { + ++n; + } + return n; + } + + //! Check whether the mesh contains bones + bool HasBones() const { + return mBones != nullptr && mNumBones > 0; + } + +#endif // __cplusplus +}; + +#ifdef __cplusplus +} +#endif //! extern "C" +#endif // AI_MESH_H_INC + diff --git a/thirdparty/assimp/include/assimp/metadata.h b/thirdparty/assimp/include/assimp/metadata.h new file mode 100644 index 0000000000..3a1dd1442a --- /dev/null +++ b/thirdparty/assimp/include/assimp/metadata.h @@ -0,0 +1,380 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file metadata.h + * @brief Defines the data structures for holding node meta information. + */ +#pragma once +#ifndef AI_METADATA_H_INC +#define AI_METADATA_H_INC + +#if defined(_MSC_VER) && (_MSC_VER <= 1500) +# include "Compiler/pstdint.h" +#else +# include <stdint.h> +#endif + +// ------------------------------------------------------------------------------- +/** + * Enum used to distinguish data types + */ + // ------------------------------------------------------------------------------- +typedef enum aiMetadataType { + AI_BOOL = 0, + AI_INT32 = 1, + AI_UINT64 = 2, + AI_FLOAT = 3, + AI_DOUBLE = 4, + AI_AISTRING = 5, + AI_AIVECTOR3D = 6, + AI_META_MAX = 7, + +#ifndef SWIG + FORCE_32BIT = INT_MAX +#endif +} aiMetadataType; + +// ------------------------------------------------------------------------------- +/** + * Metadata entry + * + * The type field uniquely identifies the underlying type of the data field + */ + // ------------------------------------------------------------------------------- +struct aiMetadataEntry { + aiMetadataType mType; + void* mData; +}; + +#ifdef __cplusplus + +#include <string> + +// ------------------------------------------------------------------------------- +/** + * Helper functions to get the aiType enum entry for a type + */ + // ------------------------------------------------------------------------------- + +inline aiMetadataType GetAiType( bool ) { return AI_BOOL; } +inline aiMetadataType GetAiType( int32_t ) { return AI_INT32; } +inline aiMetadataType GetAiType( uint64_t ) { return AI_UINT64; } +inline aiMetadataType GetAiType( float ) { return AI_FLOAT; } +inline aiMetadataType GetAiType( double ) { return AI_DOUBLE; } +inline aiMetadataType GetAiType( const aiString & ) { return AI_AISTRING; } +inline aiMetadataType GetAiType( const aiVector3D & ) { return AI_AIVECTOR3D; } + +#endif // __cplusplus + +// ------------------------------------------------------------------------------- +/** + * Container for holding metadata. + * + * Metadata is a key-value store using string keys and values. + */ + // ------------------------------------------------------------------------------- +struct aiMetadata { + /** Length of the mKeys and mValues arrays, respectively */ + unsigned int mNumProperties; + + /** Arrays of keys, may not be NULL. Entries in this array may not be NULL as well. */ + C_STRUCT aiString* mKeys; + + /** Arrays of values, may not be NULL. Entries in this array may be NULL if the + * corresponding property key has no assigned value. */ + C_STRUCT aiMetadataEntry* mValues; + +#ifdef __cplusplus + + /** + * @brief The default constructor, set all members to zero by default. + */ + aiMetadata() AI_NO_EXCEPT + : mNumProperties(0) + , mKeys(nullptr) + , mValues(nullptr) { + // empty + } + + aiMetadata( const aiMetadata &rhs ) + : mNumProperties( rhs.mNumProperties ) + , mKeys( nullptr ) + , mValues( nullptr ) { + mKeys = new aiString[ mNumProperties ]; + for ( size_t i = 0; i < static_cast<size_t>( mNumProperties ); ++i ) { + mKeys[ i ] = rhs.mKeys[ i ]; + } + mValues = new aiMetadataEntry[ mNumProperties ]; + for ( size_t i = 0; i < static_cast<size_t>(mNumProperties); ++i ) { + mValues[ i ].mType = rhs.mValues[ i ].mType; + switch ( rhs.mValues[ i ].mType ) { + case AI_BOOL: + mValues[ i ].mData = new bool; + ::memcpy( mValues[ i ].mData, rhs.mValues[ i ].mData, sizeof(bool) ); + break; + case AI_INT32: { + int32_t v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( int32_t ) ); + mValues[ i ].mData = new int32_t( v ); + } + break; + case AI_UINT64: { + uint64_t v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( uint64_t ) ); + mValues[ i ].mData = new uint64_t( v ); + } + break; + case AI_FLOAT: { + float v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( float ) ); + mValues[ i ].mData = new float( v ); + } + break; + case AI_DOUBLE: { + double v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( double ) ); + mValues[ i ].mData = new double( v ); + } + break; + case AI_AISTRING: { + aiString v; + rhs.Get<aiString>( mKeys[ i ], v ); + mValues[ i ].mData = new aiString( v ); + } + break; + case AI_AIVECTOR3D: { + aiVector3D v; + rhs.Get<aiVector3D>( mKeys[ i ], v ); + mValues[ i ].mData = new aiVector3D( v ); + } + break; +#ifndef SWIG + case FORCE_32BIT: +#endif + default: + break; + } + + } + } + + /** + * @brief The destructor. + */ + ~aiMetadata() { + delete [] mKeys; + mKeys = nullptr; + if (mValues) { + // Delete each metadata entry + for (unsigned i=0; i<mNumProperties; ++i) { + void* data = mValues[i].mData; + switch (mValues[i].mType) { + case AI_BOOL: + delete static_cast< bool* >( data ); + break; + case AI_INT32: + delete static_cast< int32_t* >( data ); + break; + case AI_UINT64: + delete static_cast< uint64_t* >( data ); + break; + case AI_FLOAT: + delete static_cast< float* >( data ); + break; + case AI_DOUBLE: + delete static_cast< double* >( data ); + break; + case AI_AISTRING: + delete static_cast< aiString* >( data ); + break; + case AI_AIVECTOR3D: + delete static_cast< aiVector3D* >( data ); + break; +#ifndef SWIG + case FORCE_32BIT: +#endif + default: + break; + } + } + + // Delete the metadata array + delete [] mValues; + mValues = nullptr; + } + } + + /** + * @brief Allocates property fields + keys. + * @param numProperties Number of requested properties. + */ + static inline + aiMetadata *Alloc( unsigned int numProperties ) { + if ( 0 == numProperties ) { + return nullptr; + } + + aiMetadata *data = new aiMetadata; + data->mNumProperties = numProperties; + data->mKeys = new aiString[ data->mNumProperties ](); + data->mValues = new aiMetadataEntry[ data->mNumProperties ](); + + return data; + } + + /** + * @brief Deallocates property fields + keys. + */ + static inline + void Dealloc( aiMetadata *metadata ) { + delete metadata; + } + + template<typename T> + inline + void Add(const std::string& key, const T& value) { + aiString* new_keys = new aiString[mNumProperties + 1]; + aiMetadataEntry* new_values = new aiMetadataEntry[mNumProperties + 1]; + + for(unsigned int i = 0; i < mNumProperties; ++i) + { + new_keys[i] = mKeys[i]; + new_values[i] = mValues[i]; + } + + delete mKeys; + delete mValues; + + mKeys = new_keys; + mValues = new_values; + + mNumProperties++; + + Set(mNumProperties - 1, key, value); + } + + template<typename T> + inline + bool Set( unsigned index, const std::string& key, const T& value ) { + // In range assertion + if ( index >= mNumProperties ) { + return false; + } + + // Ensure that we have a valid key. + if ( key.empty() ) { + return false; + } + + // Set metadata key + mKeys[index] = key; + + // Set metadata type + mValues[index].mType = GetAiType(value); + // Copy the given value to the dynamic storage + mValues[index].mData = new T(value); + + return true; + } + + template<typename T> + inline + bool Get( unsigned index, T& value ) const { + // In range assertion + if ( index >= mNumProperties ) { + return false; + } + + // Return false if the output data type does + // not match the found value's data type + if ( GetAiType( value ) != mValues[ index ].mType ) { + return false; + } + + // Otherwise, output the found value and + // return true + value = *static_cast<T*>(mValues[index].mData); + + return true; + } + + template<typename T> + inline + bool Get( const aiString& key, T& value ) const { + // Search for the given key + for ( unsigned int i = 0; i < mNumProperties; ++i ) { + if ( mKeys[ i ] == key ) { + return Get( i, value ); + } + } + return false; + } + + template<typename T> + inline + bool Get( const std::string& key, T& value ) const { + return Get(aiString(key), value); + } + + /// Return metadata entry for analyzing it by user. + /// \param [in] pIndex - index of the entry. + /// \param [out] pKey - pointer to the key value. + /// \param [out] pEntry - pointer to the entry: type and value. + /// \return false - if pIndex is out of range, else - true. + inline + bool Get(size_t index, const aiString*& key, const aiMetadataEntry*& entry) const { + if ( index >= mNumProperties ) { + return false; + } + + key = &mKeys[index]; + entry = &mValues[index]; + + return true; + } + +#endif // __cplusplus + +}; + +#endif // AI_METADATA_H_INC diff --git a/thirdparty/assimp/include/assimp/pbrmaterial.h b/thirdparty/assimp/include/assimp/pbrmaterial.h new file mode 100644 index 0000000000..ce5f822173 --- /dev/null +++ b/thirdparty/assimp/include/assimp/pbrmaterial.h @@ -0,0 +1,77 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file pbrmaterial.h + * @brief Defines the material system of the library + */ +#ifndef AI_PBRMATERIAL_H_INC +#define AI_PBRMATERIAL_H_INC + +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 +#define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 +#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 +#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 +#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 + +#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" +#define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" +#define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid" +#define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag" +#define _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE "$tex.mappingfiltermin" +#define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale" +#define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength" + +#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGFILTER_MIN(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE, type, N +#define AI_MATKEY_GLTF_TEXTURE_SCALE(type, N) _AI_MATKEY_GLTF_SCALE_BASE, type, N +#define AI_MATKEY_GLTF_TEXTURE_STRENGTH(type, N) _AI_MATKEY_GLTF_STRENGTH_BASE, type, N + +#endif //!!AI_PBRMATERIAL_H_INC diff --git a/thirdparty/assimp/include/assimp/port/AndroidJNI/AndroidJNIIOSystem.h b/thirdparty/assimp/include/assimp/port/AndroidJNI/AndroidJNIIOSystem.h new file mode 100644 index 0000000000..41d8004877 --- /dev/null +++ b/thirdparty/assimp/include/assimp/port/AndroidJNI/AndroidJNIIOSystem.h @@ -0,0 +1,92 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2016, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Android implementation of IOSystem using the standard C file functions. + * Aimed to ease the access to android assets */ + +#if __ANDROID__ and __ANDROID_API__ > 9 and defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT) +#ifndef AI_ANDROIDJNIIOSYSTEM_H_INC +#define AI_ANDROIDJNIIOSYSTEM_H_INC + +#include <assimp/DefaultIOSystem.h> +#include <android/asset_manager.h> +#include <android/asset_manager_jni.h> +#include <android/native_activity.h> + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** Android extension to DefaultIOSystem using the standard C file functions */ +class ASSIMP_API AndroidJNIIOSystem : public DefaultIOSystem +{ +public: + + /** Initialize android activity data */ + std::string mApkWorkspacePath; + AAssetManager* mApkAssetManager; + + /** Constructor. */ + AndroidJNIIOSystem(ANativeActivity* activity); + + /** Destructor. */ + ~AndroidJNIIOSystem(); + + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists( const char* pFile) const; + + // ------------------------------------------------------------------- + /** Opens a file at the given path, with given mode */ + IOStream* Open( const char* strFile, const char* strMode); + + // ------------------------------------------------------------------------------------------------ + // Inits Android extractor + void AndroidActivityInit(ANativeActivity* activity); + + // ------------------------------------------------------------------------------------------------ + // Extracts android asset + bool AndroidExtractAsset(std::string name); + +}; + +} //!ns Assimp + +#endif //AI_ANDROIDJNIIOSYSTEM_H_INC +#endif //__ANDROID__ and __ANDROID_API__ > 9 and defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT) diff --git a/thirdparty/assimp/include/assimp/postprocess.h b/thirdparty/assimp/include/assimp/postprocess.h new file mode 100644 index 0000000000..c23a5490a5 --- /dev/null +++ b/thirdparty/assimp/include/assimp/postprocess.h @@ -0,0 +1,679 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file postprocess.h + * @brief Definitions for import post processing steps + */ +#pragma once +#ifndef AI_POSTPROCESS_H_INC +#define AI_POSTPROCESS_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// ----------------------------------------------------------------------------------- +/** @enum aiPostProcessSteps + * @brief Defines the flags for all possible post processing steps. + * + * @note Some steps are influenced by properties set on the Assimp::Importer itself + * + * @see Assimp::Importer::ReadFile() + * @see Assimp::Importer::SetPropertyInteger() + * @see aiImportFile + * @see aiImportFileEx + */ +// ----------------------------------------------------------------------------------- +enum aiPostProcessSteps +{ + + // ------------------------------------------------------------------------- + /** <hr>Calculates the tangents and bitangents for the imported meshes. + * + * Does nothing if a mesh does not have normals. You might want this post + * processing step to be executed if you plan to use tangent space calculations + * such as normal mapping applied to the meshes. There's an importer property, + * <tt>#AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE</tt>, which allows you to specify + * a maximum smoothing angle for the algorithm. However, usually you'll + * want to leave it at the default value. + */ + aiProcess_CalcTangentSpace = 0x1, + + // ------------------------------------------------------------------------- + /** <hr>Identifies and joins identical vertex data sets within all + * imported meshes. + * + * After this step is run, each mesh contains unique vertices, + * so a vertex may be used by multiple faces. You usually want + * to use this post processing step. If your application deals with + * indexed geometry, this step is compulsory or you'll just waste rendering + * time. <b>If this flag is not specified</b>, no vertices are referenced by + * more than one face and <b>no index buffer is required</b> for rendering. + */ + aiProcess_JoinIdenticalVertices = 0x2, + + // ------------------------------------------------------------------------- + /** <hr>Converts all the imported data to a left-handed coordinate space. + * + * By default the data is returned in a right-handed coordinate space (which + * OpenGL prefers). In this space, +X points to the right, + * +Z points towards the viewer, and +Y points upwards. In the DirectX + * coordinate space +X points to the right, +Y points upwards, and +Z points + * away from the viewer. + * + * You'll probably want to consider this flag if you use Direct3D for + * rendering. The #aiProcess_ConvertToLeftHanded flag supersedes this + * setting and bundles all conversions typically required for D3D-based + * applications. + */ + aiProcess_MakeLeftHanded = 0x4, + + // ------------------------------------------------------------------------- + /** <hr>Triangulates all faces of all meshes. + * + * By default the imported mesh data might contain faces with more than 3 + * indices. For rendering you'll usually want all faces to be triangles. + * This post processing step splits up faces with more than 3 indices into + * triangles. Line and point primitives are *not* modified! If you want + * 'triangles only' with no other kinds of primitives, try the following + * solution: + * <ul> + * <li>Specify both #aiProcess_Triangulate and #aiProcess_SortByPType </li> + * <li>Ignore all point and line meshes when you process assimp's output</li> + * </ul> + */ + aiProcess_Triangulate = 0x8, + + // ------------------------------------------------------------------------- + /** <hr>Removes some parts of the data structure (animations, materials, + * light sources, cameras, textures, vertex components). + * + * The components to be removed are specified in a separate + * importer property, <tt>#AI_CONFIG_PP_RVC_FLAGS</tt>. This is quite useful + * if you don't need all parts of the output structure. Vertex colors + * are rarely used today for example... Calling this step to remove unneeded + * data from the pipeline as early as possible results in increased + * performance and a more optimized output data structure. + * This step is also useful if you want to force Assimp to recompute + * normals or tangents. The corresponding steps don't recompute them if + * they're already there (loaded from the source asset). By using this + * step you can make sure they are NOT there. + * + * This flag is a poor one, mainly because its purpose is usually + * misunderstood. Consider the following case: a 3D model has been exported + * from a CAD app, and it has per-face vertex colors. Vertex positions can't be + * shared, thus the #aiProcess_JoinIdenticalVertices step fails to + * optimize the data because of these nasty little vertex colors. + * Most apps don't even process them, so it's all for nothing. By using + * this step, unneeded components are excluded as early as possible + * thus opening more room for internal optimizations. + */ + aiProcess_RemoveComponent = 0x10, + + // ------------------------------------------------------------------------- + /** <hr>Generates normals for all faces of all meshes. + * + * This is ignored if normals are already there at the time this flag + * is evaluated. Model importers try to load them from the source file, so + * they're usually already there. Face normals are shared between all points + * of a single face, so a single point can have multiple normals, which + * forces the library to duplicate vertices in some cases. + * #aiProcess_JoinIdenticalVertices is *senseless* then. + * + * This flag may not be specified together with #aiProcess_GenSmoothNormals. + */ + aiProcess_GenNormals = 0x20, + + // ------------------------------------------------------------------------- + /** <hr>Generates smooth normals for all vertices in the mesh. + * + * This is ignored if normals are already there at the time this flag + * is evaluated. Model importers try to load them from the source file, so + * they're usually already there. + * + * This flag may not be specified together with + * #aiProcess_GenNormals. There's a importer property, + * <tt>#AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE</tt> which allows you to specify + * an angle maximum for the normal smoothing algorithm. Normals exceeding + * this limit are not smoothed, resulting in a 'hard' seam between two faces. + * Using a decent angle here (e.g. 80 degrees) results in very good visual + * appearance. + */ + aiProcess_GenSmoothNormals = 0x40, + + // ------------------------------------------------------------------------- + /** <hr>Splits large meshes into smaller sub-meshes. + * + * This is quite useful for real-time rendering, where the number of triangles + * which can be maximally processed in a single draw-call is limited + * by the video driver/hardware. The maximum vertex buffer is usually limited + * too. Both requirements can be met with this step: you may specify both a + * triangle and vertex limit for a single mesh. + * + * The split limits can (and should!) be set through the + * <tt>#AI_CONFIG_PP_SLM_VERTEX_LIMIT</tt> and <tt>#AI_CONFIG_PP_SLM_TRIANGLE_LIMIT</tt> + * importer properties. The default values are <tt>#AI_SLM_DEFAULT_MAX_VERTICES</tt> and + * <tt>#AI_SLM_DEFAULT_MAX_TRIANGLES</tt>. + * + * Note that splitting is generally a time-consuming task, but only if there's + * something to split. The use of this step is recommended for most users. + */ + aiProcess_SplitLargeMeshes = 0x80, + + // ------------------------------------------------------------------------- + /** <hr>Removes the node graph and pre-transforms all vertices with + * the local transformation matrices of their nodes. + * + * The output scene still contains nodes, however there is only a + * root node with children, each one referencing only one mesh, + * and each mesh referencing one material. For rendering, you can + * simply render all meshes in order - you don't need to pay + * attention to local transformations and the node hierarchy. + * Animations are removed during this step. + * This step is intended for applications without a scenegraph. + * The step CAN cause some problems: if e.g. a mesh of the asset + * contains normals and another, using the same material index, does not, + * they will be brought together, but the first meshes's part of + * the normal list is zeroed. However, these artifacts are rare. + * @note The <tt>#AI_CONFIG_PP_PTV_NORMALIZE</tt> configuration property + * can be set to normalize the scene's spatial dimension to the -1...1 + * range. + */ + aiProcess_PreTransformVertices = 0x100, + + // ------------------------------------------------------------------------- + /** <hr>Limits the number of bones simultaneously affecting a single vertex + * to a maximum value. + * + * If any vertex is affected by more than the maximum number of bones, the least + * important vertex weights are removed and the remaining vertex weights are + * renormalized so that the weights still sum up to 1. + * The default bone weight limit is 4 (defined as <tt>#AI_LMW_MAX_WEIGHTS</tt> in + * config.h), but you can use the <tt>#AI_CONFIG_PP_LBW_MAX_WEIGHTS</tt> importer + * property to supply your own limit to the post processing step. + * + * If you intend to perform the skinning in hardware, this post processing + * step might be of interest to you. + */ + aiProcess_LimitBoneWeights = 0x200, + + // ------------------------------------------------------------------------- + /** <hr>Validates the imported scene data structure. + * This makes sure that all indices are valid, all animations and + * bones are linked correctly, all material references are correct .. etc. + * + * It is recommended that you capture Assimp's log output if you use this flag, + * so you can easily find out what's wrong if a file fails the + * validation. The validator is quite strict and will find *all* + * inconsistencies in the data structure... It is recommended that plugin + * developers use it to debug their loaders. There are two types of + * validation failures: + * <ul> + * <li>Error: There's something wrong with the imported data. Further + * postprocessing is not possible and the data is not usable at all. + * The import fails. #Importer::GetErrorString() or #aiGetErrorString() + * carry the error message around.</li> + * <li>Warning: There are some minor issues (e.g. 1000000 animation + * keyframes with the same time), but further postprocessing and use + * of the data structure is still safe. Warning details are written + * to the log file, <tt>#AI_SCENE_FLAGS_VALIDATION_WARNING</tt> is set + * in #aiScene::mFlags</li> + * </ul> + * + * This post-processing step is not time-consuming. Its use is not + * compulsory, but recommended. + */ + aiProcess_ValidateDataStructure = 0x400, + + // ------------------------------------------------------------------------- + /** <hr>Reorders triangles for better vertex cache locality. + * + * The step tries to improve the ACMR (average post-transform vertex cache + * miss ratio) for all meshes. The implementation runs in O(n) and is + * roughly based on the 'tipsify' algorithm (see <a href=" + * http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf">this + * paper</a>). + * + * If you intend to render huge models in hardware, this step might + * be of interest to you. The <tt>#AI_CONFIG_PP_ICL_PTCACHE_SIZE</tt> + * importer property can be used to fine-tune the cache optimization. + */ + aiProcess_ImproveCacheLocality = 0x800, + + // ------------------------------------------------------------------------- + /** <hr>Searches for redundant/unreferenced materials and removes them. + * + * This is especially useful in combination with the + * #aiProcess_PreTransformVertices and #aiProcess_OptimizeMeshes flags. + * Both join small meshes with equal characteristics, but they can't do + * their work if two meshes have different materials. Because several + * material settings are lost during Assimp's import filters, + * (and because many exporters don't check for redundant materials), huge + * models often have materials which are are defined several times with + * exactly the same settings. + * + * Several material settings not contributing to the final appearance of + * a surface are ignored in all comparisons (e.g. the material name). + * So, if you're passing additional information through the + * content pipeline (probably using *magic* material names), don't + * specify this flag. Alternatively take a look at the + * <tt>#AI_CONFIG_PP_RRM_EXCLUDE_LIST</tt> importer property. + */ + aiProcess_RemoveRedundantMaterials = 0x1000, + + // ------------------------------------------------------------------------- + /** <hr>This step tries to determine which meshes have normal vectors + * that are facing inwards and inverts them. + * + * The algorithm is simple but effective: + * the bounding box of all vertices + their normals is compared against + * the volume of the bounding box of all vertices without their normals. + * This works well for most objects, problems might occur with planar + * surfaces. However, the step tries to filter such cases. + * The step inverts all in-facing normals. Generally it is recommended + * to enable this step, although the result is not always correct. + */ + aiProcess_FixInfacingNormals = 0x2000, + + // ------------------------------------------------------------------------- + /** <hr>This step splits meshes with more than one primitive type in + * homogeneous sub-meshes. + * + * The step is executed after the triangulation step. After the step + * returns, just one bit is set in aiMesh::mPrimitiveTypes. This is + * especially useful for real-time rendering where point and line + * primitives are often ignored or rendered separately. + * You can use the <tt>#AI_CONFIG_PP_SBP_REMOVE</tt> importer property to + * specify which primitive types you need. This can be used to easily + * exclude lines and points, which are rarely used, from the import. + */ + aiProcess_SortByPType = 0x8000, + + // ------------------------------------------------------------------------- + /** <hr>This step searches all meshes for degenerate primitives and + * converts them to proper lines or points. + * + * A face is 'degenerate' if one or more of its points are identical. + * To have the degenerate stuff not only detected and collapsed but + * removed, try one of the following procedures: + * <br><b>1.</b> (if you support lines and points for rendering but don't + * want the degenerates)<br> + * <ul> + * <li>Specify the #aiProcess_FindDegenerates flag. + * </li> + * <li>Set the <tt>#AI_CONFIG_PP_FD_REMOVE</tt> importer property to + * 1. This will cause the step to remove degenerate triangles from the + * import as soon as they're detected. They won't pass any further + * pipeline steps. + * </li> + * </ul> + * <br><b>2.</b>(if you don't support lines and points at all)<br> + * <ul> + * <li>Specify the #aiProcess_FindDegenerates flag. + * </li> + * <li>Specify the #aiProcess_SortByPType flag. This moves line and + * point primitives to separate meshes. + * </li> + * <li>Set the <tt>#AI_CONFIG_PP_SBP_REMOVE</tt> importer property to + * @code aiPrimitiveType_POINTS | aiPrimitiveType_LINES + * @endcode to cause SortByPType to reject point + * and line meshes from the scene. + * </li> + * </ul> + * + * This step also removes very small triangles with a surface area smaller + * than 10^-6. If you rely on having these small triangles, or notice holes + * in your model, set the property <tt>#AI_CONFIG_PP_FD_CHECKAREA</tt> to + * false. + * @note Degenerate polygons are not necessarily evil and that's why + * they're not removed by default. There are several file formats which + * don't support lines or points, and some exporters bypass the + * format specification and write them as degenerate triangles instead. + */ + aiProcess_FindDegenerates = 0x10000, + + // ------------------------------------------------------------------------- + /** <hr>This step searches all meshes for invalid data, such as zeroed + * normal vectors or invalid UV coords and removes/fixes them. This is + * intended to get rid of some common exporter errors. + * + * This is especially useful for normals. If they are invalid, and + * the step recognizes this, they will be removed and can later + * be recomputed, i.e. by the #aiProcess_GenSmoothNormals flag.<br> + * The step will also remove meshes that are infinitely small and reduce + * animation tracks consisting of hundreds if redundant keys to a single + * key. The <tt>AI_CONFIG_PP_FID_ANIM_ACCURACY</tt> config property decides + * the accuracy of the check for duplicate animation tracks. + */ + aiProcess_FindInvalidData = 0x20000, + + // ------------------------------------------------------------------------- + /** <hr>This step converts non-UV mappings (such as spherical or + * cylindrical mapping) to proper texture coordinate channels. + * + * Most applications will support UV mapping only, so you will + * probably want to specify this step in every case. Note that Assimp is not + * always able to match the original mapping implementation of the + * 3D app which produced a model perfectly. It's always better to let the + * modelling app compute the UV channels - 3ds max, Maya, Blender, + * LightWave, and Modo do this for example. + * + * @note If this step is not requested, you'll need to process the + * <tt>#AI_MATKEY_MAPPING</tt> material property in order to display all assets + * properly. + */ + aiProcess_GenUVCoords = 0x40000, + + // ------------------------------------------------------------------------- + /** <hr>This step applies per-texture UV transformations and bakes + * them into stand-alone vtexture coordinate channels. + * + * UV transformations are specified per-texture - see the + * <tt>#AI_MATKEY_UVTRANSFORM</tt> material key for more information. + * This step processes all textures with + * transformed input UV coordinates and generates a new (pre-transformed) UV channel + * which replaces the old channel. Most applications won't support UV + * transformations, so you will probably want to specify this step. + * + * @note UV transformations are usually implemented in real-time apps by + * transforming texture coordinates at vertex shader stage with a 3x3 + * (homogenous) transformation matrix. + */ + aiProcess_TransformUVCoords = 0x80000, + + // ------------------------------------------------------------------------- + /** <hr>This step searches for duplicate meshes and replaces them + * with references to the first mesh. + * + * This step takes a while, so don't use it if speed is a concern. + * Its main purpose is to workaround the fact that many export + * file formats don't support instanced meshes, so exporters need to + * duplicate meshes. This step removes the duplicates again. Please + * note that Assimp does not currently support per-node material + * assignment to meshes, which means that identical meshes with + * different materials are currently *not* joined, although this is + * planned for future versions. + */ + aiProcess_FindInstances = 0x100000, + + // ------------------------------------------------------------------------- + /** <hr>A postprocessing step to reduce the number of meshes. + * + * This will, in fact, reduce the number of draw calls. + * + * This is a very effective optimization and is recommended to be used + * together with #aiProcess_OptimizeGraph, if possible. The flag is fully + * compatible with both #aiProcess_SplitLargeMeshes and #aiProcess_SortByPType. + */ + aiProcess_OptimizeMeshes = 0x200000, + + + // ------------------------------------------------------------------------- + /** <hr>A postprocessing step to optimize the scene hierarchy. + * + * Nodes without animations, bones, lights or cameras assigned are + * collapsed and joined. + * + * Node names can be lost during this step. If you use special 'tag nodes' + * to pass additional information through your content pipeline, use the + * <tt>#AI_CONFIG_PP_OG_EXCLUDE_LIST</tt> importer property to specify a + * list of node names you want to be kept. Nodes matching one of the names + * in this list won't be touched or modified. + * + * Use this flag with caution. Most simple files will be collapsed to a + * single node, so complex hierarchies are usually completely lost. This is not + * useful for editor environments, but probably a very effective + * optimization if you just want to get the model data, convert it to your + * own format, and render it as fast as possible. + * + * This flag is designed to be used with #aiProcess_OptimizeMeshes for best + * results. + * + * @note 'Crappy' scenes with thousands of extremely small meshes packed + * in deeply nested nodes exist for almost all file formats. + * #aiProcess_OptimizeMeshes in combination with #aiProcess_OptimizeGraph + * usually fixes them all and makes them renderable. + */ + aiProcess_OptimizeGraph = 0x400000, + + // ------------------------------------------------------------------------- + /** <hr>This step flips all UV coordinates along the y-axis and adjusts + * material settings and bitangents accordingly. + * + * <b>Output UV coordinate system:</b> + * @code + * 0y|0y ---------- 1x|0y + * | | + * | | + * | | + * 0x|1y ---------- 1x|1y + * @endcode + * + * You'll probably want to consider this flag if you use Direct3D for + * rendering. The #aiProcess_ConvertToLeftHanded flag supersedes this + * setting and bundles all conversions typically required for D3D-based + * applications. + */ + aiProcess_FlipUVs = 0x800000, + + // ------------------------------------------------------------------------- + /** <hr>This step adjusts the output face winding order to be CW. + * + * The default face winding order is counter clockwise (CCW). + * + * <b>Output face order:</b> + * @code + * x2 + * + * x0 + * x1 + * @endcode + */ + aiProcess_FlipWindingOrder = 0x1000000, + + // ------------------------------------------------------------------------- + /** <hr>This step splits meshes with many bones into sub-meshes so that each + * su-bmesh has fewer or as many bones as a given limit. + */ + aiProcess_SplitByBoneCount = 0x2000000, + + // ------------------------------------------------------------------------- + /** <hr>This step removes bones losslessly or according to some threshold. + * + * In some cases (i.e. formats that require it) exporters are forced to + * assign dummy bone weights to otherwise static meshes assigned to + * animated meshes. Full, weight-based skinning is expensive while + * animating nodes is extremely cheap, so this step is offered to clean up + * the data in that regard. + * + * Use <tt>#AI_CONFIG_PP_DB_THRESHOLD</tt> to control this. + * Use <tt>#AI_CONFIG_PP_DB_ALL_OR_NONE</tt> if you want bones removed if and + * only if all bones within the scene qualify for removal. + */ + aiProcess_Debone = 0x4000000, + + // ------------------------------------------------------------------------- + /** <hr>This step will perform a global scale of the model. + * + * Some importers are providing a mechanism to define a scaling unit for the + * model. This post processing step can be used to do so. You need to get the + * global scaling from your importer settings like in FBX. Use the flag + * AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY from the global property table to configure this. + * + * Use <tt>#AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY</tt> to setup the global scaing factor. + */ + aiProcess_GlobalScale = 0x8000000, + + // ------------------------------------------------------------------------- + /** <hr>A postprocessing step to embed of textures. + * + * This will remove external data dependencies for textures. + * If a texture's file does not exist at the specified path + * (due, for instance, to an absolute path generated on another system), + * it will check if a file with the same name exists at the root folder + * of the imported model. And if so, it uses that. + */ + aiProcess_EmbedTextures = 0x10000000, + + // aiProcess_GenEntityMeshes = 0x100000, + // aiProcess_OptimizeAnimations = 0x200000 + // aiProcess_FixTexturePaths = 0x200000 + + + aiProcess_ForceGenNormals = 0x20000000, + + // ------------------------------------------------------------------------- + /** <hr>Drops normals for all faces of all meshes. + * + * This is ignored if no normals are present. + * Face normals are shared between all points of a single face, + * so a single point can have multiple normals, which + * forces the library to duplicate vertices in some cases. + * #aiProcess_JoinIdenticalVertices is *senseless* then. + * This process gives sense back to aiProcess_JoinIdenticalVertices + */ + aiProcess_DropNormals = 0x40000000, +}; + + +// --------------------------------------------------------------------------------------- +/** @def aiProcess_ConvertToLeftHanded + * @brief Shortcut flag for Direct3D-based applications. + * + * Supersedes the #aiProcess_MakeLeftHanded and #aiProcess_FlipUVs and + * #aiProcess_FlipWindingOrder flags. + * The output data matches Direct3D's conventions: left-handed geometry, upper-left + * origin for UV coordinates and finally clockwise face order, suitable for CCW culling. + * + * @deprecated + */ +#define aiProcess_ConvertToLeftHanded ( \ + aiProcess_MakeLeftHanded | \ + aiProcess_FlipUVs | \ + aiProcess_FlipWindingOrder | \ + 0 ) + + +// --------------------------------------------------------------------------------------- +/** @def aiProcessPreset_TargetRealtime_Fast + * @brief Default postprocess configuration optimizing the data for real-time rendering. + * + * Applications would want to use this preset to load models on end-user PCs, + * maybe for direct use in game. + * + * If you're using DirectX, don't forget to combine this value with + * the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations + * in your application apply the #aiProcess_TransformUVCoords step, too. + * @note Please take the time to read the docs for the steps enabled by this preset. + * Some of them offer further configurable properties, while some of them might not be of + * use for you so it might be better to not specify them. + */ +#define aiProcessPreset_TargetRealtime_Fast ( \ + aiProcess_CalcTangentSpace | \ + aiProcess_GenNormals | \ + aiProcess_JoinIdenticalVertices | \ + aiProcess_Triangulate | \ + aiProcess_GenUVCoords | \ + aiProcess_SortByPType | \ + 0 ) + + // --------------------------------------------------------------------------------------- + /** @def aiProcessPreset_TargetRealtime_Quality + * @brief Default postprocess configuration optimizing the data for real-time rendering. + * + * Unlike #aiProcessPreset_TargetRealtime_Fast, this configuration + * performs some extra optimizations to improve rendering speed and + * to minimize memory usage. It could be a good choice for a level editor + * environment where import speed is not so important. + * + * If you're using DirectX, don't forget to combine this value with + * the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations + * in your application apply the #aiProcess_TransformUVCoords step, too. + * @note Please take the time to read the docs for the steps enabled by this preset. + * Some of them offer further configurable properties, while some of them might not be + * of use for you so it might be better to not specify them. + */ +#define aiProcessPreset_TargetRealtime_Quality ( \ + aiProcess_CalcTangentSpace | \ + aiProcess_GenSmoothNormals | \ + aiProcess_JoinIdenticalVertices | \ + aiProcess_ImproveCacheLocality | \ + aiProcess_LimitBoneWeights | \ + aiProcess_RemoveRedundantMaterials | \ + aiProcess_SplitLargeMeshes | \ + aiProcess_Triangulate | \ + aiProcess_GenUVCoords | \ + aiProcess_SortByPType | \ + aiProcess_FindDegenerates | \ + aiProcess_FindInvalidData | \ + 0 ) + + // --------------------------------------------------------------------------------------- + /** @def aiProcessPreset_TargetRealtime_MaxQuality + * @brief Default postprocess configuration optimizing the data for real-time rendering. + * + * This preset enables almost every optimization step to achieve perfectly + * optimized data. It's your choice for level editor environments where import speed + * is not important. + * + * If you're using DirectX, don't forget to combine this value with + * the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations + * in your application, apply the #aiProcess_TransformUVCoords step, too. + * @note Please take the time to read the docs for the steps enabled by this preset. + * Some of them offer further configurable properties, while some of them might not be + * of use for you so it might be better to not specify them. + */ +#define aiProcessPreset_TargetRealtime_MaxQuality ( \ + aiProcessPreset_TargetRealtime_Quality | \ + aiProcess_FindInstances | \ + aiProcess_ValidateDataStructure | \ + aiProcess_OptimizeMeshes | \ + 0 ) + + +#ifdef __cplusplus +} // end of extern "C" +#endif + +#endif // AI_POSTPROCESS_H_INC diff --git a/thirdparty/assimp/include/assimp/qnan.h b/thirdparty/assimp/include/assimp/qnan.h new file mode 100644 index 0000000000..0918bde5e7 --- /dev/null +++ b/thirdparty/assimp/include/assimp/qnan.h @@ -0,0 +1,165 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file qnan.h + * @brief Some utilities for our dealings with qnans. + * + * @note Some loaders use qnans to mark invalid values tempoarily, also + * Assimp explicitly enforces undefined normals to be set to qnan. + * qnan utilities are available in standard libraries (C99 for example) + * but last time I checked compiler coverage was so bad that I decided + * to reinvent the wheel. + */ + +#ifndef AI_QNAN_H_INCLUDED +#define AI_QNAN_H_INCLUDED + +#include <assimp/defs.h> +#include <limits> +#include <stdint.h> + +// --------------------------------------------------------------------------- +/** Data structure to represent the bit pattern of a 32 Bit + * IEEE 754 floating-point number. */ +union _IEEESingle +{ + float Float; + struct + { + uint32_t Frac : 23; + uint32_t Exp : 8; + uint32_t Sign : 1; + } IEEE; +}; + +// --------------------------------------------------------------------------- +/** Data structure to represent the bit pattern of a 64 Bit + * IEEE 754 floating-point number. */ +union _IEEEDouble +{ + double Double; + struct + { + uint64_t Frac : 52; + uint64_t Exp : 11; + uint64_t Sign : 1; + } IEEE; +}; + +// --------------------------------------------------------------------------- +/** Check whether a given float is qNaN. + * @param in Input value */ +AI_FORCE_INLINE bool is_qnan(float in) +{ + // the straightforward solution does not work: + // return (in != in); + // compiler generates code like this + // load <in> to <register-with-different-width> + // compare <register-with-different-width> against <in> + + // FIXME: Use <float> stuff instead? I think fpclassify needs C99 + _IEEESingle temp; + memcpy(&temp, &in, sizeof(float)); + return (temp.IEEE.Exp == (1u << 8)-1 && + temp.IEEE.Frac); +} + +// --------------------------------------------------------------------------- +/** Check whether a given double is qNaN. + * @param in Input value */ +AI_FORCE_INLINE bool is_qnan(double in) +{ + // the straightforward solution does not work: + // return (in != in); + // compiler generates code like this + // load <in> to <register-with-different-width> + // compare <register-with-different-width> against <in> + + // FIXME: Use <float> stuff instead? I think fpclassify needs C99 + _IEEEDouble temp; + memcpy(&temp, &in, sizeof(in)); + return (temp.IEEE.Exp == (1u << 11)-1 && + temp.IEEE.Frac); +} + +// --------------------------------------------------------------------------- +/** @brief check whether a float is either NaN or (+/-) INF. + * + * Denorms return false, they're treated like normal values. + * @param in Input value */ +AI_FORCE_INLINE bool is_special_float(float in) +{ + _IEEESingle temp; + memcpy(&temp, &in, sizeof(float)); + return (temp.IEEE.Exp == (1u << 8)-1); +} + +// --------------------------------------------------------------------------- +/** @brief check whether a double is either NaN or (+/-) INF. + * + * Denorms return false, they're treated like normal values. + * @param in Input value */ +AI_FORCE_INLINE bool is_special_float(double in) +{ + _IEEESingle temp; + memcpy(&temp, &in, sizeof(float)); + return (temp.IEEE.Exp == (1u << 11)-1); +} + +// --------------------------------------------------------------------------- +/** Check whether a float is NOT qNaN. + * @param in Input value */ +template<class TReal> +AI_FORCE_INLINE bool is_not_qnan(TReal in) +{ + return !is_qnan(in); +} + +// --------------------------------------------------------------------------- +/** @brief Get a fresh qnan. */ +AI_FORCE_INLINE ai_real get_qnan() +{ + return std::numeric_limits<ai_real>::quiet_NaN(); +} + +#endif // !! AI_QNAN_H_INCLUDED diff --git a/thirdparty/assimp/include/assimp/quaternion.h b/thirdparty/assimp/include/assimp/quaternion.h new file mode 100644 index 0000000000..96574d24b9 --- /dev/null +++ b/thirdparty/assimp/include/assimp/quaternion.h @@ -0,0 +1,130 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file quaternion.h + * @brief Quaternion structure, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_QUATERNION_H_INC +#define AI_QUATERNION_H_INC + +#ifdef __cplusplus + +#include "defs.h" + +template <typename TReal> class aiVector3t; +template <typename TReal> class aiMatrix3x3t; + +// --------------------------------------------------------------------------- +/** Represents a quaternion in a 4D vector. */ +template <typename TReal> +class aiQuaterniont +{ +public: + aiQuaterniont() AI_NO_EXCEPT : w(1.0), x(), y(), z() {} + aiQuaterniont(TReal pw, TReal px, TReal py, TReal pz) + : w(pw), x(px), y(py), z(pz) {} + + /** Construct from rotation matrix. Result is undefined if the matrix is not orthonormal. */ + explicit aiQuaterniont( const aiMatrix3x3t<TReal>& pRotMatrix); + + /** Construct from euler angles */ + aiQuaterniont( TReal rotx, TReal roty, TReal rotz); + + /** Construct from an axis-angle pair */ + aiQuaterniont( aiVector3t<TReal> axis, TReal angle); + + /** Construct from a normalized quaternion stored in a vec3 */ + explicit aiQuaterniont( aiVector3t<TReal> normalized); + + /** Returns a matrix representation of the quaternion */ + aiMatrix3x3t<TReal> GetMatrix() const; + +public: + + bool operator== (const aiQuaterniont& o) const; + bool operator!= (const aiQuaterniont& o) const; + + bool Equal(const aiQuaterniont& o, TReal epsilon = 1e-6) const; + +public: + + /** Normalize the quaternion */ + aiQuaterniont& Normalize(); + + /** Compute quaternion conjugate */ + aiQuaterniont& Conjugate (); + + /** Rotate a point by this quaternion */ + aiVector3t<TReal> Rotate (const aiVector3t<TReal>& in); + + /** Multiply two quaternions */ + aiQuaterniont operator* (const aiQuaterniont& two) const; + +public: + + /** Performs a spherical interpolation between two quaternions and writes the result into the third. + * @param pOut Target object to received the interpolated rotation. + * @param pStart Start rotation of the interpolation at factor == 0. + * @param pEnd End rotation, factor == 1. + * @param pFactor Interpolation factor between 0 and 1. Values outside of this range yield undefined results. + */ + static void Interpolate( aiQuaterniont& pOut, const aiQuaterniont& pStart, + const aiQuaterniont& pEnd, TReal pFactor); + +public: + + //! w,x,y,z components of the quaternion + TReal w, x, y, z; +} ; + +typedef aiQuaterniont<ai_real> aiQuaternion; + +#else + +struct aiQuaternion { + ai_real w, x, y, z; +}; + +#endif + +#endif // AI_QUATERNION_H_INC diff --git a/thirdparty/assimp/include/assimp/quaternion.inl b/thirdparty/assimp/include/assimp/quaternion.inl new file mode 100644 index 0000000000..c26648215e --- /dev/null +++ b/thirdparty/assimp/include/assimp/quaternion.inl @@ -0,0 +1,286 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file quaternion.inl + * @brief Inline implementation of aiQuaterniont<TReal> operators + */ +#pragma once +#ifndef AI_QUATERNION_INL_INC +#define AI_QUATERNION_INL_INC + +#ifdef __cplusplus +#include "quaternion.h" + +#include <cmath> + +// --------------------------------------------------------------------------- +template<typename TReal> +bool aiQuaterniont<TReal>::operator== (const aiQuaterniont& o) const +{ + return x == o.x && y == o.y && z == o.z && w == o.w; +} + +// --------------------------------------------------------------------------- +template<typename TReal> +bool aiQuaterniont<TReal>::operator!= (const aiQuaterniont& o) const +{ + return !(*this == o); +} + +// --------------------------------------------------------------------------- +template<typename TReal> +inline bool aiQuaterniont<TReal>::Equal(const aiQuaterniont& o, TReal epsilon) const { + return + std::abs(x - o.x) <= epsilon && + std::abs(y - o.y) <= epsilon && + std::abs(z - o.z) <= epsilon && + std::abs(w - o.w) <= epsilon; +} + +// --------------------------------------------------------------------------- +// Constructs a quaternion from a rotation matrix +template<typename TReal> +inline aiQuaterniont<TReal>::aiQuaterniont( const aiMatrix3x3t<TReal> &pRotMatrix) +{ + TReal t = pRotMatrix.a1 + pRotMatrix.b2 + pRotMatrix.c3; + + // large enough + if( t > static_cast<TReal>(0)) + { + TReal s = std::sqrt(1 + t) * static_cast<TReal>(2.0); + x = (pRotMatrix.c2 - pRotMatrix.b3) / s; + y = (pRotMatrix.a3 - pRotMatrix.c1) / s; + z = (pRotMatrix.b1 - pRotMatrix.a2) / s; + w = static_cast<TReal>(0.25) * s; + } // else we have to check several cases + else if( pRotMatrix.a1 > pRotMatrix.b2 && pRotMatrix.a1 > pRotMatrix.c3 ) + { + // Column 0: + TReal s = std::sqrt( static_cast<TReal>(1.0) + pRotMatrix.a1 - pRotMatrix.b2 - pRotMatrix.c3) * static_cast<TReal>(2.0); + x = static_cast<TReal>(0.25) * s; + y = (pRotMatrix.b1 + pRotMatrix.a2) / s; + z = (pRotMatrix.a3 + pRotMatrix.c1) / s; + w = (pRotMatrix.c2 - pRotMatrix.b3) / s; + } + else if( pRotMatrix.b2 > pRotMatrix.c3) + { + // Column 1: + TReal s = std::sqrt( static_cast<TReal>(1.0) + pRotMatrix.b2 - pRotMatrix.a1 - pRotMatrix.c3) * static_cast<TReal>(2.0); + x = (pRotMatrix.b1 + pRotMatrix.a2) / s; + y = static_cast<TReal>(0.25) * s; + z = (pRotMatrix.c2 + pRotMatrix.b3) / s; + w = (pRotMatrix.a3 - pRotMatrix.c1) / s; + } else + { + // Column 2: + TReal s = std::sqrt( static_cast<TReal>(1.0) + pRotMatrix.c3 - pRotMatrix.a1 - pRotMatrix.b2) * static_cast<TReal>(2.0); + x = (pRotMatrix.a3 + pRotMatrix.c1) / s; + y = (pRotMatrix.c2 + pRotMatrix.b3) / s; + z = static_cast<TReal>(0.25) * s; + w = (pRotMatrix.b1 - pRotMatrix.a2) / s; + } +} + +// --------------------------------------------------------------------------- +// Construction from euler angles +template<typename TReal> +inline aiQuaterniont<TReal>::aiQuaterniont( TReal fPitch, TReal fYaw, TReal fRoll ) +{ + const TReal fSinPitch(std::sin(fPitch*static_cast<TReal>(0.5))); + const TReal fCosPitch(std::cos(fPitch*static_cast<TReal>(0.5))); + const TReal fSinYaw(std::sin(fYaw*static_cast<TReal>(0.5))); + const TReal fCosYaw(std::cos(fYaw*static_cast<TReal>(0.5))); + const TReal fSinRoll(std::sin(fRoll*static_cast<TReal>(0.5))); + const TReal fCosRoll(std::cos(fRoll*static_cast<TReal>(0.5))); + const TReal fCosPitchCosYaw(fCosPitch*fCosYaw); + const TReal fSinPitchSinYaw(fSinPitch*fSinYaw); + x = fSinRoll * fCosPitchCosYaw - fCosRoll * fSinPitchSinYaw; + y = fCosRoll * fSinPitch * fCosYaw + fSinRoll * fCosPitch * fSinYaw; + z = fCosRoll * fCosPitch * fSinYaw - fSinRoll * fSinPitch * fCosYaw; + w = fCosRoll * fCosPitchCosYaw + fSinRoll * fSinPitchSinYaw; +} + +// --------------------------------------------------------------------------- +// Returns a matrix representation of the quaternion +template<typename TReal> +inline aiMatrix3x3t<TReal> aiQuaterniont<TReal>::GetMatrix() const +{ + aiMatrix3x3t<TReal> resMatrix; + resMatrix.a1 = static_cast<TReal>(1.0) - static_cast<TReal>(2.0) * (y * y + z * z); + resMatrix.a2 = static_cast<TReal>(2.0) * (x * y - z * w); + resMatrix.a3 = static_cast<TReal>(2.0) * (x * z + y * w); + resMatrix.b1 = static_cast<TReal>(2.0) * (x * y + z * w); + resMatrix.b2 = static_cast<TReal>(1.0) - static_cast<TReal>(2.0) * (x * x + z * z); + resMatrix.b3 = static_cast<TReal>(2.0) * (y * z - x * w); + resMatrix.c1 = static_cast<TReal>(2.0) * (x * z - y * w); + resMatrix.c2 = static_cast<TReal>(2.0) * (y * z + x * w); + resMatrix.c3 = static_cast<TReal>(1.0) - static_cast<TReal>(2.0) * (x * x + y * y); + + return resMatrix; +} + +// --------------------------------------------------------------------------- +// Construction from an axis-angle pair +template<typename TReal> +inline aiQuaterniont<TReal>::aiQuaterniont( aiVector3t<TReal> axis, TReal angle) +{ + axis.Normalize(); + + const TReal sin_a = std::sin( angle / 2 ); + const TReal cos_a = std::cos( angle / 2 ); + x = axis.x * sin_a; + y = axis.y * sin_a; + z = axis.z * sin_a; + w = cos_a; +} +// --------------------------------------------------------------------------- +// Construction from am existing, normalized quaternion +template<typename TReal> +inline aiQuaterniont<TReal>::aiQuaterniont( aiVector3t<TReal> normalized) +{ + x = normalized.x; + y = normalized.y; + z = normalized.z; + + const TReal t = static_cast<TReal>(1.0) - (x*x) - (y*y) - (z*z); + + if (t < static_cast<TReal>(0.0)) { + w = static_cast<TReal>(0.0); + } + else w = std::sqrt (t); +} + +// --------------------------------------------------------------------------- +// Performs a spherical interpolation between two quaternions +// Implementation adopted from the gmtl project. All others I found on the net fail in some cases. +// Congrats, gmtl! +template<typename TReal> +inline void aiQuaterniont<TReal>::Interpolate( aiQuaterniont& pOut, const aiQuaterniont& pStart, const aiQuaterniont& pEnd, TReal pFactor) +{ + // calc cosine theta + TReal cosom = pStart.x * pEnd.x + pStart.y * pEnd.y + pStart.z * pEnd.z + pStart.w * pEnd.w; + + // adjust signs (if necessary) + aiQuaterniont end = pEnd; + if( cosom < static_cast<TReal>(0.0)) + { + cosom = -cosom; + end.x = -end.x; // Reverse all signs + end.y = -end.y; + end.z = -end.z; + end.w = -end.w; + } + + // Calculate coefficients + TReal sclp, sclq; + if( (static_cast<TReal>(1.0) - cosom) > static_cast<TReal>(0.0001)) // 0.0001 -> some epsillon + { + // Standard case (slerp) + TReal omega, sinom; + omega = std::acos( cosom); // extract theta from dot product's cos theta + sinom = std::sin( omega); + sclp = std::sin( (static_cast<TReal>(1.0) - pFactor) * omega) / sinom; + sclq = std::sin( pFactor * omega) / sinom; + } else + { + // Very close, do linear interp (because it's faster) + sclp = static_cast<TReal>(1.0) - pFactor; + sclq = pFactor; + } + + pOut.x = sclp * pStart.x + sclq * end.x; + pOut.y = sclp * pStart.y + sclq * end.y; + pOut.z = sclp * pStart.z + sclq * end.z; + pOut.w = sclp * pStart.w + sclq * end.w; +} + +// --------------------------------------------------------------------------- +template<typename TReal> +inline aiQuaterniont<TReal>& aiQuaterniont<TReal>::Normalize() +{ + // compute the magnitude and divide through it + const TReal mag = std::sqrt(x*x + y*y + z*z + w*w); + if (mag) + { + const TReal invMag = static_cast<TReal>(1.0)/mag; + x *= invMag; + y *= invMag; + z *= invMag; + w *= invMag; + } + return *this; +} + +// --------------------------------------------------------------------------- +template<typename TReal> +inline aiQuaterniont<TReal> aiQuaterniont<TReal>::operator* (const aiQuaterniont& t) const +{ + return aiQuaterniont(w*t.w - x*t.x - y*t.y - z*t.z, + w*t.x + x*t.w + y*t.z - z*t.y, + w*t.y + y*t.w + z*t.x - x*t.z, + w*t.z + z*t.w + x*t.y - y*t.x); +} + +// --------------------------------------------------------------------------- +template<typename TReal> +inline aiQuaterniont<TReal>& aiQuaterniont<TReal>::Conjugate () +{ + x = -x; + y = -y; + z = -z; + return *this; +} + +// --------------------------------------------------------------------------- +template<typename TReal> +inline aiVector3t<TReal> aiQuaterniont<TReal>::Rotate (const aiVector3t<TReal>& v) +{ + aiQuaterniont q2(0.f,v.x,v.y,v.z), q = *this, qinv = q; + qinv.Conjugate(); + + q = q*q2*qinv; + return aiVector3t<TReal>(q.x,q.y,q.z); +} + +#endif +#endif // AI_QUATERNION_INL_INC diff --git a/thirdparty/assimp/include/assimp/scene.h b/thirdparty/assimp/include/assimp/scene.h new file mode 100644 index 0000000000..de0239702d --- /dev/null +++ b/thirdparty/assimp/include/assimp/scene.h @@ -0,0 +1,416 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file scene.h + * @brief Defines the data structures in which the imported scene is returned. + */ +#pragma once +#ifndef AI_SCENE_H_INC +#define AI_SCENE_H_INC + +#include "types.h" +#include "texture.h" +#include "mesh.h" +#include "light.h" +#include "camera.h" +#include "material.h" +#include "anim.h" +#include "metadata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" +#endif + +// ------------------------------------------------------------------------------- +/** + * A node in the imported hierarchy. + * + * Each node has name, a parent node (except for the root node), + * a transformation relative to its parent and possibly several child nodes. + * Simple file formats don't support hierarchical structures - for these formats + * the imported scene does consist of only a single root node without children. + */ +// ------------------------------------------------------------------------------- +struct ASSIMP_API aiNode +{ + /** The name of the node. + * + * The name might be empty (length of zero) but all nodes which + * need to be referenced by either bones or animations are named. + * Multiple nodes may have the same name, except for nodes which are referenced + * by bones (see #aiBone and #aiMesh::mBones). Their names *must* be unique. + * + * Cameras and lights reference a specific node by name - if there + * are multiple nodes with this name, they are assigned to each of them. + * <br> + * There are no limitations with regard to the characters contained in + * the name string as it is usually taken directly from the source file. + * + * Implementations should be able to handle tokens such as whitespace, tabs, + * line feeds, quotation marks, ampersands etc. + * + * Sometimes assimp introduces new nodes not present in the source file + * into the hierarchy (usually out of necessity because sometimes the + * source hierarchy format is simply not compatible). Their names are + * surrounded by @verbatim <> @endverbatim e.g. + * @verbatim<DummyRootNode> @endverbatim. + */ + C_STRUCT aiString mName; + + /** The transformation relative to the node's parent. */ + C_STRUCT aiMatrix4x4 mTransformation; + + /** Parent node. NULL if this node is the root node. */ + C_STRUCT aiNode* mParent; + + /** The number of child nodes of this node. */ + unsigned int mNumChildren; + + /** The child nodes of this node. NULL if mNumChildren is 0. */ + C_STRUCT aiNode** mChildren; + + /** The number of meshes of this node. */ + unsigned int mNumMeshes; + + /** The meshes of this node. Each entry is an index into the + * mesh list of the #aiScene. + */ + unsigned int* mMeshes; + + /** Metadata associated with this node or NULL if there is no metadata. + * Whether any metadata is generated depends on the source file format. See the + * @link importer_notes @endlink page for more information on every source file + * format. Importers that don't document any metadata don't write any. + */ + C_STRUCT aiMetadata* mMetaData; + +#ifdef __cplusplus + /** Constructor */ + aiNode(); + + /** Construction from a specific name */ + explicit aiNode(const std::string& name); + + /** Destructor */ + ~aiNode(); + + /** Searches for a node with a specific name, beginning at this + * nodes. Normally you will call this method on the root node + * of the scene. + * + * @param name Name to search for + * @return NULL or a valid Node if the search was successful. + */ + inline + const aiNode* FindNode(const aiString& name) const { + return FindNode(name.data); + } + + inline + aiNode* FindNode(const aiString& name) { + return FindNode(name.data); + } + + const aiNode* FindNode(const char* name) const; + + aiNode* FindNode(const char* name); + + /** + * @brief Will add new children. + * @param numChildren Number of children to add. + * @param children The array with pointers showing to the children. + */ + void addChildren(unsigned int numChildren, aiNode **children); +#endif // __cplusplus +}; + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +// ------------------------------------------------------------------------------- +/** + * Specifies that the scene data structure that was imported is not complete. + * This flag bypasses some internal validations and allows the import + * of animation skeletons, material libraries or camera animation paths + * using Assimp. Most applications won't support such data. + */ +#define AI_SCENE_FLAGS_INCOMPLETE 0x1 + +/** + * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS) + * if the validation is successful. In a validated scene you can be sure that + * any cross references in the data structure (e.g. vertex indices) are valid. + */ +#define AI_SCENE_FLAGS_VALIDATED 0x2 + +/** + * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS) + * if the validation is successful but some issues have been found. + * This can for example mean that a texture that does not exist is referenced + * by a material or that the bone weights for a vertex don't sum to 1.0 ... . + * In most cases you should still be able to use the import. This flag could + * be useful for applications which don't capture Assimp's log output. + */ +#define AI_SCENE_FLAGS_VALIDATION_WARNING 0x4 + +/** + * This flag is currently only set by the aiProcess_JoinIdenticalVertices step. + * It indicates that the vertices of the output meshes aren't in the internal + * verbose format anymore. In the verbose format all vertices are unique, + * no vertex is ever referenced by more than one face. + */ +#define AI_SCENE_FLAGS_NON_VERBOSE_FORMAT 0x8 + + /** + * Denotes pure height-map terrain data. Pure terrains usually consist of quads, + * sometimes triangles, in a regular grid. The x,y coordinates of all vertex + * positions refer to the x,y coordinates on the terrain height map, the z-axis + * stores the elevation at a specific point. + * + * TER (Terragen) and HMP (3D Game Studio) are height map formats. + * @note Assimp is probably not the best choice for loading *huge* terrains - + * fully triangulated data takes extremely much free store and should be avoided + * as long as possible (typically you'll do the triangulation when you actually + * need to render it). + */ +#define AI_SCENE_FLAGS_TERRAIN 0x10 + + /** + * Specifies that the scene data can be shared between structures. For example: + * one vertex in few faces. \ref AI_SCENE_FLAGS_NON_VERBOSE_FORMAT can not be + * used for this because \ref AI_SCENE_FLAGS_NON_VERBOSE_FORMAT has internal + * meaning about postprocessing steps. + */ +#define AI_SCENE_FLAGS_ALLOW_SHARED 0x20 + +// ------------------------------------------------------------------------------- +/** The root structure of the imported data. + * + * Everything that was imported from the given file can be accessed from here. + * Objects of this class are generally maintained and owned by Assimp, not + * by the caller. You shouldn't want to instance it, nor should you ever try to + * delete a given scene on your own. + */ +// ------------------------------------------------------------------------------- +struct aiScene +{ + /** Any combination of the AI_SCENE_FLAGS_XXX flags. By default + * this value is 0, no flags are set. Most applications will + * want to reject all scenes with the AI_SCENE_FLAGS_INCOMPLETE + * bit set. + */ + unsigned int mFlags; + + /** The root node of the hierarchy. + * + * There will always be at least the root node if the import + * was successful (and no special flags have been set). + * Presence of further nodes depends on the format and content + * of the imported file. + */ + C_STRUCT aiNode* mRootNode; + + /** The number of meshes in the scene. */ + unsigned int mNumMeshes; + + /** The array of meshes. + * + * Use the indices given in the aiNode structure to access + * this array. The array is mNumMeshes in size. If the + * AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always + * be at least ONE material. + */ + C_STRUCT aiMesh** mMeshes; + + /** The number of materials in the scene. */ + unsigned int mNumMaterials; + + /** The array of materials. + * + * Use the index given in each aiMesh structure to access this + * array. The array is mNumMaterials in size. If the + * AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always + * be at least ONE material. + */ + C_STRUCT aiMaterial** mMaterials; + + /** The number of animations in the scene. */ + unsigned int mNumAnimations; + + /** The array of animations. + * + * All animations imported from the given file are listed here. + * The array is mNumAnimations in size. + */ + C_STRUCT aiAnimation** mAnimations; + + /** The number of textures embedded into the file */ + unsigned int mNumTextures; + + /** The array of embedded textures. + * + * Not many file formats embed their textures into the file. + * An example is Quake's MDL format (which is also used by + * some GameStudio versions) + */ + C_STRUCT aiTexture** mTextures; + + /** The number of light sources in the scene. Light sources + * are fully optional, in most cases this attribute will be 0 + */ + unsigned int mNumLights; + + /** The array of light sources. + * + * All light sources imported from the given file are + * listed here. The array is mNumLights in size. + */ + C_STRUCT aiLight** mLights; + + /** The number of cameras in the scene. Cameras + * are fully optional, in most cases this attribute will be 0 + */ + unsigned int mNumCameras; + + /** The array of cameras. + * + * All cameras imported from the given file are listed here. + * The array is mNumCameras in size. The first camera in the + * array (if existing) is the default camera view into + * the scene. + */ + C_STRUCT aiCamera** mCameras; + + /** + * @brief The global metadata assigned to the scene itself. + * + * This data contains global metadata which belongs to the scene like + * unit-conversions, versions, vendors or other model-specific data. This + * can be used to store format-specific metadata as well. + */ + C_STRUCT aiMetadata* mMetaData; + + +#ifdef __cplusplus + + //! Default constructor - set everything to 0/NULL + ASSIMP_API aiScene(); + + //! Destructor + ASSIMP_API ~aiScene(); + + //! Check whether the scene contains meshes + //! Unless no special scene flags are set this will always be true. + inline bool HasMeshes() const { + return mMeshes != NULL && mNumMeshes > 0; + } + + //! Check whether the scene contains materials + //! Unless no special scene flags are set this will always be true. + inline bool HasMaterials() const { + return mMaterials != NULL && mNumMaterials > 0; + } + + //! Check whether the scene contains lights + inline bool HasLights() const { + return mLights != NULL && mNumLights > 0; + } + + //! Check whether the scene contains textures + inline bool HasTextures() const { + return mTextures != NULL && mNumTextures > 0; + } + + //! Check whether the scene contains cameras + inline bool HasCameras() const { + return mCameras != NULL && mNumCameras > 0; + } + + //! Check whether the scene contains animations + inline bool HasAnimations() const { + return mAnimations != NULL && mNumAnimations > 0; + } + + //! Returns a short filename from a full path + static const char* GetShortFilename(const char* filename) { + const char* lastSlash = strrchr(filename, '/'); + if (lastSlash == nullptr) { + lastSlash = strrchr(filename, '\\'); + } + const char* shortFilename = lastSlash != nullptr ? lastSlash + 1 : filename; + return shortFilename; + } + + //! Returns an embedded texture + const aiTexture* GetEmbeddedTexture(const char* filename) const { + const char* shortFilename = GetShortFilename(filename); + for (unsigned int i = 0; i < mNumTextures; i++) { + const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str()); + if (strcmp(shortTextureFilename, shortFilename) == 0) { + return mTextures[i]; + } + } + return nullptr; + } +#endif // __cplusplus + + /** Internal data, do not touch */ +#ifdef __cplusplus + void* mPrivate; +#else + char* mPrivate; +#endif + +}; + +#ifdef __cplusplus +} //! namespace Assimp +#endif + +#endif // AI_SCENE_H_INC diff --git a/thirdparty/assimp/include/assimp/texture.h b/thirdparty/assimp/include/assimp/texture.h new file mode 100644 index 0000000000..dc6cbef65c --- /dev/null +++ b/thirdparty/assimp/include/assimp/texture.h @@ -0,0 +1,227 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file texture.h + * @brief Defines texture helper structures for the library + * + * Used for file formats which embed their textures into the model file. + * Supported are both normal textures, which are stored as uncompressed + * pixels, and "compressed" textures, which are stored in a file format + * such as PNG or TGA. + */ +#pragma once +#ifndef AI_TEXTURE_H_INC +#define AI_TEXTURE_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +// -------------------------------------------------------------------------------- + +/** \def AI_EMBEDDED_TEXNAME_PREFIX + * \ref AI_MAKE_EMBEDDED_TEXNAME + */ +#ifndef AI_EMBEDDED_TEXNAME_PREFIX +# define AI_EMBEDDED_TEXNAME_PREFIX "*" +#endif + +/** @def AI_MAKE_EMBEDDED_TEXNAME + * Used to build the reserved path name used by the material system to + * reference textures that are embedded into their corresponding + * model files. The parameter specifies the index of the texture + * (zero-based, in the aiScene::mTextures array) + */ +#if (!defined AI_MAKE_EMBEDDED_TEXNAME) +# define AI_MAKE_EMBEDDED_TEXNAME(_n_) AI_EMBEDDED_TEXNAME_PREFIX # _n_ +#endif + + +#include "./Compiler/pushpack1.h" + +// -------------------------------------------------------------------------------- +/** @brief Helper structure to represent a texel in a ARGB8888 format +* +* Used by aiTexture. +*/ +struct aiTexel +{ + unsigned char b,g,r,a; + +#ifdef __cplusplus + //! Comparison operator + bool operator== (const aiTexel& other) const + { + return b == other.b && r == other.r && + g == other.g && a == other.a; + } + + //! Inverse comparison operator + bool operator!= (const aiTexel& other) const + { + return b != other.b || r != other.r || + g != other.g || a != other.a; + } + + //! Conversion to a floating-point 4d color + operator aiColor4D() const + { + return aiColor4D(r/255.f,g/255.f,b/255.f,a/255.f); + } +#endif // __cplusplus + +} PACK_STRUCT; + +#include "./Compiler/poppack1.h" + +#define HINTMAXTEXTURELEN 9 + +// -------------------------------------------------------------------------------- +/** Helper structure to describe an embedded texture + * + * Normally textures are contained in external files but some file formats embed + * them directly in the model file. There are two types of embedded textures: + * 1. Uncompressed textures. The color data is given in an uncompressed format. + * 2. Compressed textures stored in a file format like png or jpg. The raw file + * bytes are given so the application must utilize an image decoder (e.g. DevIL) to + * get access to the actual color data. + * + * Embedded textures are referenced from materials using strings like "*0", "*1", etc. + * as the texture paths (a single asterisk character followed by the + * zero-based index of the texture in the aiScene::mTextures array). + */ +struct aiTexture { + /** Width of the texture, in pixels + * + * If mHeight is zero the texture is compressed in a format + * like JPEG. In this case mWidth specifies the size of the + * memory area pcData is pointing to, in bytes. + */ + unsigned int mWidth; + + /** Height of the texture, in pixels + * + * If this value is zero, pcData points to an compressed texture + * in any format (e.g. JPEG). + */ + unsigned int mHeight; + + /** A hint from the loader to make it easier for applications + * to determine the type of embedded textures. + * + * If mHeight != 0 this member is show how data is packed. Hint will consist of + * two parts: channel order and channel bitness (count of the bits for every + * color channel). For simple parsing by the viewer it's better to not omit + * absent color channel and just use 0 for bitness. For example: + * 1. Image contain RGBA and 8 bit per channel, achFormatHint == "rgba8888"; + * 2. Image contain ARGB and 8 bit per channel, achFormatHint == "argb8888"; + * 3. Image contain RGB and 5 bit for R and B channels and 6 bit for G channel, achFormatHint == "rgba5650"; + * 4. One color image with B channel and 1 bit for it, achFormatHint == "rgba0010"; + * If mHeight == 0 then achFormatHint is set set to '\\0\\0\\0\\0' if the loader has no additional + * information about the texture file format used OR the + * file extension of the format without a trailing dot. If there + * are multiple file extensions for a format, the shortest + * extension is chosen (JPEG maps to 'jpg', not to 'jpeg'). + * E.g. 'dds\\0', 'pcx\\0', 'jpg\\0'. All characters are lower-case. + * The fourth character will always be '\\0'. + */ + char achFormatHint[ HINTMAXTEXTURELEN ];// 8 for string + 1 for terminator. + + /** Data of the texture. + * + * Points to an array of mWidth * mHeight aiTexel's. + * The format of the texture data is always ARGB8888 to + * make the implementation for user of the library as easy + * as possible. If mHeight = 0 this is a pointer to a memory + * buffer of size mWidth containing the compressed texture + * data. Good luck, have fun! + */ + C_STRUCT aiTexel* pcData; + + /** Texture original filename + * + * Used to get the texture reference + */ + C_STRUCT aiString mFilename; + +#ifdef __cplusplus + + //! For compressed textures (mHeight == 0): compare the + //! format hint against a given string. + //! @param s Input string. 3 characters are maximally processed. + //! Example values: "jpg", "png" + //! @return true if the given string matches the format hint + bool CheckFormat(const char* s) const { + if (nullptr == s) { + return false; + } + + return (0 == ::strncmp(achFormatHint, s, sizeof(achFormatHint))); + } + + // Construction + aiTexture() AI_NO_EXCEPT + : mWidth(0) + , mHeight(0) + , pcData(nullptr) + , mFilename() { + achFormatHint[0] = achFormatHint[1] = 0; + achFormatHint[2] = achFormatHint[3] = 0; + } + + // Destruction + ~aiTexture () { + delete[] pcData; + } +#endif +}; + + +#ifdef __cplusplus +} +#endif + +#endif // AI_TEXTURE_H_INC diff --git a/thirdparty/assimp/include/assimp/types.h b/thirdparty/assimp/include/assimp/types.h new file mode 100644 index 0000000000..748e4851f3 --- /dev/null +++ b/thirdparty/assimp/include/assimp/types.h @@ -0,0 +1,529 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file types.h + * Basic data types and primitives, such as vectors or colors. + */ +#pragma once +#ifndef AI_TYPES_H_INC +#define AI_TYPES_H_INC + +// Some runtime headers +#include <sys/types.h> +#include <stddef.h> +#include <string.h> +#include <limits.h> + +// Our compile configuration +#include "defs.h" + +// Some types moved to separate header due to size of operators +#include "vector3.h" +#include "vector2.h" +#include "color4.h" +#include "matrix3x3.h" +#include "matrix4x4.h" +#include "quaternion.h" + +#ifdef __cplusplus +#include <cstring> +#include <new> // for std::nothrow_t +#include <string> // for aiString::Set(const std::string&) + +namespace Assimp { + //! @cond never +namespace Intern { + // -------------------------------------------------------------------- + /** @brief Internal helper class to utilize our internal new/delete + * routines for allocating object of this and derived classes. + * + * By doing this you can safely share class objects between Assimp + * and the application - it works even over DLL boundaries. A good + * example is the #IOSystem where the application allocates its custom + * #IOSystem, then calls #Importer::SetIOSystem(). When the Importer + * destructs, Assimp calls operator delete on the stored #IOSystem. + * If it lies on a different heap than Assimp is working with, + * the application is determined to crash. + */ + // -------------------------------------------------------------------- +#ifndef SWIG + struct ASSIMP_API AllocateFromAssimpHeap { + // http://www.gotw.ca/publications/mill15.htm + + // new/delete overload + void *operator new ( size_t num_bytes) /* throw( std::bad_alloc ) */; + void *operator new ( size_t num_bytes, const std::nothrow_t& ) throw(); + void operator delete ( void* data); + + // array new/delete overload + void *operator new[] ( size_t num_bytes) /* throw( std::bad_alloc ) */; + void *operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw(); + void operator delete[] ( void* data); + + }; // struct AllocateFromAssimpHeap +#endif +} // namespace Intern + //! @endcond +} // namespace Assimp + +extern "C" { +#endif + +/** Maximum dimension for strings, ASSIMP strings are zero terminated. */ +#ifdef __cplusplus + static const size_t MAXLEN = 1024; +#else +# define MAXLEN 1024 +#endif + +// ---------------------------------------------------------------------------------- +/** Represents a plane in a three-dimensional, euclidean space +*/ +struct aiPlane { +#ifdef __cplusplus + aiPlane () AI_NO_EXCEPT : a(0.f), b(0.f), c(0.f), d(0.f) {} + aiPlane (ai_real _a, ai_real _b, ai_real _c, ai_real _d) + : a(_a), b(_b), c(_c), d(_d) {} + + aiPlane (const aiPlane& o) : a(o.a), b(o.b), c(o.c), d(o.d) {} + +#endif // !__cplusplus + + //! Plane equation + ai_real a,b,c,d; +}; // !struct aiPlane + +// ---------------------------------------------------------------------------------- +/** Represents a ray +*/ +struct aiRay { +#ifdef __cplusplus + aiRay () AI_NO_EXCEPT {} + aiRay (const aiVector3D& _pos, const aiVector3D& _dir) + : pos(_pos), dir(_dir) {} + + aiRay (const aiRay& o) : pos (o.pos), dir (o.dir) {} + +#endif // !__cplusplus + + //! Position and direction of the ray + C_STRUCT aiVector3D pos, dir; +}; // !struct aiRay + +// ---------------------------------------------------------------------------------- +/** Represents a color in Red-Green-Blue space. +*/ +struct aiColor3D +{ +#ifdef __cplusplus + aiColor3D () AI_NO_EXCEPT : r(0.0f), g(0.0f), b(0.0f) {} + aiColor3D (ai_real _r, ai_real _g, ai_real _b) : r(_r), g(_g), b(_b) {} + explicit aiColor3D (ai_real _r) : r(_r), g(_r), b(_r) {} + aiColor3D (const aiColor3D& o) : r(o.r), g(o.g), b(o.b) {} + + /** Component-wise comparison */ + // TODO: add epsilon? + bool operator == (const aiColor3D& other) const + {return r == other.r && g == other.g && b == other.b;} + + /** Component-wise inverse comparison */ + // TODO: add epsilon? + bool operator != (const aiColor3D& other) const + {return r != other.r || g != other.g || b != other.b;} + + /** Component-wise comparison */ + // TODO: add epsilon? + bool operator < (const aiColor3D& other) const { + return r < other.r || ( r == other.r && (g < other.g || (g == other.g && b < other.b ) ) ); + } + + /** Component-wise addition */ + aiColor3D operator+(const aiColor3D& c) const { + return aiColor3D(r+c.r,g+c.g,b+c.b); + } + + /** Component-wise subtraction */ + aiColor3D operator-(const aiColor3D& c) const { + return aiColor3D(r-c.r,g-c.g,b-c.b); + } + + /** Component-wise multiplication */ + aiColor3D operator*(const aiColor3D& c) const { + return aiColor3D(r*c.r,g*c.g,b*c.b); + } + + /** Multiply with a scalar */ + aiColor3D operator*(ai_real f) const { + return aiColor3D(r*f,g*f,b*f); + } + + /** Access a specific color component */ + ai_real operator[](unsigned int i) const { + return *(&r + i); + } + + /** Access a specific color component */ + ai_real& operator[](unsigned int i) { + if ( 0 == i ) { + return r; + } else if ( 1 == i ) { + return g; + } else if ( 2 == i ) { + return b; + } + return r; + } + + /** Check whether a color is black */ + bool IsBlack() const { + static const ai_real epsilon = ai_real(10e-3); + return std::fabs( r ) < epsilon && std::fabs( g ) < epsilon && std::fabs( b ) < epsilon; + } + +#endif // !__cplusplus + + //! Red, green and blue color values + ai_real r, g, b; +}; // !struct aiColor3D + +// ---------------------------------------------------------------------------------- +/** Represents an UTF-8 string, zero byte terminated. + * + * The character set of an aiString is explicitly defined to be UTF-8. This Unicode + * transformation was chosen in the belief that most strings in 3d files are limited + * to ASCII, thus the character set needed to be strictly ASCII compatible. + * + * Most text file loaders provide proper Unicode input file handling, special unicode + * characters are correctly transcoded to UTF8 and are kept throughout the libraries' + * import pipeline. + * + * For most applications, it will be absolutely sufficient to interpret the + * aiString as ASCII data and work with it as one would work with a plain char*. + * Windows users in need of proper support for i.e asian characters can use the + * MultiByteToWideChar(), WideCharToMultiByte() WinAPI functionality to convert the + * UTF-8 strings to their working character set (i.e. MBCS, WideChar). + * + * We use this representation instead of std::string to be C-compatible. The + * (binary) length of such a string is limited to MAXLEN characters (including the + * the terminating zero). +*/ +struct aiString +{ +#ifdef __cplusplus + /** Default constructor, the string is set to have zero length */ + aiString() AI_NO_EXCEPT + : length( 0 ) { + data[0] = '\0'; + +#ifdef ASSIMP_BUILD_DEBUG + // Debug build: overwrite the string on its full length with ESC (27) + memset(data+1,27,MAXLEN-1); +#endif + } + + /** Copy constructor */ + aiString(const aiString& rOther) : + length(rOther.length) + { + // Crop the string to the maximum length + length = length>=MAXLEN?MAXLEN-1:length; + memcpy( data, rOther.data, length); + data[length] = '\0'; + } + + /** Constructor from std::string */ + explicit aiString(const std::string& pString) : + length(pString.length()) + { + length = length>=MAXLEN?MAXLEN-1:length; + memcpy( data, pString.c_str(), length); + data[length] = '\0'; + } + + /** Copy a std::string to the aiString */ + void Set( const std::string& pString) { + if( pString.length() > MAXLEN - 1) { + return; + } + length = pString.length(); + memcpy( data, pString.c_str(), length); + data[length] = 0; + } + + /** Copy a const char* to the aiString */ + void Set( const char* sz) { + const size_t len = ::strlen(sz); + if( len > MAXLEN - 1) { + return; + } + length = len; + memcpy( data, sz, len); + data[len] = 0; + } + + + /** Assignment operator */ + aiString& operator = (const aiString &rOther) { + if (this == &rOther) { + return *this; + } + + length = rOther.length;; + memcpy( data, rOther.data, length); + data[length] = '\0'; + return *this; + } + + + /** Assign a const char* to the string */ + aiString& operator = (const char* sz) { + Set(sz); + return *this; + } + + /** Assign a cstd::string to the string */ + aiString& operator = ( const std::string& pString) { + Set(pString); + return *this; + } + + /** Comparison operator */ + bool operator==(const aiString& other) const { + return (length == other.length && 0 == memcmp(data,other.data,length)); + } + + /** Inverse comparison operator */ + bool operator!=(const aiString& other) const { + return (length != other.length || 0 != memcmp(data,other.data,length)); + } + + /** Append a string to the string */ + void Append (const char* app) { + const size_t len = ::strlen(app); + if (!len) { + return; + } + if (length + len >= MAXLEN) { + return; + } + + memcpy(&data[length],app,len+1); + length += len; + } + + /** Clear the string - reset its length to zero */ + void Clear () { + length = 0; + data[0] = '\0'; + +#ifdef ASSIMP_BUILD_DEBUG + // Debug build: overwrite the string on its full length with ESC (27) + memset(data+1,27,MAXLEN-1); +#endif + } + + /** Returns a pointer to the underlying zero-terminated array of characters */ + const char* C_Str() const { + return data; + } + +#endif // !__cplusplus + + /** Binary length of the string excluding the terminal 0. This is NOT the + * logical length of strings containing UTF-8 multi-byte sequences! It's + * the number of bytes from the beginning of the string to its end.*/ + size_t length; + + /** String buffer. Size limit is MAXLEN */ + char data[MAXLEN]; +} ; // !struct aiString + + +// ---------------------------------------------------------------------------------- +/** Standard return type for some library functions. + * Rarely used, and if, mostly in the C API. + */ +typedef enum aiReturn +{ + /** Indicates that a function was successful */ + aiReturn_SUCCESS = 0x0, + + /** Indicates that a function failed */ + aiReturn_FAILURE = -0x1, + + /** Indicates that not enough memory was available + * to perform the requested operation + */ + aiReturn_OUTOFMEMORY = -0x3, + + /** @cond never + * Force 32-bit size enum + */ + _AI_ENFORCE_ENUM_SIZE = 0x7fffffff + + /// @endcond +} aiReturn; // !enum aiReturn + +// just for backwards compatibility, don't use these constants anymore +#define AI_SUCCESS aiReturn_SUCCESS +#define AI_FAILURE aiReturn_FAILURE +#define AI_OUTOFMEMORY aiReturn_OUTOFMEMORY + +// ---------------------------------------------------------------------------------- +/** Seek origins (for the virtual file system API). + * Much cooler than using SEEK_SET, SEEK_CUR or SEEK_END. + */ +enum aiOrigin +{ + /** Beginning of the file */ + aiOrigin_SET = 0x0, + + /** Current position of the file pointer */ + aiOrigin_CUR = 0x1, + + /** End of the file, offsets must be negative */ + aiOrigin_END = 0x2, + + /** @cond never + * Force 32-bit size enum + */ + _AI_ORIGIN_ENFORCE_ENUM_SIZE = 0x7fffffff + + /// @endcond +}; // !enum aiOrigin + +// ---------------------------------------------------------------------------------- +/** @brief Enumerates predefined log streaming destinations. + * Logging to these streams can be enabled with a single call to + * #LogStream::createDefaultStream. + */ +enum aiDefaultLogStream +{ + /** Stream the log to a file */ + aiDefaultLogStream_FILE = 0x1, + + /** Stream the log to std::cout */ + aiDefaultLogStream_STDOUT = 0x2, + + /** Stream the log to std::cerr */ + aiDefaultLogStream_STDERR = 0x4, + + /** MSVC only: Stream the log the the debugger + * (this relies on OutputDebugString from the Win32 SDK) + */ + aiDefaultLogStream_DEBUGGER = 0x8, + + /** @cond never + * Force 32-bit size enum + */ + _AI_DLS_ENFORCE_ENUM_SIZE = 0x7fffffff + /// @endcond +}; // !enum aiDefaultLogStream + +// just for backwards compatibility, don't use these constants anymore +#define DLS_FILE aiDefaultLogStream_FILE +#define DLS_STDOUT aiDefaultLogStream_STDOUT +#define DLS_STDERR aiDefaultLogStream_STDERR +#define DLS_DEBUGGER aiDefaultLogStream_DEBUGGER + +// ---------------------------------------------------------------------------------- +/** Stores the memory requirements for different components (e.g. meshes, materials, + * animations) of an import. All sizes are in bytes. + * @see Importer::GetMemoryRequirements() +*/ +struct aiMemoryInfo +{ +#ifdef __cplusplus + + /** Default constructor */ + aiMemoryInfo() AI_NO_EXCEPT + : textures (0) + , materials (0) + , meshes (0) + , nodes (0) + , animations (0) + , cameras (0) + , lights (0) + , total (0) + {} + +#endif + + /** Storage allocated for texture data */ + unsigned int textures; + + /** Storage allocated for material data */ + unsigned int materials; + + /** Storage allocated for mesh data */ + unsigned int meshes; + + /** Storage allocated for node data */ + unsigned int nodes; + + /** Storage allocated for animation data */ + unsigned int animations; + + /** Storage allocated for camera data */ + unsigned int cameras; + + /** Storage allocated for light data */ + unsigned int lights; + + /** Total storage allocated for the full import. */ + unsigned int total; +}; // !struct aiMemoryInfo + +#ifdef __cplusplus +} +#endif //! __cplusplus + +// Include implementation files +#include "vector2.inl" +#include "vector3.inl" +#include "color4.inl" +#include "quaternion.inl" +#include "matrix3x3.inl" +#include "matrix4x4.inl" + +#endif // AI_TYPES_H_INC diff --git a/thirdparty/assimp/include/assimp/vector2.h b/thirdparty/assimp/include/assimp/vector2.h new file mode 100644 index 0000000000..d5ef001542 --- /dev/null +++ b/thirdparty/assimp/include/assimp/vector2.h @@ -0,0 +1,107 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file vector2.h + * @brief 2D vector structure, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_VECTOR2D_H_INC +#define AI_VECTOR2D_H_INC + +#ifdef __cplusplus +# include <cmath> +#else +# include <math.h> +#endif + +#include "defs.h" + +// ---------------------------------------------------------------------------------- +/** Represents a two-dimensional vector. + */ + +#ifdef __cplusplus +template <typename TReal> +class aiVector2t { +public: + aiVector2t () : x(), y() {} + aiVector2t (TReal _x, TReal _y) : x(_x), y(_y) {} + explicit aiVector2t (TReal _xyz) : x(_xyz), y(_xyz) {} + aiVector2t (const aiVector2t& o) = default; + + void Set( TReal pX, TReal pY); + TReal SquareLength() const ; + TReal Length() const ; + aiVector2t& Normalize(); + + const aiVector2t& operator += (const aiVector2t& o); + const aiVector2t& operator -= (const aiVector2t& o); + const aiVector2t& operator *= (TReal f); + const aiVector2t& operator /= (TReal f); + + TReal operator[](unsigned int i) const; + + bool operator== (const aiVector2t& other) const; + bool operator!= (const aiVector2t& other) const; + + bool Equal(const aiVector2t& other, TReal epsilon = 1e-6) const; + + aiVector2t& operator= (TReal f); + const aiVector2t SymMul(const aiVector2t& o); + + template <typename TOther> + operator aiVector2t<TOther> () const; + + TReal x, y; +}; + +typedef aiVector2t<ai_real> aiVector2D; + +#else + +struct aiVector2D { + ai_real x, y; +}; + +#endif // __cplusplus + +#endif // AI_VECTOR2D_H_INC diff --git a/thirdparty/assimp/include/assimp/vector2.inl b/thirdparty/assimp/include/assimp/vector2.inl new file mode 100644 index 0000000000..3b7a7beabb --- /dev/null +++ b/thirdparty/assimp/include/assimp/vector2.inl @@ -0,0 +1,244 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file vector2.inl + * @brief Inline implementation of aiVector2t<TReal> operators + */ +#pragma once +#ifndef AI_VECTOR2D_INL_INC +#define AI_VECTOR2D_INL_INC + +#ifdef __cplusplus +#include "vector2.h" + +#include <cmath> + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +template <typename TOther> +aiVector2t<TReal>::operator aiVector2t<TOther> () const { + return aiVector2t<TOther>(static_cast<TOther>(x),static_cast<TOther>(y)); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +void aiVector2t<TReal>::Set( TReal pX, TReal pY) { + x = pX; y = pY; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +TReal aiVector2t<TReal>::SquareLength() const { + return x*x + y*y; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +TReal aiVector2t<TReal>::Length() const { + return std::sqrt( SquareLength()); +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +aiVector2t<TReal>& aiVector2t<TReal>::Normalize() { + *this /= Length(); + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +const aiVector2t<TReal>& aiVector2t<TReal>::operator += (const aiVector2t& o) { + x += o.x; y += o.y; + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +const aiVector2t<TReal>& aiVector2t<TReal>::operator -= (const aiVector2t& o) { + x -= o.x; y -= o.y; + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +const aiVector2t<TReal>& aiVector2t<TReal>::operator *= (TReal f) { + x *= f; y *= f; + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +const aiVector2t<TReal>& aiVector2t<TReal>::operator /= (TReal f) { + x /= f; y /= f; + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +TReal aiVector2t<TReal>::operator[](unsigned int i) const { + switch (i) { + case 0: + return x; + case 1: + return y; + default: + break; + + } + return x; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +bool aiVector2t<TReal>::operator== (const aiVector2t& other) const { + return x == other.x && y == other.y; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +bool aiVector2t<TReal>::operator!= (const aiVector2t& other) const { + return x != other.x || y != other.y; +} + +// --------------------------------------------------------------------------- +template<typename TReal> +inline +bool aiVector2t<TReal>::Equal(const aiVector2t& other, TReal epsilon) const { + return + std::abs(x - other.x) <= epsilon && + std::abs(y - other.y) <= epsilon; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +aiVector2t<TReal>& aiVector2t<TReal>::operator= (TReal f) { + x = y = f; + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +inline +const aiVector2t<TReal> aiVector2t<TReal>::SymMul(const aiVector2t& o) { + return aiVector2t(x*o.x,y*o.y); +} + + +// ------------------------------------------------------------------------------------------------ +// symmetric addition +template <typename TReal> +inline +aiVector2t<TReal> operator + (const aiVector2t<TReal>& v1, const aiVector2t<TReal>& v2) { + return aiVector2t<TReal>( v1.x + v2.x, v1.y + v2.y); +} + +// ------------------------------------------------------------------------------------------------ +// symmetric subtraction +template <typename TReal> +inline +aiVector2t<TReal> operator - (const aiVector2t<TReal>& v1, const aiVector2t<TReal>& v2) { + return aiVector2t<TReal>( v1.x - v2.x, v1.y - v2.y); +} + +// ------------------------------------------------------------------------------------------------ +// scalar product +template <typename TReal> +inline +TReal operator * (const aiVector2t<TReal>& v1, const aiVector2t<TReal>& v2) { + return v1.x*v2.x + v1.y*v2.y; +} + +// ------------------------------------------------------------------------------------------------ +// scalar multiplication +template <typename TReal> +inline +aiVector2t<TReal> operator * ( TReal f, const aiVector2t<TReal>& v) { + return aiVector2t<TReal>( f*v.x, f*v.y); +} + +// ------------------------------------------------------------------------------------------------ +// and the other way around +template <typename TReal> +inline +aiVector2t<TReal> operator * ( const aiVector2t<TReal>& v, TReal f) { + return aiVector2t<TReal>( f*v.x, f*v.y); +} + +// ------------------------------------------------------------------------------------------------ +// scalar division +template <typename TReal> +inline +aiVector2t<TReal> operator / ( const aiVector2t<TReal>& v, TReal f) { + return v * (1/f); +} + +// ------------------------------------------------------------------------------------------------ +// vector division +template <typename TReal> +inline +aiVector2t<TReal> operator / ( const aiVector2t<TReal>& v, const aiVector2t<TReal>& v2) { + return aiVector2t<TReal>(v.x / v2.x,v.y / v2.y); +} + +// ------------------------------------------------------------------------------------------------ +// vector negation +template <typename TReal> +inline +aiVector2t<TReal> operator - ( const aiVector2t<TReal>& v) { + return aiVector2t<TReal>( -v.x, -v.y); +} + +#endif + +#endif // AI_VECTOR2D_INL_INC diff --git a/thirdparty/assimp/include/assimp/vector3.h b/thirdparty/assimp/include/assimp/vector3.h new file mode 100644 index 0000000000..7ff25cf0ad --- /dev/null +++ b/thirdparty/assimp/include/assimp/vector3.h @@ -0,0 +1,146 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file vector3.h + * @brief 3D vector structure, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_VECTOR3D_H_INC +#define AI_VECTOR3D_H_INC + +#ifdef __cplusplus +# include <cmath> +#else +# include <math.h> +#endif + +#include "defs.h" + +#ifdef __cplusplus + +template<typename TReal> class aiMatrix3x3t; +template<typename TReal> class aiMatrix4x4t; + +// --------------------------------------------------------------------------- +/** Represents a three-dimensional vector. */ +template <typename TReal> +class aiVector3t +{ +public: + aiVector3t() AI_NO_EXCEPT : x(), y(), z() {} + aiVector3t(TReal _x, TReal _y, TReal _z) : x(_x), y(_y), z(_z) {} + explicit aiVector3t (TReal _xyz ) : x(_xyz), y(_xyz), z(_xyz) {} + aiVector3t( const aiVector3t& o ) = default; + +public: + + // combined operators + const aiVector3t& operator += (const aiVector3t& o); + const aiVector3t& operator -= (const aiVector3t& o); + const aiVector3t& operator *= (TReal f); + const aiVector3t& operator /= (TReal f); + + // transform vector by matrix + aiVector3t& operator *= (const aiMatrix3x3t<TReal>& mat); + aiVector3t& operator *= (const aiMatrix4x4t<TReal>& mat); + + // access a single element + TReal operator[](unsigned int i) const; + TReal& operator[](unsigned int i); + + // comparison + bool operator== (const aiVector3t& other) const; + bool operator!= (const aiVector3t& other) const; + bool operator < (const aiVector3t& other) const; + + bool Equal(const aiVector3t& other, TReal epsilon = 1e-6) const; + + template <typename TOther> + operator aiVector3t<TOther> () const; + +public: + /** @brief Set the components of a vector + * @param pX X component + * @param pY Y component + * @param pZ Z component */ + void Set( TReal pX, TReal pY, TReal pZ); + + /** @brief Get the squared length of the vector + * @return Square length */ + TReal SquareLength() const; + + /** @brief Get the length of the vector + * @return length */ + TReal Length() const; + + + /** @brief Normalize the vector */ + aiVector3t& Normalize(); + + /** @brief Normalize the vector with extra check for zero vectors */ + aiVector3t& NormalizeSafe(); + + /** @brief Componentwise multiplication of two vectors + * + * Note that vec*vec yields the dot product. + * @param o Second factor */ + const aiVector3t SymMul(const aiVector3t& o); + + TReal x, y, z; +}; + + +typedef aiVector3t<ai_real> aiVector3D; + +#else + +struct aiVector3D { + ai_real x, y, z; +}; + +#endif // __cplusplus + +#ifdef __cplusplus + +#endif // __cplusplus + +#endif // AI_VECTOR3D_H_INC diff --git a/thirdparty/assimp/include/assimp/vector3.inl b/thirdparty/assimp/include/assimp/vector3.inl new file mode 100644 index 0000000000..2fce6eddee --- /dev/null +++ b/thirdparty/assimp/include/assimp/vector3.inl @@ -0,0 +1,309 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file vector3.inl + * @brief Inline implementation of aiVector3t<TReal> operators + */ +#pragma once +#ifndef AI_VECTOR3D_INL_INC +#define AI_VECTOR3D_INL_INC + +#ifdef __cplusplus +#include "vector3.h" + +#include <cmath> + +// ------------------------------------------------------------------------------------------------ +/** Transformation of a vector by a 3x3 matrix */ +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal> operator * (const aiMatrix3x3t<TReal>& pMatrix, const aiVector3t<TReal>& pVector) { + aiVector3t<TReal> res; + res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z; + res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z; + res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z; + return res; +} + +// ------------------------------------------------------------------------------------------------ +/** Transformation of a vector by a 4x4 matrix */ +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal> operator * (const aiMatrix4x4t<TReal>& pMatrix, const aiVector3t<TReal>& pVector) { + aiVector3t<TReal> res; + res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z + pMatrix.a4; + res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z + pMatrix.b4; + res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z + pMatrix.c4; + return res; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +template <typename TOther> +aiVector3t<TReal>::operator aiVector3t<TOther> () const { + return aiVector3t<TOther>(static_cast<TOther>(x),static_cast<TOther>(y),static_cast<TOther>(z)); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +void aiVector3t<TReal>::Set( TReal pX, TReal pY, TReal pZ) { + x = pX; + y = pY; + z = pZ; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +TReal aiVector3t<TReal>::SquareLength() const { + return x*x + y*y + z*z; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +TReal aiVector3t<TReal>::Length() const { + return std::sqrt( SquareLength()); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal>& aiVector3t<TReal>::Normalize() { + *this /= Length(); + + return *this; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal>& aiVector3t<TReal>::NormalizeSafe() { + TReal len = Length(); + if ( len > static_cast< TReal >( 0 ) ) { + *this /= len; + } + return *this; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +const aiVector3t<TReal>& aiVector3t<TReal>::operator += (const aiVector3t<TReal>& o) { + x += o.x; + y += o.y; + z += o.z; + + return *this; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +const aiVector3t<TReal>& aiVector3t<TReal>::operator -= (const aiVector3t<TReal>& o) { + x -= o.x; + y -= o.y; + z -= o.z; + + return *this; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +const aiVector3t<TReal>& aiVector3t<TReal>::operator *= (TReal f) { + x *= f; + y *= f; + z *= f; + + return *this; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +const aiVector3t<TReal>& aiVector3t<TReal>::operator /= (TReal f) { + const TReal invF = (TReal) 1.0 / f; + x *= invF; + y *= invF; + z *= invF; + + return *this; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal>& aiVector3t<TReal>::operator *= (const aiMatrix3x3t<TReal>& mat){ + return (*this = mat * (*this)); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal>& aiVector3t<TReal>::operator *= (const aiMatrix4x4t<TReal>& mat){ + return (*this = mat * (*this)); +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +TReal aiVector3t<TReal>::operator[](unsigned int i) const { + switch (i) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + break; + } + return x; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +TReal& aiVector3t<TReal>::operator[](unsigned int i) { +// return *(&x + i); + switch (i) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + break; + } + return x; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +bool aiVector3t<TReal>::operator== (const aiVector3t<TReal>& other) const { + return x == other.x && y == other.y && z == other.z; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +bool aiVector3t<TReal>::operator!= (const aiVector3t<TReal>& other) const { + return x != other.x || y != other.y || z != other.z; +} +// --------------------------------------------------------------------------- +template<typename TReal> +AI_FORCE_INLINE +bool aiVector3t<TReal>::Equal(const aiVector3t<TReal>& other, TReal epsilon) const { + return + std::abs(x - other.x) <= epsilon && + std::abs(y - other.y) <= epsilon && + std::abs(z - other.z) <= epsilon; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +bool aiVector3t<TReal>::operator < (const aiVector3t<TReal>& other) const { + return x != other.x ? x < other.x : y != other.y ? y < other.y : z < other.z; +} +// ------------------------------------------------------------------------------------------------ +template <typename TReal> +AI_FORCE_INLINE +const aiVector3t<TReal> aiVector3t<TReal>::SymMul(const aiVector3t<TReal>& o) { + return aiVector3t<TReal>(x*o.x,y*o.y,z*o.z); +} +// ------------------------------------------------------------------------------------------------ +// symmetric addition +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal> operator + (const aiVector3t<TReal>& v1, const aiVector3t<TReal>& v2) { + return aiVector3t<TReal>( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); +} +// ------------------------------------------------------------------------------------------------ +// symmetric subtraction +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal> operator - (const aiVector3t<TReal>& v1, const aiVector3t<TReal>& v2) { + return aiVector3t<TReal>( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); +} +// ------------------------------------------------------------------------------------------------ +// scalar product +template <typename TReal> +AI_FORCE_INLINE +TReal operator * (const aiVector3t<TReal>& v1, const aiVector3t<TReal>& v2) { + return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; +} +// ------------------------------------------------------------------------------------------------ +// scalar multiplication +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal> operator * ( TReal f, const aiVector3t<TReal>& v) { + return aiVector3t<TReal>( f*v.x, f*v.y, f*v.z); +} +// ------------------------------------------------------------------------------------------------ +// and the other way around +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal> operator * ( const aiVector3t<TReal>& v, TReal f) { + return aiVector3t<TReal>( f*v.x, f*v.y, f*v.z); +} +// ------------------------------------------------------------------------------------------------ +// scalar division +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal> operator / ( const aiVector3t<TReal>& v, TReal f) { + return v * (1/f); +} +// ------------------------------------------------------------------------------------------------ +// vector division +template <typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal> operator / ( const aiVector3t<TReal>& v, const aiVector3t<TReal>& v2) { + return aiVector3t<TReal>(v.x / v2.x,v.y / v2.y,v.z / v2.z); +} +// ------------------------------------------------------------------------------------------------ +// cross product +template<typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal> operator ^ ( const aiVector3t<TReal>& v1, const aiVector3t<TReal>& v2) { + return aiVector3t<TReal>( v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); +} +// ------------------------------------------------------------------------------------------------ +// vector negation +template<typename TReal> +AI_FORCE_INLINE +aiVector3t<TReal> operator - ( const aiVector3t<TReal>& v) { + return aiVector3t<TReal>( -v.x, -v.y, -v.z); +} + +// ------------------------------------------------------------------------------------------------ + +#endif // __cplusplus +#endif // AI_VECTOR3D_INL_INC diff --git a/thirdparty/assimp/include/assimp/version.h b/thirdparty/assimp/include/assimp/version.h new file mode 100644 index 0000000000..c62a40e118 --- /dev/null +++ b/thirdparty/assimp/include/assimp/version.h @@ -0,0 +1,115 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file version.h + * @brief Functions to query the version of the Assimp runtime, check + * compile flags, ... + */ +#pragma once +#ifndef AI_VERSION_H_INC +#define AI_VERSION_H_INC + +#include "defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** @brief Returns a string with legal copyright and licensing information + * about Assimp. The string may include multiple lines. + * @return Pointer to static string. + */ +ASSIMP_API const char* aiGetLegalString (void); + +// --------------------------------------------------------------------------- +/** @brief Returns the current minor version number of Assimp. + * @return Minor version of the Assimp runtime the application was + * linked/built against + */ +ASSIMP_API unsigned int aiGetVersionMinor (void); + +// --------------------------------------------------------------------------- +/** @brief Returns the current major version number of Assimp. + * @return Major version of the Assimp runtime the application was + * linked/built against + */ +ASSIMP_API unsigned int aiGetVersionMajor (void); + +// --------------------------------------------------------------------------- +/** @brief Returns the repository revision of the Assimp runtime. + * @return SVN Repository revision number of the Assimp runtime the + * application was linked/built against. + */ +ASSIMP_API unsigned int aiGetVersionRevision (void); + +// --------------------------------------------------------------------------- +/** @brief Returns the branchname of the Assimp runtime. + * @return The current branch name. + */ +ASSIMP_API const char *aiGetBranchName(); + +//! Assimp was compiled as a shared object (Windows: DLL) +#define ASSIMP_CFLAGS_SHARED 0x1 +//! Assimp was compiled against STLport +#define ASSIMP_CFLAGS_STLPORT 0x2 +//! Assimp was compiled as a debug build +#define ASSIMP_CFLAGS_DEBUG 0x4 + +//! Assimp was compiled with ASSIMP_BUILD_BOOST_WORKAROUND defined +#define ASSIMP_CFLAGS_NOBOOST 0x8 +//! Assimp was compiled with ASSIMP_BUILD_SINGLETHREADED defined +#define ASSIMP_CFLAGS_SINGLETHREADED 0x10 + +// --------------------------------------------------------------------------- +/** @brief Returns assimp's compile flags + * @return Any bitwise combination of the ASSIMP_CFLAGS_xxx constants. + */ +ASSIMP_API unsigned int aiGetCompileFlags (void); + +#ifdef __cplusplus +} // end extern "C" +#endif + +#endif // !! #ifndef AI_VERSION_H_INC + diff --git a/thirdparty/thekla_atlas/godot-changes.patch b/thirdparty/thekla_atlas/godot-changes.patch deleted file mode 100644 index 0e56403336..0000000000 --- a/thirdparty/thekla_atlas/godot-changes.patch +++ /dev/null @@ -1,154 +0,0 @@ -diff --git a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp b/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp -index 5ce452c..11e635d 100644 ---- a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp -+++ b/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp -@@ -142,9 +142,11 @@ AtlasPacker::AtlasPacker(Atlas * atlas) : m_atlas(atlas), m_bitmap(256, 256) - { - m_width = 0; - m_height = 0; -- -- m_debug_bitmap.allocate(256, 256); -- m_debug_bitmap.fill(Color32(0,0,0,0)); -+ -+ // -- GODOT start -- -+ //m_debug_bitmap.allocate(256, 256); -+ //m_debug_bitmap.fill(Color32(0,0,0,0)); -+ // -- GODOT end -- - } - - AtlasPacker::~AtlasPacker() -@@ -465,7 +467,11 @@ void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned - nvDebug("origin: %f %f\n", origin.x, origin.y); - nvDebug("majorAxis: %f %f\n", majorAxis.x, majorAxis.y); - nvDebug("minorAxis: %f %f\n", minorAxis.x, minorAxis.y); -- nvDebugBreak(); -+ // -- GODOT start -- -+ //nvDebugBreak(); -+ m_atlas->setFailed(); -+ return; -+ // -- GODOT end -- - } - //nvCheck(tmp.x >= 0 && tmp.y >= 0); - -@@ -597,8 +603,10 @@ void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned - m_bitmap.clearAll(); - if (approximateExtent > m_bitmap.width()) { - m_bitmap.resize(approximateExtent, approximateExtent, false); -- m_debug_bitmap.resize(approximateExtent, approximateExtent); -- m_debug_bitmap.fill(Color32(0,0,0,0)); -+ // -- GODOT start -- -+ //m_debug_bitmap.resize(approximateExtent, approximateExtent); -+ //m_debug_bitmap.fill(Color32(0,0,0,0)); -+ // -- GODOT end -- - } - - -@@ -680,20 +688,24 @@ void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned - { - //nvDebug("Resize bitmap (%d, %d).\n", nextPowerOfTwo(w), nextPowerOfTwo(h)); - m_bitmap.resize(nextPowerOfTwo(U32(w)), nextPowerOfTwo(U32(h)), false); -- m_debug_bitmap.resize(nextPowerOfTwo(U32(w)), nextPowerOfTwo(U32(h))); -+ // -- GODOT start -- -+ //m_debug_bitmap.resize(nextPowerOfTwo(U32(w)), nextPowerOfTwo(U32(h))); -+ // -- GODOT end -- - } - - //nvDebug("Add chart at (%d, %d).\n", best_x, best_y); - - addChart(&chart_bitmap, w, h, best_x, best_y, best_r, /*debugOutput=*/NULL); - -+ // -- GODOT start -- - // IC: Output chart again to debug bitmap. -- if (chart->isVertexMapped()) { -+ /*if (chart->isVertexMapped()) { - addChart(&chart_bitmap, w, h, best_x, best_y, best_r, &m_debug_bitmap); - } - else { - addChart(chart, w, h, best_x, best_y, best_r, &m_debug_bitmap); -- } -+ }*/ -+ // -- GODOT end -- - - //float best_angle = 2 * PI * best_r; - -@@ -842,8 +854,10 @@ void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned - nvCheck(isAligned(m_width, 4)); - nvCheck(isAligned(m_height, 4)); - -- m_debug_bitmap.resize(m_width, m_height); -- m_debug_bitmap.setFormat(Image::Format_ARGB); -+ // -- GODOT start -- -+ //m_debug_bitmap.resize(m_width, m_height); -+ //m_debug_bitmap.setFormat(Image::Format_ARGB); -+ // -- GODOT end -- - - #if DEBUG_OUTPUT - //outputDebugBitmap("debug_packer_final.tga", m_bitmap, w, h); -diff --git a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.h b/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.h -index 2d305f3..845dbfb 100644 ---- a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.h -+++ b/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.h -@@ -48,7 +48,9 @@ namespace nv - - Atlas * m_atlas; - BitMap m_bitmap; -- Image m_debug_bitmap; -+ // -- GODOT start -- -+ //Image m_debug_bitmap; -+ // -- GODOT end -- - RadixSort m_radix; - - uint m_width; -diff --git a/thirdparty/thekla_atlas/thekla/thekla_atlas.cpp b/thirdparty/thekla_atlas/thekla/thekla_atlas.cpp -index d6f0acc..de1953d 100644 ---- a/thirdparty/thekla_atlas/thekla/thekla_atlas.cpp -+++ b/thirdparty/thekla_atlas/thekla/thekla_atlas.cpp -@@ -2,6 +2,9 @@ - #include "thekla_atlas.h" - - #include <cfloat> -+// -- GODOT start -- -+#include <stdio.h> -+// -- GODOT end -- - - #include "nvmesh/halfedge/Edge.h" - #include "nvmesh/halfedge/Mesh.h" -@@ -112,6 +115,8 @@ static Atlas_Output_Mesh * mesh_atlas_to_output(const HalfEdge::Mesh * mesh, con - output->index_count = face_count * 3; - output->index_array = new int[face_count * 3]; - -+ // -- GODOT start -- -+ int face_ofs = 0; - // Set face indices. - for (int f = 0; f < face_count; f++) { - uint c = charts->faceChartAt(f); -@@ -121,14 +126,26 @@ static Atlas_Output_Mesh * mesh_atlas_to_output(const HalfEdge::Mesh * mesh, con - const Chart * chart = charts->chartAt(c); - nvDebugCheck(chart->faceAt(i) == f); - -+ if (i >= chart->chartMesh()->faceCount()) { -+ printf("WARNING: Faces may be missing in the final vertex, which could not be packed\n"); -+ continue; -+ } -+ - const HalfEdge::Face * face = chart->chartMesh()->faceAt(i); - const HalfEdge::Edge * edge = face->edge; - -- output->index_array[3*f+0] = vertexOffset + edge->vertex->id; -- output->index_array[3*f+1] = vertexOffset + edge->next->vertex->id; -- output->index_array[3*f+2] = vertexOffset + edge->next->next->vertex->id; -+ //output->index_array[3*f+0] = vertexOffset + edge->vertex->id; -+ //output->index_array[3*f+1] = vertexOffset + edge->next->vertex->id; -+ //output->index_array[3*f+2] = vertexOffset + edge->next->next->vertex->id; -+ output->index_array[3 * face_ofs + 0] = vertexOffset + edge->vertex->id; -+ output->index_array[3 * face_ofs + 1] = vertexOffset + edge->next->vertex->id; -+ output->index_array[3 * face_ofs + 2] = vertexOffset + edge->next->next->vertex->id; -+ face_ofs++; - } - -+ output->index_count = face_ofs * 3; -+ // -- GODOT end -- -+ - *error = Atlas_Error_Success; - output->atlas_width = w; - output->atlas_height = h; diff --git a/thirdparty/thekla_atlas/nvconfig.h b/thirdparty/thekla_atlas/nvconfig.h deleted file mode 100644 index 815bc3ec75..0000000000 --- a/thirdparty/thekla_atlas/nvconfig.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef NV_CONFIG -#define NV_CONFIG - -#if NV_OS_DARWIN - -// Hardcoded. - -#define NV_HAVE_UNISTD_H -#define NV_HAVE_STDARG_H -#define NV_HAVE_SIGNAL_H -#define NV_HAVE_EXECINFO_H -//#define NV_HAVE_MALLOC_H - -#else - -//#define HAVE_UNISTD_H -#define NV_HAVE_STDARG_H -//#define HAVE_SIGNAL_H -//#define HAVE_EXECINFO_H -//#define HAVE_MALLOC_H - -#endif - -//#define HAVE_OPENMP // Only in MSVC pro edition. - -//#cmakedefine HAVE_PNG -//#cmakedefine HAVE_JPEG -//#cmakedefine HAVE_TIFF -//#cmakedefine HAVE_OPENEXR -//#cmakedefine HAVE_FREEIMAGE -#if !NV_OS_IOS -#define NV_HAVE_STBIMAGE -#endif - -//#cmakedefine HAVE_MAYA - -#endif // NV_CONFIG diff --git a/thirdparty/thekla_atlas/nvcore/Array.h b/thirdparty/thekla_atlas/nvcore/Array.h deleted file mode 100644 index b295cb2b0c..0000000000 --- a/thirdparty/thekla_atlas/nvcore/Array.h +++ /dev/null @@ -1,182 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_ARRAY_H -#define NV_CORE_ARRAY_H - -/* -This array class requires the elements to be relocable; it uses memmove and realloc. Ideally I should be -using swap, but I honestly don't care. The only thing that you should be aware of is that internal pointers -are not supported. - -Note also that push_back and resize does not support inserting arguments elements that are in the same -container. This is forbidden to prevent an extra copy. -*/ - - -#include "Memory.h" -#include "Debug.h" -#include "ForEach.h" // PseudoIndex - - -namespace nv -{ - class Stream; - - /** - * Replacement for std::vector that is easier to debug and provides - * some nice foreach enumerators. - */ - template<typename T> - class NVCORE_CLASS Array { - public: - typedef uint size_type; - - // Default constructor. - NV_FORCEINLINE Array() : m_buffer(NULL), m_capacity(0), m_size(0) {} - - // Copy constructor. - NV_FORCEINLINE Array(const Array & a) : m_buffer(NULL), m_capacity(0), m_size(0) { - copy(a.m_buffer, a.m_size); - } - - // Constructor that initializes the vector with the given elements. - NV_FORCEINLINE Array(const T * ptr, uint num) : m_buffer(NULL), m_capacity(0), m_size(0) { - copy(ptr, num); - } - - // Allocate array. - NV_FORCEINLINE explicit Array(uint capacity) : m_buffer(NULL), m_capacity(0), m_size(0) { - setArrayCapacity(capacity); - } - - // Destructor. - NV_FORCEINLINE ~Array() { - clear(); - free<T>(m_buffer); - } - - - /// Const element access. - NV_FORCEINLINE const T & operator[]( uint index ) const - { - nvDebugCheck(index < m_size); - return m_buffer[index]; - } - NV_FORCEINLINE const T & at( uint index ) const - { - nvDebugCheck(index < m_size); - return m_buffer[index]; - } - - /// Element access. - NV_FORCEINLINE T & operator[] ( uint index ) - { - nvDebugCheck(index < m_size); - return m_buffer[index]; - } - NV_FORCEINLINE T & at( uint index ) - { - nvDebugCheck(index < m_size); - return m_buffer[index]; - } - - /// Get vector size. - NV_FORCEINLINE uint size() const { return m_size; } - - /// Get vector size. - NV_FORCEINLINE uint count() const { return m_size; } - - /// Get vector capacity. - NV_FORCEINLINE uint capacity() const { return m_capacity; } - - /// Get const vector pointer. - NV_FORCEINLINE const T * buffer() const { return m_buffer; } - - /// Get vector pointer. - NV_FORCEINLINE T * buffer() { return m_buffer; } - - /// Provide begin/end pointers for C++11 range-based for loops. - NV_FORCEINLINE T * begin() { return m_buffer; } - NV_FORCEINLINE T * end() { return m_buffer + m_size; } - NV_FORCEINLINE const T * begin() const { return m_buffer; } - NV_FORCEINLINE const T * end() const { return m_buffer + m_size; } - - /// Is vector empty. - NV_FORCEINLINE bool isEmpty() const { return m_size == 0; } - - /// Is a null vector. - NV_FORCEINLINE bool isNull() const { return m_buffer == NULL; } - - - T & append(); - void push_back( const T & val ); - void pushBack( const T & val ); - Array<T> & append( const T & val ); - Array<T> & operator<< ( T & t ); - void pop_back(); - void popBack(uint count = 1); - void popFront(uint count = 1); - const T & back() const; - T & back(); - const T & front() const; - T & front(); - bool contains(const T & e) const; - bool find(const T & element, uint * indexPtr) const; - bool find(const T & element, uint begin, uint end, uint * indexPtr) const; - void removeAt(uint index); - bool remove(const T & element); - void insertAt(uint index, const T & val = T()); - void append(const Array<T> & other); - void append(const T other[], uint count); - void replaceWithLast(uint index); - void resize(uint new_size); - void resize(uint new_size, const T & elem); - void fill(const T & elem); - void clear(); - void shrink(); - void reserve(uint desired_size); - void copy(const T * data, uint count); - Array<T> & operator=( const Array<T> & a ); - T * release(); - - - // Array enumerator. - typedef uint PseudoIndex; - - NV_FORCEINLINE PseudoIndex start() const { return 0; } - NV_FORCEINLINE bool isDone(const PseudoIndex & i) const { nvDebugCheck(i <= this->m_size); return i == this->m_size; } - NV_FORCEINLINE void advance(PseudoIndex & i) const { nvDebugCheck(i <= this->m_size); i++; } - -#if NV_NEED_PSEUDOINDEX_WRAPPER - NV_FORCEINLINE T & operator[]( const PseudoIndexWrapper & i ) { - return m_buffer[i(this)]; - } - NV_FORCEINLINE const T & operator[]( const PseudoIndexWrapper & i ) const { - return m_buffer[i(this)]; - } -#endif - - // Friends. - template <typename Typ> - friend Stream & operator<< ( Stream & s, Array<Typ> & p ); - - template <typename Typ> - friend void swap(Array<Typ> & a, Array<Typ> & b); - - - protected: - - void setArraySize(uint new_size); - void setArrayCapacity(uint new_capacity); - - T * m_buffer; - uint m_capacity; - uint m_size; - - }; - - -} // nv namespace - -#endif // NV_CORE_ARRAY_H diff --git a/thirdparty/thekla_atlas/nvcore/Array.inl b/thirdparty/thekla_atlas/nvcore/Array.inl deleted file mode 100644 index 0b4de28ba9..0000000000 --- a/thirdparty/thekla_atlas/nvcore/Array.inl +++ /dev/null @@ -1,452 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_ARRAY_INL -#define NV_CORE_ARRAY_INL - -#include "Array.h" - -#include "Stream.h" -#include "Utils.h" // swap - -#include <string.h> // memmove -#include <new> // for placement new - - - -namespace nv -{ - template <typename T> - NV_FORCEINLINE T & Array<T>::append() - { - uint old_size = m_size; - uint new_size = m_size + 1; - - setArraySize(new_size); - - construct_range(m_buffer, new_size, old_size); - - return m_buffer[old_size]; // Return reference to last element. - } - - // Push an element at the end of the vector. - template <typename T> - NV_FORCEINLINE void Array<T>::push_back( const T & val ) - { -#if 1 - nvDebugCheck(&val < m_buffer || &val >= m_buffer+m_size); - - uint old_size = m_size; - uint new_size = m_size + 1; - - setArraySize(new_size); - - construct_range(m_buffer, new_size, old_size, val); -#else - uint new_size = m_size + 1; - - if (new_size > m_capacity) - { - // @@ Is there any way to avoid this copy? - // @@ Can we create a copy without side effects? Ie. without calls to constructor/destructor. Use alloca + memcpy? - // @@ Assert instead of copy? - const T copy(val); // create a copy in case value is inside of this array. - - setArraySize(new_size); - - new (m_buffer+new_size-1) T(copy); - } - else - { - m_size = new_size; - new(m_buffer+new_size-1) T(val); - } -#endif // 0/1 - } - template <typename T> - NV_FORCEINLINE void Array<T>::pushBack( const T & val ) - { - push_back(val); - } - template <typename T> - NV_FORCEINLINE Array<T> & Array<T>::append( const T & val ) - { - push_back(val); - return *this; - } - - // Qt like push operator. - template <typename T> - NV_FORCEINLINE Array<T> & Array<T>::operator<< ( T & t ) - { - push_back(t); - return *this; - } - - // Pop the element at the end of the vector. - template <typename T> - NV_FORCEINLINE void Array<T>::pop_back() - { - nvDebugCheck( m_size > 0 ); - resize( m_size - 1 ); - } - template <typename T> - NV_FORCEINLINE void Array<T>::popBack(uint count) - { - nvDebugCheck(m_size >= count); - resize(m_size - count); - } - - template <typename T> - NV_FORCEINLINE void Array<T>::popFront(uint count) - { - nvDebugCheck(m_size >= count); - //resize(m_size - count); - - if (m_size == count) { - clear(); - } - else { - destroy_range(m_buffer, 0, count); - - memmove(m_buffer, m_buffer + count, sizeof(T) * (m_size - count)); - - m_size -= count; - } - - } - - - // Get back element. - template <typename T> - NV_FORCEINLINE const T & Array<T>::back() const - { - nvDebugCheck( m_size > 0 ); - return m_buffer[m_size-1]; - } - - // Get back element. - template <typename T> - NV_FORCEINLINE T & Array<T>::back() - { - nvDebugCheck( m_size > 0 ); - return m_buffer[m_size-1]; - } - - // Get front element. - template <typename T> - NV_FORCEINLINE const T & Array<T>::front() const - { - nvDebugCheck( m_size > 0 ); - return m_buffer[0]; - } - - // Get front element. - template <typename T> - NV_FORCEINLINE T & Array<T>::front() - { - nvDebugCheck( m_size > 0 ); - return m_buffer[0]; - } - - // Check if the given element is contained in the array. - template <typename T> - NV_FORCEINLINE bool Array<T>::contains(const T & e) const - { - return find(e, NULL); - } - - // Return true if element found. - template <typename T> - NV_FORCEINLINE bool Array<T>::find(const T & element, uint * indexPtr) const - { - return find(element, 0, m_size, indexPtr); - } - - // Return true if element found within the given range. - template <typename T> - NV_FORCEINLINE bool Array<T>::find(const T & element, uint begin, uint end, uint * indexPtr) const - { - return ::nv::find(element, m_buffer, begin, end, indexPtr); - } - - - // Remove the element at the given index. This is an expensive operation! - template <typename T> - void Array<T>::removeAt(uint index) - { - nvDebugCheck(index >= 0 && index < m_size); - - if (m_size == 1) { - clear(); - } - else { - m_buffer[index].~T(); - - memmove(m_buffer+index, m_buffer+index+1, sizeof(T) * (m_size - 1 - index)); - m_size--; - } - } - - // Remove the first instance of the given element. - template <typename T> - bool Array<T>::remove(const T & element) - { - uint index; - if (find(element, &index)) { - removeAt(index); - return true; - } - return false; - } - - // Insert the given element at the given index shifting all the elements up. - template <typename T> - void Array<T>::insertAt(uint index, const T & val/*=T()*/) - { - nvDebugCheck( index >= 0 && index <= m_size ); - - setArraySize(m_size + 1); - - if (index < m_size - 1) { - memmove(m_buffer+index+1, m_buffer+index, sizeof(T) * (m_size - 1 - index)); - } - - // Copy-construct into the newly opened slot. - new(m_buffer+index) T(val); - } - - // Append the given data to our vector. - template <typename T> - NV_FORCEINLINE void Array<T>::append(const Array<T> & other) - { - append(other.m_buffer, other.m_size); - } - - // Append the given data to our vector. - template <typename T> - void Array<T>::append(const T other[], uint count) - { - if (count > 0) { - const uint old_size = m_size; - - setArraySize(m_size + count); - - for (uint i = 0; i < count; i++ ) { - new(m_buffer + old_size + i) T(other[i]); - } - } - } - - - // Remove the given element by replacing it with the last one. - template <typename T> - void Array<T>::replaceWithLast(uint index) - { - nvDebugCheck( index < m_size ); - nv::swap(m_buffer[index], back()); // @@ Is this OK when index == size-1? - (m_buffer+m_size-1)->~T(); - m_size--; - } - - // Resize the vector preserving existing elements. - template <typename T> - void Array<T>::resize(uint new_size) - { - uint old_size = m_size; - - // Destruct old elements (if we're shrinking). - destroy_range(m_buffer, new_size, old_size); - - setArraySize(new_size); - - // Call default constructors - construct_range(m_buffer, new_size, old_size); - } - - - // Resize the vector preserving existing elements and initializing the - // new ones with the given value. - template <typename T> - void Array<T>::resize(uint new_size, const T & elem) - { - nvDebugCheck(&elem < m_buffer || &elem > m_buffer+m_size); - - uint old_size = m_size; - - // Destruct old elements (if we're shrinking). - destroy_range(m_buffer, new_size, old_size); - - setArraySize(new_size); - - // Call copy constructors - construct_range(m_buffer, new_size, old_size, elem); - } - - // Fill array with the given value. - template <typename T> - void Array<T>::fill(const T & elem) - { - fill(m_buffer, m_size, elem); - } - - // Clear the buffer. - template <typename T> - NV_FORCEINLINE void Array<T>::clear() - { - nvDebugCheck(isValidPtr(m_buffer)); - - // Destruct old elements - destroy_range(m_buffer, 0, m_size); - - m_size = 0; - } - - // Shrink the allocated vector. - template <typename T> - NV_FORCEINLINE void Array<T>::shrink() - { - if (m_size < m_capacity) { - setArrayCapacity(m_size); - } - } - - // Preallocate space. - template <typename T> - NV_FORCEINLINE void Array<T>::reserve(uint desired_size) - { - if (desired_size > m_capacity) { - setArrayCapacity(desired_size); - } - } - - // Copy elements to this array. Resizes it if needed. - template <typename T> - NV_FORCEINLINE void Array<T>::copy(const T * data, uint count) - { -#if 1 // More simple, but maybe not be as efficient? - destroy_range(m_buffer, 0, m_size); - - setArraySize(count); - - construct_range(m_buffer, count, 0, data); -#else - const uint old_size = m_size; - - destroy_range(m_buffer, count, old_size); - - setArraySize(count); - - copy_range(m_buffer, data, old_size); - - construct_range(m_buffer, count, old_size, data); -#endif - } - - // Assignment operator. - template <typename T> - NV_FORCEINLINE Array<T> & Array<T>::operator=( const Array<T> & a ) - { - copy(a.m_buffer, a.m_size); - return *this; - } - - // Release ownership of allocated memory and returns pointer to it. - template <typename T> - T * Array<T>::release() { - T * tmp = m_buffer; - m_buffer = NULL; - m_capacity = 0; - m_size = 0; - return tmp; - } - - - - // Change array size. - template <typename T> - inline void Array<T>::setArraySize(uint new_size) { - m_size = new_size; - - if (new_size > m_capacity) { - uint new_buffer_size; - if (m_capacity == 0) { - // first allocation is exact - new_buffer_size = new_size; - } - else { - // following allocations grow array by 25% - new_buffer_size = new_size + (new_size >> 2); - } - - setArrayCapacity( new_buffer_size ); - } - } - - // Change array capacity. - template <typename T> - inline void Array<T>::setArrayCapacity(uint new_capacity) { - nvDebugCheck(new_capacity >= m_size); - - if (new_capacity == 0) { - // free the buffer. - if (m_buffer != NULL) { - free<T>(m_buffer); - m_buffer = NULL; - } - } - else { - // realloc the buffer - m_buffer = realloc<T>(m_buffer, new_capacity); - } - - m_capacity = new_capacity; - } - - // Array serialization. - template <typename Typ> - inline Stream & operator<< ( Stream & s, Array<Typ> & p ) - { - if (s.isLoading()) { - uint size; - s << size; - p.resize( size ); - } - else { - s << p.m_size; - } - - for (uint i = 0; i < p.m_size; i++) { - s << p.m_buffer[i]; - } - - return s; - } - - // Swap the members of the two given vectors. - template <typename Typ> - inline void swap(Array<Typ> & a, Array<Typ> & b) - { - nv::swap(a.m_buffer, b.m_buffer); - nv::swap(a.m_capacity, b.m_capacity); - nv::swap(a.m_size, b.m_size); - } - - -} // nv namespace - -// IC: These functions are for compatibility with the Foreach macro in The Witness. -template <typename T> inline int item_count(const nv::Array<T> & array) { return array.count(); } -template <typename T> inline const T & item_at(const nv::Array<T> & array, int i) { return array.at(i); } -template <typename T> inline T & item_at(nv::Array<T> & array, int i) { return array.at(i); } -template <typename T> inline int item_advance(const nv::Array<T> & array, int i) { return ++i; } -template <typename T> inline int item_remove(nv::Array<T> & array, int i) { array.replaceWithLast(i); return i - 1; } - -template <typename T> inline int item_count(const nv::Array<T> * array) { return array->count(); } -template <typename T> inline const T & item_at(const nv::Array<T> * array, int i) { return array->at(i); } -template <typename T> inline T & item_at(nv::Array<T> * array, int i) { return array->at(i); } -template <typename T> inline int item_advance(const nv::Array<T> * array, int i) { return ++i; } -template <typename T> inline int item_remove(nv::Array<T> * array, int i) { array->replaceWithLast(i); return i - 1; } - - -#endif // NV_CORE_ARRAY_INL diff --git a/thirdparty/thekla_atlas/nvcore/BitArray.h b/thirdparty/thekla_atlas/nvcore/BitArray.h deleted file mode 100644 index 23cf880694..0000000000 --- a/thirdparty/thekla_atlas/nvcore/BitArray.h +++ /dev/null @@ -1,250 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_BITARRAY_H -#define NV_CORE_BITARRAY_H - -#include "nvcore.h" -#include "Array.inl" - -namespace nv -{ - - // @@ Uh, this could be much faster. - inline uint countSetBits(uint32 x) { - uint count = 0; - for(; x != 0; x >>= 1) { - count += (x & 1); - } - return count; - } - - // @@ This is even more lame. What was I thinking? - inline uint countSetBits(uint32 x, int bits) { - uint count = 0; - for(; x != 0 && bits != 0; x >>= 1, bits--) { - count += (x & 1); - } - return count; - } - - // See "Conditionally set or clear bits without branching" at http://graphics.stanford.edu/~seander/bithacks.html - inline uint setBits(uint w, uint m, bool b) { - return (w & ~m) | (-int(b) & m); - } - - - - // Simple bit array. - class BitArray - { - public: - - BitArray() {} - BitArray(uint sz) { - resize(sz); - } - - uint size() const { return m_size; } - void clear() { resize(0); } - - void resize(uint new_size) - { - m_size = new_size; - m_wordArray.resize( (m_size + 31) >> 5 ); - } - - void resize(uint new_size, bool init) - { - //if (new_size == m_size) return; - - uint old_size = m_size; - uint size_mod_32 = old_size & 31; - uint last_word_index = ((old_size + 31) >> 5) - 1; - uint mask = (1 << size_mod_32) - 1; - - uint init_dword; - if (init) { - if (size_mod_32) m_wordArray[last_word_index] |= ~mask; - init_dword = ~0; - } - else { - if (size_mod_32) m_wordArray[last_word_index] &= mask; - init_dword = 0; - } - - m_size = new_size; - m_wordArray.resize((new_size + 31) >> 5, init_dword); - - // Make sure new bits are initialized correctly. - /*for (uint i = old_size; i < new_size; i++) { - nvCheck(bitAt(i) == init); - }*/ - } - - - /// Get bit. - bool bitAt(uint b) const - { - nvDebugCheck( b < m_size ); - return (m_wordArray[b >> 5] & (1 << (b & 31))) != 0; - } - - // It may be useful to pack mulitple bit arrays together interleaving their bits. - uint bitsAt(uint idx, uint count) const - { - //nvDebugCheck(count == 2 || count == 4 || count == 8 || count == 16 || count == 32); - nvDebugCheck(count == 2); // @@ Hardcoded for two. - uint b = idx * count; - nvDebugCheck(b < m_size); - return (m_wordArray[b >> 5] & (0x3 << (b & 31))) >> (b & 31); - } - - // It would be useful to have a function to set two bits simultaneously. - /*void setBitsAt(uint idx, uint count, uint bits) const - { - //nvDebugCheck(count == 2 || count == 4 || count == 8 || count == 16 || count == 32); - nvDebugCheck(count == 2); // @@ Hardcoded for two. - uint b = idx * count; - nvDebugCheck(b < m_size); - return (m_wordArray[b >> 5] & (0x3 << (b & 31))) >> (b & 31); - }*/ - - - - // Set a bit. - void setBitAt(uint idx) - { - nvDebugCheck(idx < m_size); - m_wordArray[idx >> 5] |= (1 << (idx & 31)); - } - - // Clear a bit. - void clearBitAt(uint idx) - { - nvDebugCheck(idx < m_size); - m_wordArray[idx >> 5] &= ~(1 << (idx & 31)); - } - - // Toggle a bit. - void toggleBitAt(uint idx) - { - nvDebugCheck(idx < m_size); - m_wordArray[idx >> 5] ^= (1 << (idx & 31)); - } - - // Set a bit to the given value. @@ Rename modifyBitAt? - void setBitAt(uint idx, bool b) - { - nvDebugCheck(idx < m_size); - m_wordArray[idx >> 5] = setBits(m_wordArray[idx >> 5], 1 << (idx & 31), b); - nvDebugCheck(bitAt(idx) == b); - } - - void append(bool value) - { - resize(m_size + 1); - setBitAt(m_size - 1, value); - } - - - // Clear all the bits. - void clearAll() - { - memset(m_wordArray.buffer(), 0, m_wordArray.size() * sizeof(uint)); - } - - // Set all the bits. - void setAll() - { - memset(m_wordArray.buffer(), 0xFF, m_wordArray.size() * sizeof(uint)); - } - - // Toggle all the bits. - void toggleAll() - { - const uint wordCount = m_wordArray.count(); - for(uint b = 0; b < wordCount; b++) { - m_wordArray[b] ^= 0xFFFFFFFF; - } - } - - // Count the number of bits set. - uint countSetBits() const - { - const uint num = m_wordArray.size(); - if( num == 0 ) { - return 0; - } - - uint count = 0; - for(uint i = 0; i < num - 1; i++) { - count += nv::countSetBits(m_wordArray[i]); - } - count += nv::countSetBits(m_wordArray[num - 1], m_size & 31); - - //piDebugCheck(count + countClearBits() == m_size); - return count; - } - - // Count the number of bits clear. - uint countClearBits() const { - - const uint num = m_wordArray.size(); - if( num == 0 ) { - return 0; - } - - uint count = 0; - for(uint i = 0; i < num - 1; i++) { - count += nv::countSetBits(~m_wordArray[i]); - } - count += nv::countSetBits(~m_wordArray[num - 1], m_size & 31); - - //piDebugCheck(count + countSetBits() == m_size); - return count; - } - - friend void swap(BitArray & a, BitArray & b) - { - swap(a.m_size, b.m_size); - swap(a.m_wordArray, b.m_wordArray); - } - - void operator &= (const BitArray & other) { - if (other.m_size != m_size) { - resize(other.m_size); - } - - const uint wordCount = m_wordArray.count(); - for (uint i = 0; i < wordCount; i++) { - m_wordArray[i] &= other.m_wordArray[i]; - } - } - - void operator |= (const BitArray & other) { - if (other.m_size != m_size) { - resize(other.m_size); - } - - const uint wordCount = m_wordArray.count(); - for (uint i = 0; i < wordCount; i++) { - m_wordArray[i] |= other.m_wordArray[i]; - } - } - - - private: - - // Number of bits stored. - uint m_size; - - // Array of bits. - Array<uint> m_wordArray; - - }; - -} // nv namespace - -#endif // NV_CORE_BITARRAY_H - diff --git a/thirdparty/thekla_atlas/nvcore/Debug.cpp b/thirdparty/thekla_atlas/nvcore/Debug.cpp deleted file mode 100644 index 4980ffa916..0000000000 --- a/thirdparty/thekla_atlas/nvcore/Debug.cpp +++ /dev/null @@ -1,1357 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "Debug.h" -#include "Array.inl" -#include "StrLib.h" // StringBuilder - -#include "StdStream.h" // fileOpen - -#include <stdlib.h> - -// Extern -#if NV_OS_WIN32 //&& NV_CC_MSVC -# define WIN32_LEAN_AND_MEAN -# define VC_EXTRALEAN -# include <windows.h> -# include <direct.h> -// -- GODOT start - -# include <crtdbg.h> -# if _MSC_VER < 1300 -# define DECLSPEC_DEPRECATED -// VC6: change this path to your Platform SDK headers -# include <dbghelp.h> // must be XP version of file -// include "M:\\dev7\\vs\\devtools\\common\\win32sdk\\include\\dbghelp.h" -# else -// VC7: ships with updated headers -# include <dbghelp.h> -# endif -// -- GODOT end - -# pragma comment(lib,"dbghelp.lib") -#endif - -#if NV_OS_XBOX -# include <Xtl.h> -# ifdef _DEBUG -# include <xbdm.h> -# endif //_DEBUG -#endif //NV_OS_XBOX - -#if !NV_OS_WIN32 && defined(NV_HAVE_SIGNAL_H) -# include <signal.h> -#endif - -#if NV_OS_UNIX -# include <unistd.h> // getpid -#endif - -#if NV_OS_LINUX && defined(NV_HAVE_EXECINFO_H) -# include <execinfo.h> // backtrace -# if NV_CC_GNUC // defined(NV_HAVE_CXXABI_H) -# include <cxxabi.h> -# endif -#endif - -#if NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD -# include <sys/types.h> -# include <sys/param.h> -# include <sys/sysctl.h> // sysctl -# if !defined(NV_OS_OPENBSD) -# include <sys/ucontext.h> -# endif -# if defined(NV_HAVE_EXECINFO_H) // only after OSX 10.5 -# include <execinfo.h> // backtrace -# if NV_CC_GNUC // defined(NV_HAVE_CXXABI_H) -# include <cxxabi.h> -# endif -# endif -#endif - -#if NV_OS_ORBIS -#include <libdbg.h> -#endif - -#if NV_OS_DURANGO -#include "Windows.h" -#include <winnt.h> -#include <crtdbg.h> -#include <dbghelp.h> -#include <errhandlingapi.h> -#define NV_USE_SEPARATE_THREAD 0 -#else -#define NV_USE_SEPARATE_THREAD 1 -#endif - - - -using namespace nv; - -namespace -{ - - static MessageHandler * s_message_handler = NULL; - static AssertHandler * s_assert_handler = NULL; - - static bool s_sig_handler_enabled = false; - static bool s_interactive = true; - -#if (NV_OS_WIN32 && NV_CC_MSVC) || NV_OS_DURANGO - - // Old exception filter. - static LPTOP_LEVEL_EXCEPTION_FILTER s_old_exception_filter = NULL; - -#elif !NV_OS_WIN32 && defined(NV_HAVE_SIGNAL_H) - - // Old signal handlers. - struct sigaction s_old_sigsegv; - struct sigaction s_old_sigtrap; - struct sigaction s_old_sigfpe; - struct sigaction s_old_sigbus; - -#endif - -// -- GODOT start - -#if NV_OS_WIN32 || NV_OS_DURANGO -// -- GODOT end - - - // We should try to simplify the top level filter as much as possible. - // http://www.nynaeve.net/?p=128 - - // The critical section enforcing the requirement that only one exception be - // handled by a handler at a time. - static CRITICAL_SECTION s_handler_critical_section; - -#if NV_USE_SEPARATE_THREAD - // Semaphores used to move exception handling between the exception thread - // and the handler thread. handler_start_semaphore_ is signalled by the - // exception thread to wake up the handler thread when an exception occurs. - // handler_finish_semaphore_ is signalled by the handler thread to wake up - // the exception thread when handling is complete. - static HANDLE s_handler_start_semaphore = NULL; - static HANDLE s_handler_finish_semaphore = NULL; - - // The exception handler thread. - static HANDLE s_handler_thread = NULL; - - static DWORD s_requesting_thread_id = 0; - static EXCEPTION_POINTERS * s_exception_info = NULL; - -#endif // NV_USE_SEPARATE_THREAD - - - struct MinidumpCallbackContext { - ULONG64 memory_base; - ULONG memory_size; - bool finished; - }; - -#if NV_OS_WIN32 - // static - static BOOL CALLBACK miniDumpWriteDumpCallback(PVOID context, const PMINIDUMP_CALLBACK_INPUT callback_input, PMINIDUMP_CALLBACK_OUTPUT callback_output) - { - switch (callback_input->CallbackType) - { - case MemoryCallback: { - MinidumpCallbackContext* callback_context = reinterpret_cast<MinidumpCallbackContext*>(context); - if (callback_context->finished) - return FALSE; - - // Include the specified memory region. - callback_output->MemoryBase = callback_context->memory_base; - callback_output->MemorySize = callback_context->memory_size; - callback_context->finished = true; - return TRUE; - } - - // Include all modules. - case IncludeModuleCallback: - case ModuleCallback: - return TRUE; - - // Include all threads. - case IncludeThreadCallback: - case ThreadCallback: - return TRUE; - - // Stop receiving cancel callbacks. - case CancelCallback: - callback_output->CheckCancel = FALSE; - callback_output->Cancel = FALSE; - return TRUE; - } - - // Ignore other callback types. - return FALSE; - } -#endif - - static bool writeMiniDump(EXCEPTION_POINTERS * pExceptionInfo) - { -#if NV_OS_DURANGO - // Get a handle to the minidump method. - typedef BOOL(WINAPI* MiniDumpWriteDumpPfn) ( - _In_ HANDLE hProcess, - _In_ DWORD ProcessId, - _In_ HANDLE hFile, - _In_ MINIDUMP_TYPE DumpType, - _In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - _In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - _Reserved_ PVOID CallbackParam - ); - MiniDumpWriteDumpPfn MiniDumpWriteDump = NULL; - HMODULE hToolHelpModule = ::LoadLibraryW(L"toolhelpx.dll"); - if (hToolHelpModule != INVALID_HANDLE_VALUE) { - MiniDumpWriteDump = reinterpret_cast<MiniDumpWriteDumpPfn>(::GetProcAddress(hToolHelpModule, "MiniDumpWriteDump")); - if (!MiniDumpWriteDump) { - FreeLibrary(hToolHelpModule); - return false; - } - } - else - return false; - - // Generate a decent filename. - nv::Path application_path(256); - HINSTANCE hinstance = GetModuleHandle(NULL); - GetModuleFileName(hinstance, application_path.str(), 256); - application_path.stripExtension(); - const char * application_name = application_path.fileName(); - - SYSTEMTIME local_time; - GetLocalTime(&local_time); - - char dump_filename[MAX_PATH] = {}; - sprintf_s(dump_filename, "d:\\%s-%04d%02d%02d-%02d%02d%02d.dmp", - application_name, - local_time.wYear, local_time.wMonth, local_time.wDay, - local_time.wHour, local_time.wMinute, local_time.wSecond ); -#else - const char* dump_filename = "crash.dmp"; -#endif - - // create the file - HANDLE hFile = CreateFileA(dump_filename, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - //nvDebug("*** Failed to create dump file.\n"); -#if NV_OS_DURANGO - FreeLibrary(hToolHelpModule); -#endif - return false; - } - - MINIDUMP_EXCEPTION_INFORMATION * pExInfo = NULL; -#if NV_OS_WIN32 - MINIDUMP_CALLBACK_INFORMATION * pCallback = NULL; -#else - void * pCallback = NULL; -#endif - - MINIDUMP_EXCEPTION_INFORMATION ExInfo; - if (pExceptionInfo != NULL) { - ExInfo.ThreadId = ::GetCurrentThreadId(); - ExInfo.ExceptionPointers = pExceptionInfo; - ExInfo.ClientPointers = NULL; - pExInfo = &ExInfo; - -#if NV_OS_WIN32 - MINIDUMP_CALLBACK_INFORMATION callback; - MinidumpCallbackContext context; - - // Find a memory region of 256 bytes centered on the - // faulting instruction pointer. - const ULONG64 instruction_pointer = - #if defined(_M_IX86) - pExceptionInfo->ContextRecord->Eip; - #elif defined(_M_AMD64) - pExceptionInfo->ContextRecord->Rip; - #else - #error Unsupported platform - #endif - - MEMORY_BASIC_INFORMATION info; - - if (VirtualQuery(reinterpret_cast<LPCVOID>(instruction_pointer), &info, sizeof(MEMORY_BASIC_INFORMATION)) != 0 && info.State == MEM_COMMIT) - { - // Attempt to get 128 bytes before and after the instruction - // pointer, but settle for whatever's available up to the - // boundaries of the memory region. - const ULONG64 kIPMemorySize = 256; - context.memory_base = max(reinterpret_cast<ULONG64>(info.BaseAddress), instruction_pointer - (kIPMemorySize / 2)); - ULONG64 end_of_range = min(instruction_pointer + (kIPMemorySize / 2), reinterpret_cast<ULONG64>(info.BaseAddress) + info.RegionSize); - context.memory_size = static_cast<ULONG>(end_of_range - context.memory_base); - context.finished = false; - - callback.CallbackRoutine = miniDumpWriteDumpCallback; - callback.CallbackParam = reinterpret_cast<void*>(&context); - pCallback = &callback; - } -#endif - } - - MINIDUMP_TYPE miniDumpType = (MINIDUMP_TYPE)(MiniDumpNormal|MiniDumpWithHandleData|MiniDumpWithThreadInfo); - - // write the dump - BOOL ok = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, miniDumpType, pExInfo, NULL, pCallback) != 0; - CloseHandle(hFile); -#if NV_OS_DURANGO - FreeLibrary(hToolHelpModule); -#endif - - if (ok == FALSE) { - //nvDebug("*** Failed to save dump file.\n"); - return false; - } - - //nvDebug("\nDump file saved.\n"); - - return true; - } - -#if NV_USE_SEPARATE_THREAD - - static DWORD WINAPI ExceptionHandlerThreadMain(void* lpParameter) { - nvDebugCheck(s_handler_start_semaphore != NULL); - nvDebugCheck(s_handler_finish_semaphore != NULL); - - while (true) { - if (WaitForSingleObject(s_handler_start_semaphore, INFINITE) == WAIT_OBJECT_0) { - writeMiniDump(s_exception_info); - - // Allow the requesting thread to proceed. - ReleaseSemaphore(s_handler_finish_semaphore, 1, NULL); - } - } - - // This statement is not reached when the thread is unconditionally - // terminated by the ExceptionHandler destructor. - return 0; - } - -#endif // NV_USE_SEPARATE_THREAD - - static bool hasStackTrace() { - return true; - } - - /*static NV_NOINLINE int backtrace(void * trace[], int maxcount) { - - // In Windows XP and Windows Server 2003, the sum of the FramesToSkip and FramesToCapture parameters must be less than 63. - int xp_maxcount = min(63-1, maxcount); - - int count = RtlCaptureStackBackTrace(1, xp_maxcount, trace, NULL); - nvDebugCheck(count <= maxcount); - - return count; - }*/ - -#if NV_OS_WIN32 - static NV_NOINLINE int backtraceWithSymbols(CONTEXT * ctx, void * trace[], int maxcount, int skip = 0) { - - // Init the stack frame for this function - STACKFRAME64 stackFrame = { 0 }; - - #if NV_CPU_X86_64 - DWORD dwMachineType = IMAGE_FILE_MACHINE_AMD64; - stackFrame.AddrPC.Offset = ctx->Rip; - stackFrame.AddrFrame.Offset = ctx->Rbp; - stackFrame.AddrStack.Offset = ctx->Rsp; - #elif NV_CPU_X86 - DWORD dwMachineType = IMAGE_FILE_MACHINE_I386; - stackFrame.AddrPC.Offset = ctx->Eip; - stackFrame.AddrFrame.Offset = ctx->Ebp; - stackFrame.AddrStack.Offset = ctx->Esp; - #else - #error "Platform not supported!" - #endif - stackFrame.AddrPC.Mode = AddrModeFlat; - stackFrame.AddrFrame.Mode = AddrModeFlat; - stackFrame.AddrStack.Mode = AddrModeFlat; - - // Walk up the stack - const HANDLE hThread = GetCurrentThread(); - const HANDLE hProcess = GetCurrentProcess(); - int i; - for (i = 0; i < maxcount; i++) - { - // walking once first makes us skip self - if (!StackWalk64(dwMachineType, hProcess, hThread, &stackFrame, ctx, NULL, &SymFunctionTableAccess64, &SymGetModuleBase64, NULL)) { - break; - } - - /*if (stackFrame.AddrPC.Offset == stackFrame.AddrReturn.Offset || stackFrame.AddrPC.Offset == 0) { - break; - }*/ - - if (i >= skip) { - trace[i - skip] = (PVOID)stackFrame.AddrPC.Offset; - } - } - - return i - skip; - } - -#pragma warning(push) -#pragma warning(disable:4748) - static NV_NOINLINE int backtrace(void * trace[], int maxcount) { - CONTEXT ctx = { 0 }; -// -- GODOT start -- -#if NV_CPU_X86 && !NV_CPU_X86_64 - ctx.ContextFlags = CONTEXT_CONTROL; -#if NV_CC_MSVC - _asm { - call x - x: pop eax - mov ctx.Eip, eax - mov ctx.Ebp, ebp - mov ctx.Esp, esp - } -#else - register long unsigned int ebp asm("ebp"); - ctx.Eip = (DWORD) __builtin_return_address(0); - ctx.Ebp = ebp; - ctx.Esp = (DWORD) __builtin_frame_address(0); -#endif -// -- GODOT end -- -#else - RtlCaptureContext(&ctx); // Not implemented correctly in x86. -#endif - - return backtraceWithSymbols(&ctx, trace, maxcount, 1); - } -#pragma warning(pop) - - static NV_NOINLINE void writeStackTrace(void * trace[], int size, int start, Array<const char *> & lines) - { - StringBuilder builder(512); - - HANDLE hProcess = GetCurrentProcess(); - - // Resolve PC to function names - for (int i = start; i < size; i++) - { - // Check for end of stack walk - DWORD64 ip = (DWORD64)trace[i]; - if (ip == NULL) - break; - - // Get function name - #define MAX_STRING_LEN (512) - unsigned char byBuffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_STRING_LEN] = { 0 }; - IMAGEHLP_SYMBOL64 * pSymbol = (IMAGEHLP_SYMBOL64*)byBuffer; - pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); - pSymbol->MaxNameLength = MAX_STRING_LEN; - - DWORD64 dwDisplacement; - - if (SymGetSymFromAddr64(hProcess, ip, &dwDisplacement, pSymbol)) - { - pSymbol->Name[MAX_STRING_LEN-1] = 0; - - /* - // Make the symbol readable for humans - UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, - UNDNAME_COMPLETE | - UNDNAME_NO_THISTYPE | - UNDNAME_NO_SPECIAL_SYMS | - UNDNAME_NO_MEMBER_TYPE | - UNDNAME_NO_MS_KEYWORDS | - UNDNAME_NO_ACCESS_SPECIFIERS ); - */ - - // pSymbol->Name - const char * pFunc = pSymbol->Name; - - // Get file/line number - IMAGEHLP_LINE64 theLine = { 0 }; - theLine.SizeOfStruct = sizeof(theLine); - - DWORD dwDisplacement; - if (!SymGetLineFromAddr64(hProcess, ip, &dwDisplacement, &theLine)) - { - // Do not print unknown symbols anymore. - //break; - builder.format("unknown(%08X) : %s\n", (uint32)ip, pFunc); - } - else - { - /* - const char* pFile = strrchr(theLine.FileName, '\\'); - if ( pFile == NULL ) pFile = theLine.FileName; - else pFile++; - */ - const char * pFile = theLine.FileName; - - int line = theLine.LineNumber; - - builder.format("%s(%d) : %s\n", pFile, line, pFunc); - } - - lines.append(builder.release()); - - if (pFunc != NULL && strcmp(pFunc, "WinMain") == 0) { - break; - } - } - } - } -#endif - - // Write mini dump and print stack trace. - static LONG WINAPI handleException(EXCEPTION_POINTERS * pExceptionInfo) - { - EnterCriticalSection(&s_handler_critical_section); -#if NV_USE_SEPARATE_THREAD - s_requesting_thread_id = GetCurrentThreadId(); - s_exception_info = pExceptionInfo; - - // This causes the handler thread to call writeMiniDump. - ReleaseSemaphore(s_handler_start_semaphore, 1, NULL); - - // Wait until WriteMinidumpWithException is done and collect its return value. - WaitForSingleObject(s_handler_finish_semaphore, INFINITE); - //bool status = s_handler_return_value; - - // Clean up. - s_requesting_thread_id = 0; - s_exception_info = NULL; -#else - // First of all, write mini dump. - writeMiniDump(pExceptionInfo); -#endif - LeaveCriticalSection(&s_handler_critical_section); - - nvDebug("\nDump file saved.\n"); - - // Try to attach to debugger. - if (s_interactive && debug::attachToDebugger()) { - nvDebugBreak(); - return EXCEPTION_CONTINUE_EXECUTION; - } - -#if NV_OS_WIN32 - // If that fails, then try to pretty print a stack trace and terminate. - void * trace[64]; - - int size = backtraceWithSymbols(pExceptionInfo->ContextRecord, trace, 64); - - // @@ Use win32's CreateFile? - FILE * fp = fileOpen("crash.txt", "wb"); - if (fp != NULL) { - Array<const char *> lines; - writeStackTrace(trace, size, 0, lines); - - for (uint i = 0; i < lines.count(); i++) { - fputs(lines[i], fp); - delete lines[i]; - } - - // @@ Add more info to crash.txt? - - fclose(fp); - } -#endif - - // This should terminate the process and set the error exit code. - TerminateProcess(GetCurrentProcess(), EXIT_FAILURE + 2); - - return EXCEPTION_EXECUTE_HANDLER; // Terminate app. In case terminate process did not succeed. - } - - static void handlePureVirtualCall() { - nvDebugBreak(); - TerminateProcess(GetCurrentProcess(), EXIT_FAILURE + 8); - } - - static void handleInvalidParameter(const wchar_t * wexpresion, const wchar_t * wfunction, const wchar_t * wfile, unsigned int line, uintptr_t reserved) { - - size_t convertedCharCount = 0; - - StringBuilder expresion; - if (wexpresion != NULL) { - uint size = U32(wcslen(wexpresion) + 1); - expresion.reserve(size); - wcstombs_s(&convertedCharCount, expresion.str(), size, wexpresion, _TRUNCATE); - } - - StringBuilder file; - if (wfile != NULL) { - uint size = U32(wcslen(wfile) + 1); - file.reserve(size); - wcstombs_s(&convertedCharCount, file.str(), size, wfile, _TRUNCATE); - } - - StringBuilder function; - if (wfunction != NULL) { - uint size = U32(wcslen(wfunction) + 1); - function.reserve(size); - wcstombs_s(&convertedCharCount, function.str(), size, wfunction, _TRUNCATE); - } - - int result = nvAbort(expresion.str(), file.str(), line, function.str()); - if (result == NV_ABORT_DEBUG) { - nvDebugBreak(); - } - } - -#elif !NV_OS_WIN32 && defined(NV_HAVE_SIGNAL_H) // NV_OS_LINUX || NV_OS_DARWIN - -#if defined(NV_HAVE_EXECINFO_H) - - static bool hasStackTrace() { - return true; - } - - - static void writeStackTrace(void * trace[], int size, int start, Array<const char *> & lines) { - StringBuilder builder(512); - char ** string_array = backtrace_symbols(trace, size); - - for(int i = start; i < size-1; i++ ) { - // IC: Just in case. - if (string_array[i] == NULL || string_array[i][0] == '\0') break; - -# if NV_CC_GNUC // defined(NV_HAVE_CXXABI_H) - // @@ Write a better parser for the possible formats. - char * begin = strchr(string_array[i], '('); - char * end = strrchr(string_array[i], '+'); - char * module = string_array[i]; - - if (begin == 0 && end != 0) { - *(end - 1) = '\0'; - begin = strrchr(string_array[i], ' '); - module = NULL; // Ignore module. - } - - if (begin != 0 && begin < end) { - int stat; - *end = '\0'; - *begin = '\0'; - char * name = abi::__cxa_demangle(begin+1, 0, 0, &stat); - if (module == NULL) { - if (name == NULL || stat != 0) { - builder.format(" In: '%s'\n", begin+1); - } - else { - builder.format(" In: '%s'\n", name); - } - } - else { - if (name == NULL || stat != 0) { - builder.format(" In: [%s] '%s'\n", module, begin+1); - } - else { - builder.format(" In: [%s] '%s'\n", module, name); - } - } - free(name); - } - else { - builder.format(" In: '%s'\n", string_array[i]); - } -# else - builder.format(" In: '%s'\n", string_array[i]); -# endif - lines.append(builder.release()); - } - - free(string_array); - } - - static void printStackTrace(void * trace[], int size, int start=0) { - nvDebug( "\nDumping stacktrace:\n" ); - - Array<const char *> lines; - writeStackTrace(trace, size, 1, lines); - - for (uint i = 0; i < lines.count(); i++) { - nvDebug("%s", lines[i]); - delete lines[i]; - } - - nvDebug("\n"); - } - -#endif // defined(NV_HAVE_EXECINFO_H) - - static void * callerAddress(void * secret) - { -#if NV_OS_DARWIN -# if defined(_STRUCT_MCONTEXT) -# if NV_CPU_PPC - ucontext_t * ucp = (ucontext_t *)secret; - return (void *) ucp->uc_mcontext->__ss.__srr0; -# elif NV_CPU_X86_64 - ucontext_t * ucp = (ucontext_t *)secret; - return (void *) ucp->uc_mcontext->__ss.__rip; -# elif NV_CPU_X86 - ucontext_t * ucp = (ucontext_t *)secret; - return (void *) ucp->uc_mcontext->__ss.__eip; -# elif NV_CPU_ARM - ucontext_t * ucp = (ucontext_t *)secret; - return (void *) ucp->uc_mcontext->__ss.__pc; -# else -# error "Unknown CPU" -# endif -# else -# if NV_CPU_PPC - ucontext_t * ucp = (ucontext_t *)secret; - return (void *) ucp->uc_mcontext->ss.srr0; -# elif NV_CPU_X86 - ucontext_t * ucp = (ucontext_t *)secret; - return (void *) ucp->uc_mcontext->ss.eip; -# else -# error "Unknown CPU" -# endif -# endif -#elif NV_OS_FREEBSD -# if NV_CPU_X86_64 - ucontext_t * ucp = (ucontext_t *)secret; - return (void *)ucp->uc_mcontext.mc_rip; -# elif NV_CPU_X86 - ucontext_t * ucp = (ucontext_t *)secret; - return (void *)ucp->uc_mcontext.mc_eip; -# else -# error "Unknown CPU" -# endif -#elif NV_OS_OPENBSD -# if NV_CPU_X86_64 - ucontext_t * ucp = (ucontext_t *)secret; - return (void *)ucp->sc_rip; -# elif NV_CPU_X86 - ucontext_t * ucp = (ucontext_t *)secret; - return (void *)ucp->sc_eip; -# else -# error "Unknown CPU" -# endif -#else -# if NV_CPU_X86_64 - // #define REG_RIP REG_INDEX(rip) // seems to be 16 - ucontext_t * ucp = (ucontext_t *)secret; - return (void *)ucp->uc_mcontext.gregs[REG_RIP]; -# elif NV_CPU_X86 - ucontext_t * ucp = (ucontext_t *)secret; - return (void *)ucp->uc_mcontext.gregs[14/*REG_EIP*/]; -# elif NV_CPU_PPC - ucontext_t * ucp = (ucontext_t *)secret; - return (void *) ucp->uc_mcontext.regs->nip; -# else -# error "Unknown CPU" -# endif -#endif - - // How to obtain the instruction pointers in different platforms, from mlton's source code. - // http://mlton.org/ - // OpenBSD && NetBSD - // ucp->sc_eip - // FreeBSD: - // ucp->uc_mcontext.mc_eip - // HPUX: - // ucp->uc_link - // Solaris: - // ucp->uc_mcontext.gregs[REG_PC] - // Linux hppa: - // uc->uc_mcontext.sc_iaoq[0] & ~0x3UL - // Linux sparc: - // ((struct sigcontext*) secret)->sigc_regs.tpc - // Linux sparc64: - // ((struct sigcontext*) secret)->si_regs.pc - - // potentially correct for other archs: - // Linux alpha: ucp->m_context.sc_pc - // Linux arm: ucp->m_context.ctx.arm_pc - // Linux ia64: ucp->m_context.sc_ip & ~0x3UL - // Linux mips: ucp->m_context.sc_pc - // Linux s390: ucp->m_context.sregs->regs.psw.addr - } - - static void nvSigHandler(int sig, siginfo_t *info, void *secret) - { - void * pnt = callerAddress(secret); - - // Do something useful with siginfo_t - if (sig == SIGSEGV) { - if (pnt != NULL) nvDebug("Got signal %d, faulty address is %p, from %p\n", sig, info->si_addr, pnt); - else nvDebug("Got signal %d, faulty address is %p\n", sig, info->si_addr); - } - else if(sig == SIGTRAP) { - nvDebug("Breakpoint hit.\n"); - } - else { - nvDebug("Got signal %d\n", sig); - } - -#if defined(NV_HAVE_EXECINFO_H) - if (hasStackTrace()) // in case of weak linking - { - void * trace[64]; - int size = backtrace(trace, 64); - - if (pnt != NULL) { - // Overwrite sigaction with caller's address. - trace[1] = pnt; - } - - printStackTrace(trace, size, 1); - } -#endif // defined(NV_HAVE_EXECINFO_H) - - exit(0); - } - -#endif // defined(NV_HAVE_SIGNAL_H) - - - -#if NV_OS_WIN32 //&& NV_CC_MSVC - - /** Win32 assert handler. */ - struct Win32AssertHandler : public AssertHandler - { - // Flush the message queue. This is necessary for the message box to show up. - static void flushMessageQueue() - { - MSG msg; - while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { - //if( msg.message == WM_QUIT ) break; - TranslateMessage( &msg ); - DispatchMessage( &msg ); - } - } - - // Assert handler method. - virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg) - { - int ret = NV_ABORT_EXIT; - - StringBuilder error_string; - error_string.format("*** Assertion failed: %s\n On file: %s\n On line: %d\n", exp, file, line ); - if (func != NULL) { - error_string.appendFormat(" On function: %s\n", func); - } - if (msg != NULL) { - error_string.append(" Message: "); - va_list tmp; - va_copy(tmp, arg); - error_string.appendFormatList(msg, tmp); - va_end(tmp); - error_string.append("\n"); - } - nvDebug( error_string.str() ); - - // Print stack trace: - debug::dumpInfo(); - - if (debug::isDebuggerPresent()) { - return NV_ABORT_DEBUG; - } - - if (s_interactive) { - flushMessageQueue(); - int action = MessageBoxA(NULL, error_string.str(), "Assertion failed", MB_ABORTRETRYIGNORE | MB_ICONERROR | MB_TOPMOST); - switch( action ) { - case IDRETRY: - ret = NV_ABORT_DEBUG; - break; - case IDIGNORE: - ret = NV_ABORT_IGNORE; - break; - case IDABORT: - default: - ret = NV_ABORT_EXIT; - break; - } - /*if( _CrtDbgReport( _CRT_ASSERT, file, line, module, exp ) == 1 ) { - return NV_ABORT_DEBUG; - }*/ - } - - if (ret == NV_ABORT_EXIT) { - // Exit cleanly. - exit(EXIT_FAILURE + 1); - } - - return ret; - } - }; -#elif NV_OS_XBOX - - /** Xbox360 assert handler. */ - struct Xbox360AssertHandler : public AssertHandler - { - // Assert handler method. - virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg) - { - int ret = NV_ABORT_EXIT; - - StringBuilder error_string; - if( func != NULL ) { - error_string.format( "*** Assertion failed: %s\n On file: %s\n On function: %s\n On line: %d\n ", exp, file, func, line ); - nvDebug( error_string.str() ); - } - else { - error_string.format( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line ); - nvDebug( error_string.str() ); - } - - if (debug::isDebuggerPresent()) { - return NV_ABORT_DEBUG; - } - - if( ret == NV_ABORT_EXIT ) { - // Exit cleanly. - exit(EXIT_FAILURE + 1); - } - - return ret; - } - }; -#elif NV_OS_ORBIS || NV_OS_DURANGO - - /** Console assert handler. */ - struct ConsoleAssertHandler : public AssertHandler - { - // Assert handler method. - virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg) - { - if( func != NULL ) { - nvDebug( "*** Assertion failed: %s\n On file: %s\n On function: %s\n On line: %d\n ", exp, file, func, line ); - } - else { - nvDebug( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line ); - } - - //SBtodoORBIS print stack trace - /*if (hasStackTrace()) - { - void * trace[64]; - int size = backtrace(trace, 64); - printStackTrace(trace, size, 2); - }*/ - - if (debug::isDebuggerPresent()) - return NV_ABORT_DEBUG; - - return NV_ABORT_IGNORE; - } - }; - -#else - - /** Unix assert handler. */ - struct UnixAssertHandler : public AssertHandler - { - // Assert handler method. - virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg) - { - int ret = NV_ABORT_EXIT; - - if( func != NULL ) { - nvDebug( "*** Assertion failed: %s\n On file: %s\n On function: %s\n On line: %d\n ", exp, file, func, line ); - } - else { - nvDebug( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line ); - } - -#if _DEBUG - if (debug::isDebuggerPresent()) { - return NV_ABORT_DEBUG; - } -#endif - -#if defined(NV_HAVE_EXECINFO_H) - if (hasStackTrace()) - { - void * trace[64]; - int size = backtrace(trace, 64); - printStackTrace(trace, size, 2); - } -#endif - - if( ret == NV_ABORT_EXIT ) { - // Exit cleanly. - exit(EXIT_FAILURE + 1); - } - - return ret; - } - }; - -#endif - -} // namespace - - -/// Handle assertion through the assert handler. -int nvAbort(const char * exp, const char * file, int line, const char * func/*=NULL*/, const char * msg/*= NULL*/, ...) -{ -#if NV_OS_WIN32 //&& NV_CC_MSVC - static Win32AssertHandler s_default_assert_handler; -#elif NV_OS_XBOX - static Xbox360AssertHandler s_default_assert_handler; -#elif NV_OS_ORBIS || NV_OS_DURANGO - static ConsoleAssertHandler s_default_assert_handler; -#else - static UnixAssertHandler s_default_assert_handler; -#endif - - va_list arg; - va_start(arg,msg); - - AssertHandler * handler = s_assert_handler != NULL ? s_assert_handler : &s_default_assert_handler; - int result = handler->assertion(exp, file, line, func, msg, arg); - - va_end(arg); - - return result; -} - -// Abnormal termination. Create mini dump and output call stack. -void debug::terminate(int code) -{ -#if NV_OS_WIN32 || NV_OS_DURANGO - EnterCriticalSection(&s_handler_critical_section); - - writeMiniDump(NULL); - -#if NV_OS_WIN32 - const int max_stack_size = 64; - void * trace[max_stack_size]; - int size = backtrace(trace, max_stack_size); - - // @@ Use win32's CreateFile? - FILE * fp = fileOpen("crash.txt", "wb"); - if (fp != NULL) { - Array<const char *> lines; - writeStackTrace(trace, size, 0, lines); - - for (uint i = 0; i < lines.count(); i++) { - fputs(lines[i], fp); - delete lines[i]; - } - - // @@ Add more info to crash.txt? - - fclose(fp); - } -#endif - - LeaveCriticalSection(&s_handler_critical_section); -#endif - - exit(code); -} - - -/// Shows a message through the message handler. -void NV_CDECL nvDebugPrint(const char *msg, ...) -{ - va_list arg; - va_start(arg,msg); - if (s_message_handler != NULL) { - s_message_handler->log( msg, arg ); - } - else { - vprintf(msg, arg); - } - va_end(arg); -} - - -/// Dump debug info. -void debug::dumpInfo() -{ -#if (NV_OS_WIN32 && NV_CC_MSVC) || (defined(NV_HAVE_SIGNAL_H) && defined(NV_HAVE_EXECINFO_H)) - if (hasStackTrace()) - { - void * trace[64]; - int size = backtrace(trace, 64); - - nvDebug( "\nDumping stacktrace:\n" ); - - Array<const char *> lines; - writeStackTrace(trace, size, 1, lines); - - for (uint i = 0; i < lines.count(); i++) { - nvDebug("%s", lines[i]); - delete lines[i]; - } - } -#endif -} - -/// Dump callstack using the specified handler. -void debug::dumpCallstack(MessageHandler *messageHandler, int callstackLevelsToSkip /*= 0*/) -{ -#if (NV_OS_WIN32 && NV_CC_MSVC) || (defined(NV_HAVE_SIGNAL_H) && defined(NV_HAVE_EXECINFO_H)) - if (hasStackTrace()) - { - void * trace[64]; - int size = backtrace(trace, 64); - - Array<const char *> lines; - writeStackTrace(trace, size, callstackLevelsToSkip + 1, lines); // + 1 to skip the call to dumpCallstack - - for (uint i = 0; i < lines.count(); i++) { - messageHandler->log(lines[i], NULL); - delete lines[i]; - } - } -#endif -} - - -/// Set the debug message handler. -void debug::setMessageHandler(MessageHandler * message_handler) -{ - s_message_handler = message_handler; -} - -/// Reset the debug message handler. -void debug::resetMessageHandler() -{ - s_message_handler = NULL; -} - -/// Set the assert handler. -void debug::setAssertHandler(AssertHandler * assert_handler) -{ - s_assert_handler = assert_handler; -} - -/// Reset the assert handler. -void debug::resetAssertHandler() -{ - s_assert_handler = NULL; -} - -#if NV_OS_WIN32 || NV_OS_DURANGO -#if NV_USE_SEPARATE_THREAD - -static void initHandlerThread() -{ - static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024; - - // Set synchronization primitives and the handler thread. Each - // ExceptionHandler object gets its own handler thread because that's the - // only way to reliably guarantee sufficient stack space in an exception, - // and it allows an easy way to get a snapshot of the requesting thread's - // context outside of an exception. - InitializeCriticalSection(&s_handler_critical_section); - - s_handler_start_semaphore = CreateSemaphoreExW(NULL, 0, 1, NULL, 0, - SEMAPHORE_MODIFY_STATE | DELETE | SYNCHRONIZE); - nvDebugCheck(s_handler_start_semaphore != NULL); - - s_handler_finish_semaphore = CreateSemaphoreExW(NULL, 0, 1, NULL, 0, - SEMAPHORE_MODIFY_STATE | DELETE | SYNCHRONIZE); - nvDebugCheck(s_handler_finish_semaphore != NULL); - - // Don't attempt to create the thread if we could not create the semaphores. - if (s_handler_finish_semaphore != NULL && s_handler_start_semaphore != NULL) { - DWORD thread_id; - s_handler_thread = CreateThread(NULL, // lpThreadAttributes - kExceptionHandlerThreadInitialStackSize, - ExceptionHandlerThreadMain, - NULL, // lpParameter - 0, // dwCreationFlags - &thread_id); - nvDebugCheck(s_handler_thread != NULL); - } - - /* @@ We should avoid loading modules in the exception handler! - dbghelp_module_ = LoadLibrary(L"dbghelp.dll"); - if (dbghelp_module_) { - minidump_write_dump_ = reinterpret_cast<MiniDumpWriteDump_type>(GetProcAddress(dbghelp_module_, "MiniDumpWriteDump")); - } - */ -} - -static void shutHandlerThread() { - // @@ Free stuff. Terminate thread. -} - -#endif // NV_USE_SEPARATE_THREAD -#endif // NV_OS_WIN32 - - -// Enable signal handler. -void debug::enableSigHandler(bool interactive) -{ - if (s_sig_handler_enabled) return; - - s_sig_handler_enabled = true; - s_interactive = interactive; - -#if (NV_OS_WIN32 && NV_CC_MSVC) || NV_OS_DURANGO - if (interactive) { -#if NV_OS_WIN32 - // Do not display message boxes on error. - // http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx - SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX); -#endif - - // CRT reports errors to debug output only. - // http://msdn.microsoft.com/en-us/library/1y71x448(v=vs.80).aspx - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); - } - - -#if NV_USE_SEPARATE_THREAD - initHandlerThread(); -#else - InitializeCriticalSection(&s_handler_critical_section); -#endif - - s_old_exception_filter = ::SetUnhandledExceptionFilter( handleException ); - -#if _MSC_VER >= 1400 // MSVC 2005/8 - _set_invalid_parameter_handler(handleInvalidParameter); -#endif // _MSC_VER >= 1400 - - _set_purecall_handler(handlePureVirtualCall); - -#if NV_OS_WIN32 - // SYMOPT_DEFERRED_LOADS make us not take a ton of time unless we actual log traces - SymSetOptions(SYMOPT_DEFERRED_LOADS|SYMOPT_FAIL_CRITICAL_ERRORS|SYMOPT_LOAD_LINES|SYMOPT_UNDNAME); - - if (!SymInitialize(GetCurrentProcess(), NULL, TRUE)) { - DWORD error = GetLastError(); - nvDebug("SymInitialize returned error : %d\n", error); - } -#endif - -#elif !NV_OS_WIN32 && defined(NV_HAVE_SIGNAL_H) - - // Install our signal handler - struct sigaction sa; - sa.sa_sigaction = nvSigHandler; - sigemptyset (&sa.sa_mask); - sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; - - sigaction(SIGSEGV, &sa, &s_old_sigsegv); - sigaction(SIGTRAP, &sa, &s_old_sigtrap); - sigaction(SIGFPE, &sa, &s_old_sigfpe); - sigaction(SIGBUS, &sa, &s_old_sigbus); - -#endif -} - -/// Disable signal handler. -void debug::disableSigHandler() -{ - nvCheck(s_sig_handler_enabled == true); - s_sig_handler_enabled = false; - -#if (NV_OS_WIN32 && NV_CC_MSVC) || NV_OS_DURANGO - - ::SetUnhandledExceptionFilter( s_old_exception_filter ); - s_old_exception_filter = NULL; - -#if NV_OS_WIN32 - SymCleanup(GetCurrentProcess()); -#endif - -#elif !NV_OS_WIN32 && defined(NV_HAVE_SIGNAL_H) - - sigaction(SIGSEGV, &s_old_sigsegv, NULL); - sigaction(SIGTRAP, &s_old_sigtrap, NULL); - sigaction(SIGFPE, &s_old_sigfpe, NULL); - sigaction(SIGBUS, &s_old_sigbus, NULL); - -#endif -} - - -bool debug::isDebuggerPresent() -{ -#if NV_OS_WIN32 - HINSTANCE kernel32 = GetModuleHandleA("kernel32.dll"); - if (kernel32) { - FARPROC IsDebuggerPresent = GetProcAddress(kernel32, "IsDebuggerPresent"); - if (IsDebuggerPresent != NULL && IsDebuggerPresent()) { - return true; - } - } - return false; -#elif NV_OS_XBOX -#ifdef _DEBUG - return DmIsDebuggerPresent() == TRUE; -#else - return false; -#endif -#elif NV_OS_ORBIS - #if PS4_FINAL_REQUIREMENTS - return false; - #else - return sceDbgIsDebuggerAttached() == 1; - #endif -#elif NV_OS_DURANGO - #if XB1_FINAL_REQUIREMENTS - return false; - #else - return IsDebuggerPresent() == TRUE; - #endif -#elif NV_OS_DARWIN - int mib[4]; - struct kinfo_proc info; - size_t size; - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); - size = sizeof(info); - info.kp_proc.p_flag = 0; - sysctl(mib,4,&info,&size,NULL,0); - return ((info.kp_proc.p_flag & P_TRACED) == P_TRACED); -#else - // if ppid != sid, some process spawned our app, probably a debugger. - return getsid(getpid()) != getppid(); -#endif -} - -bool debug::attachToDebugger() -{ -#if NV_OS_WIN32 - if (isDebuggerPresent() == FALSE) { - Path process(1024); - process.copy("\""); - GetSystemDirectoryA(process.str() + 1, 1024 - 1); - - process.appendSeparator(); - - process.appendFormat("VSJitDebugger.exe\" -p %lu", ::GetCurrentProcessId()); - - STARTUPINFOA sSi; - memset(&sSi, 0, sizeof(sSi)); - - PROCESS_INFORMATION sPi; - memset(&sPi, 0, sizeof(sPi)); - - BOOL b = CreateProcessA(NULL, process.str(), NULL, NULL, FALSE, 0, NULL, NULL, &sSi, &sPi); - if (b != FALSE) { - ::WaitForSingleObject(sPi.hProcess, INFINITE); - - DWORD dwExitCode; - ::GetExitCodeProcess(sPi.hProcess, &dwExitCode); - if (dwExitCode != 0) //if exit code is zero, a debugger was selected - b = FALSE; - } - - if (sPi.hThread != NULL) ::CloseHandle(sPi.hThread); - if (sPi.hProcess != NULL) ::CloseHandle(sPi.hProcess); - - if (b == FALSE) - return false; - - for (int i = 0; i < 5*60; i++) { - if (isDebuggerPresent()) - break; - ::Sleep(200); - } - } -#endif // NV_OS_WIN32 - - return true; -} diff --git a/thirdparty/thekla_atlas/nvcore/Debug.h b/thirdparty/thekla_atlas/nvcore/Debug.h deleted file mode 100644 index 3804ed4763..0000000000 --- a/thirdparty/thekla_atlas/nvcore/Debug.h +++ /dev/null @@ -1,246 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_DEBUG_H -#define NV_CORE_DEBUG_H - -#include "nvcore.h" - -#include <stdarg.h> // va_list - -#if NV_OS_IOS //ACS: maybe we want this for OSX too? -# ifdef __APPLE__ -# include <TargetConditionals.h> -# include <signal.h> -# endif -#endif - -// Make sure we are using our assert. -#undef assert - -#define NV_ABORT_DEBUG 1 -#define NV_ABORT_IGNORE 2 -#define NV_ABORT_EXIT 3 - -#define nvNoAssert(exp) \ - NV_MULTI_LINE_MACRO_BEGIN \ - (void)sizeof(exp); \ - NV_MULTI_LINE_MACRO_END - -#if NV_NO_ASSERT - -# define nvAssert(exp) nvNoAssert(exp) -# define nvCheck(exp) nvNoAssert(exp) -# define nvDebugAssert(exp) nvNoAssert(exp) -# define nvDebugCheck(exp) nvNoAssert(exp) -# define nvDebugBreak() nvNoAssert(0) - -#else // NV_NO_ASSERT - -# if NV_CC_MSVC - // @@ Does this work in msvc-6 and earlier? -# define nvDebugBreak() __debugbreak() -//# define nvDebugBreak() __asm { int 3 } -# elif NV_OS_ORBIS -# define nvDebugBreak() __debugbreak() -# elif NV_OS_IOS && TARGET_OS_IPHONE -# define nvDebugBreak() raise(SIGINT) -# elif NV_CC_CLANG -# define nvDebugBreak() __builtin_debugtrap() -# elif NV_CC_GNUC -//# define nvDebugBreak() __builtin_debugtrap() // Does GCC have debugtrap? -# define nvDebugBreak() __builtin_trap() -/* -# elif NV_CC_GNUC && NV_CPU_PPC && NV_OS_DARWIN -// @@ Use __builtin_trap() on GCC -# define nvDebugBreak() __asm__ volatile ("trap") -# elif NV_CC_GNUC && NV_CPU_X86 && NV_OS_DARWIN -# define nvDebugBreak() __asm__ volatile ("int3") -# elif NV_CC_GNUC && NV_CPU_X86 -# define nvDebugBreak() __asm__ ( "int %0" : :"I"(3) ) -# elif NV_OS_ORBIS -# define nvDebugBreak() __asm volatile ("int $0x41") -# else -# include <signal.h> -# define nvDebugBreak() raise(SIGTRAP); -// define nvDebugBreak() *((int *)(0)) = 0 -*/ -# endif - -# if NV_CC_MSVC -# define nvExpect(expr) (expr) -#else -# define nvExpect(expr) __builtin_expect((expr) != 0, true) -#endif - -#if NV_CC_CLANG -# if __has_feature(attribute_analyzer_noreturn) -# define NV_ANALYZER_NORETURN __attribute__((analyzer_noreturn)) -# else -# define NV_ANALYZER_NORETURN -# endif -#else -# define NV_ANALYZER_NORETURN -#endif - -#define nvDebugBreakOnce() \ - NV_MULTI_LINE_MACRO_BEGIN \ - static bool firstTime = true; \ - if (firstTime) { firstTime = false; nvDebugBreak(); } \ - NV_MULTI_LINE_MACRO_END - -#define nvAssertMacro(exp) \ - NV_MULTI_LINE_MACRO_BEGIN \ - if (!nvExpect(exp)) { \ - if (nvAbort(#exp, __FILE__, __LINE__, __FUNC__) == NV_ABORT_DEBUG) { \ - nvDebugBreak(); \ - } \ - } \ - NV_MULTI_LINE_MACRO_END - -// GCC, LLVM need "##" before the __VA_ARGS__, MSVC doesn't care -#define nvAssertMacroWithIgnoreAll(exp,...) \ - NV_MULTI_LINE_MACRO_BEGIN \ - static bool ignoreAll = false; \ - if (!ignoreAll && !nvExpect(exp)) { \ - int _result = nvAbort(#exp, __FILE__, __LINE__, __FUNC__, ##__VA_ARGS__); \ - if (_result == NV_ABORT_DEBUG) { \ - nvDebugBreak(); \ - } else if (_result == NV_ABORT_IGNORE) { \ - ignoreAll = true; \ - } \ - } \ - NV_MULTI_LINE_MACRO_END - -// Interesting assert macro from Insomniac: -// http://www.gdcvault.com/play/1015319/Developing-Imperfect-Software-How-to -// Used as follows: -// if (nvCheck(i < count)) { -// normal path -// } else { -// fixup code. -// } -// This style of macro could be combined with __builtin_expect to let the compiler know failure is unlikely. -#define nvCheckMacro(exp) \ - (\ - (exp) ? true : ( \ - (nvAbort(#exp, __FILE__, __LINE__, __FUNC__) == NV_ABORT_DEBUG) ? (nvDebugBreak(), true) : ( false ) \ - ) \ - ) - - -#define nvAssert(exp) nvAssertMacro(exp) -#define nvCheck(exp) nvAssertMacro(exp) - -#if defined(_DEBUG) -# define nvDebugAssert(exp) nvAssertMacro(exp) -# define nvDebugCheck(exp) nvAssertMacro(exp) -#else // _DEBUG -# define nvDebugAssert(exp) nvNoAssert(exp) -# define nvDebugCheck(exp) nvNoAssert(exp) -#endif // _DEBUG - -#endif // NV_NO_ASSERT - -// Use nvAssume for very simple expresions only: nvAssume(0), nvAssume(value == true), etc. -/*#if !defined(_DEBUG) -# if NV_CC_MSVC -# define nvAssume(exp) __assume(exp) -# else -# define nvAssume(exp) nvCheck(exp) -# endif -#else -# define nvAssume(exp) nvCheck(exp) -#endif*/ - -#if defined(_DEBUG) -# if NV_CC_MSVC -# define nvUnreachable() nvAssert(0 && "unreachable"); __assume(0) -# else -# define nvUnreachable() nvAssert(0 && "unreachable"); __builtin_unreachable() -# endif -#else -# if NV_CC_MSVC -# define nvUnreachable() __assume(0) -# else -# define nvUnreachable() __builtin_unreachable() -# endif -#endif - -#define nvError(x) nvAbort(x, __FILE__, __LINE__, __FUNC__) -#define nvWarning(x) nvDebugPrint("*** Warning %s/%d: %s\n", __FILE__, __LINE__, (x)) - -#ifndef NV_DEBUG_PRINT -#define NV_DEBUG_PRINT 1 //defined(_DEBUG) -#endif - -#if NV_DEBUG_PRINT -#define nvDebug(...) nvDebugPrint(__VA_ARGS__) -#else -#if NV_CC_MSVC -#define nvDebug(...) __noop(__VA_ARGS__) -#else -#define nvDebug(...) ((void)0) // Non-msvc platforms do not evaluate arguments? -#endif -#endif - - -NVCORE_API int nvAbort(const char *exp, const char *file, int line, const char * func = NULL, const char * msg = NULL, ...) __attribute__((format (printf, 5, 6))) NV_ANALYZER_NORETURN; -NVCORE_API void NV_CDECL nvDebugPrint( const char *msg, ... ) __attribute__((format (printf, 1, 2))); - -namespace nv -{ - inline bool isValidPtr(const void * ptr) { - #if NV_OS_DARWIN - return true; // IC: Not sure what ranges are OK on OSX. - #endif - - #if NV_CPU_X86_64 - if (ptr == NULL) return true; - if (reinterpret_cast<uint64>(ptr) < 0x10000ULL) return false; - if (reinterpret_cast<uint64>(ptr) >= 0x000007FFFFFEFFFFULL) return false; - #else - if (reinterpret_cast<uintptr_t>(ptr) == 0xcccccccc) return false; - if (reinterpret_cast<uintptr_t>(ptr) == 0xcdcdcdcd) return false; - if (reinterpret_cast<uintptr_t>(ptr) == 0xdddddddd) return false; - if (reinterpret_cast<uintptr_t>(ptr) == 0xffffffff) return false; - #endif - return true; - } - - // Message handler interface. - struct MessageHandler { - virtual void log(const char * str, va_list arg) = 0; - virtual ~MessageHandler() {} - }; - - // Assert handler interface. - struct AssertHandler { - virtual int assertion(const char *exp, const char *file, int line, const char *func, const char *msg, va_list arg) = 0; - virtual ~AssertHandler() {} - }; - - - namespace debug - { - NVCORE_API void dumpInfo(); - NVCORE_API void dumpCallstack( MessageHandler *messageHandler, int callstackLevelsToSkip = 0 ); - - NVCORE_API void setMessageHandler( MessageHandler * messageHandler ); - NVCORE_API void resetMessageHandler(); - - NVCORE_API void setAssertHandler( AssertHandler * assertHanlder ); - NVCORE_API void resetAssertHandler(); - - NVCORE_API void enableSigHandler(bool interactive); - NVCORE_API void disableSigHandler(); - - NVCORE_API bool isDebuggerPresent(); - NVCORE_API bool attachToDebugger(); - - NVCORE_API void terminate(int code); - } - -} // nv namespace - -#endif // NV_CORE_DEBUG_H diff --git a/thirdparty/thekla_atlas/nvcore/DefsGnucDarwin.h b/thirdparty/thekla_atlas/nvcore/DefsGnucDarwin.h deleted file mode 100644 index afb21c3d25..0000000000 --- a/thirdparty/thekla_atlas/nvcore/DefsGnucDarwin.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef NV_CORE_H -#error "Do not include this file directly." -#endif - -#include <stdint.h> // uint8_t, int8_t, ... uintptr_t -#include <stddef.h> // operator new, size_t, NULL - -// Function linkage -#define DLL_IMPORT -#if __GNUC__ >= 4 -# define DLL_EXPORT __attribute__((visibility("default"))) -# define DLL_EXPORT_CLASS DLL_EXPORT -#else -# define DLL_EXPORT -# define DLL_EXPORT_CLASS -#endif - -// Function calling modes -#if NV_CPU_X86 -# define NV_CDECL __attribute__((cdecl)) -# define NV_STDCALL __attribute__((stdcall)) -#else -# define NV_CDECL -# define NV_STDCALL -#endif - -#define NV_FASTCALL __attribute__((fastcall)) -#define NV_FORCEINLINE __attribute__((always_inline)) inline -#define NV_DEPRECATED __attribute__((deprecated)) -#if NV_OS_IOS -#define NV_THREAD_LOCAL // @@ IC: Looks like iOS does not have support for TLS declarations. -#else -#define NV_THREAD_LOCAL __thread -#endif - -#if __GNUC__ > 2 -#define NV_PURE __attribute__((pure)) -#define NV_CONST __attribute__((const)) -#else -#define NV_PURE -#define NV_CONST -#endif - -#define NV_NOINLINE __attribute__((noinline)) - -// Define __FUNC__ properly. -#if __STDC_VERSION__ < 199901L -# if __GNUC__ >= 2 -# define __FUNC__ __PRETTY_FUNCTION__ // __FUNCTION__ -# else -# define __FUNC__ "<unknown>" -# endif -#else -# define __FUNC__ __PRETTY_FUNCTION__ -#endif - -#define restrict __restrict__ diff --git a/thirdparty/thekla_atlas/nvcore/DefsGnucLinux.h b/thirdparty/thekla_atlas/nvcore/DefsGnucLinux.h deleted file mode 100644 index 2126d866f5..0000000000 --- a/thirdparty/thekla_atlas/nvcore/DefsGnucLinux.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef NV_CORE_H -#error "Do not include this file directly." -#endif - -#include <stdint.h> // uint8_t, int8_t, ... uintptr_t -#include <stddef.h> // operator new, size_t, NULL - -// Function linkage -#define DLL_IMPORT -#if __GNUC__ >= 4 -# define DLL_EXPORT __attribute__((visibility("default"))) -# define DLL_EXPORT_CLASS DLL_EXPORT -#else -# define DLL_EXPORT -# define DLL_EXPORT_CLASS -#endif - -// Function calling modes -#if NV_CPU_X86 -# define NV_CDECL __attribute__((cdecl)) -# define NV_STDCALL __attribute__((stdcall)) -#else -# define NV_CDECL -# define NV_STDCALL -#endif - -#define NV_FASTCALL __attribute__((fastcall)) -//#if __GNUC__ > 3 -// It seems that GCC does not assume always_inline implies inline. I think this depends on the GCC version :( -#define NV_FORCEINLINE inline __attribute__((always_inline)) -//#else -// Some compilers complain that inline and always_inline are redundant. -//#define NV_FORCEINLINE __attribute__((always_inline)) -//#endif -#define NV_DEPRECATED __attribute__((deprecated)) -#define NV_THREAD_LOCAL __thread - -#if __GNUC__ > 2 -#define NV_PURE __attribute__((pure)) -#define NV_CONST __attribute__((const)) -#else -#define NV_PURE -#define NV_CONST -#endif - -#define NV_NOINLINE __attribute__((noinline)) - -// Define __FUNC__ properly. -#if __STDC_VERSION__ < 199901L -# if __GNUC__ >= 2 -# define __FUNC__ __PRETTY_FUNCTION__ // __FUNCTION__ -# else -# define __FUNC__ "<unknown>" -# endif -#else -# define __FUNC__ __PRETTY_FUNCTION__ -#endif - -#define restrict __restrict__ diff --git a/thirdparty/thekla_atlas/nvcore/DefsGnucWin32.h b/thirdparty/thekla_atlas/nvcore/DefsGnucWin32.h deleted file mode 100644 index e1c8d6e4f8..0000000000 --- a/thirdparty/thekla_atlas/nvcore/DefsGnucWin32.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef NV_CORE_H -#error "Do not include this file directly." -#endif - -//#include <cstddef> // size_t, NULL - -// Function linkage -#define DLL_IMPORT __declspec(dllimport) -#define DLL_EXPORT __declspec(dllexport) -#define DLL_EXPORT_CLASS DLL_EXPORT - -// Function calling modes -#if NV_CPU_X86 -# define NV_CDECL __attribute__((cdecl)) -# define NV_STDCALL __attribute__((stdcall)) -#else -# define NV_CDECL -# define NV_STDCALL -#endif - -#define NV_FASTCALL __attribute__((fastcall)) -// -- GODOT start - -#define NV_FORCEINLINE __attribute__((always_inline)) inline -// -- GODOT end - -#define NV_DEPRECATED __attribute__((deprecated)) - -#if __GNUC__ > 2 -#define NV_PURE __attribute__((pure)) -#define NV_CONST __attribute__((const)) -#else -#define NV_PURE -#define NV_CONST -#endif - -#define NV_NOINLINE __attribute__((noinline)) - -// Define __FUNC__ properly. -#if __STDC_VERSION__ < 199901L -# if __GNUC__ >= 2 -# define __FUNC__ __PRETTY_FUNCTION__ // __FUNCTION__ -# else -# define __FUNC__ "<unknown>" -# endif -#else -# define __FUNC__ __PRETTY_FUNCTION__ -#endif - -#define restrict __restrict__ - -/* -// Type definitions -typedef unsigned char uint8; -typedef signed char int8; - -typedef unsigned short uint16; -typedef signed short int16; - -typedef unsigned int uint32; -typedef signed int int32; - -typedef unsigned long long uint64; -typedef signed long long int64; - -// Aliases -typedef uint32 uint; -*/ - diff --git a/thirdparty/thekla_atlas/nvcore/DefsVcWin32.h b/thirdparty/thekla_atlas/nvcore/DefsVcWin32.h deleted file mode 100644 index a915f3791a..0000000000 --- a/thirdparty/thekla_atlas/nvcore/DefsVcWin32.h +++ /dev/null @@ -1,94 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#ifndef NV_CORE_H -#error "Do not include this file directly." -#endif - -// Function linkage -#define DLL_IMPORT __declspec(dllimport) -#define DLL_EXPORT __declspec(dllexport) -#define DLL_EXPORT_CLASS DLL_EXPORT - -// Function calling modes -#define NV_CDECL __cdecl -#define NV_STDCALL __stdcall -#define NV_FASTCALL __fastcall -#define NV_DEPRECATED - -#define NV_PURE -#define NV_CONST - -// Set standard function names. -#if _MSC_VER < 1900 -# define snprintf _snprintf -#endif -#if _MSC_VER < 1500 -# define vsnprintf _vsnprintf -#endif -#if _MSC_VER < 1700 -# define strtoll _strtoi64 -# define strtoull _strtoui64 -#endif -//#define chdir _chdir -#define getcwd _getcwd - -#if _MSC_VER <= 1600 -#define va_copy(a, b) (a) = (b) -#endif - -#if !defined restrict -#define restrict -#endif - -// Ignore gcc attributes. -#define __attribute__(X) - -#if !defined __FUNC__ -#define __FUNC__ __FUNCTION__ -#endif - -#define NV_NOINLINE __declspec(noinline) -#define NV_FORCEINLINE __forceinline - -#define NV_THREAD_LOCAL __declspec(thread) - -/* -// Type definitions -typedef unsigned char uint8; -typedef signed char int8; - -typedef unsigned short uint16; -typedef signed short int16; - -typedef unsigned int uint32; -typedef signed int int32; - -typedef unsigned __int64 uint64; -typedef signed __int64 int64; - -// Aliases -typedef uint32 uint; -*/ - -// Unwanted VC++ warnings to disable. -/* -#pragma warning(disable : 4244) // conversion to float, possible loss of data -#pragma warning(disable : 4245) // conversion from 'enum ' to 'unsigned long', signed/unsigned mismatch -#pragma warning(disable : 4100) // unreferenced formal parameter -#pragma warning(disable : 4514) // unreferenced inline function has been removed -#pragma warning(disable : 4710) // inline function not expanded -#pragma warning(disable : 4127) // Conditional expression is constant -#pragma warning(disable : 4305) // truncation from 'const double' to 'float' -#pragma warning(disable : 4505) // unreferenced local function has been removed - -#pragma warning(disable : 4702) // unreachable code in inline expanded function -#pragma warning(disable : 4711) // function selected for automatic inlining -#pragma warning(disable : 4725) // Pentium fdiv bug - -#pragma warning(disable : 4786) // Identifier was truncated and cannot be debugged. - -#pragma warning(disable : 4675) // resolved overload was found by argument-dependent lookup -*/ - -#pragma warning(1 : 4705) // Report unused local variables. -#pragma warning(1 : 4555) // Expression has no effect. diff --git a/thirdparty/thekla_atlas/nvcore/FileSystem.cpp b/thirdparty/thekla_atlas/nvcore/FileSystem.cpp deleted file mode 100644 index 5ed0ca074f..0000000000 --- a/thirdparty/thekla_atlas/nvcore/FileSystem.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "FileSystem.h" - -#if NV_OS_WIN32 -#define _CRT_NONSTDC_NO_WARNINGS // _chdir is defined deprecated, but that's a bug, chdir is deprecated, _chdir is *not*. -//#include <shlwapi.h> // PathFileExists -#include <windows.h> // GetFileAttributes -#include <direct.h> // _mkdir -#elif NV_OS_XBOX -#include <Xtl.h> -#elif NV_OS_DURANGO -#include <Windows.h> -#else -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> -#endif -#include <stdio.h> // remove, unlink - -using namespace nv; - - -bool FileSystem::exists(const char * path) -{ -#if NV_OS_UNIX - return access(path, F_OK|R_OK) == 0; - //struct stat buf; - //return stat(path, &buf) == 0; -#elif NV_OS_WIN32 || NV_OS_XBOX || NV_OS_DURANGO - // PathFileExists requires linking to shlwapi.lib - //return PathFileExists(path) != 0; - return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES; -#else - if (FILE * fp = fopen(path, "r")) - { - fclose(fp); - return true; - } - return false; -#endif -} - -bool FileSystem::createDirectory(const char * path) -{ -#if NV_OS_WIN32 || NV_OS_XBOX || NV_OS_DURANGO - return CreateDirectoryA(path, NULL) != 0; -#elif NV_OS_ORBIS - // not implemented - return false; -#else - return mkdir(path, 0777) != -1; -#endif -} - -bool FileSystem::changeDirectory(const char * path) -{ -#if NV_OS_WIN32 - return _chdir(path) != -1; -#elif NV_OS_XBOX || NV_OS_DURANGO - // Xbox doesn't support Current Working Directory! - return false; -#elif NV_OS_ORBIS - // Orbis doesn't support Current Working Directory! - return false; -#else - return chdir(path) != -1; -#endif -} - -bool FileSystem::removeFile(const char * path) -{ - // @@ Use unlink or remove? - return remove(path) == 0; -} diff --git a/thirdparty/thekla_atlas/nvcore/FileSystem.h b/thirdparty/thekla_atlas/nvcore/FileSystem.h deleted file mode 100644 index afd0f449d3..0000000000 --- a/thirdparty/thekla_atlas/nvcore/FileSystem.h +++ /dev/null @@ -1,24 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#pragma once -#ifndef NV_CORE_FILESYSTEM_H -#define NV_CORE_FILESYSTEM_H - -#include "nvcore.h" - -namespace nv -{ - - namespace FileSystem - { - NVCORE_API bool exists(const char * path); - NVCORE_API bool createDirectory(const char * path); - NVCORE_API bool changeDirectory(const char * path); - NVCORE_API bool removeFile(const char * path); - - } // FileSystem namespace - -} // nv namespace - - -#endif // NV_CORE_FILESYSTEM_H diff --git a/thirdparty/thekla_atlas/nvcore/ForEach.h b/thirdparty/thekla_atlas/nvcore/ForEach.h deleted file mode 100644 index bc66f424ef..0000000000 --- a/thirdparty/thekla_atlas/nvcore/ForEach.h +++ /dev/null @@ -1,71 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_FOREACH_H -#define NV_CORE_FOREACH_H - -/* -These foreach macros are very non-standard and somewhat confusing, but I like them. -*/ - -#include "nvcore.h" - - -#if NV_CC_CPP11 - -#define NV_FOREACH(i, container) \ - for (auto i = (container).start(); !(container).isDone(i); (container).advance(i)) - -#elif NV_CC_GNUC // If typeof is available: - -/* -Ideally we would like to write this: - -#define NV_FOREACH(i, container) \ - for(decltype(container)::PseudoIndex i((container).start()); !(container).isDone(i); (container).advance(i)) - -But gcc versions prior to 4.7 required an intermediate type. See: -https://gcc.gnu.org/bugzilla/show_bug.cgi?id=6709 -*/ - -#define NV_FOREACH(i, container) \ - typedef typeof(container) NV_STRING_JOIN2(cont,__LINE__); \ - for(NV_STRING_JOIN2(cont,__LINE__)::PseudoIndex i((container).start()); !(container).isDone(i); (container).advance(i)) - -#else // If typeof not available: - -#define NV_NEED_PSEUDOINDEX_WRAPPER 1 - -#include <new> // placement new - -struct PseudoIndexWrapper { - template <typename T> - PseudoIndexWrapper(const T & container) { - nvStaticCheck(sizeof(typename T::PseudoIndex) <= sizeof(memory)); - new (memory) typename T::PseudoIndex(container.start()); - } - // PseudoIndex cannot have a dtor! - - template <typename T> typename T::PseudoIndex & operator()(const T * /*container*/) { - return *reinterpret_cast<typename T::PseudoIndex *>(memory); - } - template <typename T> const typename T::PseudoIndex & operator()(const T * /*container*/) const { - return *reinterpret_cast<const typename T::PseudoIndex *>(memory); - } - - uint8 memory[4]; // Increase the size if we have bigger enumerators. -}; - -#define NV_FOREACH(i, container) \ - for(PseudoIndexWrapper i(container); !(container).isDone(i(&(container))); (container).advance(i(&(container)))) - -#endif - -// Declare foreach keyword. -#if !defined NV_NO_USE_KEYWORDS -# define foreach NV_FOREACH -# define foreach_index NV_FOREACH -#endif - - -#endif // NV_CORE_FOREACH_H diff --git a/thirdparty/thekla_atlas/nvcore/Hash.h b/thirdparty/thekla_atlas/nvcore/Hash.h deleted file mode 100644 index a8b0b2c63b..0000000000 --- a/thirdparty/thekla_atlas/nvcore/Hash.h +++ /dev/null @@ -1,83 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_HASH_H -#define NV_CORE_HASH_H - -#include "nvcore.h" - -namespace nv -{ - inline uint sdbmHash(const void * data_in, uint size, uint h = 5381) - { - const uint8 * data = (const uint8 *) data_in; - uint i = 0; - while (i < size) { - h = (h << 16) + (h << 6) - h + (uint) data[i++]; - } - return h; - } - - // Note that this hash does not handle NaN properly. - inline uint sdbmFloatHash(const float * f, uint count, uint h = 5381) - { - for (uint i = 0; i < count; i++) { - //nvDebugCheck(nv::isFinite(*f)); - union { float f; uint32 i; } x = { f[i] }; - if (x.i == 0x80000000) x.i = 0; - h = sdbmHash(&x, 4, h); - } - return h; - } - - - template <typename T> - inline uint hash(const T & t, uint h = 5381) - { - return sdbmHash(&t, sizeof(T), h); - } - - template <> - inline uint hash(const float & f, uint h) - { - return sdbmFloatHash(&f, 1, h); - } - - - // Functors for hash table: - template <typename Key> struct Hash - { - uint 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; - } - }; - - - // @@ Move to Utils.h? - template <typename T1, typename T2> - struct Pair { - T1 first; - T2 second; - }; - - template <typename T1, typename T2> - bool operator==(const Pair<T1,T2> & p0, const Pair<T1,T2> & p1) { - return p0.first == p1.first && p0.second == p1.second; - } - - template <typename T1, typename T2> - uint hash(const Pair<T1,T2> & p, uint h = 5381) { - return hash(p.second, hash(p.first)); - } - - -} // nv namespace - -#endif // NV_CORE_HASH_H diff --git a/thirdparty/thekla_atlas/nvcore/HashMap.h b/thirdparty/thekla_atlas/nvcore/HashMap.h deleted file mode 100644 index 7856d6a8c9..0000000000 --- a/thirdparty/thekla_atlas/nvcore/HashMap.h +++ /dev/null @@ -1,174 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_HASHMAP_H -#define NV_CORE_HASHMAP_H - -/* -HashMap based on Thatcher Ulrich <tu@tulrich.com> container, donated to the Public Domain. - -I'd like to do something to reduce the amount of code generated with this template. The type of -U is largely irrelevant to the generated code, except for calls to constructors and destructors, -but the combination of all T and U pairs, generate a large amounts of code. - -HashMap is not used in NVTT, so it could be removed from the repository. -*/ - - -#include "Memory.h" -#include "Debug.h" -#include "ForEach.h" -#include "Hash.h" - -namespace nv -{ - class Stream; - - /** Thatcher Ulrich's hash table. - * - * Hash table, linear probing, internal chaining. One - * interesting/nice thing about this implementation is that the table - * itself is a flat chunk of memory containing no pointers, only - * relative indices. If the key and value types of the hash contain - * no pointers, then the hash can be serialized using raw IO. Could - * come in handy. - * - * Never shrinks, unless you explicitly clear() it. Expands on - * demand, though. For best results, if you know roughly how big your - * table will be, default it to that size when you create it. - */ - template<typename T, typename U, typename H = Hash<T>, typename E = Equal<T> > - class NVCORE_CLASS HashMap - { - NV_FORBID_COPY(HashMap); - public: - - /// Default ctor. - HashMap() : entry_count(0), size_mask(-1), table(NULL) { } - - /// Ctor with size hint. - explicit HashMap(int size_hint) : entry_count(0), size_mask(-1), table(NULL) { setCapacity(size_hint); } - - /// Dtor. - ~HashMap() { clear(); } - - - void set(const T& key, const U& value); - void add(const T& key, const U& value); - bool remove(const T& key); - void clear(); - bool isEmpty() const; - bool get(const T& key, U* value = NULL, T* other_key = NULL) const; - bool contains(const T & key) const; - int size() const; - int count() const; - int capacity() const; - void checkExpand(); - void resize(int n); - - void setCapacity(int new_size); - - // Behaves much like std::pair. - struct Entry - { - int next_in_chain; // internal chaining for collisions - uint hash_value; // avoids recomputing. Worthwhile? - T key; - U value; - - Entry() : next_in_chain(-2) {} - Entry(const Entry& e) : next_in_chain(e.next_in_chain), hash_value(e.hash_value), key(e.key), value(e.value) {} - Entry(const T& k, const U& v, int next, int hash) : next_in_chain(next), hash_value(hash), key(k), value(v) {} - - bool isEmpty() const { return next_in_chain == -2; } - bool isEndOfChain() const { return next_in_chain == -1; } - bool isTombstone() const { return hash_value == TOMBSTONE_HASH; } - - void clear() { - key.~T(); // placement delete - value.~U(); // placement delete - next_in_chain = -2; - hash_value = ~TOMBSTONE_HASH; - } - - void makeTombstone() { - key.~T(); - value.~U(); - hash_value = TOMBSTONE_HASH; - } - }; - - - // HashMap enumerator. - typedef int PseudoIndex; - PseudoIndex start() const { PseudoIndex i = 0; findNext(i); return i; } - bool isDone(const PseudoIndex & i) const { nvDebugCheck(i <= size_mask+1); return i == size_mask+1; }; - void advance(PseudoIndex & i) const { nvDebugCheck(i <= size_mask+1); i++; findNext(i); } - -#if NV_NEED_PSEUDOINDEX_WRAPPER - Entry & operator[]( const PseudoIndexWrapper & i ) { - Entry & e = entry(i(this)); - nvDebugCheck(e.isTombstone() == false); - return e; - } - const Entry & operator[]( const PseudoIndexWrapper & i ) const { - const Entry & e = entry(i(this)); - nvDebugCheck(e.isTombstone() == false); - return e; - } -#else - Entry & operator[](const PseudoIndex & i) { - Entry & e = entry(i); - nvDebugCheck(e.isTombstone() == false); - return e; - } - const Entry & operator[](const PseudoIndex & i) const { - const Entry & e = entry(i); - nvDebugCheck(e.isTombstone() == false); - return e; - } -#endif - - - // By default we serialize the key-value pairs compactl y. - template<typename _T, typename _U, typename _H, typename _E> - friend Stream & operator<< (Stream & s, HashMap<_T, _U, _H, _E> & map); - - // This requires more storage, but saves us from rehashing the elements. - template<typename _T, typename _U, typename _H, typename _E> - friend Stream & rawSerialize(Stream & s, HashMap<_T, _U, _H, _E> & map); - - /// Swap the members of this vector and the given vector. - template<typename _T, typename _U, typename _H, typename _E> - friend void swap(HashMap<_T, _U, _H, _E> & a, HashMap<_T, _U, _H, _E> & b); - - private: - static const uint TOMBSTONE_HASH = (uint) -1; - - uint compute_hash(const T& key) const; - - // Find the index of the matching entry. If no match, then return -1. - int findIndex(const T& key) const; - - // Return the index of the newly cleared element. - int removeTombstone(int index); - - // Helpers. - Entry & entry(int index); - const Entry & entry(int index) const; - - void setRawCapacity(int new_size); - - // Move the enumerator to the next valid element. - void findNext(PseudoIndex & i) const; - - - int entry_count; - int size_mask; - Entry * table; - - }; - -} // nv namespace - -#endif // NV_CORE_HASHMAP_H diff --git a/thirdparty/thekla_atlas/nvcore/HashMap.inl b/thirdparty/thekla_atlas/nvcore/HashMap.inl deleted file mode 100644 index f0b6bfea62..0000000000 --- a/thirdparty/thekla_atlas/nvcore/HashMap.inl +++ /dev/null @@ -1,550 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_HASHMAP_INL -#define NV_CORE_HASHMAP_INL - -#include "HashMap.h" - -#include "Stream.h" -#include "Utils.h" // swap - -#include <new> // for placement new - - -namespace nv -{ - - // Set a new or existing value under the key, to the value. - template<typename T, typename U, typename H, typename E> - void HashMap<T, U, H, E>::set(const T& key, const U& value) - { - int index = findIndex(key); - if (index >= 0) - { - entry(index).value = value; - return; - } - - // Entry under key doesn't exist. - add(key, value); - } - - - // Add a new value to the hash table, under the specified key. - template<typename T, typename U, typename H, typename E> - void HashMap<T, U, H, E>::add(const T& key, const U& value) - { - nvCheck(findIndex(key) == -1); - - checkExpand(); - nvCheck(table != NULL); - entry_count++; - - const uint hash_value = compute_hash(key); - const int index = hash_value & size_mask; - - Entry * natural_entry = &(entry(index)); - - if (natural_entry->isEmpty()) - { - // Put the new entry in. - new (natural_entry) Entry(key, value, -1, hash_value); - } - else if (natural_entry->isTombstone()) { - // Put the new entry in, without disturbing the rest of the chain. - int next_in_chain = natural_entry->next_in_chain; - new (natural_entry) Entry(key, value, next_in_chain, hash_value); - } - else - { - // Find a blank spot. - int blank_index = index; - for (int search_count = 0; ; search_count++) - { - blank_index = (blank_index + 1) & size_mask; - if (entry(blank_index).isEmpty()) break; // found it - if (entry(blank_index).isTombstone()) { - blank_index = removeTombstone(blank_index); - break; - } - nvCheck(search_count < this->size_mask); - } - Entry * blank_entry = &entry(blank_index); - - if (int(natural_entry->hash_value & size_mask) == index) - { - // Collision. Link into this chain. - - // Move existing list head. - new (blank_entry) Entry(*natural_entry); // placement new, copy ctor - - // Put the new info in the natural entry. - natural_entry->key = key; - natural_entry->value = value; - natural_entry->next_in_chain = blank_index; - natural_entry->hash_value = hash_value; - } - else - { - // Existing entry does not naturally - // belong in this slot. Existing - // entry must be moved. - - // Find natural location of collided element (i.e. root of chain) - int collided_index = natural_entry->hash_value & size_mask; - for (int search_count = 0; ; search_count++) - { - Entry * e = &entry(collided_index); - if (e->next_in_chain == index) - { - // Here's where we need to splice. - new (blank_entry) Entry(*natural_entry); - e->next_in_chain = blank_index; - break; - } - collided_index = e->next_in_chain; - nvCheck(collided_index >= 0 && collided_index <= size_mask); - nvCheck(search_count <= size_mask); - } - - // Put the new data in the natural entry. - natural_entry->key = key; - natural_entry->value = value; - natural_entry->hash_value = hash_value; - natural_entry->next_in_chain = -1; - } - } - } - - - // Remove the first value under the specified key. - template<typename T, typename U, typename H, typename E> - bool HashMap<T, U, H, E>::remove(const T& key) - { - if (table == NULL) - { - return false; - } - - int index = findIndex(key); - if (index < 0) - { - return false; - } - - Entry * pos = &entry(index); - - int natural_index = (int) (pos->hash_value & size_mask); - - if (index != natural_index) { - // We're not the head of our chain, so we can - // be spliced out of it. - - // Iterate up the chain, and splice out when - // we get to m_index. - Entry* e = &entry(natural_index); - while (e->next_in_chain != index) { - nvDebugCheck(e->isEndOfChain() == false); - e = &entry(e->next_in_chain); - } - - if (e->isTombstone() && pos->isEndOfChain()) { - // Tombstone has nothing else to point - // to, so mark it empty. - e->next_in_chain = -2; - } else { - e->next_in_chain = pos->next_in_chain; - } - - pos->clear(); - } - else if (pos->isEndOfChain() == false) { - // We're the head of our chain, and there are - // additional elements. - // - // We need to put a tombstone here. - // - // We can't clear the element, because the - // rest of the elements in the chain must be - // linked to this position. - // - // We can't move any of the succeeding - // elements in the chain (i.e. to fill this - // entry), because we don't want to invalidate - // any other existing iterators. - pos->makeTombstone(); - } else { - // We're the head of the chain, but we're the - // only member of the chain. - pos->clear(); - } - - entry_count--; - - return true; - } - - - // Remove all entries from the hash table. - template<typename T, typename U, typename H, typename E> - void HashMap<T, U, H, E>::clear() - { - if (table != NULL) - { - // Delete the entries. - for (int i = 0, n = size_mask; i <= n; i++) - { - Entry * e = &entry(i); - if (e->isEmpty() == false && e->isTombstone() == false) - { - e->clear(); - } - } - free(table); - table = NULL; - entry_count = 0; - size_mask = -1; - } - } - - - // Returns true if the hash is empty. - template<typename T, typename U, typename H, typename E> - bool HashMap<T, U, H, E>::isEmpty() const - { - return table == NULL || entry_count == 0; - } - - - // Retrieve the value under the given key. - // - If there's no value under the key, then return false and leave *value alone. - // - If there is a value, return true, and set *value to the entry's value. - // - If value == NULL, return true or false according to the presence of the key, but don't touch *value. - template<typename T, typename U, typename H, typename E> - bool HashMap<T, U, H, E>::get(const T& key, U* value/*= NULL*/, T* other_key/*= NULL*/) const - { - int index = findIndex(key); - if (index >= 0) - { - if (value != NULL) { - *value = entry(index).value; // take care with side-effects! - } - if (other_key != NULL) { - *other_key = entry(index).key; - } - return true; - } - return false; - } - - // Determine if the given key is contained in the hash. - template<typename T, typename U, typename H, typename E> - bool HashMap<T, U, H, E>::contains(const T & key) const - { - return get(key); - } - - // Number of entries in the hash. - template<typename T, typename U, typename H, typename E> - int HashMap<T, U, H, E>::size() const - { - return entry_count; - } - - // Number of entries in the hash. - template<typename T, typename U, typename H, typename E> - int HashMap<T, U, H, E>::count() const - { - return size(); - } - - template<typename T, typename U, typename H, typename E> - int HashMap<T, U, H, E>::capacity() const - { - return size_mask+1; - } - - - // Resize the hash table to fit one more entry. Often this doesn't involve any action. - template<typename T, typename U, typename H, typename E> - void HashMap<T, U, H, E>::checkExpand() - { - if (table == NULL) { - // Initial creation of table. Make a minimum-sized table. - setRawCapacity(16); - } - else if (entry_count * 3 > (size_mask + 1) * 2) { - // Table is more than 2/3rds full. Expand. - setRawCapacity(entry_count * 2); - } - } - - - // Hint the bucket count to >= n. - template<typename T, typename U, typename H, typename E> - void HashMap<T, U, H, E>::resize(int n) - { - // Not really sure what this means in relation to - // STLport's hash_map... they say they "increase the - // bucket count to at least n" -- but does that mean - // their real capacity after resize(n) is more like - // n*2 (since they do linked-list chaining within - // buckets?). - setCapacity(n); - } - - - // Size the hash so that it can comfortably contain the given number of elements. If the hash already contains more - // elements than new_size, then this may be a no-op. - template<typename T, typename U, typename H, typename E> - void HashMap<T, U, H, E>::setCapacity(int new_size) - { - int new_raw_size = (new_size * 3) / 2; - if (new_raw_size < size()) { return; } - - setRawCapacity(new_raw_size); - } - - - // By default we serialize the key-value pairs compactly. - template<typename _T, typename _U, typename _H, typename _E> - Stream & operator<< (Stream & s, HashMap<_T, _U, _H, _E> & map) - { - typedef typename HashMap<_T, _U, _H, _E>::Entry HashMapEntry; - - int entry_count = map.entry_count; - s << entry_count; - - if (s.isLoading()) { - map.clear(); - if(entry_count == 0) { - return s; - } - map.entry_count = entry_count; - map.size_mask = nextPowerOfTwo(U32(entry_count)) - 1; - map.table = malloc<HashMapEntry>(map.size_mask + 1); - - for (int i = 0; i <= map.size_mask; i++) { - map.table[i].next_in_chain = -2; // mark empty - } - - _T key; - _U value; - for (int i = 0; i < entry_count; i++) { - s << key << value; - map.add(key, value); - } - } - else { - int i = 0; - map.findNext(i); - while (i != map.size_mask+1) { - HashMapEntry & e = map.entry(i); - - s << e.key << e.value; - - i++; - map.findNext(i); - } - //for(HashMap<_T, _U, _H, _E>::PseudoIndex i((map).start()); !(map).isDone(i); (map).advance(i)) { - //foreach(i, map) { - // s << map[i].key << map[i].value; - //} - } - - return s; - } - - // This requires more storage, but saves us from rehashing the elements. - template<typename _T, typename _U, typename _H, typename _E> - Stream & rawSerialize(Stream & s, HashMap<_T, _U, _H, _E> & map) - { - typedef typename HashMap<_T, _U, _H, _E>::Entry HashMapEntry; - - if (s.isLoading()) { - map.clear(); - } - - s << map.size_mask; - - if (map.size_mask != -1) { - s << map.entry_count; - - if (s.isLoading()) { - map.table = new HashMapEntry[map.size_mask+1]; - } - - for (int i = 0; i <= map.size_mask; i++) { - HashMapEntry & e = map.table[i]; - s << e.next_in_chain << e.hash_value; - s << e.key; - s << e.value; - } - } - - return s; - } - - // Swap the members of this vector and the given vector. - template<typename _T, typename _U, typename _H, typename _E> - void swap(HashMap<_T, _U, _H, _E> & a, HashMap<_T, _U, _H, _E> & b) - { - swap(a.entry_count, b.entry_count); - swap(a.size_mask, b.size_mask); - swap(a.table, b.table); - } - - - template<typename T, typename U, typename H, typename E> - uint HashMap<T, U, H, E>::compute_hash(const T& key) const - { - H hash; - uint hash_value = hash(key); - if (hash_value == TOMBSTONE_HASH) { - hash_value ^= 0x8000; - } - return hash_value; - } - - // Find the index of the matching entry. If no match, then return -1. - template<typename T, typename U, typename H, typename E> - int HashMap<T, U, H, E>::findIndex(const T& key) const - { - if (table == NULL) return -1; - - E equal; - - uint hash_value = compute_hash(key); - int index = hash_value & size_mask; - - const Entry * e = &entry(index); - if (e->isEmpty()) return -1; - if (e->isTombstone() == false && int(e->hash_value & size_mask) != index) { - // occupied by a collider - return -1; - } - - for (;;) - { - nvCheck(e->isTombstone() || (e->hash_value & size_mask) == (hash_value & size_mask)); - - if (e->hash_value == hash_value && equal(e->key, key)) - { - // Found it. - return index; - } - nvDebugCheck(e->isTombstone() || !equal(e->key, key)); // keys are equal, but hash differs! - - // Keep looking through the chain. - index = e->next_in_chain; - if (index == -1) break; // end of chain - - nvCheck(index >= 0 && index <= size_mask); - e = &entry(index); - - nvCheck(e->isEmpty() == false || e->isTombstone()); - } - return -1; - } - - // Return the index of the newly cleared element. - template<typename T, typename U, typename H, typename E> - int HashMap<T, U, H, E>::removeTombstone(int index) { - Entry* e = &entry(index); - nvCheck(e->isTombstone()); - nvCheck(!e->isEndOfChain()); - - // Move the next element of the chain into the - // tombstone slot, and return the vacated element. - int new_blank_index = e->next_in_chain; - Entry* new_blank = &entry(new_blank_index); - new (e) Entry(*new_blank); - new_blank->clear(); - return new_blank_index; - } - - // Helpers. - template<typename T, typename U, typename H, typename E> - typename HashMap<T, U, H, E>::Entry & HashMap<T, U, H, E>::entry(int index) - { - nvDebugCheck(table != NULL); - nvDebugCheck(index >= 0 && index <= size_mask); - return table[index]; - } - template<typename T, typename U, typename H, typename E> - const typename HashMap<T, U, H, E>::Entry & HashMap<T, U, H, E>::entry(int index) const - { - nvDebugCheck(table != NULL); - nvDebugCheck(index >= 0 && index <= size_mask); - return table[index]; - } - - - // Resize the hash table to the given size (Rehash the contents of the current table). The arg is the number of - // hash table entries, not the number of elements we should actually contain (which will be less than this). - template<typename T, typename U, typename H, typename E> - void HashMap<T, U, H, E>::setRawCapacity(int new_size) - { - if (new_size <= 0) { - // Special case. - clear(); - return; - } - - // Force new_size to be a power of two. - new_size = nextPowerOfTwo(U32(new_size)); - - HashMap<T, U, H, E> new_hash; - new_hash.table = malloc<Entry>(new_size); - nvDebugCheck(new_hash.table != NULL); - - new_hash.entry_count = 0; - new_hash.size_mask = new_size - 1; - for (int i = 0; i < new_size; i++) - { - new_hash.entry(i).next_in_chain = -2; // mark empty - } - - // Copy stuff to new_hash - if (table != NULL) - { - for (int i = 0, n = size_mask; i <= n; i++) - { - Entry * e = &entry(i); - if (e->isEmpty() == false && e->isTombstone() == false) - { - // Insert old entry into new hash. - new_hash.add(e->key, e->value); - e->clear(); // placement delete of old element - } - } - - // Delete our old data buffer. - free(table); - } - - // Steal new_hash's data. - entry_count = new_hash.entry_count; - size_mask = new_hash.size_mask; - table = new_hash.table; - new_hash.entry_count = 0; - new_hash.size_mask = -1; - new_hash.table = NULL; - } - - // Move the enumerator to the next valid element. - template<typename T, typename U, typename H, typename E> - void HashMap<T, U, H, E>::findNext(PseudoIndex & i) const { - while (i <= size_mask) { - const Entry & e = entry(i); - if (e.isEmpty() == false && e.isTombstone() == false) { - break; - } - i++; - } - } - -} // nv namespace - -#endif // NV_CORE_HASHMAP_INL diff --git a/thirdparty/thekla_atlas/nvcore/Memory.cpp b/thirdparty/thekla_atlas/nvcore/Memory.cpp deleted file mode 100644 index 302a2d84cb..0000000000 --- a/thirdparty/thekla_atlas/nvcore/Memory.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "Memory.h" -#include "Debug.h" -#include "Utils.h" - -#include <stdlib.h> - -#ifdef NV_OS_LINUX -#include <malloc.h> -#endif - -#define USE_EFENCE 0 - -#if USE_EFENCE -extern "C" void *EF_malloc(size_t size); -extern "C" void *EF_realloc(void * oldBuffer, size_t newSize); -extern "C" void EF_free(void * address); -#endif - -using namespace nv; - -#if NV_OVERRIDE_ALLOC - -void * malloc(size_t size) -{ -#if USE_EFENCE - return EF_malloc(size); -#else - return ::malloc(size); -#endif -} - -void * debug_malloc(size_t size, const char * file, int line) -{ - NV_UNUSED(file); - NV_UNUSED(line); -#if USE_EFENCE - return EF_malloc(size); -#else - return ::malloc(size); -#endif -} - -void free(void * ptr) -{ -#if USE_EFENCE - return EF_free(const_cast<void *>(ptr)); -#else - ::free(const_cast<void *>(ptr)); -#endif -} - -void * realloc(void * ptr, size_t size) -{ - nvDebugCheck(ptr != NULL || size != 0); // undefined realloc behavior. -#if USE_EFENCE - return EF_realloc(ptr, size); -#else - return ::realloc(ptr, size); -#endif -} - - -/* No need to override this unless we want line info. -void * operator new (size_t size) throw() -{ - return malloc(size); -} - -void operator delete (void *p) throw() -{ - free(p); -} - -void * operator new [] (size_t size) throw() -{ - return malloc(size); -} - -void operator delete [] (void * p) throw() -{ - free(p); -} -*/ - -#if 0 // Code from Apple: -void* operator new(std::size_t sz) throw (std::bad_alloc) -{ - void *result = std::malloc (sz == 0 ? 1 : sz); - if (result == NULL) - throw std::bad_alloc(); - gNewCounter++; - return result; -} -void operator delete(void* p) throw() -{ - if (p == NULL) - return; - std::free (p); - gDeleteCounter++; -} - -/* These are the 'nothrow' versions of the above operators. - The system version will try to call a std::new_handler if they - fail, but your overriding versions are not required to do this. */ -void* operator new(std::size_t sz, const std::nothrow_t&) throw() -{ - try { - void * result = ::operator new (sz); // calls our overridden operator new - return result; - } catch (std::bad_alloc &) { - return NULL; - } -} -void operator delete(void* p, const std::nothrow_t&) throw() -{ - ::operator delete (p); -} - -#endif // 0 - -#endif // NV_OVERRIDE_ALLOC - -void * nv::aligned_malloc(size_t size, size_t alignment) -{ - // alignment must be a power of two, multiple of sizeof(void*) - nvDebugCheck(isPowerOfTwo(alignment)); - nvDebugCheck((alignment & (sizeof(void*) - 1)) == 0); - -#if NV_OS_WIN32 || NV_OS_DURANGO - return _aligned_malloc(size, alignment); -#elif NV_OS_DARWIN && !NV_OS_IOS - void * ptr = NULL; - posix_memalign(&ptr, alignment, size); - return ptr; -#elif NV_OS_LINUX - return memalign(alignment, size); -#else // NV_OS_ORBIS || NV_OS_IOS - // @@ IC: iOS appears to be 16 byte aligned, should we check alignment and assert if we request a higher alignment factor? - return ::malloc(size); -#endif -} - -void nv::aligned_free(void * ptr) -{ -#if NV_OS_WIN32 || NV_OS_DURANGO - _aligned_free(ptr); -#else - ::free(ptr); -#endif -} - diff --git a/thirdparty/thekla_atlas/nvcore/Memory.h b/thirdparty/thekla_atlas/nvcore/Memory.h deleted file mode 100644 index 1f71b60947..0000000000 --- a/thirdparty/thekla_atlas/nvcore/Memory.h +++ /dev/null @@ -1,72 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_MEMORY_H -#define NV_CORE_MEMORY_H - -#include "nvcore.h" - -#include <stdlib.h> // malloc(), realloc() and free() -#include <string.h> // memset -//#include <stddef.h> // size_t - -//#include <new> // new and delete - -#define TRACK_MEMORY_LEAKS 0 -#if TRACK_MEMORY_LEAKS -#include <vld.h> -#endif - - -#if NV_CC_GNUC -# define NV_ALIGN_16 __attribute__ ((__aligned__ (16))) -#else -# define NV_ALIGN_16 __declspec(align(16)) -#endif - - -#define NV_OVERRIDE_ALLOC 0 - -#if NV_OVERRIDE_ALLOC - -// Custom memory allocator -extern "C" { - NVCORE_API void * malloc(size_t size); - NVCORE_API void * debug_malloc(size_t size, const char * file, int line); - NVCORE_API void free(void * ptr); - NVCORE_API void * realloc(void * ptr, size_t size); -} - -/* -#ifdef _DEBUG -#define new new(__FILE__, __LINE__) -#define malloc(i) debug_malloc(i, __FILE__, __LINE__) -#endif -*/ - -#endif - -namespace nv { - NVCORE_API void * aligned_malloc(size_t size, size_t alignment); - NVCORE_API void aligned_free(void * ); - - // C++ helpers. - template <typename T> NV_FORCEINLINE T * malloc(size_t count) { - return (T *)::malloc(sizeof(T) * count); - } - - template <typename T> NV_FORCEINLINE T * realloc(T * ptr, size_t count) { - return (T *)::realloc(ptr, sizeof(T) * count); - } - - template <typename T> NV_FORCEINLINE void free(const T * ptr) { - ::free((void *)ptr); - } - - template <typename T> NV_FORCEINLINE void zero(T & data) { - memset(&data, 0, sizeof(T)); - } - -} // nv namespace - -#endif // NV_CORE_MEMORY_H diff --git a/thirdparty/thekla_atlas/nvcore/Ptr.h b/thirdparty/thekla_atlas/nvcore/Ptr.h deleted file mode 100644 index b43039274b..0000000000 --- a/thirdparty/thekla_atlas/nvcore/Ptr.h +++ /dev/null @@ -1,322 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#ifndef NV_CORE_PTR_H -#define NV_CORE_PTR_H - -#include "nvcore.h" -#include "Debug.h" - -#include "RefCounted.h" - -namespace nv -{ - class WeakProxy; - - /** Simple auto pointer template class. - * - * This is very similar to the standard auto_ptr class, but with some - * additional limitations to make its use less error prone: - * - Copy constructor and assignment operator are disabled. - * - reset method is removed. - * - * The semantics of the standard auto_ptr are not clear and change depending - * on the std implementation. For a discussion of the problems of auto_ptr read: - * http://www.awprofessional.com/content/images/020163371X/autoptrupdate\auto_ptr_update.html - */ - template <class T> - class AutoPtr - { - NV_FORBID_COPY(AutoPtr); - NV_FORBID_HEAPALLOC(); - public: - - /// Ctor. - AutoPtr(T * p = NULL) : m_ptr(p) { } - - template <class Q> - AutoPtr(Q * p) : m_ptr(static_cast<T *>(p)) { } - - /// Dtor. Deletes owned pointer. - ~AutoPtr() { - delete m_ptr; - m_ptr = NULL; - } - - /// Delete owned pointer and assign new one. - void operator=( T * p ) { - if (p != m_ptr) - { - delete m_ptr; - m_ptr = p; - } - } - - template <class Q> - void operator=( Q * p ) { - if (p != m_ptr) - { - delete m_ptr; - m_ptr = static_cast<T *>(p); - } - } - - /// Member access. - T * operator -> () const { - nvDebugCheck(m_ptr != NULL); - return m_ptr; - } - - /// Get reference. - T & operator*() const { - nvDebugCheck(m_ptr != NULL); - return *m_ptr; - } - - /// Get pointer. - T * ptr() const { return m_ptr; } - - /// Relinquish ownership of the underlying pointer and returns that pointer. - T * release() { - T * tmp = m_ptr; - m_ptr = NULL; - return tmp; - } - - /// Const pointer equal comparation. - friend bool operator == (const AutoPtr<T> & ap, const T * const p) { - return (ap.ptr() == p); - } - - /// Const pointer nequal comparation. - friend bool operator != (const AutoPtr<T> & ap, const T * const p) { - return (ap.ptr() != p); - } - - /// Const pointer equal comparation. - friend bool operator == (const T * const p, const AutoPtr<T> & ap) { - return (ap.ptr() == p); - } - - /// Const pointer nequal comparation. - friend bool operator != (const T * const p, const AutoPtr<T> & ap) { - return (ap.ptr() != p); - } - - private: - T * m_ptr; - }; - - - /// Smart pointer template class. - template <class BaseClass> - class SmartPtr { - public: - - // BaseClass must implement addRef() and release(). - typedef SmartPtr<BaseClass> ThisType; - - /// Default ctor. - SmartPtr() : m_ptr(NULL) - { - } - - /// Other type assignment. - template <class OtherBase> - SmartPtr( const SmartPtr<OtherBase> & tc ) - { - m_ptr = static_cast<BaseClass *>( tc.ptr() ); - if (m_ptr) { - m_ptr->addRef(); - } - } - - /// Copy ctor. - SmartPtr( const ThisType & bc ) - { - m_ptr = bc.ptr(); - if (m_ptr) { - m_ptr->addRef(); - } - } - - /// Copy cast ctor. SmartPtr(NULL) is valid. - explicit SmartPtr( BaseClass * bc ) - { - m_ptr = bc; - if (m_ptr) { - m_ptr->addRef(); - } - } - - /// Dtor. - ~SmartPtr() - { - set(NULL); - } - - - /// -> operator. - BaseClass * operator -> () const - { - nvCheck( m_ptr != NULL ); - return m_ptr; - } - - /// * operator. - BaseClass & operator*() const - { - nvCheck( m_ptr != NULL ); - return *m_ptr; - } - - /// Get pointer. - BaseClass * ptr() const - { - return m_ptr; - } - - /// Other type assignment. - template <class OtherBase> - void operator = ( const SmartPtr<OtherBase> & tc ) - { - set( static_cast<BaseClass *>(tc.ptr()) ); - } - - /// This type assignment. - void operator = ( const ThisType & bc ) - { - set( bc.ptr() ); - } - - /// Pointer assignment. - void operator = ( BaseClass * bc ) - { - set( bc ); - } - - - /// Other type equal comparation. - template <class OtherBase> - bool operator == ( const SmartPtr<OtherBase> & other ) const - { - return m_ptr == other.ptr(); - } - - /// This type equal comparation. - bool operator == ( const ThisType & bc ) const - { - return m_ptr == bc.ptr(); - } - - /// Const pointer equal comparation. - bool operator == ( const BaseClass * const bc ) const - { - return m_ptr == bc; - } - - /// Other type not equal comparation. - template <class OtherBase> - bool operator != ( const SmartPtr<OtherBase> & other ) const - { - return m_ptr != other.ptr(); - } - - /// Other type not equal comparation. - bool operator != ( const ThisType & bc ) const - { - return m_ptr != bc.ptr(); - } - - /// Const pointer not equal comparation. - bool operator != (const BaseClass * const bc) const - { - return m_ptr != bc; - } - - /// This type lower than comparation. - bool operator < (const ThisType & p) const - { - return m_ptr < p.ptr(); - } - - bool isValid() const { - return isValidPtr(m_ptr); - } - - private: - - // Set this pointer. - void set( BaseClass * p ) - { - if (p) p->addRef(); - if (m_ptr) m_ptr->release(); - m_ptr = p; - } - - private: - - BaseClass * m_ptr; - - }; - - - /// Smart pointer template class. - template <class T> - class WeakPtr { - public: - - WeakPtr() {} - - WeakPtr(T * p) { operator=(p); } - WeakPtr(const SmartPtr<T> & p) { operator=(p.ptr()); } - - // Default constructor and assignment from weak_ptr<T> are OK. - - void operator=(T * p) - { - if (p) { - m_proxy = p->getWeakProxy(); - nvDebugCheck(m_proxy != NULL); - nvDebugCheck(m_proxy->ptr() == p); - } - else { - m_proxy = NULL; - } - } - - void operator=(const SmartPtr<T> & ptr) { operator=(ptr.ptr()); } - - bool operator==(const SmartPtr<T> & p) const { return ptr() == p.ptr(); } - bool operator!=(const SmartPtr<T> & p) const { return ptr() != p.ptr(); } - - bool operator==(const WeakPtr<T> & p) const { return ptr() == p.ptr(); } - bool operator!=(const WeakPtr<T> & p) const { return ptr() != p.ptr(); } - - bool operator==(T * p) const { return ptr() == p; } - bool operator!=(T * p) const { return ptr() != p; } - - T * operator->() const - { - T * p = ptr(); - nvDebugCheck(p != NULL); - return p; - } - - T * ptr() const - { - if (m_proxy != NULL) { - return static_cast<T *>(m_proxy->ptr()); - } - return NULL; - } - - private: - - mutable SmartPtr<WeakProxy> m_proxy; - - }; - - -} // nv namespace - -#endif // NV_CORE_PTR_H diff --git a/thirdparty/thekla_atlas/nvcore/RadixSort.cpp b/thirdparty/thekla_atlas/nvcore/RadixSort.cpp deleted file mode 100644 index 3f44620c99..0000000000 --- a/thirdparty/thekla_atlas/nvcore/RadixSort.cpp +++ /dev/null @@ -1,285 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "RadixSort.h" - -#include "Utils.h" - -#include <string.h> // memset - -using namespace nv; - -static inline void FloatFlip(uint32 & f) { - //uint32 mask = -int32(f >> 31) | 0x80000000; // Michael Herf. - int32 mask = (int32(f) >> 31) | 0x80000000; // Warren Hunt, Manchor Ko. - f ^= mask; -} - -static inline void IFloatFlip(uint32 & f) { - uint32 mask = ((f >> 31) - 1) | 0x80000000; // Michael Herf. - //uint32 mask = (int32(f ^ 0x80000000) >> 31) | 0x80000000; // Warren Hunt, Manchor Ko. @@ Correct, but fails in release on gcc-4.2.1 - f ^= mask; -} - - -template<typename T> -void createHistograms(const T * buffer, uint count, uint * histogram) -{ - const uint bucketCount = sizeof(T); // (8 * sizeof(T)) / log2(radix) - - // Init bucket pointers. - uint * h[bucketCount]; - for (uint i = 0; i < bucketCount; i++) { -#if NV_BIG_ENDIAN - h[sizeof(T)-1-i] = histogram + 256 * i; -#else - h[i] = histogram + 256 * i; -#endif - } - - // Clear histograms. - memset(histogram, 0, 256 * bucketCount * sizeof(uint)); - - // @@ Add support for signed integers. - - // Build histograms. - const uint8 * p = (const uint8 *)buffer; // @@ Does this break aliasing rules? - const uint8 * pe = p + count * sizeof(T); - - while (p != pe) { - h[0][*p++]++, h[1][*p++]++, h[2][*p++]++, h[3][*p++]++; - if (bucketCount == 8) h[4][*p++]++, h[5][*p++]++, h[6][*p++]++, h[7][*p++]++; - } -} - -/* -template <> -void createHistograms<float>(const float * buffer, uint count, uint * histogram) -{ - // Init bucket pointers. - uint32 * h[4]; - for (uint i = 0; i < 4; i++) { -#if NV_BIG_ENDIAN - h[3-i] = histogram + 256 * i; -#else - h[i] = histogram + 256 * i; -#endif - } - - // Clear histograms. - memset(histogram, 0, 256 * 4 * sizeof(uint32)); - - // Build histograms. - for (uint i = 0; i < count; i++) { - uint32 fi = FloatFlip(buffer[i]); - - h[0][fi & 0xFF]++; - h[1][(fi >> 8) & 0xFF]++; - h[2][(fi >> 16) & 0xFF]++; - h[3][fi >> 24]++; - } -} -*/ - -RadixSort::RadixSort() : m_size(0), m_ranks(NULL), m_ranks2(NULL), m_validRanks(false) -{ -} - -RadixSort::RadixSort(uint reserve_count) : m_size(0), m_ranks(NULL), m_ranks2(NULL), m_validRanks(false) -{ - checkResize(reserve_count); -} - -RadixSort::~RadixSort() -{ - // Release everything - free(m_ranks2); - free(m_ranks); -} - - -void RadixSort::resize(uint count) -{ - m_ranks2 = realloc<uint>(m_ranks2, count); - m_ranks = realloc<uint>(m_ranks, count); -} - -inline void RadixSort::checkResize(uint count) -{ - if (count != m_size) - { - if (count > m_size) resize(count); - m_size = count; - m_validRanks = false; - } -} - -template <typename T> inline void RadixSort::insertionSort(const T * input, uint count) -{ - if (!m_validRanks) { - /*for (uint i = 0; i < count; i++) { - m_ranks[i] = i; - }*/ - - m_ranks[0] = 0; - for (uint i = 1; i != count; ++i) - { - int rank = m_ranks[i] = i; - - uint 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 (uint i = 1; i != count; ++i) - { - int rank = m_ranks[i]; - - uint 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> inline void RadixSort::radixSort(const T * input, uint count) -{ - const uint P = sizeof(T); // pass count - - // Allocate histograms & offsets on the stack - uint histogram[256 * P]; - uint * link[256]; - - createHistograms(input, count, histogram); - - // Radix sort, j is the pass number (0=LSB, P=MSB) - for (uint j = 0; j < P; j++) - { - // Pointer to this bucket. - const uint * h = &histogram[j * 256]; - - const uint8 * inputBytes = (const uint8*)input; // @@ Is this aliasing legal? - -#if NV_BIG_ENDIAN - inputBytes += P - 1 - j; -#else - inputBytes += j; -#endif - - if (h[inputBytes[0]] == count) { - // Skip this pass, all values are the same. - continue; - } - - // Create offsets - link[0] = m_ranks2; - for (uint i = 1; i < 256; i++) link[i] = link[i-1] + h[i-1]; - - // Perform Radix Sort - if (!m_validRanks) - { - for (uint i = 0; i < count; i++) - { - *link[inputBytes[i*P]]++ = i; - } - m_validRanks = true; - } - else - { - for (uint i = 0; i < count; i++) - { - const uint 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. - swap(m_ranks, m_ranks2); - } - - // All values were equal, generate linear ranks. - if (!m_validRanks) - { - for (uint i = 0; i < count; i++) - { - m_ranks[i] = i; - } - m_validRanks = true; - } -} - - -RadixSort & RadixSort::sort(const uint32 * input, uint count) -{ - if (input == NULL || count == 0) return *this; - - // Resize lists if needed - checkResize(count); - - if (count < 32) { - insertionSort(input, count); - } - else { - radixSort<uint32>(input, count); - } - return *this; -} - - -RadixSort & RadixSort::sort(const uint64 * input, uint count) -{ - if (input == NULL || count == 0) return *this; - - // Resize lists if needed - checkResize(count); - - if (count < 64) { - insertionSort(input, count); - } - else { - radixSort(input, count); - } - return *this; -} - -RadixSort& RadixSort::sort(const float * input, uint count) -{ - if (input == NULL || count == 0) return *this; - - // Resize lists if needed - checkResize(count); - - if (count < 32) { - insertionSort(input, count); - } - else { - // @@ Avoid touching the input multiple times. - for (uint i = 0; i < count; i++) { - FloatFlip((uint32 &)input[i]); - } - - radixSort<uint32>((const uint32 *)input, count); - - for (uint i = 0; i < count; i++) { - IFloatFlip((uint32 &)input[i]); - } - } - - return *this; -} diff --git a/thirdparty/thekla_atlas/nvcore/RadixSort.h b/thirdparty/thekla_atlas/nvcore/RadixSort.h deleted file mode 100644 index 82325ebb24..0000000000 --- a/thirdparty/thekla_atlas/nvcore/RadixSort.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -#ifndef NV_CORE_RADIXSORT_H -#define NV_CORE_RADIXSORT_H - -// Based on Pierre Terdiman's and Michael Herf's source code. -// http://www.codercorner.com/RadixSortRevisited.htm -// http://www.stereopsis.com/radix.html - -#include "nvcore.h" -#include "Array.h" - -namespace nv -{ - - class NVCORE_CLASS RadixSort - { - NV_FORBID_COPY(RadixSort); - public: - // Constructor/Destructor - RadixSort(); - RadixSort(uint reserve_count); - ~RadixSort(); - - // Invalidate ranks. - RadixSort & reset() { m_validRanks = false; return *this; } - - // Sorting methods. - RadixSort & sort(const uint32 * input, uint count); - RadixSort & sort(const uint64 * input, uint count); - RadixSort & sort(const float * input, uint count); - - // Helpers. - RadixSort & sort(const Array<uint32> & input); - RadixSort & sort(const Array<uint64> & input); - RadixSort & sort(const Array<float> & input); - - // Access to results. m_ranks is a list of indices in sorted order, i.e. in the order you may further process your data - inline const uint * ranks() const { nvDebugCheck(m_validRanks); return m_ranks; } - inline uint * ranks() { nvDebugCheck(m_validRanks); return m_ranks; } - inline uint rank(uint i) const { nvDebugCheck(m_validRanks); return m_ranks[i]; } - - // query whether the sort has been performed - inline bool valid() const { return m_validRanks; } - - private: - uint m_size; - uint * m_ranks; - uint * m_ranks2; - bool m_validRanks; - - // Internal methods - template <typename T> void insertionSort(const T * input, uint count); - template <typename T> void radixSort(const T * input, uint count); - - void checkResize(uint nb); - void resize(uint nb); - }; - - inline RadixSort & RadixSort::sort(const Array<uint32> & input) { - return sort(input.buffer(), input.count()); - } - - inline RadixSort & RadixSort::sort(const Array<uint64> & input) { - return sort(input.buffer(), input.count()); - } - - inline RadixSort & RadixSort::sort(const Array<float> & input) { - return sort(input.buffer(), input.count()); - } - -} // nv namespace - - - -#endif // NV_CORE_RADIXSORT_H diff --git a/thirdparty/thekla_atlas/nvcore/RefCounted.h b/thirdparty/thekla_atlas/nvcore/RefCounted.h deleted file mode 100644 index b8d68edee3..0000000000 --- a/thirdparty/thekla_atlas/nvcore/RefCounted.h +++ /dev/null @@ -1,149 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#ifndef NV_CORE_REFCOUNTED_H -#define NV_CORE_REFCOUNTED_H - -#include "nvcore.h" -#include "Debug.h" - -#define NV_DECLARE_PTR(Class) \ - template <class T> class SmartPtr; \ - typedef SmartPtr<class Class> Class ## Ptr; \ - typedef SmartPtr<const class Class> Class ## ConstPtr - - -namespace nv -{ - /// Weak proxy. - class WeakProxy - { - NV_FORBID_COPY(WeakProxy); - public: - /// Ctor. - WeakProxy(void * ptr) : m_count(0), m_ptr(ptr) { } - - /// Dtor. - ~WeakProxy() - { - nvCheck( m_count == 0 ); - } - - /// Increase reference count. - uint addRef() const - { - m_count++; - return m_count; - } - - /// Decrease reference count and remove when 0. - uint release() const - { - nvCheck( m_count > 0 ); - - m_count--; - if( m_count == 0 ) { - delete this; - return 0; - } - return m_count; - } - - /// WeakPtr's call this to determine if their pointer is valid or not. - bool isAlive() const { - return m_ptr != NULL; - } - - /// Only the actual object should call this. - void notifyObjectDied() { - m_ptr = NULL; - } - - /// Return proxy pointer. - void * ptr() const { - return m_ptr; - } - - private: - mutable int m_count; - void * m_ptr; - }; - - - /// Reference counted base class to be used with SmartPtr and WeakPtr. - class RefCounted - { - NV_FORBID_COPY(RefCounted); - public: - - /// Ctor. - RefCounted() : m_count(0), m_weak_proxy(NULL) - { - } - - /// Virtual dtor. - virtual ~RefCounted() - { - nvCheck( m_count == 0 ); - releaseWeakProxy(); - } - - - /// Increase reference count. - uint addRef() const - { - m_count++; - return m_count; - } - - - /// Decrease reference count and remove when 0. - uint release() const - { - nvCheck( m_count > 0 ); - - m_count--; - if( m_count == 0 ) { - delete this; - return 0; - } - return m_count; - } - - /// Get weak proxy. - WeakProxy * getWeakProxy() const - { - if (m_weak_proxy == NULL) { - m_weak_proxy = new WeakProxy((void *)this); - m_weak_proxy->addRef(); - } - return m_weak_proxy; - } - - /// Release the weak proxy. - void releaseWeakProxy() const - { - if (m_weak_proxy != NULL) { - m_weak_proxy->notifyObjectDied(); - m_weak_proxy->release(); - m_weak_proxy = NULL; - } - } - - /// Get reference count. - int refCount() const - { - return m_count; - } - - - private: - - mutable int m_count; - mutable WeakProxy * m_weak_proxy; - - }; - -} // nv namespace - - -#endif // NV_CORE_REFCOUNTED_H diff --git a/thirdparty/thekla_atlas/nvcore/StdStream.h b/thirdparty/thekla_atlas/nvcore/StdStream.h deleted file mode 100644 index f65d6dab59..0000000000 --- a/thirdparty/thekla_atlas/nvcore/StdStream.h +++ /dev/null @@ -1,474 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -//#pragma once -//#ifndef NV_CORE_STDSTREAM_H -//#define NV_CORE_STDSTREAM_H - -#include "nvcore.h" -#include "Stream.h" -#include "Array.h" - -#include <stdio.h> // fopen -#include <string.h> // memcpy - -namespace nv -{ - - // Portable version of fopen. - inline FILE * fileOpen(const char * fileName, const char * mode) - { - nvCheck(fileName != NULL); -#if NV_CC_MSVC && _MSC_VER >= 1400 - FILE * fp; - if (fopen_s(&fp, fileName, mode) == 0) { - return fp; - } - return NULL; -#else - return fopen(fileName, mode); -#endif - } - - - /// Base stdio stream. - class NVCORE_CLASS StdStream : public Stream - { - NV_FORBID_COPY(StdStream); - public: - - /// Ctor. - StdStream( FILE * fp, bool autoclose ) : m_fp(fp), m_autoclose(autoclose) { } - - /// Dtor. - virtual ~StdStream() - { - if( m_fp != NULL && m_autoclose ) { -#if NV_OS_WIN32 - _fclose_nolock( m_fp ); -#else - fclose( m_fp ); -#endif - } - } - - - /** @name Stream implementation. */ - //@{ - virtual void seek( uint pos ) - { - nvDebugCheck(m_fp != NULL); - nvDebugCheck(pos <= size()); -#if NV_OS_WIN32 - _fseek_nolock(m_fp, pos, SEEK_SET); -#else - fseek(m_fp, pos, SEEK_SET); -#endif - } - - virtual uint tell() const - { - nvDebugCheck(m_fp != NULL); -#if NV_OS_WIN32 - return _ftell_nolock(m_fp); -#else - return (uint)ftell(m_fp); -#endif - } - - virtual uint size() const - { - nvDebugCheck(m_fp != NULL); -#if NV_OS_WIN32 - uint pos = _ftell_nolock(m_fp); - _fseek_nolock(m_fp, 0, SEEK_END); - uint end = _ftell_nolock(m_fp); - _fseek_nolock(m_fp, pos, SEEK_SET); -#else - uint pos = (uint)ftell(m_fp); - fseek(m_fp, 0, SEEK_END); - uint end = (uint)ftell(m_fp); - fseek(m_fp, pos, SEEK_SET); -#endif - return end; - } - - virtual bool isError() const - { - return m_fp == NULL || ferror( m_fp ) != 0; - } - - virtual void clearError() - { - nvDebugCheck(m_fp != NULL); - clearerr(m_fp); - } - - // @@ The original implementation uses feof, which only returns true when we attempt to read *past* the end of the stream. - // That is, if we read the last byte of a file, then isAtEnd would still return false, even though the stream pointer is at the file end. This is not the intent and was inconsistent with the implementation of the MemoryStream, a better - // implementation uses use ftell and fseek to determine our location within the file. - virtual bool isAtEnd() const - { - if (m_fp == NULL) return true; - //nvDebugCheck(m_fp != NULL); - //return feof( m_fp ) != 0; -#if NV_OS_WIN32 - uint pos = _ftell_nolock(m_fp); - _fseek_nolock(m_fp, 0, SEEK_END); - uint end = _ftell_nolock(m_fp); - _fseek_nolock(m_fp, pos, SEEK_SET); -#else - uint pos = (uint)ftell(m_fp); - fseek(m_fp, 0, SEEK_END); - uint end = (uint)ftell(m_fp); - fseek(m_fp, pos, SEEK_SET); -#endif - return pos == end; - } - - /// Always true. - virtual bool isSeekable() const { return true; } - //@} - - protected: - - FILE * m_fp; - bool m_autoclose; - - }; - - - /// Standard output stream. - class NVCORE_CLASS StdOutputStream : public StdStream - { - NV_FORBID_COPY(StdOutputStream); - public: - - /// Construct stream by file name. - StdOutputStream( const char * name ) : StdStream(fileOpen(name, "wb"), /*autoclose=*/true) { } - - /// Construct stream by file handle. - StdOutputStream( FILE * fp, bool autoclose ) : StdStream(fp, autoclose) - { - } - - /** @name Stream implementation. */ - //@{ - /// Write data. - virtual uint serialize( void * data, uint len ) - { - nvDebugCheck(data != NULL); - nvDebugCheck(m_fp != NULL); -#if NV_OS_WIN32 - return (uint)_fwrite_nolock(data, 1, len, m_fp); -#elif NV_OS_LINUX - return (uint)fwrite_unlocked(data, 1, len, m_fp); -#elif NV_OS_DARWIN - // @@ No error checking, always returns len. - for (uint i = 0; i < len; i++) { - putc_unlocked(((char *)data)[i], m_fp); - } - return len; -#else - return (uint)fwrite(data, 1, len, m_fp); -#endif - } - - virtual bool isLoading() const - { - return false; - } - - virtual bool isSaving() const - { - return true; - } - //@} - - }; - - - /// Standard input stream. - class NVCORE_CLASS StdInputStream : public StdStream - { - NV_FORBID_COPY(StdInputStream); - public: - - /// Construct stream by file name. - StdInputStream( const char * name ) : StdStream(fileOpen(name, "rb"), /*autoclose=*/true) { } - - /// Construct stream by file handle. - StdInputStream( FILE * fp, bool autoclose=true ) : StdStream(fp, autoclose) - { - } - - /** @name Stream implementation. */ - //@{ - /// Read data. - virtual uint serialize( void * data, uint len ) - { - nvDebugCheck(data != NULL); - nvDebugCheck(m_fp != NULL); -#if NV_OS_WIN32 - return (uint)_fread_nolock(data, 1, len, m_fp); -#elif NV_OS_LINUX - return (uint)fread_unlocked(data, 1, len, m_fp); -#elif NV_OS_DARWIN - // This is rather lame. Not sure if it's faster than the locked version. - for (uint i = 0; i < len; i++) { - ((char *)data)[i] = getc_unlocked(m_fp); - if (feof_unlocked(m_fp) != 0) { - return i; - } - } - return len; -#else - return (uint)fread(data, 1, len, m_fp); -#endif - - } - - virtual bool isLoading() const - { - return true; - } - - virtual bool isSaving() const - { - return false; - } - //@} - }; - - - - /// Memory input stream. - class NVCORE_CLASS MemoryInputStream : public Stream - { - NV_FORBID_COPY(MemoryInputStream); - public: - - /// Ctor. - MemoryInputStream( const uint8 * mem, uint size ) : m_mem(mem), m_ptr(mem), m_size(size) { } - - /** @name Stream implementation. */ - //@{ - /// Read data. - virtual uint serialize( void * data, uint len ) - { - nvDebugCheck(data != NULL); - nvDebugCheck(!isError()); - - uint left = m_size - tell(); - if (len > left) len = left; - - memcpy( data, m_ptr, len ); - m_ptr += len; - - return len; - } - - virtual void seek( uint pos ) - { - nvDebugCheck(!isError()); - m_ptr = m_mem + pos; - nvDebugCheck(!isError()); - } - - virtual uint tell() const - { - nvDebugCheck(m_ptr >= m_mem); - return uint(m_ptr - m_mem); - } - - virtual uint size() const - { - return m_size; - } - - virtual bool isError() const - { - return m_mem == NULL || m_ptr > m_mem + m_size || m_ptr < m_mem; - } - - virtual void clearError() - { - // Nothing to do. - } - - virtual bool isAtEnd() const - { - return m_ptr == m_mem + m_size; - } - - /// Always true. - virtual bool isSeekable() const - { - return true; - } - - virtual bool isLoading() const - { - return true; - } - - virtual bool isSaving() const - { - return false; - } - //@} - - const uint8 * ptr() const { return m_ptr; } - - - private: - - const uint8 * m_mem; - const uint8 * m_ptr; - uint m_size; - - }; - - - /// Buffer output stream. - class NVCORE_CLASS BufferOutputStream : public Stream - { - NV_FORBID_COPY(BufferOutputStream); - public: - - BufferOutputStream(Array<uint8> & buffer) : m_buffer(buffer) { } - - virtual uint serialize( void * data, uint len ) - { - nvDebugCheck(data != NULL); - m_buffer.append((uint8 *)data, len); - return len; - } - - virtual void seek( uint /*pos*/ ) { /*Not implemented*/ } - virtual uint tell() const { return m_buffer.size(); } - virtual uint size() const { return m_buffer.size(); } - - virtual bool isError() const { return false; } - virtual void clearError() {} - - virtual bool isAtEnd() const { return true; } - virtual bool isSeekable() const { return false; } - virtual bool isLoading() const { return false; } - virtual bool isSaving() const { return true; } - - private: - Array<uint8> & m_buffer; - }; - - - /// Protected input stream. - class NVCORE_CLASS ProtectedStream : public Stream - { - NV_FORBID_COPY(ProtectedStream); - public: - - /// Ctor. - ProtectedStream( Stream & s ) : m_s(&s), m_autodelete(false) - { - } - - /// Ctor. - ProtectedStream( Stream * s, bool autodelete = true ) : - m_s(s), m_autodelete(autodelete) - { - nvDebugCheck(m_s != NULL); - } - - /// Dtor. - virtual ~ProtectedStream() - { - if( m_autodelete ) { - delete m_s; - } - } - - /** @name Stream implementation. */ - //@{ - /// Read data. - virtual uint serialize( void * data, uint len ) - { - nvDebugCheck(data != NULL); - len = m_s->serialize( data, len ); - - if( m_s->isError() ) { -#if NV_OS_ORBIS - //SBtodoORBIS disabled (no exceptions) -#else - throw; -#endif - } - - return len; - } - - virtual void seek( uint pos ) - { - m_s->seek( pos ); - - if( m_s->isError() ) { -#if NV_OS_ORBIS - //SBtodoORBIS disabled (no exceptions) -#else - throw; -#endif - } - } - - virtual uint tell() const - { - return m_s->tell(); - } - - virtual uint size() const - { - return m_s->size(); - } - - virtual bool isError() const - { - return m_s->isError(); - } - - virtual void clearError() - { - m_s->clearError(); - } - - virtual bool isAtEnd() const - { - return m_s->isAtEnd(); - } - - virtual bool isSeekable() const - { - return m_s->isSeekable(); - } - - virtual bool isLoading() const - { - return m_s->isLoading(); - } - - virtual bool isSaving() const - { - return m_s->isSaving(); - } - //@} - - - private: - - Stream * const m_s; - bool const m_autodelete; - - }; - -} // nv namespace - - -//#endif // NV_CORE_STDSTREAM_H diff --git a/thirdparty/thekla_atlas/nvcore/StrLib.cpp b/thirdparty/thekla_atlas/nvcore/StrLib.cpp deleted file mode 100644 index 7ec6c70136..0000000000 --- a/thirdparty/thekla_atlas/nvcore/StrLib.cpp +++ /dev/null @@ -1,796 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "StrLib.h" - -#include "Memory.h" -#include "Utils.h" // swap - -#include <math.h> // log -#include <stdio.h> // vsnprintf -#include <string.h> // strlen, strcmp, etc. - -#if NV_CC_MSVC -#include <stdarg.h> // vsnprintf -#endif - -using namespace nv; - -namespace -{ - static char * strAlloc(uint size) - { - return malloc<char>(size); - } - - static char * strReAlloc(char * str, uint size) - { - return realloc<char>(str, size); - } - - static void strFree(const char * str) - { - return free<char>(str); - } - - /*static char * strDup( const char * str ) - { - nvDebugCheck( str != NULL ); - uint len = uint(strlen( str ) + 1); - char * dup = strAlloc( len ); - memcpy( dup, str, len ); - return dup; - }*/ - - // helper function for integer to string conversion. - static char * i2a( uint i, char *a, uint r ) - { - if( i / r > 0 ) { - a = i2a( i / r, a, r ); - } - *a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % r]; - return a + 1; - } - - // Locale independent functions. - static inline char toUpper( char c ) { - return (c<'a' || c>'z') ? (c) : (c+'A'-'a'); - } - static inline char toLower( char c ) { - return (c<'A' || c>'Z') ? (c) : (c+'a'-'A'); - } - static inline bool isAlpha( char c ) { - return (c>='a' && c<='z') || (c>='A' && c<='Z'); - } - static inline bool isDigit( char c ) { - return c>='0' && c<='9'; - } - static inline bool isAlnum( char c ) { - return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); - } - -} - -uint nv::strLen(const char * str) -{ - nvDebugCheck(str != NULL); - return U32(strlen(str)); -} - -int nv::strDiff(const char * s1, const char * s2) -{ - nvDebugCheck(s1 != NULL); - nvDebugCheck(s2 != NULL); - return strcmp(s1, s2); -} - -int nv::strCaseDiff(const char * s1, const char * s2) -{ - nvDebugCheck(s1 != NULL); - nvDebugCheck(s1 != NULL); -#if NV_CC_MSVC - return _stricmp(s1, s2); -#else - return strcasecmp(s1, s2); -#endif -} - -bool nv::strEqual(const char * s1, const char * s2) -{ - if (s1 == s2) return true; - if (s1 == NULL || s2 == NULL) return false; - return strcmp(s1, s2) == 0; -} - -bool nv::strCaseEqual(const char * s1, const char * s2) -{ - if (s1 == s2) return true; - if (s1 == NULL || s2 == NULL) return false; - return strCaseDiff(s1, s2) == 0; -} - -bool nv::strBeginsWith(const char * str, const char * prefix) -{ - //return strstr(str, prefix) == dst; - return strncmp(str, prefix, strlen(prefix)) == 0; -} - -bool nv::strEndsWith(const char * str, const char * suffix) -{ - uint ml = strLen(str); - uint sl = strLen(suffix); - if (ml < sl) return false; - return strncmp(str + ml - sl, suffix, sl) == 0; -} - -// @@ Add asserts to detect overlap between dst and src? -void nv::strCpy(char * dst, uint size, const char * src) -{ - nvDebugCheck(dst != NULL); - nvDebugCheck(src != NULL); -#if NV_CC_MSVC && _MSC_VER >= 1400 - strcpy_s(dst, size, src); -#else - NV_UNUSED(size); - strcpy(dst, src); -#endif -} - -void nv::strCpy(char * dst, uint size, const char * src, uint len) -{ - nvDebugCheck(dst != NULL); - nvDebugCheck(src != NULL); -#if NV_CC_MSVC && _MSC_VER >= 1400 - strncpy_s(dst, size, src, len); -#else - int n = min(len+1, size); - strncpy(dst, src, n); - dst[n-1] = '\0'; -#endif -} - -void nv::strCat(char * dst, uint size, const char * src) -{ - nvDebugCheck(dst != NULL); - nvDebugCheck(src != NULL); -#if NV_CC_MSVC && _MSC_VER >= 1400 - strcat_s(dst, size, src); -#else - NV_UNUSED(size); - strcat(dst, src); -#endif -} - -NVCORE_API const char * nv::strSkipWhiteSpace(const char * str) -{ - nvDebugCheck(str != NULL); - while (*str == ' ') str++; - return str; -} - -NVCORE_API char * nv::strSkipWhiteSpace(char * str) -{ - nvDebugCheck(str != NULL); - while (*str == ' ') str++; - return str; -} - - -/** Pattern matching routine. I don't remember where did I get this. */ -bool nv::strMatch(const char * str, const char * pat) -{ - nvDebugCheck(str != NULL); - nvDebugCheck(pat != NULL); - - char c2; - - while (true) { - if (*pat==0) { - if (*str==0) return true; - else return false; - } - if ((*str==0) && (*pat!='*')) return false; - if (*pat=='*') { - pat++; - if (*pat==0) return true; - while (true) { - if (strMatch(str, pat)) return true; - if (*str==0) return false; - str++; - } - } - if (*pat=='?') goto match; - if (*pat=='[') { - pat++; - while (true) { - if ((*pat==']') || (*pat==0)) return false; - if (*pat==*str) break; - if (pat[1] == '-') { - c2 = pat[2]; - if (c2==0) return false; - if ((*pat<=*str) && (c2>=*str)) break; - if ((*pat>=*str) && (c2<=*str)) break; - pat+=2; - } - pat++; - } - while (*pat!=']') { - if (*pat==0) { - pat--; - break; - } - pat++; - } - goto match; - } - - if (*pat == NV_PATH_SEPARATOR) { - pat++; - if (*pat==0) return false; - } - if (*pat!=*str) return false; - -match: - pat++; - str++; - } -} - -bool nv::isNumber(const char * str) { - while(*str != '\0') { - if (!isDigit(*str)) return false; - str++; - } - return true; -} - - -/** Empty string. */ -StringBuilder::StringBuilder() : m_size(0), m_str(NULL) -{ -} - -/** Preallocate space. */ -StringBuilder::StringBuilder( uint size_hint ) : m_size(size_hint) -{ - nvDebugCheck(m_size > 0); - m_str = strAlloc(m_size); - *m_str = '\0'; -} - -/** Copy ctor. */ -StringBuilder::StringBuilder( const StringBuilder & s ) : m_size(0), m_str(NULL) -{ - copy(s); -} - -/** Copy string. */ -StringBuilder::StringBuilder(const char * s) : m_size(0), m_str(NULL) -{ - if (s != NULL) { - copy(s); - } -} - -/** Copy string. */ -StringBuilder::StringBuilder(const char * s, uint len) : m_size(0), m_str(NULL) -{ - copy(s, len); -} - -/** Delete the string. */ -StringBuilder::~StringBuilder() -{ - strFree(m_str); -} - - -/** Format a string safely. */ -StringBuilder & StringBuilder::format( const char * fmt, ... ) -{ - nvDebugCheck(fmt != NULL); - va_list arg; - va_start( arg, fmt ); - - formatList( fmt, arg ); - - va_end( arg ); - - return *this; -} - - -/** Format a string safely. */ -StringBuilder & StringBuilder::formatList( const char * fmt, va_list arg ) -{ - nvDebugCheck(fmt != NULL); - - if (m_size == 0) { - m_size = 64; - m_str = strAlloc( m_size ); - } - - va_list tmp; - va_copy(tmp, arg); -#if NV_CC_MSVC && _MSC_VER >= 1400 - int n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp); -#else - int n = vsnprintf(m_str, m_size, fmt, tmp); -#endif - va_end(tmp); - - while( n < 0 || n >= int(m_size) ) { - if( n > -1 ) { - m_size = n + 1; - } - else { - m_size *= 2; - } - - m_str = strReAlloc(m_str, m_size); - - va_copy(tmp, arg); -#if NV_CC_MSVC && _MSC_VER >= 1400 - n = vsnprintf_s(m_str, m_size, _TRUNCATE, fmt, tmp); -#else - n = vsnprintf(m_str, m_size, fmt, tmp); -#endif - va_end(tmp); - } - - nvDebugCheck(n < int(m_size)); - - // Make sure it's null terminated. - nvDebugCheck(m_str[n] == '\0'); - //str[n] = '\0'; - - return *this; -} - - -// Append a character. -StringBuilder & StringBuilder::append( char c ) -{ - return append(&c, 1); -} - -// Append a string. -StringBuilder & StringBuilder::append( const char * s ) -{ - return append(s, U32(strlen( s ))); -} - -// Append a string. -StringBuilder & StringBuilder::append(const char * s, uint len) -{ - nvDebugCheck(s != NULL); - - uint offset = length(); - const uint size = offset + len + 1; - reserve(size); - strCpy(m_str + offset, len + 1, s, len); - - return *this; -} - -StringBuilder & StringBuilder::append(const StringBuilder & str) -{ - return append(str.m_str, str.length()); -} - - -/** Append a formatted string. */ -StringBuilder & StringBuilder::appendFormat( const char * fmt, ... ) -{ - nvDebugCheck( fmt != NULL ); - - va_list arg; - va_start( arg, fmt ); - - appendFormatList( fmt, arg ); - - va_end( arg ); - - return *this; -} - - -/** Append a formatted string. */ -StringBuilder & StringBuilder::appendFormatList( const char * fmt, va_list arg ) -{ - nvDebugCheck( fmt != NULL ); - - va_list tmp; - va_copy(tmp, arg); - - if (m_size == 0) { - formatList(fmt, arg); - } - else { - StringBuilder tmp_str; - tmp_str.formatList( fmt, tmp ); - append( tmp_str.str() ); - } - - va_end(tmp); - - return *this; -} - -// Append n spaces. -StringBuilder & StringBuilder::appendSpace(uint n) -{ - if (m_str == NULL) { - m_size = n + 1; - m_str = strAlloc(m_size); - memset(m_str, ' ', m_size); - m_str[n] = '\0'; - } - else { - const uint len = strLen(m_str); - if (m_size < len + n + 1) { - m_size = len + n + 1; - m_str = strReAlloc(m_str, m_size); - } - memset(m_str + len, ' ', n); - m_str[len+n] = '\0'; - } - - return *this; -} - - -/** Convert number to string in the given base. */ -StringBuilder & StringBuilder::number( int i, int base ) -{ - nvCheck( base >= 2 ); - nvCheck( base <= 36 ); - - // @@ This needs to be done correctly. - // length = floor(log(i, base)); - uint len = uint(log(float(i)) / log(float(base)) + 2); // one more if negative - reserve(len); - - if( i < 0 ) { - *m_str = '-'; - *i2a(uint(-i), m_str+1, base) = 0; - } - else { - *i2a(i, m_str, base) = 0; - } - - return *this; -} - - -/** Convert number to string in the given base. */ -StringBuilder & StringBuilder::number( uint i, int base ) -{ - nvCheck( base >= 2 ); - nvCheck( base <= 36 ); - - // @@ This needs to be done correctly. - // length = floor(log(i, base)); - uint len = uint(log(float(i)) / log(float(base)) - 0.5f + 1); - reserve(len); - - *i2a(i, m_str, base) = 0; - - return *this; -} - - -/** Resize the string preserving the contents. */ -StringBuilder & StringBuilder::reserve( uint size_hint ) -{ - nvCheck(size_hint != 0); - if (size_hint > m_size) { - m_str = strReAlloc(m_str, size_hint); - m_size = size_hint; - } - return *this; -} - - -/** Copy a string safely. */ -StringBuilder & StringBuilder::copy(const char * s) -{ - nvCheck( s != NULL ); - const uint str_size = uint(strlen( s )) + 1; - reserve(str_size); - memcpy(m_str, s, str_size); - return *this; -} - -/** Copy a string safely. */ -StringBuilder & StringBuilder::copy(const char * s, uint len) -{ - nvCheck( s != NULL ); - const uint str_size = len + 1; - reserve(str_size); - strCpy(m_str, str_size, s, len); - return *this; -} - - -/** Copy an StringBuilder. */ -StringBuilder & StringBuilder::copy( const StringBuilder & s ) -{ - if (s.m_str == NULL) { - nvCheck( s.m_size == 0 ); - reset(); - } - else { - reserve( s.m_size ); - strCpy( m_str, s.m_size, s.m_str ); - } - return *this; -} - -bool StringBuilder::endsWith(const char * str) const -{ - uint l = uint(strlen(str)); - uint ml = uint(strlen(m_str)); - if (ml < l) return false; - return strncmp(m_str + ml - l, str, l) == 0; -} - -bool StringBuilder::beginsWith(const char * str) const -{ - size_t l = strlen(str); - return strncmp(m_str, str, l) == 0; -} - -// Find given char starting from the end. -char * StringBuilder::reverseFind(char c) -{ - int length = (int)strlen(m_str) - 1; - while (length >= 0 && m_str[length] != c) { - length--; - } - if (length >= 0) { - return m_str + length; - } - else { - return NULL; - } -} - - -/** Reset the string. */ -void StringBuilder::reset() -{ - m_size = 0; - strFree( m_str ); - m_str = NULL; -} - -/** Release the allocated string. */ -char * StringBuilder::release() -{ - char * str = m_str; - m_size = 0; - m_str = NULL; - return str; -} - -// Take ownership of string. -void StringBuilder::acquire(char * str) -{ - if (str) { - m_size = strLen(str) + 1; - m_str = str; - } - else { - m_size = 0; - m_str = NULL; - } -} - -// Swap strings. -void nv::swap(StringBuilder & a, StringBuilder & b) { - swap(a.m_size, b.m_size); - swap(a.m_str, b.m_str); -} - - -/// Get the file name from a path. -const char * Path::fileName() const -{ - return fileName(m_str); -} - - -/// Get the extension from a file path. -const char * Path::extension() const -{ - return extension(m_str); -} - - -/*static */void Path::translatePath(char * path, char pathSeparator/*= NV_PATH_SEPARATOR*/) { - if (path != NULL) { - for (int i = 0;; i++) { - if (path[i] == '\0') break; - if (path[i] == '\\' || path[i] == '/') path[i] = pathSeparator; - } - } -} - -/// Toggles path separators (ie. \\ into /). -void Path::translatePath(char pathSeparator/*=NV_PATH_SEPARATOR*/) -{ - if (!isNull()) { - translatePath(m_str, pathSeparator); - } -} - -void Path::appendSeparator(char pathSeparator/*=NV_PATH_SEPARATOR*/) -{ - nvCheck(!isNull()); - - const uint l = length(); - - if (m_str[l] != '\\' && m_str[l] != '/') { - char separatorString[] = { pathSeparator, '\0' }; - append(separatorString); - } -} - - -/** -* Strip the file name from a path. -* @warning path cannot end with '/' o '\\', can't it? -*/ -void Path::stripFileName() -{ - nvCheck( m_str != NULL ); - - int length = (int)strlen(m_str) - 1; - while (length > 0 && m_str[length] != '/' && m_str[length] != '\\'){ - length--; - } - if( length ) { - m_str[length+1] = 0; - } - else { - m_str[0] = 0; - } -} - - -/// Strip the extension from a path name. -void Path::stripExtension() -{ - nvCheck( m_str != NULL ); - - int length = (int)strlen(m_str) - 1; - while (length > 0 && m_str[length] != '.') { - length--; - if( m_str[length] == NV_PATH_SEPARATOR ) { - return; // no extension - } - } - if (length > 0) { - m_str[length] = 0; - } -} - - -/// Get the path separator. -// static -char Path::separator() -{ - return NV_PATH_SEPARATOR; -} - -// static -const char * Path::fileName(const char * str) -{ - nvCheck( str != NULL ); - - int length = (int)strlen(str) - 1; - while (length >= 0 && str[length] != '\\' && str[length] != '/') { - length--; - } - - return &str[length+1]; -} - -// static -const char * Path::extension(const char * str) -{ - nvCheck( str != NULL ); - - int length, l; - l = length = (int)strlen( str ); - while (length > 0 && str[length] != '.') { - length--; - if (str[length] == '\\' || str[length] == '/') { - return &str[l]; // no extension - } - } - if (length == 0) { - return &str[l]; - } - return &str[length]; -} - - - -/// Clone this string -String String::clone() const -{ - String str(data); - return str; -} - -void String::setString(const char * str) -{ - if (str == NULL) { - data = NULL; - } - else { - allocString( str ); - addRef(); - } -} - -void String::setString(const char * str, uint length) -{ - nvDebugCheck(str != NULL); - - allocString(str, length); - addRef(); -} - -void String::setString(const StringBuilder & str) -{ - if (str.str() == NULL) { - data = NULL; - } - else { - allocString(str.str()); - addRef(); - } -} - -// Add reference count. -void String::addRef() -{ - if (data != NULL) - { - setRefCount(getRefCount() + 1); - } -} - -// Decrease reference count. -void String::release() -{ - if (data != NULL) - { - const uint16 count = getRefCount(); - setRefCount(count - 1); - if (count - 1 == 0) { - free(data - 2); - data = NULL; - } - } -} - -void String::allocString(const char * str, uint len) -{ - const char * ptr = malloc<char>(2 + len + 1); - - setData( ptr ); - setRefCount( 0 ); - - // Copy string. - strCpy(const_cast<char *>(data), len+1, str, len); - - // Add terminating character. - const_cast<char *>(data)[len] = '\0'; -} - -void nv::swap(String & a, String & b) { - swap(a.data, b.data); -} diff --git a/thirdparty/thekla_atlas/nvcore/StrLib.h b/thirdparty/thekla_atlas/nvcore/StrLib.h deleted file mode 100644 index ae4b5d12a0..0000000000 --- a/thirdparty/thekla_atlas/nvcore/StrLib.h +++ /dev/null @@ -1,433 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_STRING_H -#define NV_CORE_STRING_H - -#include "Debug.h" -#include "Hash.h" // hash - -//#include <string.h> // strlen, etc. - -#if NV_OS_WIN32 -#define NV_PATH_SEPARATOR '\\' -#else -#define NV_PATH_SEPARATOR '/' -#endif - -namespace nv -{ - - NVCORE_API uint strHash(const char * str, uint h) NV_PURE; - - /// String hash based on Bernstein's hash. - inline uint strHash(const char * data, uint h = 5381) - { - uint i = 0; - while(data[i] != 0) { - h = (33 * h) ^ uint(data[i]); - i++; - } - return h; - } - - template <> struct Hash<const char *> { - uint operator()(const char * str) const { return strHash(str); } - }; - - NVCORE_API uint strLen(const char * str) NV_PURE; // Asserts on NULL strings. - - NVCORE_API int strDiff(const char * s1, const char * s2) NV_PURE; // Asserts on NULL strings. - NVCORE_API int strCaseDiff(const char * s1, const char * s2) NV_PURE; // Asserts on NULL strings. - NVCORE_API bool strEqual(const char * s1, const char * s2) NV_PURE; // Accepts NULL strings. - NVCORE_API bool strCaseEqual(const char * s1, const char * s2) NV_PURE; // Accepts NULL strings. - - template <> struct Equal<const char *> { - bool operator()(const char * a, const char * b) const { return strEqual(a, b); } - }; - - NVCORE_API bool strBeginsWith(const char * dst, const char * prefix) NV_PURE; - NVCORE_API bool strEndsWith(const char * dst, const char * suffix) NV_PURE; - - - NVCORE_API void strCpy(char * dst, uint size, const char * src); - NVCORE_API void strCpy(char * dst, uint size, const char * src, uint len); - NVCORE_API void strCat(char * dst, uint size, const char * src); - - NVCORE_API const char * strSkipWhiteSpace(const char * str); - NVCORE_API char * strSkipWhiteSpace(char * str); - - NVCORE_API bool strMatch(const char * str, const char * pat) NV_PURE; - - NVCORE_API bool isNumber(const char * str) NV_PURE; - - /* @@ Implement these two functions and modify StringBuilder to use them? - NVCORE_API void strFormat(const char * dst, const char * fmt, ...); - NVCORE_API void strFormatList(const char * dst, const char * fmt, va_list arg); - - template <size_t count> void strFormatSafe(char (&buffer)[count], const char *fmt, ...) __attribute__((format (printf, 2, 3))); - template <size_t count> void strFormatSafe(char (&buffer)[count], const char *fmt, ...) { - va_list args; - va_start(args, fmt); - strFormatList(buffer, count, fmt, args); - va_end(args); - } - template <size_t count> void strFormatListSafe(char (&buffer)[count], const char *fmt, va_list arg) { - va_list tmp; - va_copy(tmp, args); - strFormatList(buffer, count, fmt, tmp); - va_end(tmp); - }*/ - - template <int count> void strCpySafe(char (&buffer)[count], const char *src) { - strCpy(buffer, count, src); - } - - template <int count> void strCatSafe(char (&buffer)[count], const char * src) { - strCat(buffer, count, src); - } - - - - /// String builder. - class NVCORE_CLASS StringBuilder - { - public: - - StringBuilder(); - explicit StringBuilder( uint size_hint ); - StringBuilder(const char * str); - StringBuilder(const char * str, uint len); - StringBuilder(const StringBuilder & other); - - ~StringBuilder(); - - StringBuilder & format( const char * format, ... ) __attribute__((format (printf, 2, 3))); - StringBuilder & formatList( const char * format, va_list arg ); - - StringBuilder & append(char c); - StringBuilder & append(const char * str); - StringBuilder & append(const char * str, uint len); - StringBuilder & append(const StringBuilder & str); - StringBuilder & appendFormat(const char * format, ...) __attribute__((format (printf, 2, 3))); - StringBuilder & appendFormatList(const char * format, va_list arg); - - StringBuilder & appendSpace(uint n); - - StringBuilder & number( int i, int base = 10 ); - StringBuilder & number( uint i, int base = 10 ); - - StringBuilder & reserve(uint size_hint); - StringBuilder & copy(const char * str); - StringBuilder & copy(const char * str, uint len); - StringBuilder & copy(const StringBuilder & str); - - StringBuilder & toLower(); - StringBuilder & toUpper(); - - bool endsWith(const char * str) const; - bool beginsWith(const char * str) const; - - char * reverseFind(char c); - - void reset(); - bool isNull() const { return m_size == 0; } - - // const char * accessors - //operator const char * () const { return m_str; } - //operator char * () { return m_str; } - const char * str() const { return m_str; } - char * str() { return m_str; } - - char * release(); // Release ownership of string. - void acquire(char *); // Take ownership of string. - - /// Implement value semantics. - StringBuilder & operator=( const StringBuilder & s ) { - return copy(s); - } - - /// Implement value semantics. - StringBuilder & operator=( const char * s ) { - return copy(s); - } - - /// Equal operator. - bool operator==( const StringBuilder & s ) const { - return strMatch(s.m_str, m_str); - } - - /// Return the exact length. - uint length() const { return isNull() ? 0 : strLen(m_str); } - - /// Return the size of the string container. - uint capacity() const { return m_size; } - - /// Return the hash of the string. - uint hash() const { return isNull() ? 0 : strHash(m_str); } - - // Swap strings. - friend void swap(StringBuilder & a, StringBuilder & b); - - protected: - - /// Size of the string container. - uint m_size; - - /// String. - char * m_str; - - }; - - - /// Path string. @@ This should be called PathBuilder. - class NVCORE_CLASS Path : public StringBuilder - { - public: - Path() : StringBuilder() {} - explicit Path(int size_hint) : StringBuilder(size_hint) {} - Path(const char * str) : StringBuilder(str) {} - Path(const Path & path) : StringBuilder(path) {} - - const char * fileName() const; - const char * extension() const; - - void translatePath(char pathSeparator = NV_PATH_SEPARATOR); - - void appendSeparator(char pathSeparator = NV_PATH_SEPARATOR); - - void stripFileName(); - void stripExtension(); - - // statics - NVCORE_API static char separator(); - NVCORE_API static const char * fileName(const char *); - NVCORE_API static const char * extension(const char *); - - NVCORE_API static void translatePath(char * path, char pathSeparator = NV_PATH_SEPARATOR); - }; - - - /// String class. - class NVCORE_CLASS String - { - public: - - /// Constructs a null string. @sa isNull() - String() - { - data = NULL; - } - - /// Constructs a shared copy of str. - String(const String & str) - { - data = str.data; - if (data != NULL) addRef(); - } - - /// Constructs a shared string from a standard string. - String(const char * str) - { - setString(str); - } - - /// Constructs a shared string from a standard string. - String(const char * str, int length) - { - setString(str, length); - } - - /// Constructs a shared string from a StringBuilder. - String(const StringBuilder & str) - { - setString(str); - } - - /// Dtor. - ~String() - { - release(); - } - - String clone() const; - - /// Release the current string and allocate a new one. - const String & operator=( const char * str ) - { - release(); - setString( str ); - return *this; - } - - /// Release the current string and allocate a new one. - const String & operator=( const StringBuilder & str ) - { - release(); - setString( str ); - return *this; - } - - /// Implement value semantics. - String & operator=( const String & str ) - { - if (str.data != data) - { - release(); - data = str.data; - addRef(); - } - return *this; - } - - /// Equal operator. - bool operator==( const String & str ) const - { - return strMatch(str.data, data); - } - - /// Equal operator. - bool operator==( const char * str ) const - { - return strMatch(str, data); - } - - /// Not equal operator. - bool operator!=( const String & str ) const - { - return !strMatch(str.data, data); - } - - /// Not equal operator. - bool operator!=( const char * str ) const - { - return !strMatch(str, data); - } - - /// Returns true if this string is the null string. - bool isNull() const { return data == NULL; } - - /// Return the exact length. - uint length() const { nvDebugCheck(data != NULL); return strLen(data); } - - /// Return the hash of the string. - uint hash() const { nvDebugCheck(data != NULL); return strHash(data); } - - /// const char * cast operator. - operator const char * () const { return data; } - - /// Get string pointer. - const char * str() const { return data; } - - - private: - - // Add reference count. - void addRef(); - - // Decrease reference count. - void release(); - - uint16 getRefCount() const - { - nvDebugCheck(data != NULL); - return *reinterpret_cast<const uint16 *>(data - 2); - } - - void setRefCount(uint16 count) { - nvDebugCheck(data != NULL); - nvCheck(count < 0xFFFF); - *reinterpret_cast<uint16 *>(const_cast<char *>(data - 2)) = uint16(count); - } - - void setData(const char * str) { - data = str + 2; - } - - void allocString(const char * str) - { - allocString(str, strLen(str)); - } - - void allocString(const char * str, uint length); - - void setString(const char * str); - void setString(const char * str, uint length); - void setString(const StringBuilder & str); - - // Swap strings. - friend void swap(String & a, String & b); - - private: - - const char * data; - - }; - - template <> struct Hash<String> { - uint operator()(const String & str) const { return str.hash(); } - }; - - - // Like AutoPtr, but for const char strings. - class AutoString - { - NV_FORBID_COPY(AutoString); - NV_FORBID_HEAPALLOC(); - public: - - // Ctor. - AutoString(const char * p = NULL) : m_ptr(p) { } - -#if NV_CC_CPP11 - // Move ctor. - AutoString(AutoString && ap) : m_ptr(ap.m_ptr) { ap.m_ptr = NULL; } -#endif - - // Dtor. Deletes owned pointer. - ~AutoString() { - delete [] m_ptr; - m_ptr = NULL; - } - - // Delete owned pointer and assign new one. - void operator=(const char * p) { - if (p != m_ptr) - { - delete [] m_ptr; - m_ptr = p; - } - } - - // Get pointer. - const char * ptr() const { return m_ptr; } - operator const char *() const { return m_ptr; } - - // Relinquish ownership of the underlying pointer and returns that pointer. - const char * release() { - const char * tmp = m_ptr; - m_ptr = NULL; - return tmp; - } - - // comparison operators. - friend bool operator == (const AutoString & ap, const char * const p) { - return (ap.ptr() == p); - } - friend bool operator != (const AutoString & ap, const char * const p) { - return (ap.ptr() != p); - } - friend bool operator == (const char * const p, const AutoString & ap) { - return (ap.ptr() == p); - } - friend bool operator != (const char * const p, const AutoString & ap) { - return (ap.ptr() != p); - } - - private: - const char * m_ptr; - }; - -} // nv namespace - -#endif // NV_CORE_STRING_H diff --git a/thirdparty/thekla_atlas/nvcore/Stream.h b/thirdparty/thekla_atlas/nvcore/Stream.h deleted file mode 100644 index c35c0d0c78..0000000000 --- a/thirdparty/thekla_atlas/nvcore/Stream.h +++ /dev/null @@ -1,164 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_STREAM_H -#define NV_CORE_STREAM_H - -#include "nvcore.h" -#include "Debug.h" - -namespace nv -{ - - /// Base stream class. - class NVCORE_CLASS Stream { - public: - - enum ByteOrder { - LittleEndian = false, - BigEndian = true, - }; - - /// Get the byte order of the system. - static ByteOrder getSystemByteOrder() { -#if NV_LITTLE_ENDIAN - return LittleEndian; -#else - return BigEndian; -#endif - } - - - /// Ctor. - Stream() : m_byteOrder(LittleEndian) { } - - /// Virtual destructor. - virtual ~Stream() {} - - /// Set byte order. - void setByteOrder(ByteOrder bo) { m_byteOrder = bo; } - - /// Get byte order. - ByteOrder byteOrder() const { return m_byteOrder; } - - - /// Serialize the given data. - virtual uint serialize( void * data, uint len ) = 0; - - /// Move to the given position in the archive. - virtual void seek( uint pos ) = 0; - - /// Return the current position in the archive. - virtual uint tell() const = 0; - - /// Return the current size of the archive. - virtual uint size() const = 0; - - /// Determine if there has been any error. - virtual bool isError() const = 0; - - /// Clear errors. - virtual void clearError() = 0; - - /// Return true if the stream is at the end. - virtual bool isAtEnd() const = 0; - - /// Return true if the stream is seekable. - virtual bool isSeekable() const = 0; - - /// Return true if this is an input stream. - virtual bool isLoading() const = 0; - - /// Return true if this is an output stream. - virtual bool isSaving() const = 0; - - - void advance(uint offset) { seek(tell() + offset); } - - - // friends - friend Stream & operator<<( Stream & s, bool & c ) { -#if NV_OS_DARWIN && !NV_CC_CPP11 - nvStaticCheck(sizeof(bool) == 4); - uint8 b = c ? 1 : 0; - s.serialize( &b, 1 ); - c = (b != 0); -#else - nvStaticCheck(sizeof(bool) == 1); - s.serialize( &c, 1 ); -#endif - return s; - } - friend Stream & operator<<( Stream & s, char & c ) { - nvStaticCheck(sizeof(char) == 1); - s.serialize( &c, 1 ); - return s; - } - friend Stream & operator<<( Stream & s, uint8 & c ) { - nvStaticCheck(sizeof(uint8) == 1); - s.serialize( &c, 1 ); - return s; - } - friend Stream & operator<<( Stream & s, int8 & c ) { - nvStaticCheck(sizeof(int8) == 1); - s.serialize( &c, 1 ); - return s; - } - friend Stream & operator<<( Stream & s, uint16 & c ) { - nvStaticCheck(sizeof(uint16) == 2); - return s.byteOrderSerialize( &c, 2 ); - } - friend Stream & operator<<( Stream & s, int16 & c ) { - nvStaticCheck(sizeof(int16) == 2); - return s.byteOrderSerialize( &c, 2 ); - } - friend Stream & operator<<( Stream & s, uint32 & c ) { - nvStaticCheck(sizeof(uint32) == 4); - return s.byteOrderSerialize( &c, 4 ); - } - friend Stream & operator<<( Stream & s, int32 & c ) { - nvStaticCheck(sizeof(int32) == 4); - return s.byteOrderSerialize( &c, 4 ); - } - friend Stream & operator<<( Stream & s, uint64 & c ) { - nvStaticCheck(sizeof(uint64) == 8); - return s.byteOrderSerialize( &c, 8 ); - } - friend Stream & operator<<( Stream & s, int64 & c ) { - nvStaticCheck(sizeof(int64) == 8); - return s.byteOrderSerialize( &c, 8 ); - } - friend Stream & operator<<( Stream & s, float & c ) { - nvStaticCheck(sizeof(float) == 4); - return s.byteOrderSerialize( &c, 4 ); - } - friend Stream & operator<<( Stream & s, double & c ) { - nvStaticCheck(sizeof(double) == 8); - return s.byteOrderSerialize( &c, 8 ); - } - - protected: - - /// Serialize in the stream byte order. - Stream & byteOrderSerialize( void * v, uint len ) { - if( m_byteOrder == getSystemByteOrder() ) { - serialize( v, len ); - } - else { - for( uint i = len; i > 0; i-- ) { - serialize( (uint8 *)v + i - 1, 1 ); - } - } - return *this; - } - - - private: - - ByteOrder m_byteOrder; - - }; - -} // nv namespace - -#endif // NV_CORE_STREAM_H diff --git a/thirdparty/thekla_atlas/nvcore/Utils.h b/thirdparty/thekla_atlas/nvcore/Utils.h deleted file mode 100644 index f20e42cda8..0000000000 --- a/thirdparty/thekla_atlas/nvcore/Utils.h +++ /dev/null @@ -1,315 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_UTILS_H -#define NV_CORE_UTILS_H - -#include "Debug.h" // nvDebugCheck - -#include <new> // for placement new - - -// Just in case. Grrr. -#undef min -#undef max - -#define NV_INT8_MIN (-128) -#define NV_INT8_MAX 127 -#define NV_UINT8_MAX 255 -#define NV_INT16_MIN (-32767-1) -#define NV_INT16_MAX 32767 -#define NV_UINT16_MAX 0xffff -#define NV_INT32_MIN (-2147483647-1) -#define NV_INT32_MAX 2147483647 -#define NV_UINT32_MAX 0xffffffff -#define NV_INT64_MAX POSH_I64(9223372036854775807) -#define NV_INT64_MIN (-POSH_I64(9223372036854775807)-1) -#define NV_UINT64_MAX POSH_U64(0xffffffffffffffff) - -#define NV_HALF_MAX 65504.0F -#define NV_FLOAT_MAX 3.402823466e+38F - -#define NV_INTEGER_TO_FLOAT_MAX 16777217 // Largest integer such that it and all smaller integers can be stored in a 32bit float. - - -namespace nv -{ - // Less error prone than casting. From CB: - // http://cbloomrants.blogspot.com/2011/06/06-17-11-c-casting-is-devil.html - - // These intentionally look like casts. - - // uint64 casts: - template <typename T> inline uint64 U64(T x) { return x; } - //template <> inline uint64 U64<uint64>(uint64 x) { return x; } - template <> inline uint64 U64<int64>(int64 x) { nvDebugCheck(x >= 0); return (uint64)x; } - //template <> inline uint64 U32<uint32>(uint32 x) { return x; } - template <> inline uint64 U64<int32>(int32 x) { nvDebugCheck(x >= 0); return (uint64)x; } - //template <> inline uint64 U64<uint16>(uint16 x) { return x; } - template <> inline uint64 U64<int16>(int16 x) { nvDebugCheck(x >= 0); return (uint64)x; } - //template <> inline uint64 U64<uint8>(uint8 x) { return x; } - template <> inline uint64 U64<int8>(int8 x) { nvDebugCheck(x >= 0); return (uint64)x; } - - // int64 casts: - template <typename T> inline int64 I64(T x) { return x; } - template <> inline int64 I64<uint64>(uint64 x) { nvDebugCheck(x <= NV_INT64_MAX); return (int64)x; } - //template <> inline uint64 U64<int64>(int64 x) { return x; } - //template <> inline uint64 U32<uint32>(uint32 x) { return x; } - //template <> inline uint64 U64<int32>(int32 x) { return x; } - //template <> inline uint64 U64<uint16>(uint16 x) { return x; } - //template <> inline uint64 U64<int16>(int16 x) { return x; } - //template <> inline uint64 U64<uint8>(uint8 x) { return x; } - //template <> inline uint64 U64<int8>(int8 x) { return x; } - - // uint32 casts: - template <typename T> inline uint32 U32(T x) { return x; } - template <> inline uint32 U32<uint64>(uint64 x) { nvDebugCheck(x <= NV_UINT32_MAX); return (uint32)x; } - template <> inline uint32 U32<int64>(int64 x) { nvDebugCheck(x >= 0 && x <= NV_UINT32_MAX); return (uint32)x; } - //template <> inline uint32 U32<uint32>(uint32 x) { return x; } - template <> inline uint32 U32<int32>(int32 x) { nvDebugCheck(x >= 0); return (uint32)x; } - //template <> inline uint32 U32<uint16>(uint16 x) { return x; } - template <> inline uint32 U32<int16>(int16 x) { nvDebugCheck(x >= 0); return (uint32)x; } - //template <> inline uint32 U32<uint8>(uint8 x) { return x; } - template <> inline uint32 U32<int8>(int8 x) { nvDebugCheck(x >= 0); return (uint32)x; } - - // int32 casts: - template <typename T> inline int32 I32(T x) { return x; } - template <> inline int32 I32<uint64>(uint64 x) { nvDebugCheck(x <= NV_INT32_MAX); return (int32)x; } - template <> inline int32 I32<int64>(int64 x) { nvDebugCheck(x >= NV_INT32_MIN && x <= NV_UINT32_MAX); return (int32)x; } - template <> inline int32 I32<uint32>(uint32 x) { nvDebugCheck(x <= NV_INT32_MAX); return (int32)x; } - //template <> inline int32 I32<int32>(int32 x) { return x; } - //template <> inline int32 I32<uint16>(uint16 x) { return x; } - //template <> inline int32 I32<int16>(int16 x) { return x; } - //template <> inline int32 I32<uint8>(uint8 x) { return x; } - //template <> inline int32 I32<int8>(int8 x) { return x; } - - // uint16 casts: - template <typename T> inline uint16 U16(T x) { return x; } - template <> inline uint16 U16<uint64>(uint64 x) { nvDebugCheck(x <= NV_UINT16_MAX); return (uint16)x; } - template <> inline uint16 U16<int64>(int64 x) { nvDebugCheck(x >= 0 && x <= NV_UINT16_MAX); return (uint16)x; } - template <> inline uint16 U16<uint32>(uint32 x) { nvDebugCheck(x <= NV_UINT16_MAX); return (uint16)x; } - template <> inline uint16 U16<int32>(int32 x) { nvDebugCheck(x >= 0 && x <= NV_UINT16_MAX); return (uint16)x; } - //template <> inline uint16 U16<uint16>(uint16 x) { return x; } - template <> inline uint16 U16<int16>(int16 x) { nvDebugCheck(x >= 0); return (uint16)x; } - //template <> inline uint16 U16<uint8>(uint8 x) { return x; } - template <> inline uint16 U16<int8>(int8 x) { nvDebugCheck(x >= 0); return (uint16)x; } - - // int16 casts: - template <typename T> inline int16 I16(T x) { return x; } - template <> inline int16 I16<uint64>(uint64 x) { nvDebugCheck(x <= NV_INT16_MAX); return (int16)x; } - template <> inline int16 I16<int64>(int64 x) { nvDebugCheck(x >= NV_INT16_MIN && x <= NV_UINT16_MAX); return (int16)x; } - template <> inline int16 I16<uint32>(uint32 x) { nvDebugCheck(x <= NV_INT16_MAX); return (int16)x; } - template <> inline int16 I16<int32>(int32 x) { nvDebugCheck(x >= NV_INT16_MIN && x <= NV_UINT16_MAX); return (int16)x; } - template <> inline int16 I16<uint16>(uint16 x) { nvDebugCheck(x <= NV_INT16_MAX); return (int16)x; } - //template <> inline int16 I16<int16>(int16 x) { return x; } - //template <> inline int16 I16<uint8>(uint8 x) { return x; } - //template <> inline int16 I16<int8>(int8 x) { return x; } - - // uint8 casts: - template <typename T> inline uint8 U8(T x) { return x; } - template <> inline uint8 U8<uint64>(uint64 x) { nvDebugCheck(x <= NV_UINT8_MAX); return (uint8)x; } - template <> inline uint8 U8<int64>(int64 x) { nvDebugCheck(x >= 0 && x <= NV_UINT8_MAX); return (uint8)x; } - template <> inline uint8 U8<uint32>(uint32 x) { nvDebugCheck(x <= NV_UINT8_MAX); return (uint8)x; } - template <> inline uint8 U8<int32>(int32 x) { nvDebugCheck(x >= 0 && x <= NV_UINT8_MAX); return (uint8)x; } - template <> inline uint8 U8<uint16>(uint16 x) { nvDebugCheck(x <= NV_UINT8_MAX); return (uint8)x; } - template <> inline uint8 U8<int16>(int16 x) { nvDebugCheck(x >= 0 && x <= NV_UINT8_MAX); return (uint8)x; } - //template <> inline uint8 U8<uint8>(uint8 x) { return x; } - template <> inline uint8 U8<int8>(int8 x) { nvDebugCheck(x >= 0); return (uint8)x; } - //template <> inline uint8 U8<float>(int8 x) { nvDebugCheck(x >= 0.0f && x <= 255.0f); return (uint8)x; } - - // int8 casts: - template <typename T> inline int8 I8(T x) { return x; } - template <> inline int8 I8<uint64>(uint64 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } - template <> inline int8 I8<int64>(int64 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; } - template <> inline int8 I8<uint32>(uint32 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } - template <> inline int8 I8<int32>(int32 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; } - template <> inline int8 I8<uint16>(uint16 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } - template <> inline int8 I8<int16>(int16 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; } - template <> inline int8 I8<uint8>(uint8 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } - //template <> inline int8 I8<int8>(int8 x) { return x; } - - // float casts: - template <typename T> inline float F32(T x) { return x; } - template <> inline float F32<uint64>(uint64 x) { nvDebugCheck(x <= NV_INTEGER_TO_FLOAT_MAX); return (float)x; } - template <> inline float F32<int64>(int64 x) { nvDebugCheck(x >= -NV_INTEGER_TO_FLOAT_MAX && x <= NV_INTEGER_TO_FLOAT_MAX); return (float)x; } - template <> inline float F32<uint32>(uint32 x) { nvDebugCheck(x <= NV_INTEGER_TO_FLOAT_MAX); return (float)x; } - template <> inline float F32<int32>(int32 x) { nvDebugCheck(x >= -NV_INTEGER_TO_FLOAT_MAX && x <= NV_INTEGER_TO_FLOAT_MAX); return (float)x; } - // The compiler should not complain about these conversions: - //template <> inline float F32<uint16>(uint16 x) { nvDebugCheck(return (float)x; } - //template <> inline float F32<int16>(int16 x) { nvDebugCheck(return (float)x; } - //template <> inline float F32<uint8>(uint8 x) { nvDebugCheck(return (float)x; } - //template <> inline float F32<int8>(int8 x) { nvDebugCheck(return (float)x; } - - - /// Swap two values. - template <typename T> - inline void swap(T & a, T & b) - { - T temp(a); - a = b; - b = temp; - } - - /// Return the maximum of the two arguments. For floating point values, it returns the second value if the first is NaN. - template <typename T> - //inline const T & max(const T & a, const T & b) - inline T max(const T & a, const T & b) - { - return (b < a) ? a : b; - } - - /// Return the maximum of the four arguments. - template <typename T> - //inline const T & max4(const T & a, const T & b, const T & c) - inline T max4(const T & a, const T & b, const T & c, const T & d) - { - return max(max(a, b), max(c, d)); - } - - /// Return the maximum of the three arguments. - template <typename T> - //inline const T & max3(const T & a, const T & b, const T & c) - inline T max3(const T & a, const T & b, const T & c) - { - return max(a, max(b, c)); - } - - /// Return the minimum of two values. - template <typename T> - //inline const T & min(const T & a, const T & b) - inline T min(const T & a, const T & b) - { - return (a < b) ? a : b; - } - - /// Return the maximum of the three arguments. - template <typename T> - //inline const T & min3(const T & a, const T & b, const T & c) - inline T min3(const T & a, const T & b, const T & c) - { - return min(a, min(b, c)); - } - - /// Clamp between two values. - template <typename T> - //inline const T & clamp(const T & x, const T & a, const T & b) - inline T clamp(const T & x, const T & a, const T & b) - { - return min(max(x, a), b); - } - - /** 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) - */ - inline uint32 nextPowerOfTwo(uint32 x) - { - nvDebugCheck( x != 0 ); -#if 1 // 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; -#else - uint p = 1; - while( x > p ) { - p += p; - } - return p; -#endif - } - - inline uint64 nextPowerOfTwo(uint64 x) - { - nvDebugCheck(x != 0); - uint p = 1; - while (x > p) { - p += p; - } - return p; - } - - // @@ Should I just use a macro instead? - template <typename T> - inline bool isPowerOfTwo(T n) - { - return (n & (n-1)) == 0; - } - - - // @@ Move this to utils? - /// Delete all the elements of a container. - template <typename T> - void deleteAll(T & container) - { - for (typename T::PseudoIndex i = container.start(); !container.isDone(i); container.advance(i)) - { - delete container[i]; - } - } - - - - // @@ Specialize these methods for numeric, pointer, and pod types. - - template <typename T> - void construct_range(T * restrict ptr, uint new_size, uint old_size) { - for (uint i = old_size; i < new_size; i++) { - new(ptr+i) T; // placement new - } - } - - template <typename T> - void construct_range(T * restrict ptr, uint new_size, uint old_size, const T & elem) { - for (uint i = old_size; i < new_size; i++) { - new(ptr+i) T(elem); // placement new - } - } - - template <typename T> - void construct_range(T * restrict ptr, uint new_size, uint old_size, const T * src) { - for (uint i = old_size; i < new_size; i++) { - new(ptr+i) T(src[i]); // placement new - } - } - - template <typename T> - void destroy_range(T * restrict ptr, uint new_size, uint old_size) { - for (uint i = new_size; i < old_size; i++) { - (ptr+i)->~T(); // Explicit call to the destructor - } - } - - template <typename T> - void fill(T * restrict dst, uint count, const T & value) { - for (uint i = 0; i < count; i++) { - dst[i] = value; - } - } - - template <typename T> - void copy_range(T * restrict dst, const T * restrict src, uint count) { - for (uint i = 0; i < count; i++) { - dst[i] = src[i]; - } - } - - template <typename T> - bool find(const T & element, const T * restrict ptr, uint begin, uint end, uint * index) { - for (uint i = begin; i < end; i++) { - if (ptr[i] == element) { - if (index != NULL) *index = i; - return true; - } - } - return false; - } - -} // nv namespace - -#endif // NV_CORE_UTILS_H diff --git a/thirdparty/thekla_atlas/nvcore/nvcore.h b/thirdparty/thekla_atlas/nvcore/nvcore.h deleted file mode 100644 index 5ef69668d9..0000000000 --- a/thirdparty/thekla_atlas/nvcore/nvcore.h +++ /dev/null @@ -1,360 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_CORE_H -#define NV_CORE_H - -// Function linkage -#if NVCORE_SHARED -#ifdef NVCORE_EXPORTS -#define NVCORE_API DLL_EXPORT -#define NVCORE_CLASS DLL_EXPORT_CLASS -#else -#define NVCORE_API DLL_IMPORT -#define NVCORE_CLASS DLL_IMPORT -#endif -#else // NVCORE_SHARED -#define NVCORE_API -#define NVCORE_CLASS -#endif // NVCORE_SHARED - - -// Platform definitions -#include <posh.h> - -// OS: -// NV_OS_WIN32 -// NV_OS_WIN64 -// NV_OS_MINGW -// NV_OS_CYGWIN -// NV_OS_LINUX -// NV_OS_UNIX -// NV_OS_DARWIN -// NV_OS_XBOX -// NV_OS_ORBIS -// NV_OS_IOS - -#define NV_OS_STRING POSH_OS_STRING - -#if defined POSH_OS_LINUX -# define NV_OS_LINUX 1 -# define NV_OS_UNIX 1 -#elif defined POSH_OS_ORBIS -# define NV_OS_ORBIS 1 -#elif defined POSH_OS_FREEBSD -# define NV_OS_FREEBSD 1 -# define NV_OS_UNIX 1 -#elif defined POSH_OS_HAIKU -# define NV_OS_HAIKU 1 -# define NV_OS_UNIX 1 -#elif defined POSH_OS_OPENBSD -# define NV_OS_OPENBSD 1 -# define NV_OS_UNIX 1 -#elif defined POSH_OS_CYGWIN32 -# define NV_OS_CYGWIN 1 -#elif defined POSH_OS_MINGW -# define NV_OS_MINGW 1 -# define NV_OS_WIN32 1 -#elif defined POSH_OS_OSX -# define NV_OS_OSX 1 // IC: Adding this, because iOS defines NV_OS_DARWIN too. -# define NV_OS_DARWIN 1 -# define NV_OS_UNIX 1 -#elif defined POSH_OS_IOS -# define NV_OS_DARWIN 1 //ACS should we keep this on IOS? -# define NV_OS_UNIX 1 -# define NV_OS_IOS 1 -#elif defined POSH_OS_UNIX -# define NV_OS_UNIX 1 -#elif defined POSH_OS_WIN64 -# define NV_OS_WIN32 1 -# define NV_OS_WIN64 1 -#elif defined POSH_OS_WIN32 -# define NV_OS_WIN32 1 -#elif defined POSH_OS_XBOX -# define NV_OS_XBOX 1 -#elif defined POSH_OS_DURANGO -# define NV_OS_DURANGO 1 -#else -# error "Unsupported OS" -#endif - - -// Is this a console OS? (i.e. connected to a TV) -#if NV_OS_ORBIS || NV_OS_XBOX || NV_OS_DURANGO -# define NV_OS_CONSOLE 1 -#endif - - -// Threading: -// some platforms don't implement __thread or similar for thread-local-storage -#if NV_OS_UNIX || NV_OS_ORBIS || NV_OS_IOS //ACStodoIOS darwin instead of ios? -# define NV_OS_USE_PTHREAD 1 -# if NV_OS_IOS -# define NV_OS_HAS_TLS_QUALIFIER 0 -# else -# define NV_OS_HAS_TLS_QUALIFIER 1 -# endif -#else -# define NV_OS_USE_PTHREAD 0 -# define NV_OS_HAS_TLS_QUALIFIER 1 -#endif - - -// CPUs: -// NV_CPU_X86 -// NV_CPU_X86_64 -// NV_CPU_PPC -// NV_CPU_ARM - -#define NV_CPU_STRING POSH_CPU_STRING - -#if defined POSH_CPU_X86_64 -//# define NV_CPU_X86 1 -# define NV_CPU_X86_64 1 -#elif defined POSH_CPU_X86 -# define NV_CPU_X86 1 -#elif defined POSH_CPU_PPC -# define NV_CPU_PPC 1 -#elif defined POSH_CPU_STRONGARM -# define NV_CPU_ARM 1 -#else -# error "Unsupported CPU" -#endif - - -// Compiler: -// NV_CC_GNUC -// NV_CC_MSVC -// NV_CC_CLANG - -#if defined POSH_COMPILER_CLANG -# define NV_CC_CLANG 1 -# define NV_CC_GNUC 1 // Clang is compatible with GCC. -# define NV_CC_STRING "clang" -#elif defined POSH_COMPILER_GCC -# define NV_CC_GNUC 1 -# define NV_CC_STRING "gcc" -#elif defined POSH_COMPILER_MSVC -# define NV_CC_MSVC 1 -# define NV_CC_STRING "msvc" -#else -# error "Unsupported compiler" -#endif - -#if NV_CC_MSVC -#define NV_CC_CPP11 (__cplusplus > 199711L || _MSC_VER >= 1800) // Visual Studio 2013 has all the features we use, but doesn't advertise full C++11 support yet. -#else -// @@ IC: This works in CLANG, about GCC? -// @@ ES: Doesn't work in gcc. These 3 features are available in GCC >= 4.4. -#ifdef __clang__ -#define NV_CC_CPP11 (__has_feature(cxx_deleted_functions) && __has_feature(cxx_rvalue_references) && __has_feature(cxx_static_assert)) -#elif defined __GNUC__ -#define NV_CC_CPP11 ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)) -#endif -#endif - -// Endiannes: -#define NV_LITTLE_ENDIAN POSH_LITTLE_ENDIAN -#define NV_BIG_ENDIAN POSH_BIG_ENDIAN -#define NV_ENDIAN_STRING POSH_ENDIAN_STRING - - -// Define the right printf prefix for size_t arguments: -#if POSH_64BIT_POINTER -# define NV_SIZET_PRINTF_PREFIX POSH_I64_PRINTF_PREFIX -#else -# define NV_SIZET_PRINTF_PREFIX -#endif - - -// cmake config -#include "nvconfig.h" - -#if NV_OS_DARWIN -#include <stdint.h> -//#include <inttypes.h> - -// Type definitions: -typedef uint8_t uint8; -typedef int8_t int8; - -typedef uint16_t uint16; -typedef int16_t int16; - -typedef uint32_t uint32; -typedef int32_t int32; - -typedef uint64_t uint64; -typedef int64_t int64; - -// POSH gets this wrong due to __LP64__ -#undef POSH_I64_PRINTF_PREFIX -#define POSH_I64_PRINTF_PREFIX "ll" - -#else - -// Type definitions: -typedef posh_u8_t uint8; -typedef posh_i8_t int8; - -typedef posh_u16_t uint16; -typedef posh_i16_t int16; - -typedef posh_u32_t uint32; -typedef posh_i32_t int32; - -//#if NV_OS_DARWIN -// OSX-64 is supposed to be LP64 (longs and pointers are 64 bits), thus uint64 is defined as -// unsigned long. However, some OSX headers define it as unsigned long long, producing errors, -// even though both types are 64 bit. Ideally posh should handle that, but it has not been -// updated in ages, so here I'm just falling back to the standard C99 types defined in inttypes.h -//#include <inttypes.h> -//typedef posh_u64_t uint64_t; -//typedef posh_i64_t int64_t; -//#else -typedef posh_u64_t uint64; -typedef posh_i64_t int64; -//#endif -#if NV_OS_DARWIN -// To avoid duplicate definitions. -#define _UINT64 -#endif -#endif - -// Aliases -typedef uint32 uint; - - -// Version string: -#define NV_VERSION_STRING \ - NV_OS_STRING "/" NV_CC_STRING "/" NV_CPU_STRING"/" \ - NV_ENDIAN_STRING"-endian - " __DATE__ "-" __TIME__ - - -// Disable copy constructor and assignment operator. -#if NV_CC_CPP11 -#define NV_FORBID_COPY(C) \ - C( const C & ) = delete; \ - C &operator=( const C & ) = delete -#else -#define NV_FORBID_COPY(C) \ - private: \ - C( const C & ); \ - C &operator=( const C & ) -#endif - -// Disable dynamic allocation on the heap. -// See Prohibiting Heap-Based Objects in More Effective C++. -#define NV_FORBID_HEAPALLOC() \ - private: \ - void *operator new(size_t size); \ - void *operator new[](size_t size) - //static void *operator new(size_t size); \ - //static void *operator new[](size_t size); - -// String concatenation macros. -#define NV_STRING_JOIN2(arg1, arg2) NV_DO_STRING_JOIN2(arg1, arg2) -#define NV_DO_STRING_JOIN2(arg1, arg2) arg1 ## arg2 -#define NV_STRING_JOIN3(arg1, arg2, arg3) NV_DO_STRING_JOIN3(arg1, arg2, arg3) -#define NV_DO_STRING_JOIN3(arg1, arg2, arg3) arg1 ## arg2 ## arg3 -#define NV_STRING2(x) #x -#define NV_STRING(x) NV_STRING2(x) - -#if NV_CC_MSVC -#define NV_MULTI_LINE_MACRO_BEGIN do { -#define NV_MULTI_LINE_MACRO_END \ - __pragma(warning(push)) \ - __pragma(warning(disable:4127)) \ - } while(false) \ - __pragma(warning(pop)) -#else -#define NV_MULTI_LINE_MACRO_BEGIN do { -#define NV_MULTI_LINE_MACRO_END } while(false) -#endif - -#if NV_CC_CPP11 -#define nvStaticCheck(x) static_assert((x), "Static assert "#x" failed") -#else -#define nvStaticCheck(x) typedef char NV_STRING_JOIN2(__static_assert_,__LINE__)[(x)] -#endif -#define NV_COMPILER_CHECK(x) nvStaticCheck(x) // I like this name best. - -// Make sure type definitions are fine. -NV_COMPILER_CHECK(sizeof(int8) == 1); -NV_COMPILER_CHECK(sizeof(uint8) == 1); -NV_COMPILER_CHECK(sizeof(int16) == 2); -NV_COMPILER_CHECK(sizeof(uint16) == 2); -NV_COMPILER_CHECK(sizeof(int32) == 4); -NV_COMPILER_CHECK(sizeof(uint32) == 4); -NV_COMPILER_CHECK(sizeof(int32) == 4); -NV_COMPILER_CHECK(sizeof(uint32) == 4); - -#include <stddef.h> // for size_t -template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N]; -#define NV_ARRAY_SIZE(x) sizeof(ArraySizeHelper(x)) -//#define NV_ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) - -#if 0 // Disabled in The Witness. -#if NV_CC_MSVC -#define NV_MESSAGE(x) message(__FILE__ "(" NV_STRING(__LINE__) ") : " x) -#else -#define NV_MESSAGE(x) message(x) -#endif -#else -#define NV_MESSAGE(x) -#endif - - -// Startup initialization macro. -#define NV_AT_STARTUP(some_code) \ - namespace { \ - static struct NV_STRING_JOIN2(AtStartup_, __LINE__) { \ - NV_STRING_JOIN2(AtStartup_, __LINE__)() { some_code; } \ - } \ - NV_STRING_JOIN3(AtStartup_, __LINE__, Instance); \ - } - -// Indicate the compiler that the parameter is not used to suppress compier warnings. -#if NV_CC_MSVC -#define NV_UNUSED(a) ((a)=(a)) -#else -#define NV_UNUSED(a) _Pragma(NV_STRING(unused(a))) -#endif - -// Null index. @@ Move this somewhere else... it's only used by nvmesh. -//const unsigned int NIL = unsigned int(~0); -#define NIL uint(~0) - -// Null pointer. -#ifndef NULL -#define NULL 0 -#endif - -// Platform includes -#if NV_CC_MSVC -# if NV_OS_WIN32 -# include "DefsVcWin32.h" -# elif NV_OS_XBOX -# include "DefsVcXBox.h" -# elif NV_OS_DURANGO -# include "DefsVcDurango.h" -# else -# error "MSVC: Platform not supported" -# endif -#elif NV_CC_GNUC -# if NV_OS_LINUX -# include "DefsGnucLinux.h" -# elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD || NV_OS_HAIKU -# include "DefsGnucDarwin.h" -# elif NV_OS_ORBIS -# include "DefsOrbis.h" -# elif NV_OS_MINGW -# include "DefsGnucWin32.h" -# elif NV_OS_CYGWIN -# error "GCC: Cygwin not supported" -# else -# error "GCC: Platform not supported" -# endif -#endif - -#endif // NV_CORE_H diff --git a/thirdparty/thekla_atlas/nvcore/scanf.c b/thirdparty/thekla_atlas/nvcore/scanf.c deleted file mode 100644 index bf9d293154..0000000000 --- a/thirdparty/thekla_atlas/nvcore/scanf.c +++ /dev/null @@ -1,641 +0,0 @@ -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Chris Torek. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * From: Id: vfscanf.c,v 1.13 1998/09/25 12:20:27 obrien Exp - * From: static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93"; - * From: static char sccsid[] = "@(#)strtoul.c 8.1 (Berkeley) 6/4/93"; - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <ctype.h> -#include <string.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#pragma warning(disable : 4244) // conversion from '*' to '*', possible loss of data -#pragma warning(disable : 4018) // signed/unsigned mismatch -#pragma warning(disable : 4267) // '=' : conversion from 'size_t' to 'int', possible loss of data - -#define strtoq _strtoi64 -#define strtouq _strtoui64 -#define bcopy(b1,b2,len) (memmove((b2), (b1), (len)), (void) 0) - -typedef int long long quad_t; -typedef unsigned long long u_quad_t; -typedef unsigned char u_char; - -#define BUF 32 /* Maximum length of numeric string. */ - -/* - * Flags used during conversion. - */ -#define LONG 0x01 /* l: long or double */ -#define SHORT 0x04 /* h: short */ -#define SUPPRESS 0x08 /* suppress assignment */ -#define POINTER 0x10 /* weird %p pointer (`fake hex') */ -#define NOSKIP 0x20 /* do not skip blanks */ -#define QUAD 0x400 - -/* - * The following are used in numeric conversions only: - * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; - * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. - */ -#define SIGNOK 0x40 /* +/- is (still) legal */ -#define NDIGITS 0x80 /* no digits detected */ - -#define DPTOK 0x100 /* (float) decimal point is still legal */ -#define EXPOK 0x200 /* (float) exponent (e+3, etc) still legal */ - -#define PFXOK 0x100 /* 0x prefix is (still) legal */ -#define NZDIGITS 0x200 /* no zero digits detected */ - -/* - * Conversion types. - */ -#define CT_CHAR 0 /* %c conversion */ -#define CT_CCL 1 /* %[...] conversion */ -#define CT_STRING 2 /* %s conversion */ -#define CT_INT 3 /* integer, i.e., strtoq or strtouq */ -typedef u_quad_t (*ccfntype)(const char *, char **, int); - -static const u_char *__sccl(char *, const u_char *); - -int -vsscanf(const char *inp, char const *fmt0, va_list ap) -{ - int inr; - const u_char *fmt = (const u_char *)fmt0; - int c; /* character from format, or conversion */ - size_t width; /* field width, or 0 */ - char *p; /* points into all kinds of strings */ - int n; /* handy integer */ - int flags; /* flags as defined above */ - char *p0; /* saves original value of p when necessary */ - int nassigned; /* number of fields assigned */ - int nconversions; /* number of conversions */ - int nread; /* number of characters consumed from fp */ - int base; /* base argument to strtoq/strtouq */ - ccfntype ccfn; /* conversion function (strtoq/strtouq) */ - char ccltab[256]; /* character class table for %[...] */ - char buf[BUF]; /* buffer for numeric conversions */ - - /* `basefix' is used to avoid `if' tests in the integer scanner */ - static short basefix[17] = - { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; - - inr = strlen(inp); - - nassigned = 0; - nconversions = 0; - nread = 0; - base = 0; /* XXX just to keep gcc happy */ - ccfn = NULL; /* XXX just to keep gcc happy */ - for (;;) { - c = *fmt++; - if (c == 0) - return (nassigned); - if (isspace(c)) { - while (inr > 0 && isspace(*inp)) - nread++, inr--, inp++; - continue; - } - if (c != '%') - goto literal; - width = 0; - flags = 0; - /* - * switch on the format. continue if done; - * break once format type is derived. - */ -again: c = *fmt++; - switch (c) { - case '%': -literal: - if (inr <= 0) - goto input_failure; - if (*inp != c) - goto match_failure; - inr--, inp++; - nread++; - continue; - - case '*': - flags |= SUPPRESS; - goto again; - case 'l': - flags |= LONG; - goto again; - case 'q': - flags |= QUAD; - goto again; - case 'h': - flags |= SHORT; - goto again; - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - width = width * 10 + c - '0'; - goto again; - - /* - * Conversions. - * - */ - case 'd': - c = CT_INT; - ccfn = (ccfntype)strtoq; - base = 10; - break; - - case 'i': - c = CT_INT; - ccfn = (ccfntype)strtoq; - base = 0; - break; - - case 'o': - c = CT_INT; - ccfn = strtouq; - base = 8; - break; - - case 'u': - c = CT_INT; - ccfn = strtouq; - base = 10; - break; - - case 'x': - flags |= PFXOK; /* enable 0x prefixing */ - c = CT_INT; - ccfn = strtouq; - base = 16; - break; - - case 's': - c = CT_STRING; - break; - - case '[': - fmt = __sccl(ccltab, fmt); - flags |= NOSKIP; - c = CT_CCL; - break; - - case 'c': - flags |= NOSKIP; - c = CT_CHAR; - break; - - case 'p': /* pointer format is like hex */ - flags |= POINTER | PFXOK; - c = CT_INT; - ccfn = strtouq; - base = 16; - break; - - case 'n': - nconversions++; - if (flags & SUPPRESS) /* ??? */ - continue; - if (flags & SHORT) - *va_arg(ap, short *) = nread; - else if (flags & LONG) - *va_arg(ap, long *) = nread; - else if (flags & QUAD) - *va_arg(ap, quad_t *) = nread; - else - *va_arg(ap, int *) = nread; - continue; - } - - /* - * We have a conversion that requires input. - */ - if (inr <= 0) - goto input_failure; - - /* - * Consume leading white space, except for formats - * that suppress this. - */ - if ((flags & NOSKIP) == 0) { - while (isspace(*inp)) { - nread++; - if (--inr > 0) - inp++; - else - goto input_failure; - } - /* - * Note that there is at least one character in - * the buffer, so conversions that do not set NOSKIP - * can no longer result in an input failure. - */ - } - - /* - * Do the conversion. - */ - switch (c) { - - case CT_CHAR: - /* scan arbitrary characters (sets NOSKIP) */ - if (width == 0) - width = 1; - if (flags & SUPPRESS) { - size_t sum = 0; - for (;;) { - if ((n = inr) < width) { - sum += n; - width -= n; - inp += n; - if (sum == 0) - goto input_failure; - break; - } else { - sum += width; - inr -= width; - inp += width; - break; - } - } - nread += sum; - } else { - bcopy(inp, va_arg(ap, char *), width); - inr -= width; - inp += width; - nread += width; - nassigned++; - } - nconversions++; - break; - - case CT_CCL: - /* scan a (nonempty) character class (sets NOSKIP) */ - if (width == 0) - width = (size_t)~0; /* `infinity' */ - /* take only those things in the class */ - if (flags & SUPPRESS) { - n = 0; - while (ccltab[(unsigned char)*inp]) { - n++, inr--, inp++; - if (--width == 0) - break; - if (inr <= 0) { - if (n == 0) - goto input_failure; - break; - } - } - if (n == 0) - goto match_failure; - } else { - p0 = p = va_arg(ap, char *); - while (ccltab[(unsigned char)*inp]) { - inr--; - *p++ = *inp++; - if (--width == 0) - break; - if (inr <= 0) { - if (p == p0) - goto input_failure; - break; - } - } - n = p - p0; - if (n == 0) - goto match_failure; - *p = 0; - nassigned++; - } - nread += n; - nconversions++; - break; - - case CT_STRING: - /* like CCL, but zero-length string OK, & no NOSKIP */ - if (width == 0) - width = (size_t)~0; - if (flags & SUPPRESS) { - n = 0; - while (!isspace(*inp)) { - n++, inr--, inp++; - if (--width == 0) - break; - if (inr <= 0) - break; - } - nread += n; - } else { - p0 = p = va_arg(ap, char *); - while (!isspace(*inp)) { - inr--; - *p++ = *inp++; - if (--width == 0) - break; - if (inr <= 0) - break; - } - *p = 0; - nread += p - p0; - nassigned++; - } - nconversions++; - continue; - - case CT_INT: - /* scan an integer as if by strtoq/strtouq */ -#ifdef hardway - if (width == 0 || width > sizeof(buf) - 1) - width = sizeof(buf) - 1; -#else - /* size_t is unsigned, hence this optimisation */ - if (--width > sizeof(buf) - 2) - width = sizeof(buf) - 2; - width++; -#endif - flags |= SIGNOK | NDIGITS | NZDIGITS; - for (p = buf; width; width--) { - c = *inp; - /* - * Switch on the character; `goto ok' - * if we accept it as a part of number. - */ - switch (c) { - - /* - * The digit 0 is always legal, but is - * special. For %i conversions, if no - * digits (zero or nonzero) have been - * scanned (only signs), we will have - * base==0. In that case, we should set - * it to 8 and enable 0x prefixing. - * Also, if we have not scanned zero digits - * before this, do not turn off prefixing - * (someone else will turn it off if we - * have scanned any nonzero digits). - */ - case '0': - if (base == 0) { - base = 8; - flags |= PFXOK; - } - if (flags & NZDIGITS) - flags &= ~(SIGNOK|NZDIGITS|NDIGITS); - else - flags &= ~(SIGNOK|PFXOK|NDIGITS); - goto ok; - - /* 1 through 7 always legal */ - case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - base = basefix[base]; - flags &= ~(SIGNOK | PFXOK | NDIGITS); - goto ok; - - /* digits 8 and 9 ok iff decimal or hex */ - case '8': case '9': - base = basefix[base]; - if (base <= 8) - break; /* not legal here */ - flags &= ~(SIGNOK | PFXOK | NDIGITS); - goto ok; - - /* letters ok iff hex */ - case 'A': case 'B': case 'C': - case 'D': case 'E': case 'F': - case 'a': case 'b': case 'c': - case 'd': case 'e': case 'f': - /* no need to fix base here */ - if (base <= 10) - break; /* not legal here */ - flags &= ~(SIGNOK | PFXOK | NDIGITS); - goto ok; - - /* sign ok only as first character */ - case '+': case '-': - if (flags & SIGNOK) { - flags &= ~SIGNOK; - goto ok; - } - break; - - /* x ok iff flag still set & 2nd char */ - case 'x': case 'X': - if (flags & PFXOK && p == buf + 1) { - base = 16; /* if %i */ - flags &= ~PFXOK; - goto ok; - } - break; - } - - /* - * If we got here, c is not a legal character - * for a number. Stop accumulating digits. - */ - break; - ok: - /* - * c is legal: store it and look at the next. - */ - *p++ = c; - if (--inr > 0) - inp++; - else - break; /* end of input */ - } - /* - * If we had only a sign, it is no good; push - * back the sign. If the number ends in `x', - * it was [sign] '0' 'x', so push back the x - * and treat it as [sign] '0'. - */ - if (flags & NDIGITS) { - if (p > buf) { - inp--; - inr++; - } - goto match_failure; - } - c = ((u_char *)p)[-1]; - if (c == 'x' || c == 'X') { - --p; - inp--; - inr++; - } - if ((flags & SUPPRESS) == 0) { - u_quad_t res; - - *p = 0; - res = (*ccfn)(buf, (char **)NULL, base); - if (flags & POINTER) - *va_arg(ap, void **) = - (void *)(uintptr_t)res; - else if (flags & SHORT) - *va_arg(ap, short *) = res; - else if (flags & LONG) - *va_arg(ap, long *) = res; - else if (flags & QUAD) - *va_arg(ap, quad_t *) = res; - else - *va_arg(ap, int *) = res; - nassigned++; - } - nread += p - buf; - nconversions++; - break; - - } - } -input_failure: - return (nconversions != 0 ? nassigned : -1); -match_failure: - return (nassigned); -} - - -/* - * Fill in the given table from the scanset at the given format - * (just after `['). Return a pointer to the character past the - * closing `]'. The table has a 1 wherever characters should be - * considered part of the scanset. - */ -static const u_char * -__sccl(char *tab, const u_char *fmt) -{ - int c, n, v; - - /* first `clear' the whole table */ - c = *fmt++; /* first char hat => negated scanset */ - if (c == '^') { - v = 1; /* default => accept */ - c = *fmt++; /* get new first char */ - } else - v = 0; /* default => reject */ - - /* XXX: Will not work if sizeof(tab*) > sizeof(char) */ - for (n = 0; n < 256; n++) - tab[n] = v; /* memset(tab, v, 256) */ - - if (c == 0) - return (fmt - 1);/* format ended before closing ] */ - - /* - * Now set the entries corresponding to the actual scanset - * to the opposite of the above. - * - * The first character may be ']' (or '-') without being special; - * the last character may be '-'. - */ - v = 1 - v; - for (;;) { - tab[c] = v; /* take character c */ -doswitch: - n = *fmt++; /* and examine the next */ - switch (n) { - - case 0: /* format ended too soon */ - return (fmt - 1); - - case '-': - /* - * A scanset of the form - * [01+-] - * is defined as `the digit 0, the digit 1, - * the character +, the character -', but - * the effect of a scanset such as - * [a-zA-Z0-9] - * is implementation defined. The V7 Unix - * scanf treats `a-z' as `the letters a through - * z', but treats `a-a' as `the letter a, the - * character -, and the letter a'. - * - * For compatibility, the `-' is not considerd - * to define a range if the character following - * it is either a close bracket (required by ANSI) - * or is not numerically greater than the character - * we just stored in the table (c). - */ - n = *fmt; - if (n == ']' || n < c) { - c = '-'; - break; /* resume the for(;;) */ - } - fmt++; - /* fill in the range */ - do { - tab[++c] = v; - } while (c < n); - c = n; - /* - * Alas, the V7 Unix scanf also treats formats - * such as [a-c-e] as `the letters a through e'. - * This too is permitted by the standard.... - */ - goto doswitch; - break; - - case ']': /* end of scanset */ - return (fmt); - - default: /* just another character */ - c = n; - break; - } - } - /* NOTREACHED */ -} - -/* -int -sscanf(const char *ibuf, const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = vsscanf(ibuf, fmt, ap); - va_end(ap); - - return(ret); -} -*/ - -#ifdef __cplusplus -} -#endif diff --git a/thirdparty/thekla_atlas/nvimage/BitMap.cpp b/thirdparty/thekla_atlas/nvimage/BitMap.cpp deleted file mode 100644 index 8cc49644ea..0000000000 --- a/thirdparty/thekla_atlas/nvimage/BitMap.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "BitMap.h" - -using namespace nv; - -void BitMap::resize(uint w, uint 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 (uint y = 0; y < m_height; y++) - { - for (uint x = 0; x < m_width; x++) - { - //tmp.setBitAt(y*w + x, bitAt(x, y)); - if (bitAt(x, y) != initValue) tmp.toggleBitAt(y*w + x); - } - } - - swap(m_bitArray, tmp); - m_width = w; - m_height = h; -} diff --git a/thirdparty/thekla_atlas/nvimage/BitMap.h b/thirdparty/thekla_atlas/nvimage/BitMap.h deleted file mode 100644 index a285321176..0000000000 --- a/thirdparty/thekla_atlas/nvimage/BitMap.h +++ /dev/null @@ -1,87 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_IMAGE_BITMAP_H -#define NV_IMAGE_BITMAP_H - -#include "nvimage.h" - -#include "nvcore/BitArray.h" - -namespace nv -{ - /// Bit map. This should probably be called BitImage. - class NVIMAGE_CLASS BitMap - { - public: - BitMap() : m_width(0), m_height(0) {} - BitMap(uint w, uint h) : m_width(w), m_height(h), m_bitArray(w*h) {} - - uint width() const { return m_width; } - uint height() const { return m_height; } - - void resize(uint w, uint h, bool initValue); - - bool bitAt(uint x, uint y) const - { - nvDebugCheck(x < m_width && y < m_height); - return m_bitArray.bitAt(y * m_width + x); - } - bool bitAt(uint idx) const - { - return m_bitArray.bitAt(idx); - } - - void setBitAt(uint x, uint y) - { - nvDebugCheck(x < m_width && y < m_height); - m_bitArray.setBitAt(y * m_width + x); - } - void setBitAt(uint idx) - { - m_bitArray.setBitAt(idx); - } - - void clearBitAt(uint x, uint y) - { - nvDebugCheck(x < m_width && y < m_height); - m_bitArray.clearBitAt(y * m_width + x); - } - void clearBitAt(uint idx) - { - m_bitArray.clearBitAt(idx); - } - - void clearAll() - { - m_bitArray.clearAll(); - } - - void setAll() - { - m_bitArray.setAll(); - } - - void toggleAll() - { - m_bitArray.toggleAll(); - } - - friend void swap(BitMap & a, BitMap & b) - { - nvCheck(a.m_width == b.m_width); - nvCheck(a.m_height == b.m_height); - swap(a.m_bitArray, b.m_bitArray); - } - - private: - - uint m_width; - uint m_height; - BitArray m_bitArray; - - }; - -} // nv namespace - -#endif // NV_IMAGE_BITMAP_H diff --git a/thirdparty/thekla_atlas/nvimage/Image.cpp b/thirdparty/thekla_atlas/nvimage/Image.cpp deleted file mode 100644 index 8c0cbcf4e3..0000000000 --- a/thirdparty/thekla_atlas/nvimage/Image.cpp +++ /dev/null @@ -1,210 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "Image.h" -//#include "ImageIO.h" - -#include "nvmath/Color.h" - -#include "nvcore/Debug.h" -#include "nvcore/Ptr.h" -#include "nvcore/Utils.h" // swap -#include "nvcore/Memory.h" // realloc, free - -#include <string.h> // memcpy - - -using namespace nv; - -Image::Image() : m_width(0), m_height(0), m_format(Format_RGB), m_data(NULL) -{ -} - -Image::Image(const Image & img) : m_data(NULL) -{ - allocate(img.m_width, img.m_height, img.m_depth); - m_format = img.m_format; - memcpy(m_data, img.m_data, sizeof(Color32) * m_width * m_height * m_depth); -} - -Image::~Image() -{ - free(); -} - -const Image & Image::operator=(const Image & img) -{ - allocate(img.m_width, img.m_height, m_depth); - m_format = img.m_format; - memcpy(m_data, img.m_data, sizeof(Color32) * m_width * m_height * m_depth); - return *this; -} - - -void Image::allocate(uint w, uint h, uint d/*= 1*/) -{ - m_width = w; - m_height = h; - m_depth = d; - m_data = realloc<Color32>(m_data, w * h * d); -} - -void Image::acquire(Color32 * data, uint w, uint h, uint d/*= 1*/) -{ - free(); - m_width = w; - m_height = h; - m_depth = d; - m_data = data; -} - -void Image::resize(uint w, uint h, uint d/*= 1*/) { - - Image img; - img.allocate(w, h, d); - - Color32 background(0,0,0,0); - - // Copy image. - uint x, y, z; - for(z = 0; z < min(d, m_depth); z++) { - for(y = 0; y < min(h, m_height); y++) { - for(x = 0; x < min(w, m_width); x++) { - img.pixel(x, y, z) = pixel(x, y, z); - } - for(; x < w; x++) { - img.pixel(x, y, z) = background; - } - } - for(; y < h; y++) { - for(x = 0; x < w; x++) { - img.pixel(x, y, z) = background; - } - } - } - for(; z < d; z++) { - for(y = 0; y < h; y++) { - for(x = 0; x < w; x++) { - img.pixel(x, y, z) = background; - } - } - } - - swap(m_width, img.m_width); - swap(m_height, img.m_height); - swap(m_depth, img.m_depth); - swap(m_format, img.m_format); - swap(m_data, img.m_data); -} - -/*bool Image::load(const char * name) -{ - free(); - - AutoPtr<Image> img(ImageIO::load(name)); - if (img == NULL) { - return false; - } - - swap(m_width, img->m_width); - swap(m_height, img->m_height); - swap(m_depth, img->m_depth); - swap(m_format, img->m_format); - swap(m_data, img->m_data); - - return true; -}*/ - -void Image::wrap(void * data, uint w, uint h, uint d) -{ - free(); - m_data = (Color32 *)data; - m_width = w; - m_height = h; - m_depth = d; -} - -void Image::unwrap() -{ - m_data = NULL; - m_width = 0; - m_height = 0; - m_depth = 0; -} - - -void Image::free() -{ - ::free(m_data); - m_data = NULL; -} - - -uint Image::width() const -{ - return m_width; -} - -uint Image::height() const -{ - return m_height; -} - -uint Image::depth() const -{ - return m_depth; -} - -const Color32 * Image::scanline(uint h) const -{ - nvDebugCheck(h < m_height); - return m_data + h * m_width; -} - -Color32 * Image::scanline(uint h) -{ - nvDebugCheck(h < m_height); - return m_data + h * m_width; -} - -const Color32 * Image::pixels() const -{ - return m_data; -} - -Color32 * Image::pixels() -{ - return m_data; -} - -const Color32 & Image::pixel(uint idx) const -{ - nvDebugCheck(idx < m_width * m_height * m_depth); - return m_data[idx]; -} - -Color32 & Image::pixel(uint idx) -{ - nvDebugCheck(idx < m_width * m_height * m_depth); - return m_data[idx]; -} - - -Image::Format Image::format() const -{ - return m_format; -} - -void Image::setFormat(Image::Format f) -{ - m_format = f; -} - -void Image::fill(Color32 c) -{ - const uint size = m_width * m_height * m_depth; - for (uint i = 0; i < size; ++i) - { - m_data[i] = c; - } -} - diff --git a/thirdparty/thekla_atlas/nvimage/Image.h b/thirdparty/thekla_atlas/nvimage/Image.h deleted file mode 100644 index 4c5748cb00..0000000000 --- a/thirdparty/thekla_atlas/nvimage/Image.h +++ /dev/null @@ -1,89 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_IMAGE_IMAGE_H -#define NV_IMAGE_IMAGE_H - -#include "nvimage.h" -#include "nvcore/Debug.h" - -namespace nv -{ - class Color32; - - /// 32 bit RGBA image. - class NVIMAGE_CLASS Image - { - public: - - enum Format - { - Format_RGB, - Format_ARGB, - }; - - Image(); - Image(const Image & img); - ~Image(); - - const Image & operator=(const Image & img); - - - void allocate(uint w, uint h, uint d = 1); - void acquire(Color32 * data, uint w, uint h, uint d = 1); - //bool load(const char * name); - - void resize(uint w, uint h, uint d = 1); - - void wrap(void * data, uint w, uint h, uint d = 1); - void unwrap(); - - uint width() const; - uint height() const; - uint depth() const; - - const Color32 * scanline(uint h) const; - Color32 * scanline(uint h); - - const Color32 * pixels() const; - Color32 * pixels(); - - const Color32 & pixel(uint idx) const; - Color32 & pixel(uint idx); - - const Color32 & pixel(uint x, uint y, uint z = 0) const; - Color32 & pixel(uint x, uint y, uint z = 0); - - Format format() const; - void setFormat(Format f); - - void fill(Color32 c); - - private: - void free(); - - private: - uint m_width; - uint m_height; - uint m_depth; - Format m_format; - Color32 * m_data; - }; - - - inline const Color32 & Image::pixel(uint x, uint y, uint z) const - { - nvDebugCheck(x < m_width && y < m_height && z < m_depth); - return pixel((z * m_height + y) * m_width + x); - } - - inline Color32 & Image::pixel(uint x, uint y, uint z) - { - nvDebugCheck(x < m_width && y < m_height && z < m_depth); - return pixel((z * m_height + y) * m_width + x); - } - -} // nv namespace - - -#endif // NV_IMAGE_IMAGE_H diff --git a/thirdparty/thekla_atlas/nvimage/nvimage.h b/thirdparty/thekla_atlas/nvimage/nvimage.h deleted file mode 100644 index 5c89bd4726..0000000000 --- a/thirdparty/thekla_atlas/nvimage/nvimage.h +++ /dev/null @@ -1,48 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_IMAGE_H -#define NV_IMAGE_H - -#include "nvcore/nvcore.h" -#include "nvcore/Debug.h" // nvDebugCheck -#include "nvcore/Utils.h" // isPowerOfTwo - -// Function linkage -#if NVIMAGE_SHARED -#ifdef NVIMAGE_EXPORTS -#define NVIMAGE_API DLL_EXPORT -#define NVIMAGE_CLASS DLL_EXPORT_CLASS -#else -#define NVIMAGE_API DLL_IMPORT -#define NVIMAGE_CLASS DLL_IMPORT -#endif -#else -#define NVIMAGE_API -#define NVIMAGE_CLASS -#endif - - -namespace nv { - - // Some utility functions: - - inline uint computeBitPitch(uint w, uint bitsize, uint alignmentInBits) - { - nvDebugCheck(isPowerOfTwo(alignmentInBits)); - - return ((w * bitsize + alignmentInBits - 1) / alignmentInBits) * alignmentInBits; - } - - inline uint computeBytePitch(uint w, uint bitsize, uint alignmentInBytes) - { - uint pitch = computeBitPitch(w, bitsize, 8*alignmentInBytes); - nvDebugCheck((pitch & 7) == 0); - - return (pitch + 7) / 8; - } - - -} // nv namespace - -#endif // NV_IMAGE_H diff --git a/thirdparty/thekla_atlas/nvmath/Basis.cpp b/thirdparty/thekla_atlas/nvmath/Basis.cpp deleted file mode 100644 index 0824179633..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Basis.cpp +++ /dev/null @@ -1,270 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "Basis.h" - -using namespace nv; - - -/// Normalize basis vectors. -void Basis::normalize(float epsilon /*= NV_EPSILON*/) -{ - normal = ::normalizeSafe(normal, Vector3(0.0f), epsilon); - tangent = ::normalizeSafe(tangent, Vector3(0.0f), epsilon); - bitangent = ::normalizeSafe(bitangent, Vector3(0.0f), epsilon); -} - - -/// Gram-Schmidt orthogonalization. -/// @note Works only if the vectors are close to orthogonal. -void Basis::orthonormalize(float epsilon /*= NV_EPSILON*/) -{ - // N' = |N| - // T' = |T - (N' dot T) N'| - // B' = |B - (N' dot B) N' - (T' dot B) T'| - - normal = ::normalize(normal, epsilon); - - tangent -= normal * dot(normal, tangent); - tangent = ::normalize(tangent, epsilon); - - bitangent -= normal * dot(normal, bitangent); - bitangent -= tangent * dot(tangent, bitangent); - bitangent = ::normalize(bitangent, epsilon); -} - - - - -/// Robust orthonormalization. -/// Returns an orthonormal basis even when the original is degenerate. -void Basis::robustOrthonormalize(float epsilon /*= NV_EPSILON*/) -{ - // Normalize all vectors. - normalize(epsilon); - - if (lengthSquared(normal) < epsilon*epsilon) - { - // Build normal from tangent and bitangent. - normal = cross(tangent, bitangent); - - if (lengthSquared(normal) < epsilon*epsilon) - { - // Arbitrary basis. - tangent = Vector3(1, 0, 0); - bitangent = Vector3(0, 1, 0); - normal = Vector3(0, 0, 1); - return; - } - - normal = nv::normalize(normal, epsilon); - } - - // Project tangents to normal plane. - tangent -= normal * dot(normal, tangent); - bitangent -= normal * dot(normal, bitangent); - - if (lengthSquared(tangent) < epsilon*epsilon) - { - if (lengthSquared(bitangent) < epsilon*epsilon) - { - // Arbitrary basis. - buildFrameForDirection(normal); - } - else - { - // Build tangent from bitangent. - bitangent = nv::normalize(bitangent, epsilon); - - tangent = cross(bitangent, normal); - nvDebugCheck(isNormalized(tangent, epsilon)); - } - } - else - { - tangent = nv::normalize(tangent, epsilon); -#if 0 - bitangent -= tangent * dot(tangent, bitangent); - - if (lengthSquared(bitangent) < epsilon*epsilon) - { - bitangent = cross(tangent, normal); - nvDebugCheck(isNormalized(bitangent, epsilon)); - } - else - { - bitangent = nv::normalize(bitangent, epsilon); - } -#else - if (lengthSquared(bitangent) < epsilon*epsilon) - { - // Build bitangent from tangent. - bitangent = cross(tangent, normal); - nvDebugCheck(isNormalized(bitangent, epsilon)); - } - else - { - bitangent = nv::normalize(bitangent, epsilon); - - // At this point tangent and bitangent are orthogonal to normal, but we don't know whether their orientation. - - Vector3 bisector; - if (lengthSquared(tangent + bitangent) < epsilon*epsilon) - { - bisector = tangent; - } - else - { - bisector = nv::normalize(tangent + bitangent); - } - Vector3 axis = nv::normalize(cross(bisector, normal)); - - //nvDebugCheck(isNormalized(axis, epsilon)); - nvDebugCheck(equal(dot(axis, tangent), -dot(axis, bitangent), epsilon)); - - if (dot(axis, tangent) > 0) - { - tangent = bisector + axis; - bitangent = bisector - axis; - } - else - { - tangent = bisector - axis; - bitangent = bisector + axis; - } - - // Make sure the resulting tangents are still perpendicular to the normal. - tangent -= normal * dot(normal, tangent); - bitangent -= normal * dot(normal, bitangent); - - // Double check. - nvDebugCheck(equal(dot(normal, tangent), 0.0f, epsilon)); - nvDebugCheck(equal(dot(normal, bitangent), 0.0f, epsilon)); - - // Normalize. - tangent = nv::normalize(tangent); - bitangent = nv::normalize(bitangent); - - // If tangent and bitangent are not orthogonal, then derive bitangent from tangent, just in case... - if (!equal(dot(tangent, bitangent), 0.0f, epsilon)) { - bitangent = cross(tangent, normal); - bitangent = nv::normalize(bitangent); - } - } -#endif - } - - /*// Check vector lengths. - if (!isNormalized(normal, epsilon)) - { - nvDebug("%f %f %f\n", normal.x, normal.y, normal.z); - nvDebug("%f %f %f\n", tangent.x, tangent.y, tangent.z); - nvDebug("%f %f %f\n", bitangent.x, bitangent.y, bitangent.z); - }*/ - - nvDebugCheck(isNormalized(normal, epsilon)); - nvDebugCheck(isNormalized(tangent, epsilon)); - nvDebugCheck(isNormalized(bitangent, epsilon)); - - // Check vector angles. - nvDebugCheck(equal(dot(normal, tangent), 0.0f, epsilon)); - nvDebugCheck(equal(dot(normal, bitangent), 0.0f, epsilon)); - nvDebugCheck(equal(dot(tangent, bitangent), 0.0f, epsilon)); - - // Check vector orientation. - const float det = dot(cross(normal, tangent), bitangent); - nvDebugCheck(equal(det, 1.0f, epsilon) || equal(det, -1.0f, epsilon)); -} - - -/// Build an arbitrary frame for the given direction. -void Basis::buildFrameForDirection(Vector3::Arg d, float angle/*= 0*/) -{ - nvCheck(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; - } -} - -bool Basis::isValid() const -{ - if (equal(normal, Vector3(0.0f))) return false; - if (equal(tangent, Vector3(0.0f))) return false; - if (equal(bitangent, Vector3(0.0f))) return false; - - if (equal(determinant(), 0.0f)) return false; - - return true; -} - - -/// Transform by this basis. (From this basis to object space). -Vector3 Basis::transform(Vector3::Arg v) const -{ - Vector3 o = tangent * v.x; - o += bitangent * v.y; - o += normal * v.z; - return o; -} - -/// Transform by the transpose. (From object space to this basis). -Vector3 Basis::transformT(Vector3::Arg v) -{ - return Vector3(dot(tangent, v), dot(bitangent, v), dot(normal, v)); -} - -/// Transform by the inverse. (From object space to this basis). -/// @note Uses Cramer's rule so the inverse is not accurate if the basis is ill-conditioned. -Vector3 Basis::transformI(Vector3::Arg v) const -{ - const float det = determinant(); - nvDebugCheck(!equal(det, 0.0f, 0.0f)); - - const float idet = 1.0f / det; - - // Rows of the inverse matrix. - Vector3 r0( - (bitangent.y * normal.z - bitangent.z * normal.y), - -(bitangent.x * normal.z - bitangent.z * normal.x), - (bitangent.x * normal.y - bitangent.y * normal.x)); - - Vector3 r1( - -(tangent.y * normal.z - tangent.z * normal.y), - (tangent.x * normal.z - tangent.z * normal.x), - -(tangent.x * normal.y - tangent.y * normal.x)); - - Vector3 r2( - (tangent.y * bitangent.z - tangent.z * bitangent.y), - -(tangent.x * bitangent.z - tangent.z * bitangent.x), - (tangent.x * bitangent.y - tangent.y * bitangent.x)); - - return Vector3(dot(v, r0), dot(v, r1), dot(v, r2)) * idet; -} - - diff --git a/thirdparty/thekla_atlas/nvmath/Basis.h b/thirdparty/thekla_atlas/nvmath/Basis.h deleted file mode 100644 index e8146afdbe..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Basis.h +++ /dev/null @@ -1,82 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MATH_BASIS_H -#define NV_MATH_BASIS_H - -#include "nvmath.h" -#include "Vector.inl" -#include "Matrix.h" - -namespace nv -{ - - /// 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) {} - - /// Create a basis given three vectors. - Basis(Vector3::Arg n, Vector3::Arg t, Vector3::Arg b) : tangent(t), bitangent(b), normal(n) {} - - /// Create a basis with the given tangent vectors and the handness. - Basis(Vector3::Arg n, Vector3::Arg t, float sign) - { - build(n, t, sign); - } - - NVMATH_API void normalize(float epsilon = NV_EPSILON); - NVMATH_API void orthonormalize(float epsilon = NV_EPSILON); - NVMATH_API void robustOrthonormalize(float epsilon = NV_EPSILON); - NVMATH_API void buildFrameForDirection(Vector3::Arg d, float angle = 0); - - /// Calculate the determinant [ F G N ] to obtain the handness of the basis. - float handness() const - { - return determinant() > 0.0f ? 1.0f : -1.0f; - } - - /// Build a basis from 2 vectors and a handness flag. - void build(Vector3::Arg n, Vector3::Arg t, float sign) - { - normal = n; - tangent = t; - bitangent = sign * cross(t, n); - } - - /// Compute the determinant of this basis. - float determinant() const - { - return - tangent.x * bitangent.y * normal.z - tangent.z * bitangent.y * normal.x + - tangent.y * bitangent.z * normal.x - tangent.y * bitangent.x * normal.z + - tangent.z * bitangent.x * normal.y - tangent.x * bitangent.z * normal.y; - } - - bool isValid() const; - - // Get transform matrix for this basis. - NVMATH_API Matrix matrix() const; - - // Transform by this basis. (From this basis to object space). - NVMATH_API Vector3 transform(Vector3::Arg v) const; - - // Transform by the transpose. (From object space to this basis). - NVMATH_API Vector3 transformT(Vector3::Arg v); - - // Transform by the inverse. (From object space to this basis). - NVMATH_API Vector3 transformI(Vector3::Arg v) const; - - - Vector3 tangent; - Vector3 bitangent; - Vector3 normal; - }; - -} // nv namespace - -#endif // NV_MATH_BASIS_H diff --git a/thirdparty/thekla_atlas/nvmath/Box.cpp b/thirdparty/thekla_atlas/nvmath/Box.cpp deleted file mode 100644 index 8f2014a077..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Box.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "Box.h" -#include "Box.inl" -#include "Sphere.h" - -using namespace nv; - - - - -// Clip the given segment against this box. -bool Box::clipSegment(const Vector3 & origin, const Vector3 & dir, float * t_near, float * t_far) const { - - // Avoid aliasing. - float tnear = *t_near; - float tfar = *t_far; - - // clip ray segment to box - for (int i = 0; i < 3; i++) - { - const float pos = origin.component[i] + tfar * dir.component[i]; - const float dt = tfar - tnear; - - if (dir.component[i] < 0) { - - // clip end point - if (pos < minCorner.component[i]) { - tfar = tnear + dt * (origin.component[i] - minCorner.component[i]) / (origin.component[i] - pos); - } - - // clip start point - if (origin.component[i] > maxCorner.component[i]) { - tnear = tnear + dt * (origin.component[i] - maxCorner.component[i]) / (tfar * dir.component[i]); - } - } - else { - - // clip end point - if (pos > maxCorner.component[i]) { - tfar = tnear + dt * (maxCorner.component[i] - origin.component[i]) / (pos - origin.component[i]); - } - - // clip start point - if (origin.component[i] < minCorner.component[i]) { - tnear = tnear + dt * (minCorner.component[i] - origin.component[i]) / (tfar * dir.component[i]); - } - } - - if (tnear > tfar) { - // Clipped away. - return false; - } - } - - // Return result. - *t_near = tnear; - *t_far = tfar; - return true; -} - - -float nv::distanceSquared(const Box &box, const Vector3 &point) { - Vector3 closest; - - if (point.x < box.minCorner.x) closest.x = box.minCorner.x; - else if (point.x > box.maxCorner.x) closest.x = box.maxCorner.x; - else closest.x = point.x; - - if (point.y < box.minCorner.y) closest.y = box.minCorner.y; - else if (point.y > box.maxCorner.y) closest.y = box.maxCorner.y; - else closest.y = point.y; - - if (point.z < box.minCorner.z) closest.z = box.minCorner.z; - else if (point.z > box.maxCorner.z) closest.z = box.maxCorner.z; - else closest.z = point.z; - - return lengthSquared(point - closest); -} - -bool nv::overlap(const Box &box, const Sphere &sphere) { - return distanceSquared(box, sphere.center) < sphere.radius * sphere.radius; -} - - -bool nv::intersect(const Box & box, const Vector3 & p, const Vector3 & id, float * t /*= NULL*/) { - // Precompute these in ray structure? - int sdx = (id.x < 0); - int sdy = (id.y < 0); - int sdz = (id.z < 0); - - float tmin = (box.corner( sdx).x - p.x) * id.x; - float tmax = (box.corner(1-sdx).x - p.x) * id.x; - float tymin = (box.corner( sdy).y - p.y) * id.y; - float tymax = (box.corner(1-sdy).y - p.y) * id.y; - - if ((tmin > tymax) || (tymin > tmax)) - return false; - - if (tymin > tmin) tmin = tymin; - if (tymax < tmax) tmax = tymax; - - float tzmin = (box.corner( sdz).z - p.z) * id.z; - float tzmax = (box.corner(1-sdz).z - p.z) * id.z; - - if ((tmin > tzmax) || (tzmin > tmax)) - return false; - - if (tzmin > tmin) tmin = tzmin; - if (tzmax < tmax) tmax = tzmax; - - if (tmax < 0) - return false; - - if (t != NULL) *t = tmin; - - return true; -} - diff --git a/thirdparty/thekla_atlas/nvmath/Box.h b/thirdparty/thekla_atlas/nvmath/Box.h deleted file mode 100644 index 19b5f2a3a5..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Box.h +++ /dev/null @@ -1,103 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MATH_BOX_H -#define NV_MATH_BOX_H - -#include "Vector.h" - -#include <float.h> // FLT_MAX - -namespace nv -{ - class Vector; - class Stream; - class Sphere; - - // Axis Aligned Bounding Box. - class Box - { - public: - - inline Box() {} - inline Box(const Box & b) : minCorner(b.minCorner), maxCorner(b.maxCorner) {} - inline Box(const Vector3 & mins, const Vector3 & maxs) : minCorner(mins), maxCorner(maxs) {} - - Box & operator=(const Box & b); - - operator const float * () const { return reinterpret_cast<const float *>(this); } - - // Clear the bounds. - void clearBounds(); - - // min < max - bool isValid() const; - - // Build a cube centered on center and with edge = 2*dist - void cube(const Vector3 & center, float dist); - - // Build a box, given center and extents. - void setCenterExtents(const Vector3 & center, const Vector3 & extents); - - // Get box center. - Vector3 center() const; - - // Return extents of the box. - Vector3 extents() const; - - // Return extents of the box. - float extents(uint axis) const; - - // Add a point to this box. - void addPointToBounds(const Vector3 & p); - - // Add a box to this box. - void addBoxToBounds(const Box & b); - - // Add sphere to this box. - void addSphereToBounds(const Vector3 & p, float r); - - // Translate box. - void translate(const Vector3 & v); - - // Scale the box. - void scale(float s); - - // Expand the box by a fixed amount. - void expand(float r); - - // Get the area of the box. - float area() const; - - // Get the volume of the box. - float volume() const; - - // Return true if the box contains the given point. - bool contains(const Vector3 & p) const; - - // Split the given box in 8 octants and assign the ith one to this box. - void setOctant(const Box & box, const Vector3 & center, int i); - - - // Clip the given segment against this box. - bool clipSegment(const Vector3 & origin, const Vector3 & dir, float * t_near, float * t_far) const; - - - friend Stream & operator<< (Stream & s, Box & box); - - const Vector3 & corner(int i) const { return (&minCorner)[i]; } - - Vector3 minCorner; - Vector3 maxCorner; - }; - - float distanceSquared(const Box &box, const Vector3 &point); - bool overlap(const Box &box, const Sphere &sphere); - - // p is ray origin, id is inverse ray direction. - bool intersect(const Box & box, const Vector3 & p, const Vector3 & id, float * t); - -} // nv namespace - - -#endif // NV_MATH_BOX_H diff --git a/thirdparty/thekla_atlas/nvmath/Box.inl b/thirdparty/thekla_atlas/nvmath/Box.inl deleted file mode 100644 index dcfa70ff96..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Box.inl +++ /dev/null @@ -1,154 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MATH_BOX_INL -#define NV_MATH_BOX_INL - -#include "Box.h" -#include "Vector.inl" - -#include <float.h> // FLT_MAX - -namespace nv -{ - // Default ctor. - //inline Box::Box() { }; - - // Copy ctor. - //inline Box::Box(const Box & b) : minCorner(b.minCorner), maxCorner(b.maxCorner) { } - - // Init ctor. - //inline Box::Box(const Vector3 & mins, const Vector3 & maxs) : minCorner(mins), maxCorner(maxs) { } - - // Assignment operator. - inline Box & Box::operator=(const Box & b) { minCorner = b.minCorner; maxCorner = b.maxCorner; return *this; } - - // Clear the bounds. - inline void Box::clearBounds() - { - minCorner.set(FLT_MAX, FLT_MAX, FLT_MAX); - maxCorner.set(-FLT_MAX, -FLT_MAX, -FLT_MAX); - } - - // min < max - inline bool Box::isValid() const - { - return minCorner.x <= maxCorner.x && minCorner.y <= maxCorner.y && minCorner.z <= maxCorner.z; - } - - // Build a cube centered on center and with edge = 2*dist - inline void Box::cube(const Vector3 & center, float dist) - { - setCenterExtents(center, Vector3(dist)); - } - - // Build a box, given center and extents. - inline void Box::setCenterExtents(const Vector3 & center, const Vector3 & extents) - { - minCorner = center - extents; - maxCorner = center + extents; - } - - // Get box center. - inline Vector3 Box::center() const - { - return (minCorner + maxCorner) * 0.5f; - } - - // Return extents of the box. - inline Vector3 Box::extents() const - { - return (maxCorner - minCorner) * 0.5f; - } - - // Return extents of the box. - inline float Box::extents(uint axis) const - { - nvDebugCheck(axis < 3); - if (axis == 0) return (maxCorner.x - minCorner.x) * 0.5f; - if (axis == 1) return (maxCorner.y - minCorner.y) * 0.5f; - if (axis == 2) return (maxCorner.z - minCorner.z) * 0.5f; - nvUnreachable(); - return 0.0f; - } - - // Add a point to this box. - inline void Box::addPointToBounds(const Vector3 & p) - { - minCorner = min(minCorner, p); - maxCorner = max(maxCorner, p); - } - - // Add a box to this box. - inline void Box::addBoxToBounds(const Box & b) - { - minCorner = min(minCorner, b.minCorner); - maxCorner = max(maxCorner, b.maxCorner); - } - - // Add sphere to this box. - inline void Box::addSphereToBounds(const Vector3 & p, float r) { - minCorner = min(minCorner, p - Vector3(r)); - maxCorner = min(maxCorner, p + Vector3(r)); - } - - // Translate box. - inline void Box::translate(const Vector3 & v) - { - minCorner += v; - maxCorner += v; - } - - // Scale the box. - inline void Box::scale(float s) - { - minCorner *= s; - maxCorner *= s; - } - - // Expand the box by a fixed amount. - inline void Box::expand(float r) { - minCorner -= Vector3(r,r,r); - maxCorner += Vector3(r,r,r); - } - - // Get the area of the box. - inline float Box::area() const - { - const Vector3 d = extents(); - return 8.0f * (d.x*d.y + d.x*d.z + d.y*d.z); - } - - // Get the volume of the box. - inline float Box::volume() const - { - Vector3 d = extents(); - return 8.0f * (d.x * d.y * d.z); - } - - // Return true if the box contains the given point. - inline bool Box::contains(const Vector3 & p) const - { - return - minCorner.x < p.x && minCorner.y < p.y && minCorner.z < p.z && - maxCorner.x > p.x && maxCorner.y > p.y && maxCorner.z > p.z; - } - - // Split the given box in 8 octants and assign the ith one to this box. - inline void Box::setOctant(const Box & box, const Vector3 & center, int i) - { - minCorner = box.minCorner; - maxCorner = box.maxCorner; - - if (i & 4) minCorner.x = center.x; - else maxCorner.x = center.x; - if (i & 2) minCorner.y = center.y; - else maxCorner.y = center.y; - if (i & 1) minCorner.z = center.z; - else maxCorner.z = center.z; - } - -} // nv namespace - - -#endif // NV_MATH_BOX_INL diff --git a/thirdparty/thekla_atlas/nvmath/Color.h b/thirdparty/thekla_atlas/nvmath/Color.h deleted file mode 100644 index 5cdc374bd9..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Color.h +++ /dev/null @@ -1,150 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MATH_COLOR_H -#define NV_MATH_COLOR_H - -#include "nvmath.h" - -namespace nv -{ - - /// 64 bit color stored as BGRA. - class NVMATH_CLASS Color64 - { - public: - Color64() { } - Color64(const Color64 & c) : u(c.u) { } - Color64(uint16 R, uint16 G, uint16 B, uint16 A) { setRGBA(R, G, B, A); } - explicit Color64(uint64 U) : u(U) { } - - void setRGBA(uint16 R, uint16 G, uint16 B, uint16 A) - { - r = R; - g = G; - b = B; - a = A; - } - - operator uint64 () const { - return u; - } - - union { - struct { -#if NV_LITTLE_ENDIAN - uint16 r, a, b, g; -#else - uint16 a: 16; - uint16 r: 16; - uint16 g: 16; - uint16 b: 16; -#endif - }; - uint64 u; - }; - }; - - /// 32 bit color stored as BGRA. - class NVMATH_CLASS Color32 - { - public: - Color32() { } - Color32(const Color32 & c) : u(c.u) { } - Color32(uint8 R, uint8 G, uint8 B) { setRGBA(R, G, B, 0xFF); } - Color32(uint8 R, uint8 G, uint8 B, uint8 A) { setRGBA( R, G, B, A); } - //Color32(uint8 c[4]) { setRGBA(c[0], c[1], c[2], c[3]); } - //Color32(float R, float G, float B) { setRGBA(uint(R*255), uint(G*255), uint(B*255), 0xFF); } - //Color32(float R, float G, float B, float A) { setRGBA(uint(R*255), uint(G*255), uint(B*255), uint(A*255)); } - explicit Color32(uint32 U) : u(U) { } - - void setRGBA(uint8 R, uint8 G, uint8 B, uint8 A) - { - r = R; - g = G; - b = B; - a = A; - } - - void setBGRA(uint8 B, uint8 G, uint8 R, uint8 A = 0xFF) - { - r = R; - g = G; - b = B; - a = A; - } - - operator uint32 () const { - return u; - } - - union { - struct { -#if NV_LITTLE_ENDIAN - uint8 b, g, r, a; -#else - uint8 a: 8; - uint8 r: 8; - uint8 g: 8; - uint8 b: 8; -#endif - }; - uint8 component[4]; - uint32 u; - }; - }; - - - /// 16 bit 565 BGR color. - class NVMATH_CLASS Color16 - { - public: - Color16() { } - Color16(const Color16 & c) : u(c.u) { } - explicit Color16(uint16 U) : u(U) { } - - union { - struct { -#if NV_LITTLE_ENDIAN - uint16 b : 5; - uint16 g : 6; - uint16 r : 5; -#else - uint16 r : 5; - uint16 g : 6; - uint16 b : 5; -#endif - }; - uint16 u; - }; - }; - - /// 16 bit 4444 BGRA color. - class NVMATH_CLASS Color16_4444 - { - public: - Color16_4444() { } - Color16_4444(const Color16_4444 & c) : u(c.u) { } - explicit Color16_4444(uint16 U) : u(U) { } - - union { - struct { -#if NV_LITTLE_ENDIAN - uint16 b : 4; - uint16 g : 4; - uint16 r : 4; - uint16 a : 4; -#else - uint16 a : 4; - uint16 r : 4; - uint16 g : 4; - uint16 b : 4; -#endif - }; - uint16 u; - }; - }; - -} // nv namespace - -#endif // NV_MATH_COLOR_H diff --git a/thirdparty/thekla_atlas/nvmath/ConvexHull.cpp b/thirdparty/thekla_atlas/nvmath/ConvexHull.cpp deleted file mode 100644 index a4a95dace4..0000000000 --- a/thirdparty/thekla_atlas/nvmath/ConvexHull.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "ConvexHull.h" - -#include "Vector.inl" - -#include "nvcore/RadixSort.h" -#include "nvcore/Array.inl" - -using namespace nv; - -inline static float triangleArea(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); -} - - -// Compute the convex hull using Graham Scan. -void nv::convexHull(const Array<Vector2> & input, Array<Vector2> & output, float epsilon/*=0*/) -{ - const uint inputCount = input.count(); - - Array<float> coords; - coords.resize(inputCount); - - for (uint i = 0; i < inputCount; i++) { - coords[i] = input[i].x; - } - - RadixSort radix; - radix.sort(coords); - - const uint * ranks = radix.ranks(); - - Array<Vector2> top(inputCount); - Array<Vector2> bottom(inputCount); - - Vector2 P = input[ranks[0]]; - Vector2 Q = input[ranks[inputCount-1]]; - - float topy = max(P.y, Q.y); - float boty = min(P.y, Q.y); - - for (uint i = 0; i < inputCount; i++) { - Vector2 p = input[ranks[i]]; - if (p.y >= boty) top.append(p); - } - - for (uint i = 0; i < inputCount; i++) { - Vector2 p = input[ranks[inputCount-1-i]]; - if (p.y <= topy) bottom.append(p); - } - - // Filter top list. - output.clear(); - output.append(top[0]); - output.append(top[1]); - - for (uint i = 2; i < top.count(); ) { - Vector2 a = output[output.count()-2]; - Vector2 b = output[output.count()-1]; - Vector2 c = top[i]; - - float area = triangleArea(a, b, c); - - if (area >= -epsilon) { - output.popBack(); - } - - if (area < -epsilon || output.count() == 1) { - output.append(c); - i++; - } - } - - uint top_count = output.count(); - output.append(bottom[1]); - - // Filter bottom list. - for (uint i = 2; i < bottom.count(); ) { - Vector2 a = output[output.count()-2]; - Vector2 b = output[output.count()-1]; - Vector2 c = bottom[i]; - - float area = triangleArea(a, b, c); - - if (area >= -epsilon) { - output.popBack(); - } - - if (area < -epsilon || output.count() == top_count) { - output.append(c); - i++; - } - } - - // Remove duplicate element. - nvDebugCheck(output.front() == output.back()); - output.popBack(); -} - -/* -void testConvexHull() { - - Array<Vector2> points; - points.append(Vector2(1.00, 1.00)); - points.append(Vector2(0.00, 0.00)); - points.append(Vector2(1.00, 1.00)); - points.append(Vector2(1.00, -1.00)); - points.append(Vector2(2.00, 5.00)); - points.append(Vector2(-5.00, 3.00)); - points.append(Vector2(-4.00, -3.00)); - points.append(Vector2(7.00, -4.00)); - - Array<Vector2> hull; - convexHull(points, hull); - -} -*/ - diff --git a/thirdparty/thekla_atlas/nvmath/ConvexHull.h b/thirdparty/thekla_atlas/nvmath/ConvexHull.h deleted file mode 100644 index 6c2db5d73f..0000000000 --- a/thirdparty/thekla_atlas/nvmath/ConvexHull.h +++ /dev/null @@ -1,17 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MATH_CONVEXHULL_H -#define NV_MATH_CONVEXHULL_H - -#include "nvmath.h" -#include "nvcore/Array.h" - -namespace nv { - class Vector2; - - void convexHull(const Array<Vector2> & input, Array<Vector2> & output, float epsilon = 0); - -} // namespace nv - -#endif // NV_MATH_CONVEXHULL_H diff --git a/thirdparty/thekla_atlas/nvmath/Fitting.cpp b/thirdparty/thekla_atlas/nvmath/Fitting.cpp deleted file mode 100644 index 6cd5cb0f32..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Fitting.cpp +++ /dev/null @@ -1,1205 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "Fitting.h" -#include "Vector.inl" -#include "Plane.inl" - -#include "nvcore/Array.inl" -#include "nvcore/Utils.h" // max, swap - -#include <float.h> // FLT_MAX -//#include <vector> -#include <string.h> - -using namespace nv; - -// @@ Move to EigenSolver.h - -// @@ We should be able to do something cheaper... -static Vector3 estimatePrincipalComponent(const float * __restrict matrix) -{ - const Vector3 row0(matrix[0], matrix[1], matrix[2]); - const Vector3 row1(matrix[1], matrix[3], matrix[4]); - const Vector3 row2(matrix[2], matrix[4], matrix[5]); - - float r0 = lengthSquared(row0); - float r1 = lengthSquared(row1); - float r2 = lengthSquared(row2); - - if (r0 > r1 && r0 > r2) return row0; - if (r1 > r2) return row1; - return row2; -} - - -static inline Vector3 firstEigenVector_PowerMethod(const float *__restrict matrix) -{ - if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0) - { - return Vector3(0.0f); - } - - Vector3 v = estimatePrincipalComponent(matrix); - - const int NUM = 8; - for (int i = 0; i < NUM; i++) - { - float x = v.x * matrix[0] + v.y * matrix[1] + v.z * matrix[2]; - float y = v.x * matrix[1] + v.y * matrix[3] + v.z * matrix[4]; - float z = v.x * matrix[2] + v.y * matrix[4] + v.z * matrix[5]; - - float norm = max(max(x, y), z); - - v = Vector3(x, y, z) / norm; - } - - return v; -} - - -Vector3 nv::Fit::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; -} - -Vector3 nv::Fit::computeCentroid(int n, const Vector3 *__restrict points, const float *__restrict weights, Vector3::Arg metric) -{ - Vector3 centroid(0.0f); - float total = 0.0f; - - for (int i = 0; i < n; i++) - { - total += weights[i]; - centroid += weights[i]*points[i]; - } - centroid /= total; - - return centroid; -} - -Vector4 nv::Fit::computeCentroid(int n, const Vector4 *__restrict points) -{ - Vector4 centroid(0.0f); - - for (int i = 0; i < n; i++) - { - centroid += points[i]; - } - centroid /= float(n); - - return centroid; -} - -Vector4 nv::Fit::computeCentroid(int n, const Vector4 *__restrict points, const float *__restrict weights, Vector4::Arg metric) -{ - Vector4 centroid(0.0f); - float total = 0.0f; - - for (int i = 0; i < n; i++) - { - total += weights[i]; - centroid += weights[i]*points[i]; - } - centroid /= total; - - return centroid; -} - - - -Vector3 nv::Fit::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; -} - -Vector3 nv::Fit::computeCovariance(int n, const Vector3 *__restrict points, const float *__restrict weights, Vector3::Arg metric, float *__restrict covariance) -{ - // compute the centroid - Vector3 centroid = computeCentroid(n, points, weights, metric); - - // compute covariance matrix - for (int i = 0; i < 6; i++) - { - covariance[i] = 0.0f; - } - - for (int i = 0; i < n; i++) - { - Vector3 a = (points[i] - centroid) * metric; - Vector3 b = weights[i]*a; - - covariance[0] += a.x * b.x; - covariance[1] += a.x * b.y; - covariance[2] += a.x * b.z; - covariance[3] += a.y * b.y; - covariance[4] += a.y * b.z; - covariance[5] += a.z * b.z; - } - - return centroid; -} - -Vector4 nv::Fit::computeCovariance(int n, const Vector4 *__restrict points, float *__restrict covariance) -{ - // compute the centroid - Vector4 centroid = computeCentroid(n, points); - - // compute covariance matrix - for (int i = 0; i < 10; i++) - { - covariance[i] = 0.0f; - } - - for (int i = 0; i < n; i++) - { - Vector4 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.x * v.w; - - covariance[4] += v.y * v.y; - covariance[5] += v.y * v.z; - covariance[6] += v.y * v.w; - - covariance[7] += v.z * v.z; - covariance[8] += v.z * v.w; - - covariance[9] += v.w * v.w; - } - - return centroid; -} - -Vector4 nv::Fit::computeCovariance(int n, const Vector4 *__restrict points, const float *__restrict weights, Vector4::Arg metric, float *__restrict covariance) -{ - // compute the centroid - Vector4 centroid = computeCentroid(n, points, weights, metric); - - // compute covariance matrix - for (int i = 0; i < 10; i++) - { - covariance[i] = 0.0f; - } - - for (int i = 0; i < n; i++) - { - Vector4 a = (points[i] - centroid) * metric; - Vector4 b = weights[i]*a; - - covariance[0] += a.x * b.x; - covariance[1] += a.x * b.y; - covariance[2] += a.x * b.z; - covariance[3] += a.x * b.w; - - covariance[4] += a.y * b.y; - covariance[5] += a.y * b.z; - covariance[6] += a.y * b.w; - - covariance[7] += a.z * b.z; - covariance[8] += a.z * b.w; - - covariance[9] += a.w * b.w; - } - - return centroid; -} - - - -Vector3 nv::Fit::computePrincipalComponent_PowerMethod(int n, const Vector3 *__restrict points) -{ - float matrix[6]; - computeCovariance(n, points, matrix); - - return firstEigenVector_PowerMethod(matrix); -} - -Vector3 nv::Fit::computePrincipalComponent_PowerMethod(int n, const Vector3 *__restrict points, const float *__restrict weights, Vector3::Arg metric) -{ - float matrix[6]; - computeCovariance(n, points, weights, metric, matrix); - - return firstEigenVector_PowerMethod(matrix); -} - - - -static inline Vector3 firstEigenVector_EigenSolver3(const float *__restrict matrix) -{ - if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0) - { - return Vector3(0.0f); - } - - float eigenValues[3]; - Vector3 eigenVectors[3]; - if (!nv::Fit::eigenSolveSymmetric3(matrix, eigenValues, eigenVectors)) - { - return Vector3(0.0f); - } - - return eigenVectors[0]; -} - -Vector3 nv::Fit::computePrincipalComponent_EigenSolver(int n, const Vector3 *__restrict points) -{ - float matrix[6]; - computeCovariance(n, points, matrix); - - return firstEigenVector_EigenSolver3(matrix); -} - -Vector3 nv::Fit::computePrincipalComponent_EigenSolver(int n, const Vector3 *__restrict points, const float *__restrict weights, Vector3::Arg metric) -{ - float matrix[6]; - computeCovariance(n, points, weights, metric, matrix); - - return firstEigenVector_EigenSolver3(matrix); -} - - - -static inline Vector4 firstEigenVector_EigenSolver4(const float *__restrict matrix) -{ - if (matrix[0] == 0 && matrix[4] == 0 && matrix[7] == 0&& matrix[9] == 0) - { - return Vector4(0.0f); - } - - float eigenValues[4]; - Vector4 eigenVectors[4]; - if (!nv::Fit::eigenSolveSymmetric4(matrix, eigenValues, eigenVectors)) - { - return Vector4(0.0f); - } - - return eigenVectors[0]; -} - -Vector4 nv::Fit::computePrincipalComponent_EigenSolver(int n, const Vector4 *__restrict points) -{ - float matrix[10]; - computeCovariance(n, points, matrix); - - return firstEigenVector_EigenSolver4(matrix); -} - -Vector4 nv::Fit::computePrincipalComponent_EigenSolver(int n, const Vector4 *__restrict points, const float *__restrict weights, Vector4::Arg metric) -{ - float matrix[10]; - computeCovariance(n, points, weights, metric, matrix); - - return firstEigenVector_EigenSolver4(matrix); -} - - - -void ArvoSVD(int rows, int cols, float * Q, float * diag, float * R); - -Vector3 nv::Fit::computePrincipalComponent_SVD(int n, const Vector3 *__restrict points) -{ - // Store the points in an n x n matrix - Array<float> Q; Q.resize(n*n, 0.0f); - for (int i = 0; i < n; ++i) - { - Q[i*n+0] = points[i].x; - Q[i*n+1] = points[i].y; - Q[i*n+2] = points[i].z; - } - - // Alloc space for the SVD outputs - Array<float> diag; diag.resize(n, 0.0f); - Array<float> R; R.resize(n*n, 0.0f); - - ArvoSVD(n, n, &Q[0], &diag[0], &R[0]); - - // Get the principal component - return Vector3(R[0], R[1], R[2]); -} - -Vector4 nv::Fit::computePrincipalComponent_SVD(int n, const Vector4 *__restrict points) -{ - // Store the points in an n x n matrix - Array<float> Q; Q.resize(n*n, 0.0f); - for (int i = 0; i < n; ++i) - { - Q[i*n+0] = points[i].x; - Q[i*n+1] = points[i].y; - Q[i*n+2] = points[i].z; - Q[i*n+3] = points[i].w; - } - - // Alloc space for the SVD outputs - Array<float> diag; diag.resize(n, 0.0f); - Array<float> R; R.resize(n*n, 0.0f); - - ArvoSVD(n, n, &Q[0], &diag[0], &R[0]); - - // Get the principal component - return Vector4(R[0], R[1], R[2], R[3]); -} - - - -Plane nv::Fit::bestPlane(int n, const Vector3 *__restrict points) -{ - // compute the centroid and covariance - float matrix[6]; - Vector3 centroid = computeCovariance(n, points, matrix); - - if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0) - { - // If no plane defined, then return a horizontal plane. - return Plane(Vector3(0, 0, 1), centroid); - } - - float eigenValues[3]; - Vector3 eigenVectors[3]; - if (!eigenSolveSymmetric3(matrix, eigenValues, eigenVectors)) { - // If no plane defined, then return a horizontal plane. - return Plane(Vector3(0, 0, 1), centroid); - } - - return Plane(eigenVectors[2], centroid); -} - -bool nv::Fit::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 void EigenSolver3_Tridiagonal(float mat[3][3], float * diag, float * subd); -static bool EigenSolver3_QLAlgorithm(float mat[3][3], float * diag, float * subd); - -bool nv::Fit::eigenSolveSymmetric3(const float matrix[6], float eigenValues[3], Vector3 eigenVectors[3]) -{ - nvDebugCheck(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]) - { - swap(eigenValues[0], eigenValues[2]); - swap(eigenVectors[0], eigenVectors[2]); - } - if (eigenValues[1] > eigenValues[0]) - { - swap(eigenValues[0], eigenValues[1]); - swap(eigenVectors[0], eigenVectors[1]); - } - if (eigenValues[2] > eigenValues[1]) - { - swap(eigenValues[1], eigenValues[2]); - swap(eigenVectors[1], eigenVectors[2]); - } - - nvDebugCheck(eigenValues[0] >= eigenValues[1] && eigenValues[0] >= eigenValues[2]); - nvDebugCheck(eigenValues[1] >= eigenValues[2]); - - return true; -} - -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; -} - - - -// Tridiagonal solver for 4x4 symmetric matrices. - -static void EigenSolver4_Tridiagonal(float mat[4][4], float * diag, float * subd); -static bool EigenSolver4_QLAlgorithm(float mat[4][4], float * diag, float * subd); - -bool nv::Fit::eigenSolveSymmetric4(const float matrix[10], float eigenValues[4], Vector4 eigenVectors[4]) -{ - nvDebugCheck(matrix != NULL && eigenValues != NULL && eigenVectors != NULL); - - float subd[4]; - float diag[4]; - float work[4][4]; - - work[0][0] = matrix[0]; - work[0][1] = work[1][0] = matrix[1]; - work[0][2] = work[2][0] = matrix[2]; - work[0][3] = work[3][0] = matrix[3]; - work[1][1] = matrix[4]; - work[1][2] = work[2][1] = matrix[5]; - work[1][3] = work[3][1] = matrix[6]; - work[2][2] = matrix[7]; - work[2][3] = work[3][2] = matrix[8]; - work[3][3] = matrix[9]; - - EigenSolver4_Tridiagonal(work, diag, subd); - if (!EigenSolver4_QLAlgorithm(work, diag, subd)) - { - for (int i = 0; i < 4; i++) { - eigenValues[i] = 0; - eigenVectors[i] = Vector4(0); - } - return false; - } - - for (int i = 0; i < 4; i++) { - eigenValues[i] = (float)diag[i]; - } - - // eigenvectors are the columns; make them the rows - - for (int i = 0; i < 4; i++) - { - for (int j = 0; j < 4; j++) - { - eigenVectors[j].component[i] = (float) work[i][j]; - } - } - - // sort by singular value - - for (int i = 0; i < 3; ++i) - { - for (int j = i+1; j < 4; ++j) - { - if (eigenValues[j] > eigenValues[i]) - { - swap(eigenValues[i], eigenValues[j]); - swap(eigenVectors[i], eigenVectors[j]); - } - } - } - - nvDebugCheck(eigenValues[0] >= eigenValues[1] && eigenValues[0] >= eigenValues[2] && eigenValues[0] >= eigenValues[3]); - nvDebugCheck(eigenValues[1] >= eigenValues[2] && eigenValues[1] >= eigenValues[3]); - nvDebugCheck(eigenValues[2] >= eigenValues[2]); - - return true; -} - -#include "nvmath/Matrix.inl" - -inline float signNonzero(float x) -{ - return (x >= 0.0f) ? 1.0f : -1.0f; -} - -static void EigenSolver4_Tridiagonal(float mat[4][4], 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) - - static const int n = 4; - - // Set epsilon relative to size of elements in matrix - static const float relEpsilon = 1e-6f; - float maxElement = FLT_MAX; - for (int i = 0; i < n; ++i) - for (int j = 0; j < n; ++j) - maxElement = max(maxElement, fabsf(mat[i][j])); - float epsilon = relEpsilon * maxElement; - - // Iterative algorithm, works for any size of matrix but might be slower than - // a closed-form solution for symmetric 4x4 matrices. Based on this article: - // http://en.wikipedia.org/wiki/Householder_transformation#Tridiagonalization - - Matrix A, Q(identity); - memcpy(&A, mat, sizeof(float)*n*n); - - // We proceed from left to right, making the off-tridiagonal entries zero in - // one column of the matrix at a time. - for (int k = 0; k < n - 2; ++k) - { - float sum = 0.0f; - for (int j = k+1; j < n; ++j) - sum += A(j,k)*A(j,k); - float alpha = -signNonzero(A(k+1,k)) * sqrtf(sum); - float r = sqrtf(0.5f * (alpha*alpha - A(k+1,k)*alpha)); - - // If r is zero, skip this column - already in tridiagonal form - if (fabsf(r) < epsilon) - continue; - - float v[n] = {}; - v[k+1] = 0.5f * (A(k+1,k) - alpha) / r; - for (int j = k+2; j < n; ++j) - v[j] = 0.5f * A(j,k) / r; - - Matrix P(identity); - for (int i = 0; i < n; ++i) - for (int j = 0; j < n; ++j) - P(i,j) -= 2.0f * v[i] * v[j]; - - A = mul(mul(P, A), P); - Q = mul(Q, P); - } - - nvDebugCheck(fabsf(A(2,0)) < epsilon); - nvDebugCheck(fabsf(A(0,2)) < epsilon); - nvDebugCheck(fabsf(A(3,0)) < epsilon); - nvDebugCheck(fabsf(A(0,3)) < epsilon); - nvDebugCheck(fabsf(A(3,1)) < epsilon); - nvDebugCheck(fabsf(A(1,3)) < epsilon); - - for (int i = 0; i < n; ++i) - diag[i] = A(i,i); - for (int i = 0; i < n - 1; ++i) - subd[i] = A(i+1,i); - subd[n-1] = 0.0f; - - memcpy(mat, &Q, sizeof(float)*n*n); -} - -static bool EigenSolver4_QLAlgorithm(float mat[4][4], 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 < 4; ell++) - { - int iter; - for (iter = 0; iter < maxiter; iter++) - { - int m; - for (m = ell; m < 3; 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 < 4; 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; -} - - - -int nv::Fit::compute4Means(int n, const Vector3 *__restrict points, const float *__restrict weights, Vector3::Arg metric, Vector3 *__restrict cluster) -{ - // Compute principal component. - float matrix[6]; - Vector3 centroid = computeCovariance(n, points, weights, metric, matrix); - Vector3 principal = firstEigenVector_PowerMethod(matrix); - - // Pick initial solution. - int mini, maxi; - mini = maxi = 0; - - float mindps, maxdps; - mindps = maxdps = dot(points[0] - centroid, principal); - - for (int i = 1; i < n; ++i) - { - float dps = dot(points[i] - centroid, principal); - - if (dps < mindps) { - mindps = dps; - mini = i; - } - else { - maxdps = dps; - maxi = i; - } - } - - cluster[0] = centroid + mindps * principal; - cluster[1] = centroid + maxdps * principal; - cluster[2] = (2.0f * cluster[0] + cluster[1]) / 3.0f; - cluster[3] = (2.0f * cluster[1] + cluster[0]) / 3.0f; - - // Now we have to iteratively refine the clusters. - while (true) - { - Vector3 newCluster[4] = { Vector3(0.0f), Vector3(0.0f), Vector3(0.0f), Vector3(0.0f) }; - float total[4] = {0, 0, 0, 0}; - - for (int i = 0; i < n; ++i) - { - // Find nearest cluster. - int nearest = 0; - float mindist = FLT_MAX; - for (int j = 0; j < 4; j++) - { - float dist = lengthSquared((cluster[j] - points[i]) * metric); - if (dist < mindist) - { - mindist = dist; - nearest = j; - } - } - - newCluster[nearest] += weights[i] * points[i]; - total[nearest] += weights[i]; - } - - for (int j = 0; j < 4; j++) - { - if (total[j] != 0) - newCluster[j] /= total[j]; - } - - if (equal(cluster[0], newCluster[0]) && equal(cluster[1], newCluster[1]) && - equal(cluster[2], newCluster[2]) && equal(cluster[3], newCluster[3])) - { - return (total[0] != 0) + (total[1] != 0) + (total[2] != 0) + (total[3] != 0); - } - - cluster[0] = newCluster[0]; - cluster[1] = newCluster[1]; - cluster[2] = newCluster[2]; - cluster[3] = newCluster[3]; - - // Sort clusters by weight. - for (int i = 0; i < 4; i++) - { - for (int j = i; j > 0 && total[j] > total[j - 1]; j--) - { - swap( total[j], total[j - 1] ); - swap( cluster[j], cluster[j - 1] ); - } - } - } -} - - - -// Adaptation of James Arvo's SVD code, as found in ZOH. - -inline float Sqr(float x) { return x*x; } - -inline float svd_pythag( float a, float b ) -{ - float at = fabsf(a); - float bt = fabsf(b); - if( at > bt ) - return at * sqrtf( 1.0f + Sqr( bt / at ) ); - else if( bt > 0.0f ) - return bt * sqrtf( 1.0f + Sqr( at / bt ) ); - else return 0.0f; -} - -inline float SameSign( float a, float b ) -{ - float t; - if( b >= 0.0f ) t = fabsf( a ); - else t = -fabsf( a ); - return t; -} - -void ArvoSVD(int rows, int cols, float * Q, float * diag, float * R) -{ - static const int MaxIterations = 30; - - int i, j, k, l, p, q, iter; - float c, f, h, s, x, y, z; - float norm = 0.0f; - float g = 0.0f; - float scale = 0.0f; - - Array<float> temp; temp.resize(cols, 0.0f); - - for( i = 0; i < cols; i++ ) - { - temp[i] = scale * g; - scale = 0.0f; - g = 0.0f; - s = 0.0f; - l = i + 1; - - if( i < rows ) - { - for( k = i; k < rows; k++ ) scale += fabsf( Q[k*cols+i] ); - if( scale != 0.0f ) - { - for( k = i; k < rows; k++ ) - { - Q[k*cols+i] /= scale; - s += Sqr( Q[k*cols+i] ); - } - f = Q[i*cols+i]; - g = -SameSign( sqrtf(s), f ); - h = f * g - s; - Q[i*cols+i] = f - g; - if( i != cols - 1 ) - { - for( j = l; j < cols; j++ ) - { - s = 0.0f; - for( k = i; k < rows; k++ ) s += Q[k*cols+i] * Q[k*cols+j]; - f = s / h; - for( k = i; k < rows; k++ ) Q[k*cols+j] += f * Q[k*cols+i]; - } - } - for( k = i; k < rows; k++ ) Q[k*cols+i] *= scale; - } - } - - diag[i] = scale * g; - g = 0.0f; - s = 0.0f; - scale = 0.0f; - - if( i < rows && i != cols - 1 ) - { - for( k = l; k < cols; k++ ) scale += fabsf( Q[i*cols+k] ); - if( scale != 0.0f ) - { - for( k = l; k < cols; k++ ) - { - Q[i*cols+k] /= scale; - s += Sqr( Q[i*cols+k] ); - } - f = Q[i*cols+l]; - g = -SameSign( sqrtf(s), f ); - h = f * g - s; - Q[i*cols+l] = f - g; - for( k = l; k < cols; k++ ) temp[k] = Q[i*cols+k] / h; - if( i != rows - 1 ) - { - for( j = l; j < rows; j++ ) - { - s = 0.0f; - for( k = l; k < cols; k++ ) s += Q[j*cols+k] * Q[i*cols+k]; - for( k = l; k < cols; k++ ) Q[j*cols+k] += s * temp[k]; - } - } - for( k = l; k < cols; k++ ) Q[i*cols+k] *= scale; - } - } - norm = max( norm, fabsf( diag[i] ) + fabsf( temp[i] ) ); - } - - - for( i = cols - 1; i >= 0; i-- ) - { - if( i < cols - 1 ) - { - if( g != 0.0f ) - { - for( j = l; j < cols; j++ ) R[i*cols+j] = ( Q[i*cols+j] / Q[i*cols+l] ) / g; - for( j = l; j < cols; j++ ) - { - s = 0.0f; - for( k = l; k < cols; k++ ) s += Q[i*cols+k] * R[j*cols+k]; - for( k = l; k < cols; k++ ) R[j*cols+k] += s * R[i*cols+k]; - } - } - for( j = l; j < cols; j++ ) - { - R[i*cols+j] = 0.0f; - R[j*cols+i] = 0.0f; - } - } - R[i*cols+i] = 1.0f; - g = temp[i]; - l = i; - } - - - for( i = cols - 1; i >= 0; i-- ) - { - l = i + 1; - g = diag[i]; - if( i < cols - 1 ) for( j = l; j < cols; j++ ) Q[i*cols+j] = 0.0f; - if( g != 0.0f ) - { - g = 1.0f / g; - if( i != cols - 1 ) - { - for( j = l; j < cols; j++ ) - { - s = 0.0f; - for( k = l; k < rows; k++ ) s += Q[k*cols+i] * Q[k*cols+j]; - f = ( s / Q[i*cols+i] ) * g; - for( k = i; k < rows; k++ ) Q[k*cols+j] += f * Q[k*cols+i]; - } - } - for( j = i; j < rows; j++ ) Q[j*cols+i] *= g; - } - else - { - for( j = i; j < rows; j++ ) Q[j*cols+i] = 0.0f; - } - Q[i*cols+i] += 1.0f; - } - - - for( k = cols - 1; k >= 0; k-- ) - { - for( iter = 1; iter <= MaxIterations; iter++ ) - { - int jump = 0; - - for( l = k; l >= 0; l-- ) - { - q = l - 1; - if( fabsf( temp[l] ) + norm == norm ) { jump = 1; break; } - if( fabsf( diag[q] ) + norm == norm ) { jump = 0; break; } - } - - if( !jump ) - { - c = 0.0f; - s = 1.0f; - for( i = l; i <= k; i++ ) - { - f = s * temp[i]; - temp[i] *= c; - if( fabsf( f ) + norm == norm ) break; - g = diag[i]; - h = svd_pythag( f, g ); - diag[i] = h; - h = 1.0f / h; - c = g * h; - s = -f * h; - for( j = 0; j < rows; j++ ) - { - y = Q[j*cols+q]; - z = Q[j*cols+i]; - Q[j*cols+q] = y * c + z * s; - Q[j*cols+i] = z * c - y * s; - } - } - } - - z = diag[k]; - if( l == k ) - { - if( z < 0.0f ) - { - diag[k] = -z; - for( j = 0; j < cols; j++ ) R[k*cols+j] *= -1.0f; - } - break; - } - if( iter >= MaxIterations ) return; - x = diag[l]; - q = k - 1; - y = diag[q]; - g = temp[q]; - h = temp[k]; - f = ( ( y - z ) * ( y + z ) + ( g - h ) * ( g + h ) ) / ( 2.0f * h * y ); - g = svd_pythag( f, 1.0f ); - f = ( ( x - z ) * ( x + z ) + h * ( ( y / ( f + SameSign( g, f ) ) ) - h ) ) / x; - c = 1.0f; - s = 1.0f; - for( j = l; j <= q; j++ ) - { - i = j + 1; - g = temp[i]; - y = diag[i]; - h = s * g; - g = c * g; - z = svd_pythag( f, h ); - temp[j] = z; - c = f / z; - s = h / z; - f = x * c + g * s; - g = g * c - x * s; - h = y * s; - y = y * c; - for( p = 0; p < cols; p++ ) - { - x = R[j*cols+p]; - z = R[i*cols+p]; - R[j*cols+p] = x * c + z * s; - R[i*cols+p] = z * c - x * s; - } - z = svd_pythag( f, h ); - diag[j] = z; - if( z != 0.0f ) - { - z = 1.0f / z; - c = f * z; - s = h * z; - } - f = c * g + s * y; - x = c * y - s * g; - for( p = 0; p < rows; p++ ) - { - y = Q[p*cols+j]; - z = Q[p*cols+i]; - Q[p*cols+j] = y * c + z * s; - Q[p*cols+i] = z * c - y * s; - } - } - temp[l] = 0.0f; - temp[k] = f; - diag[k] = x; - } - } - - // Sort the singular values into descending order. - - for( i = 0; i < cols - 1; i++ ) - { - float biggest = diag[i]; // Biggest singular value so far. - int bindex = i; // The row/col it occurred in. - for( j = i + 1; j < cols; j++ ) - { - if( diag[j] > biggest ) - { - biggest = diag[j]; - bindex = j; - } - } - if( bindex != i ) // Need to swap rows and columns. - { - // Swap columns in Q. - for (int j = 0; j < rows; ++j) - swap(Q[j*cols+i], Q[j*cols+bindex]); - - // Swap rows in R. - for (int j = 0; j < rows; ++j) - swap(R[i*cols+j], R[bindex*cols+j]); - - // Swap elements in diag. - swap(diag[i], diag[bindex]); - } - } -} diff --git a/thirdparty/thekla_atlas/nvmath/Fitting.h b/thirdparty/thekla_atlas/nvmath/Fitting.h deleted file mode 100644 index 7a88cd28fd..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Fitting.h +++ /dev/null @@ -1,50 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MATH_FITTING_H -#define NV_MATH_FITTING_H - -#include "Vector.h" -#include "Plane.h" - -namespace nv -{ - namespace Fit - { - Vector3 computeCentroid(int n, const Vector3 * points); - Vector3 computeCentroid(int n, const Vector3 * points, const float * weights, const Vector3 & metric); - - Vector4 computeCentroid(int n, const Vector4 * points); - Vector4 computeCentroid(int n, const Vector4 * points, const float * weights, const Vector4 & metric); - - Vector3 computeCovariance(int n, const Vector3 * points, float * covariance); - Vector3 computeCovariance(int n, const Vector3 * points, const float * weights, const Vector3 & metric, float * covariance); - - Vector4 computeCovariance(int n, const Vector4 * points, float * covariance); - Vector4 computeCovariance(int n, const Vector4 * points, const float * weights, const Vector4 & metric, float * covariance); - - Vector3 computePrincipalComponent_PowerMethod(int n, const Vector3 * points); - Vector3 computePrincipalComponent_PowerMethod(int n, const Vector3 * points, const float * weights, const Vector3 & metric); - - Vector3 computePrincipalComponent_EigenSolver(int n, const Vector3 * points); - Vector3 computePrincipalComponent_EigenSolver(int n, const Vector3 * points, const float * weights, const Vector3 & metric); - - Vector4 computePrincipalComponent_EigenSolver(int n, const Vector4 * points); - Vector4 computePrincipalComponent_EigenSolver(int n, const Vector4 * points, const float * weights, const Vector4 & metric); - - Vector3 computePrincipalComponent_SVD(int n, const Vector3 * points); - Vector4 computePrincipalComponent_SVD(int n, const Vector4 * points); - - Plane bestPlane(int n, const Vector3 * points); - bool isPlanar(int n, const Vector3 * points, float epsilon = NV_EPSILON); - - bool eigenSolveSymmetric3(const float matrix[6], float eigenValues[3], Vector3 eigenVectors[3]); - bool eigenSolveSymmetric4(const float matrix[10], float eigenValues[4], Vector4 eigenVectors[4]); - - // Returns number of clusters [1-4]. - int compute4Means(int n, const Vector3 * points, const float * weights, const Vector3 & metric, Vector3 * cluster); - } - -} // nv namespace - -#endif // NV_MATH_FITTING_H diff --git a/thirdparty/thekla_atlas/nvmath/KahanSum.h b/thirdparty/thekla_atlas/nvmath/KahanSum.h deleted file mode 100644 index 18d475e7cb..0000000000 --- a/thirdparty/thekla_atlas/nvmath/KahanSum.h +++ /dev/null @@ -1,39 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MATH_KAHANSUM_H -#define NV_MATH_KAHANSUM_H - -#include "nvmath.h" - -namespace nv -{ - - class KahanSum - { - public: - KahanSum() : accum(0.0f), err(0) {}; - - void add(float f) - { - float compensated = f + err; - float tmp = accum + compensated; - err = accum - tmp; - err += compensated; - accum = tmp; - } - - float sum() const - { - return accum; - } - - private: - float accum; - float err; - }; - -} // nv namespace - - -#endif // NV_MATH_KAHANSUM_H diff --git a/thirdparty/thekla_atlas/nvmath/Matrix.cpp b/thirdparty/thekla_atlas/nvmath/Matrix.cpp deleted file mode 100644 index 29bd19f5f8..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Matrix.cpp +++ /dev/null @@ -1,441 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "Matrix.inl" -#include "Vector.inl" - -#include "nvcore/Array.inl" - -#include <float.h> - -#if !NV_CC_MSVC && !NV_OS_ORBIS -#include <alloca.h> -#endif - -using namespace nv; - - -// Given a matrix a[1..n][1..n], this routine replaces it by the LU decomposition of a rowwise -// permutation of itself. a and n are input. a is output, arranged as in equation (2.3.14) above; -// indx[1..n] is an output vector that records the row permutation effected by the partial -// pivoting; d is output as -1 depending on whether the number of row interchanges was even -// or odd, respectively. This routine is used in combination with lubksb to solve linear equations -// or invert a matrix. -static bool ludcmp(float **a, int n, int *indx, float *d) -{ - const float TINY = 1.0e-20f; - - float * vv = (float*)alloca(sizeof(float) * n); // vv stores the implicit scaling of each row. - - *d = 1.0; // No row interchanges yet. - for (int i = 0; i < n; i++) { // Loop over rows to get the implicit scaling information. - - float big = 0.0; - for (int j = 0; j < n; j++) { - big = max(big, fabsf(a[i][j])); - } - if (big == 0) { - return false; // Singular matrix - } - - // No nonzero largest element. - vv[i] = 1.0f / big; // Save the scaling. - } - - for (int j = 0; j < n; j++) { // This is the loop over columns of Crout's method. - for (int i = 0; i < j; i++) { // This is equation (2.3.12) except for i = j. - float sum = a[i][j]; - for (int k = 0; k < i; k++) sum -= a[i][k]*a[k][j]; - a[i][j] = sum; - } - - int imax = -1; - float big = 0.0; // Initialize for the search for largest pivot element. - for (int i = j; i < n; i++) { // This is i = j of equation (2.3.12) and i = j+ 1 : : : N - float sum = a[i][j]; // of equation (2.3.13). - for (int k = 0; k < j; k++) { - sum -= a[i][k]*a[k][j]; - } - a[i][j]=sum; - - float dum = vv[i]*fabs(sum); - if (dum >= big) { - // Is the figure of merit for the pivot better than the best so far? - big = dum; - imax = i; - } - } - nvDebugCheck(imax != -1); - - if (j != imax) { // Do we need to interchange rows? - for (int k = 0; k < n; k++) { // Yes, do so... - swap(a[imax][k], a[j][k]); - } - *d = -(*d); // ...and change the parity of d. - vv[imax]=vv[j]; // Also interchange the scale factor. - } - - indx[j]=imax; - if (a[j][j] == 0.0) a[j][j] = TINY; - - // If the pivot element is zero the matrix is singular (at least to the precision of the - // algorithm). For some applications on singular matrices, it is desirable to substitute - // TINY for zero. - if (j != n-1) { // Now, finally, divide by the pivot element. - float dum = 1.0f / a[j][j]; - for (int i = j+1; i < n; i++) a[i][j] *= dum; - } - } // Go back for the next column in the reduction. - - return true; -} - - -// Solves the set of n linear equations Ax = b. Here a[1..n][1..n] is input, not as the matrix -// A but rather as its LU decomposition, determined by the routine ludcmp. indx[1..n] is input -// as the permutation vector returned by ludcmp. b[1..n] is input as the right-hand side vector -// B, and returns with the solution vector X. a, n, and indx are not modified by this routine -// and can be left in place for successive calls with different right-hand sides b. This routine takes -// into account the possibility that b will begin with many zero elements, so it is efficient for use -// in matrix inversion. -static void lubksb(float **a, int n, int *indx, float b[]) -{ - int ii = 0; - for (int i=0; i<n; i++) { // When ii is set to a positive value, it will become - int ip = indx[i]; // the index of the first nonvanishing element of b. We now - float sum = b[ip]; // do the forward substitution, equation (2.3.6). The - b[ip] = b[i]; // only new wrinkle is to unscramble the permutation as we go. - if (ii != 0) { - for (int j = ii-1; j < i; j++) sum -= a[i][j]*b[j]; - } - else if (sum != 0.0f) { - ii = i+1; // A nonzero element was encountered, so from now on we - } - b[i] = sum; // will have to do the sums in the loop above. - } - for (int i=n-1; i>=0; i--) { // Now we do the backsubstitution, equation (2.3.7). - float sum = b[i]; - for (int j = i+1; j < n; j++) { - sum -= a[i][j]*b[j]; - } - b[i] = sum/a[i][i]; // Store a component of the solution vector X. - } // All done! -} - - -bool nv::solveLU(const Matrix & A, const Vector4 & b, Vector4 * x) -{ - nvDebugCheck(x != NULL); - - float m[4][4]; - float *a[4] = {m[0], m[1], m[2], m[3]}; - int idx[4]; - float d; - - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - a[x][y] = A(x, y); - } - } - - // Create LU decomposition. - if (!ludcmp(a, 4, idx, &d)) { - // Singular matrix. - return false; - } - - // Init solution. - *x = b; - - // Do back substitution. - lubksb(a, 4, idx, x->component); - - return true; -} - -// @@ Not tested. -Matrix nv::inverseLU(const Matrix & A) -{ - Vector4 Ai[4]; - - solveLU(A, Vector4(1, 0, 0, 0), &Ai[0]); - solveLU(A, Vector4(0, 1, 0, 0), &Ai[1]); - solveLU(A, Vector4(0, 0, 1, 0), &Ai[2]); - solveLU(A, Vector4(0, 0, 0, 1), &Ai[3]); - - return Matrix(Ai[0], Ai[1], Ai[2], Ai[3]); -} - - - -bool nv::solveLU(const Matrix3 & A, const Vector3 & b, Vector3 * x) -{ - nvDebugCheck(x != NULL); - - float m[3][3]; - float *a[3] = {m[0], m[1], m[2]}; - int idx[3]; - float d; - - for (int y = 0; y < 3; y++) { - for (int x = 0; x < 3; x++) { - a[x][y] = A(x, y); - } - } - - // Create LU decomposition. - if (!ludcmp(a, 3, idx, &d)) { - // Singular matrix. - return false; - } - - // Init solution. - *x = b; - - // Do back substitution. - lubksb(a, 3, idx, x->component); - - return true; -} - - -bool nv::solveCramer(const Matrix & A, const Vector4 & b, Vector4 * x) -{ - nvDebugCheck(x != NULL); - - *x = transform(inverseCramer(A), b); - - return true; // @@ Return false if determinant(A) == 0 ! -} - -bool nv::solveCramer(const Matrix3 & A, const Vector3 & b, Vector3 * x) -{ - nvDebugCheck(x != NULL); - - const float det = A.determinant(); - if (equal(det, 0.0f)) { // @@ Use input epsilon. - return false; - } - - Matrix3 Ai = inverseCramer(A); - - *x = transform(Ai, b); - - return true; -} - - - -// Inverse using gaussian elimination. From Jon's code. -Matrix nv::inverse(const Matrix & m) { - - Matrix A = m; - Matrix B(identity); - - int i, j, k; - float max, t, det, pivot; - - det = 1.0; - for (i=0; i<4; i++) { /* eliminate in column i, below diag */ - max = -1.; - for (k=i; k<4; k++) /* find pivot for column i */ - if (fabs(A(k, i)) > max) { - max = fabs(A(k, i)); - j = k; - } - if (max<=0.) return B; /* if no nonzero pivot, PUNT */ - if (j!=i) { /* swap rows i and j */ - for (k=i; k<4; k++) - swap(A(i, k), A(j, k)); - for (k=0; k<4; k++) - swap(B(i, k), B(j, k)); - det = -det; - } - pivot = A(i, i); - det *= pivot; - for (k=i+1; k<4; k++) /* only do elems to right of pivot */ - A(i, k) /= pivot; - for (k=0; k<4; k++) - B(i, k) /= pivot; - /* we know that A(i, i) will be set to 1, so don't bother to do it */ - - for (j=i+1; j<4; j++) { /* eliminate in rows below i */ - t = A(j, i); /* we're gonna zero this guy */ - for (k=i+1; k<4; k++) /* subtract scaled row i from row j */ - A(j, k) -= A(i, k)*t; /* (ignore k<=i, we know they're 0) */ - for (k=0; k<4; k++) - B(j, k) -= B(i, k)*t; - } - } - - /*---------- backward elimination ----------*/ - - for (i=4-1; i>0; i--) { /* eliminate in column i, above diag */ - for (j=0; j<i; j++) { /* eliminate in rows above i */ - t = A(j, i); /* we're gonna zero this guy */ - for (k=0; k<4; k++) /* subtract scaled row i from row j */ - B(j, k) -= B(i, k)*t; - } - } - - return B; -} - - -Matrix3 nv::inverse(const Matrix3 & m) { - - Matrix3 A = m; - Matrix3 B(identity); - - int i, j, k; - float max, t, det, pivot; - - det = 1.0; - for (i=0; i<3; i++) { /* eliminate in column i, below diag */ - max = -1.; - for (k=i; k<3; k++) /* find pivot for column i */ - if (fabs(A(k, i)) > max) { - max = fabs(A(k, i)); - j = k; - } - if (max<=0.) return B; /* if no nonzero pivot, PUNT */ - if (j!=i) { /* swap rows i and j */ - for (k=i; k<3; k++) - swap(A(i, k), A(j, k)); - for (k=0; k<3; k++) - swap(B(i, k), B(j, k)); - det = -det; - } - pivot = A(i, i); - det *= pivot; - for (k=i+1; k<3; k++) /* only do elems to right of pivot */ - A(i, k) /= pivot; - for (k=0; k<3; k++) - B(i, k) /= pivot; - /* we know that A(i, i) will be set to 1, so don't bother to do it */ - - for (j=i+1; j<3; j++) { /* eliminate in rows below i */ - t = A(j, i); /* we're gonna zero this guy */ - for (k=i+1; k<3; k++) /* subtract scaled row i from row j */ - A(j, k) -= A(i, k)*t; /* (ignore k<=i, we know they're 0) */ - for (k=0; k<3; k++) - B(j, k) -= B(i, k)*t; - } - } - - /*---------- backward elimination ----------*/ - - for (i=3-1; i>0; i--) { /* eliminate in column i, above diag */ - for (j=0; j<i; j++) { /* eliminate in rows above i */ - t = A(j, i); /* we're gonna zero this guy */ - for (k=0; k<3; k++) /* subtract scaled row i from row j */ - B(j, k) -= B(i, k)*t; - } - } - - return B; -} - - - - - -#if 0 - -// Copyright (C) 1999-2004 Michael Garland. -// -// 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, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, provided that the above -// copyright notice(s) and this permission notice appear in all copies of -// the Software and that both the above copyright notice(s) and this -// permission notice appear in supporting documentation. -// -// 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 -// OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -// HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL -// INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING -// FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -// WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -// -// Except as contained in this notice, the name of a copyright holder -// shall not be used in advertising or otherwise to promote the sale, use -// or other dealings in this Software without prior written authorization -// of the copyright holder. - - -// Matrix inversion code for 4x4 matrices using Gaussian elimination -// with partial pivoting. This is a specialized version of a -// procedure originally due to Paul Heckbert <ph@cs.cmu.edu>. -// -// Returns determinant of A, and B=inverse(A) -// If matrix A is singular, returns 0 and leaves trash in B. -// -#define SWAP(a, b, t) {t = a; a = b; b = t;} -double invert(Mat4& B, const Mat4& m) -{ - Mat4 A = m; - int i, j, k; - double max, t, det, pivot; - - /*---------- forward elimination ----------*/ - - for (i=0; i<4; i++) /* put identity matrix in B */ - for (j=0; j<4; j++) - B(i, j) = (double)(i==j); - - det = 1.0; - for (i=0; i<4; i++) { /* eliminate in column i, below diag */ - max = -1.; - for (k=i; k<4; k++) /* find pivot for column i */ - if (fabs(A(k, i)) > max) { - max = fabs(A(k, i)); - j = k; - } - if (max<=0.) return 0.; /* if no nonzero pivot, PUNT */ - if (j!=i) { /* swap rows i and j */ - for (k=i; k<4; k++) - SWAP(A(i, k), A(j, k), t); - for (k=0; k<4; k++) - SWAP(B(i, k), B(j, k), t); - det = -det; - } - pivot = A(i, i); - det *= pivot; - for (k=i+1; k<4; k++) /* only do elems to right of pivot */ - A(i, k) /= pivot; - for (k=0; k<4; k++) - B(i, k) /= pivot; - /* we know that A(i, i) will be set to 1, so don't bother to do it */ - - for (j=i+1; j<4; j++) { /* eliminate in rows below i */ - t = A(j, i); /* we're gonna zero this guy */ - for (k=i+1; k<4; k++) /* subtract scaled row i from row j */ - A(j, k) -= A(i, k)*t; /* (ignore k<=i, we know they're 0) */ - for (k=0; k<4; k++) - B(j, k) -= B(i, k)*t; - } - } - - /*---------- backward elimination ----------*/ - - for (i=4-1; i>0; i--) { /* eliminate in column i, above diag */ - for (j=0; j<i; j++) { /* eliminate in rows above i */ - t = A(j, i); /* we're gonna zero this guy */ - for (k=0; k<4; k++) /* subtract scaled row i from row j */ - B(j, k) -= B(i, k)*t; - } - } - - return det; -} - -#endif // 0 - - - diff --git a/thirdparty/thekla_atlas/nvmath/Matrix.h b/thirdparty/thekla_atlas/nvmath/Matrix.h deleted file mode 100644 index 506bdad1ca..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Matrix.h +++ /dev/null @@ -1,113 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MATH_MATRIX_H -#define NV_MATH_MATRIX_H - -#include "Vector.h" - -// - Matrices are stored in memory in *column major* order. -// - Points are to be though of as column vectors. -// - Transformation of a point p by a matrix M is: p' = M * p - -namespace nv -{ - enum identity_t { identity }; - - // 3x3 matrix. - class NVMATH_CLASS Matrix3 - { - public: - Matrix3(); - explicit Matrix3(float f); - explicit Matrix3(identity_t); - Matrix3(const Matrix3 & m); - Matrix3(Vector3::Arg v0, Vector3::Arg v1, Vector3::Arg v2); - - float data(uint idx) const; - float & data(uint idx); - float get(uint row, uint col) const; - float operator()(uint row, uint col) const; - float & operator()(uint row, uint col); - - Vector3 row(uint i) const; - Vector3 column(uint i) const; - - void operator*=(float s); - void operator/=(float s); - void operator+=(const Matrix3 & m); - void operator-=(const Matrix3 & m); - - void scale(float s); - void scale(Vector3::Arg s); - float determinant() const; - - private: - float m_data[9]; - }; - - // Solve equation system using LU decomposition and back-substitution. - extern bool solveLU(const Matrix3 & m, const Vector3 & b, Vector3 * x); - - // Solve equation system using Cramer's inverse. - extern bool solveCramer(const Matrix3 & A, const Vector3 & b, Vector3 * x); - - - // 4x4 matrix. - class NVMATH_CLASS Matrix - { - public: - typedef Matrix const & Arg; - - Matrix(); - explicit Matrix(float f); - explicit Matrix(identity_t); - Matrix(const Matrix3 & m); - Matrix(const Matrix & m); - Matrix(Vector4::Arg v0, Vector4::Arg v1, Vector4::Arg v2, Vector4::Arg v3); - //explicit Matrix(const float m[]); // m is assumed to contain 16 elements - - float data(uint idx) const; - float & data(uint idx); - float get(uint row, uint col) const; - float operator()(uint row, uint col) const; - float & operator()(uint row, uint col); - const float * ptr() const; - - Vector4 row(uint i) const; - Vector4 column(uint i) const; - - void zero(); - void identity(); - - void scale(float s); - void scale(Vector3::Arg s); - void translate(Vector3::Arg t); - void rotate(float theta, float v0, float v1, float v2); - float determinant() const; - - void operator+=(const Matrix & m); - void operator-=(const Matrix & m); - - void apply(Matrix::Arg m); - - private: - float m_data[16]; - }; - - // Solve equation system using LU decomposition and back-substitution. - extern bool solveLU(const Matrix & A, const Vector4 & b, Vector4 * x); - - // Solve equation system using Cramer's inverse. - extern bool solveCramer(const Matrix & A, const Vector4 & b, Vector4 * x); - - // Compute inverse using LU decomposition. - extern Matrix inverseLU(const Matrix & m); - - // Compute inverse using Gaussian elimination and partial pivoting. - extern Matrix inverse(const Matrix & m); - extern Matrix3 inverse(const Matrix3 & m); - -} // nv namespace - -#endif // NV_MATH_MATRIX_H diff --git a/thirdparty/thekla_atlas/nvmath/Matrix.inl b/thirdparty/thekla_atlas/nvmath/Matrix.inl deleted file mode 100644 index c0d99d9fe0..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Matrix.inl +++ /dev/null @@ -1,1274 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MATH_MATRIX_INL -#define NV_MATH_MATRIX_INL - -#include "Matrix.h" - -namespace nv -{ - inline Matrix3::Matrix3() {} - - inline Matrix3::Matrix3(float f) - { - for(int i = 0; i < 9; i++) { - m_data[i] = f; - } - } - - inline Matrix3::Matrix3(identity_t) - { - for(int i = 0; i < 3; i++) { - for(int j = 0; j < 3; j++) { - m_data[3*j+i] = (i == j) ? 1.0f : 0.0f; - } - } - } - - inline Matrix3::Matrix3(const Matrix3 & m) - { - for(int i = 0; i < 9; i++) { - m_data[i] = m.m_data[i]; - } - } - - inline Matrix3::Matrix3(Vector3::Arg v0, Vector3::Arg v1, Vector3::Arg v2) - { - m_data[0] = v0.x; m_data[1] = v0.y; m_data[2] = v0.z; - m_data[3] = v1.x; m_data[4] = v1.y; m_data[5] = v1.z; - m_data[6] = v2.x; m_data[7] = v2.y; m_data[8] = v2.z; - } - - inline float Matrix3::data(uint idx) const - { - nvDebugCheck(idx < 9); - return m_data[idx]; - } - inline float & Matrix3::data(uint idx) - { - nvDebugCheck(idx < 9); - return m_data[idx]; - } - inline float Matrix3::get(uint row, uint col) const - { - nvDebugCheck(row < 3 && col < 3); - return m_data[col * 3 + row]; - } - inline float Matrix3::operator()(uint row, uint col) const - { - nvDebugCheck(row < 3 && col < 3); - return m_data[col * 3 + row]; - } - inline float & Matrix3::operator()(uint row, uint col) - { - nvDebugCheck(row < 3 && col < 3); - return m_data[col * 3 + row]; - } - - inline Vector3 Matrix3::row(uint i) const - { - nvDebugCheck(i < 3); - return Vector3(get(i, 0), get(i, 1), get(i, 2)); - } - inline Vector3 Matrix3::column(uint i) const - { - nvDebugCheck(i < 3); - return Vector3(get(0, i), get(1, i), get(2, i)); - } - - inline void Matrix3::operator*=(float s) - { - for(int i = 0; i < 9; i++) { - m_data[i] *= s; - } - } - - inline void Matrix3::operator/=(float s) - { - float is = 1.0f /s; - for(int i = 0; i < 9; i++) { - m_data[i] *= is; - } - } - - inline void Matrix3::operator+=(const Matrix3 & m) - { - for(int i = 0; i < 9; i++) { - m_data[i] += m.m_data[i]; - } - } - - inline void Matrix3::operator-=(const Matrix3 & m) - { - for(int i = 0; i < 9; i++) { - m_data[i] -= m.m_data[i]; - } - } - - inline Matrix3 operator+(const Matrix3 & a, const Matrix3 & b) - { - Matrix3 m = a; - m += b; - return m; - } - - inline Matrix3 operator-(const Matrix3 & a, const Matrix3 & b) - { - Matrix3 m = a; - m -= b; - return m; - } - - inline Matrix3 operator*(const Matrix3 & a, float s) - { - Matrix3 m = a; - m *= s; - return m; - } - - inline Matrix3 operator*(float s, const Matrix3 & a) - { - Matrix3 m = a; - m *= s; - return m; - } - - inline Matrix3 operator/(const Matrix3 & a, float s) - { - Matrix3 m = a; - m /= s; - return m; - } - - inline Matrix3 mul(const Matrix3 & a, const Matrix3 & b) - { - Matrix3 m; - - for(int i = 0; i < 3; i++) { - const float ai0 = a(i,0), ai1 = a(i,1), ai2 = a(i,2); - m(i, 0) = ai0 * b(0,0) + ai1 * b(1,0) + ai2 * b(2,0); - m(i, 1) = ai0 * b(0,1) + ai1 * b(1,1) + ai2 * b(2,1); - m(i, 2) = ai0 * b(0,2) + ai1 * b(1,2) + ai2 * b(2,2); - } - - return m; - } - - inline Matrix3 operator*(const Matrix3 & a, const Matrix3 & b) - { - return mul(a, b); - } - - // Transform the given 3d vector with the given matrix. - inline Vector3 transform(const Matrix3 & m, const Vector3 & p) - { - return Vector3( - p.x * m(0,0) + p.y * m(0,1) + p.z * m(0,2), - p.x * m(1,0) + p.y * m(1,1) + p.z * m(1,2), - p.x * m(2,0) + p.y * m(2,1) + p.z * m(2,2)); - } - - inline void Matrix3::scale(float s) - { - for (int i = 0; i < 9; i++) { - m_data[i] *= s; - } - } - - inline void Matrix3::scale(Vector3::Arg s) - { - m_data[0] *= s.x; m_data[1] *= s.x; m_data[2] *= s.x; - m_data[3] *= s.y; m_data[4] *= s.y; m_data[5] *= s.y; - m_data[6] *= s.z; m_data[7] *= s.z; m_data[8] *= s.z; - } - - inline float Matrix3::determinant() const - { - return - get(0,0) * get(1,1) * get(2,2) + - get(0,1) * get(1,2) * get(2,0) + - get(0,2) * get(1,0) * get(2,1) - - get(0,2) * get(1,1) * get(2,0) - - get(0,1) * get(1,0) * get(2,2) - - get(0,0) * get(1,2) * get(2,1); - } - - // Inverse using Cramer's rule. - inline Matrix3 inverseCramer(const Matrix3 & m) - { - const float det = m.determinant(); - if (equal(det, 0.0f, 0.0f)) { - return Matrix3(0); - } - - Matrix3 r; - - r.data(0) = - m.data(5) * m.data(7) + m.data(4) * m.data(8); - r.data(1) = + m.data(5) * m.data(6) - m.data(3) * m.data(8); - r.data(2) = - m.data(4) * m.data(6) + m.data(3) * m.data(7); - - r.data(3) = + m.data(2) * m.data(7) - m.data(1) * m.data(8); - r.data(4) = - m.data(2) * m.data(6) + m.data(0) * m.data(8); - r.data(5) = + m.data(1) * m.data(6) - m.data(0) * m.data(7); - - r.data(6) = - m.data(2) * m.data(4) + m.data(1) * m.data(5); - r.data(7) = + m.data(2) * m.data(3) - m.data(0) * m.data(5); - r.data(8) = - m.data(1) * m.data(3) + m.data(0) * m.data(4); - - r.scale(1.0f / det); - - return r; - } - - - - inline Matrix::Matrix() - { - } - - inline Matrix::Matrix(float f) - { - for(int i = 0; i < 16; i++) { - m_data[i] = 0.0f; - } - } - - inline Matrix::Matrix(identity_t) - { - for(int i = 0; i < 4; i++) { - for(int j = 0; j < 4; j++) { - m_data[4*j+i] = (i == j) ? 1.0f : 0.0f; - } - } - } - - inline Matrix::Matrix(const Matrix & m) - { - for(int i = 0; i < 16; i++) { - m_data[i] = m.m_data[i]; - } - } - - inline Matrix::Matrix(const Matrix3 & m) - { - for(int i = 0; i < 3; i++) { - for(int j = 0; j < 3; j++) { - operator()(i, j) = m.get(i, j); - } - } - for(int i = 0; i < 4; i++) { - operator()(3, i) = 0; - operator()(i, 3) = 0; - } - } - - inline Matrix::Matrix(Vector4::Arg v0, Vector4::Arg v1, Vector4::Arg v2, Vector4::Arg v3) - { - m_data[ 0] = v0.x; m_data[ 1] = v0.y; m_data[ 2] = v0.z; m_data[ 3] = v0.w; - m_data[ 4] = v1.x; m_data[ 5] = v1.y; m_data[ 6] = v1.z; m_data[ 7] = v1.w; - m_data[ 8] = v2.x; m_data[ 9] = v2.y; m_data[10] = v2.z; m_data[11] = v2.w; - m_data[12] = v3.x; m_data[13] = v3.y; m_data[14] = v3.z; m_data[15] = v3.w; - } - - /*inline Matrix::Matrix(const float m[]) - { - for(int i = 0; i < 16; i++) { - m_data[i] = m[i]; - } - }*/ - - - // Accessors - inline float Matrix::data(uint idx) const - { - nvDebugCheck(idx < 16); - return m_data[idx]; - } - inline float & Matrix::data(uint idx) - { - nvDebugCheck(idx < 16); - return m_data[idx]; - } - inline float Matrix::get(uint row, uint col) const - { - nvDebugCheck(row < 4 && col < 4); - return m_data[col * 4 + row]; - } - inline float Matrix::operator()(uint row, uint col) const - { - nvDebugCheck(row < 4 && col < 4); - return m_data[col * 4 + row]; - } - inline float & Matrix::operator()(uint row, uint col) - { - nvDebugCheck(row < 4 && col < 4); - return m_data[col * 4 + row]; - } - - inline const float * Matrix::ptr() const - { - return m_data; - } - - inline Vector4 Matrix::row(uint i) const - { - nvDebugCheck(i < 4); - return Vector4(get(i, 0), get(i, 1), get(i, 2), get(i, 3)); - } - - inline Vector4 Matrix::column(uint i) const - { - nvDebugCheck(i < 4); - return Vector4(get(0, i), get(1, i), get(2, i), get(3, i)); - } - - inline void Matrix::zero() - { - m_data[0] = 0; m_data[1] = 0; m_data[2] = 0; m_data[3] = 0; - m_data[4] = 0; m_data[5] = 0; m_data[6] = 0; m_data[7] = 0; - m_data[8] = 0; m_data[9] = 0; m_data[10] = 0; m_data[11] = 0; - m_data[12] = 0; m_data[13] = 0; m_data[14] = 0; m_data[15] = 0; - } - - inline void Matrix::identity() - { - m_data[0] = 1; m_data[1] = 0; m_data[2] = 0; m_data[3] = 0; - m_data[4] = 0; m_data[5] = 1; m_data[6] = 0; m_data[7] = 0; - m_data[8] = 0; m_data[9] = 0; m_data[10] = 1; m_data[11] = 0; - m_data[12] = 0; m_data[13] = 0; m_data[14] = 0; m_data[15] = 1; - } - - // Apply scale. - inline void Matrix::scale(float s) - { - m_data[0] *= s; m_data[1] *= s; m_data[2] *= s; m_data[3] *= s; - m_data[4] *= s; m_data[5] *= s; m_data[6] *= s; m_data[7] *= s; - m_data[8] *= s; m_data[9] *= s; m_data[10] *= s; m_data[11] *= s; - m_data[12] *= s; m_data[13] *= s; m_data[14] *= s; m_data[15] *= s; - } - - // Apply scale. - inline void Matrix::scale(Vector3::Arg s) - { - m_data[0] *= s.x; m_data[1] *= s.x; m_data[2] *= s.x; m_data[3] *= s.x; - m_data[4] *= s.y; m_data[5] *= s.y; m_data[6] *= s.y; m_data[7] *= s.y; - m_data[8] *= s.z; m_data[9] *= s.z; m_data[10] *= s.z; m_data[11] *= s.z; - } - - // Apply translation. - inline void Matrix::translate(Vector3::Arg t) - { - m_data[12] = m_data[0] * t.x + m_data[4] * t.y + m_data[8] * t.z + m_data[12]; - m_data[13] = m_data[1] * t.x + m_data[5] * t.y + m_data[9] * t.z + m_data[13]; - m_data[14] = m_data[2] * t.x + m_data[6] * t.y + m_data[10] * t.z + m_data[14]; - m_data[15] = m_data[3] * t.x + m_data[7] * t.y + m_data[11] * t.z + m_data[15]; - } - - Matrix rotation(float theta, float v0, float v1, float v2); - - // Apply rotation. - inline void Matrix::rotate(float theta, float v0, float v1, float v2) - { - Matrix R(rotation(theta, v0, v1, v2)); - apply(R); - } - - // Apply transform. - inline void Matrix::apply(Matrix::Arg m) - { - nvDebugCheck(this != &m); - - for(int i = 0; i < 4; i++) { - const float ai0 = get(i,0), ai1 = get(i,1), ai2 = get(i,2), ai3 = get(i,3); - m_data[0 + i] = ai0 * m(0,0) + ai1 * m(1,0) + ai2 * m(2,0) + ai3 * m(3,0); - m_data[4 + i] = ai0 * m(0,1) + ai1 * m(1,1) + ai2 * m(2,1) + ai3 * m(3,1); - m_data[8 + i] = ai0 * m(0,2) + ai1 * m(1,2) + ai2 * m(2,2) + ai3 * m(3,2); - m_data[12+ i] = ai0 * m(0,3) + ai1 * m(1,3) + ai2 * m(2,3) + ai3 * m(3,3); - } - } - - // Get scale matrix. - inline Matrix scale(Vector3::Arg s) - { - Matrix m(identity); - m(0,0) = s.x; - m(1,1) = s.y; - m(2,2) = s.z; - return m; - } - - // Get scale matrix. - inline Matrix scale(float s) - { - Matrix m(identity); - m(0,0) = m(1,1) = m(2,2) = s; - return m; - } - - // Get translation matrix. - inline Matrix translation(Vector3::Arg t) - { - Matrix m(identity); - m(0,3) = t.x; - m(1,3) = t.y; - m(2,3) = t.z; - return m; - } - - // Get rotation matrix. - inline Matrix rotation(float theta, float v0, float v1, float v2) - { - float cost = cosf(theta); - float sint = sinf(theta); - - Matrix m(identity); - - if( 1 == v0 && 0 == v1 && 0 == v2 ) { - m(1,1) = cost; m(2,1) = -sint; - m(1,2) = sint; m(2,2) = cost; - } - else if( 0 == v0 && 1 == v1 && 0 == v2 ) { - m(0,0) = cost; m(2,0) = sint; - m(1,2) = -sint; m(2,2) = cost; - } - else if( 0 == v0 && 0 == v1 && 1 == v2 ) { - m(0,0) = cost; m(1,0) = -sint; - m(0,1) = sint; m(1,1) = cost; - } - else { - float a2, b2, c2; - a2 = v0 * v0; - b2 = v1 * v1; - c2 = v2 * v2; - - float iscale = 1.0f / sqrtf(a2 + b2 + c2); - v0 *= iscale; - v1 *= iscale; - v2 *= iscale; - - float abm, acm, bcm; - float mcos, asin, bsin, csin; - mcos = 1.0f - cost; - abm = v0 * v1 * mcos; - acm = v0 * v2 * mcos; - bcm = v1 * v2 * mcos; - asin = v0 * sint; - bsin = v1 * sint; - csin = v2 * sint; - m(0,0) = a2 * mcos + cost; - m(1,0) = abm - csin; - m(2,0) = acm + bsin; - m(3,0) = abm + csin; - m(1,1) = b2 * mcos + cost; - m(2,1) = bcm - asin; - m(3,1) = acm - bsin; - m(1,2) = bcm + asin; - m(2,2) = c2 * mcos + cost; - } - return m; - } - - //Matrix rotation(float yaw, float pitch, float roll); - //Matrix skew(float angle, Vector3::Arg v1, Vector3::Arg v2); - - // Get frustum matrix. - inline Matrix frustum(float xmin, float xmax, float ymin, float ymax, float zNear, float zFar) - { - Matrix m(0.0f); - - float doubleznear = 2.0f * zNear; - float one_deltax = 1.0f / (xmax - xmin); - float one_deltay = 1.0f / (ymax - ymin); - float one_deltaz = 1.0f / (zFar - zNear); - - m(0,0) = doubleznear * one_deltax; - m(1,1) = doubleznear * one_deltay; - m(0,2) = (xmax + xmin) * one_deltax; - m(1,2) = (ymax + ymin) * one_deltay; - m(2,2) = -(zFar + zNear) * one_deltaz; - m(3,2) = -1.0f; - m(2,3) = -(zFar * doubleznear) * one_deltaz; - - return m; - } - - // Get inverse frustum matrix. - inline Matrix frustumInverse(float xmin, float xmax, float ymin, float ymax, float zNear, float zFar) - { - Matrix m(0.0f); - - float one_doubleznear = 1.0f / (2.0f * zNear); - float one_doubleznearzfar = 1.0f / (2.0f * zNear * zFar); - - m(0,0) = (xmax - xmin) * one_doubleznear; - m(0,3) = (xmax + xmin) * one_doubleznear; - m(1,1) = (ymax - ymin) * one_doubleznear; - m(1,3) = (ymax + ymin) * one_doubleznear; - m(2,3) = -1; - m(3,2) = -(zFar - zNear) * one_doubleznearzfar; - m(3,3) = (zFar + zNear) * one_doubleznearzfar; - - return m; - } - - // Get infinite frustum matrix. - inline Matrix frustum(float xmin, float xmax, float ymin, float ymax, float zNear) - { - Matrix m(0.0f); - - float doubleznear = 2.0f * zNear; - float one_deltax = 1.0f / (xmax - xmin); - float one_deltay = 1.0f / (ymax - ymin); - float nudge = 1.0; // 0.999; - - m(0,0) = doubleznear * one_deltax; - m(1,1) = doubleznear * one_deltay; - m(0,2) = (xmax + xmin) * one_deltax; - m(1,2) = (ymax + ymin) * one_deltay; - m(2,2) = -1.0f * nudge; - m(3,2) = -1.0f; - m(2,3) = -doubleznear * nudge; - - return m; - } - - // Get perspective matrix. - inline Matrix perspective(float fovy, float aspect, float zNear, float zFar) - { - float xmax = zNear * tan(fovy / 2); - float xmin = -xmax; - - float ymax = xmax / aspect; - float ymin = -ymax; - - return frustum(xmin, xmax, ymin, ymax, zNear, zFar); - } - - // Get inverse perspective matrix. - inline Matrix perspectiveInverse(float fovy, float aspect, float zNear, float zFar) - { - float xmax = zNear * tan(fovy / 2); - float xmin = -xmax; - - float ymax = xmax / aspect; - float ymin = -ymax; - - return frustumInverse(xmin, xmax, ymin, ymax, zNear, zFar); - } - - // Get infinite perspective matrix. - inline Matrix perspective(float fovy, float aspect, float zNear) - { - float x = zNear * tan(fovy / 2); - float y = x / aspect; - return frustum( -x, x, -y, y, zNear ); - } - - // Get matrix determinant. - inline float Matrix::determinant() const - { - return - m_data[3] * m_data[6] * m_data[ 9] * m_data[12] - m_data[2] * m_data[7] * m_data[ 9] * m_data[12] - m_data[3] * m_data[5] * m_data[10] * m_data[12] + m_data[1] * m_data[7] * m_data[10] * m_data[12] + - m_data[2] * m_data[5] * m_data[11] * m_data[12] - m_data[1] * m_data[6] * m_data[11] * m_data[12] - m_data[3] * m_data[6] * m_data[ 8] * m_data[13] + m_data[2] * m_data[7] * m_data[ 8] * m_data[13] + - m_data[3] * m_data[4] * m_data[10] * m_data[13] - m_data[0] * m_data[7] * m_data[10] * m_data[13] - m_data[2] * m_data[4] * m_data[11] * m_data[13] + m_data[0] * m_data[6] * m_data[11] * m_data[13] + - m_data[3] * m_data[5] * m_data[ 8] * m_data[14] - m_data[1] * m_data[7] * m_data[ 8] * m_data[14] - m_data[3] * m_data[4] * m_data[ 9] * m_data[14] + m_data[0] * m_data[7] * m_data[ 9] * m_data[14] + - m_data[1] * m_data[4] * m_data[11] * m_data[14] - m_data[0] * m_data[5] * m_data[11] * m_data[14] - m_data[2] * m_data[5] * m_data[ 8] * m_data[15] + m_data[1] * m_data[6] * m_data[ 8] * m_data[15] + - m_data[2] * m_data[4] * m_data[ 9] * m_data[15] - m_data[0] * m_data[6] * m_data[ 9] * m_data[15] - m_data[1] * m_data[4] * m_data[10] * m_data[15] + m_data[0] * m_data[5] * m_data[10] * m_data[15]; - } - - inline Matrix transpose(Matrix::Arg m) - { - Matrix r; - for (int i = 0; i < 4; i++) - { - for (int j = 0; j < 4; j++) - { - r(i, j) = m(j, i); - } - } - return r; - } - - // Inverse using Cramer's rule. - inline Matrix inverseCramer(Matrix::Arg m) - { - Matrix r; - r.data( 0) = m.data(6)*m.data(11)*m.data(13) - m.data(7)*m.data(10)*m.data(13) + m.data(7)*m.data(9)*m.data(14) - m.data(5)*m.data(11)*m.data(14) - m.data(6)*m.data(9)*m.data(15) + m.data(5)*m.data(10)*m.data(15); - r.data( 1) = m.data(3)*m.data(10)*m.data(13) - m.data(2)*m.data(11)*m.data(13) - m.data(3)*m.data(9)*m.data(14) + m.data(1)*m.data(11)*m.data(14) + m.data(2)*m.data(9)*m.data(15) - m.data(1)*m.data(10)*m.data(15); - r.data( 2) = m.data(2)*m.data( 7)*m.data(13) - m.data(3)*m.data( 6)*m.data(13) + m.data(3)*m.data(5)*m.data(14) - m.data(1)*m.data( 7)*m.data(14) - m.data(2)*m.data(5)*m.data(15) + m.data(1)*m.data( 6)*m.data(15); - r.data( 3) = m.data(3)*m.data( 6)*m.data( 9) - m.data(2)*m.data( 7)*m.data( 9) - m.data(3)*m.data(5)*m.data(10) + m.data(1)*m.data( 7)*m.data(10) + m.data(2)*m.data(5)*m.data(11) - m.data(1)*m.data( 6)*m.data(11); - r.data( 4) = m.data(7)*m.data(10)*m.data(12) - m.data(6)*m.data(11)*m.data(12) - m.data(7)*m.data(8)*m.data(14) + m.data(4)*m.data(11)*m.data(14) + m.data(6)*m.data(8)*m.data(15) - m.data(4)*m.data(10)*m.data(15); - r.data( 5) = m.data(2)*m.data(11)*m.data(12) - m.data(3)*m.data(10)*m.data(12) + m.data(3)*m.data(8)*m.data(14) - m.data(0)*m.data(11)*m.data(14) - m.data(2)*m.data(8)*m.data(15) + m.data(0)*m.data(10)*m.data(15); - r.data( 6) = m.data(3)*m.data( 6)*m.data(12) - m.data(2)*m.data( 7)*m.data(12) - m.data(3)*m.data(4)*m.data(14) + m.data(0)*m.data( 7)*m.data(14) + m.data(2)*m.data(4)*m.data(15) - m.data(0)*m.data( 6)*m.data(15); - r.data( 7) = m.data(2)*m.data( 7)*m.data( 8) - m.data(3)*m.data( 6)*m.data( 8) + m.data(3)*m.data(4)*m.data(10) - m.data(0)*m.data( 7)*m.data(10) - m.data(2)*m.data(4)*m.data(11) + m.data(0)*m.data( 6)*m.data(11); - r.data( 8) = m.data(5)*m.data(11)*m.data(12) - m.data(7)*m.data( 9)*m.data(12) + m.data(7)*m.data(8)*m.data(13) - m.data(4)*m.data(11)*m.data(13) - m.data(5)*m.data(8)*m.data(15) + m.data(4)*m.data( 9)*m.data(15); - r.data( 9) = m.data(3)*m.data( 9)*m.data(12) - m.data(1)*m.data(11)*m.data(12) - m.data(3)*m.data(8)*m.data(13) + m.data(0)*m.data(11)*m.data(13) + m.data(1)*m.data(8)*m.data(15) - m.data(0)*m.data( 9)*m.data(15); - r.data(10) = m.data(1)*m.data( 7)*m.data(12) - m.data(3)*m.data( 5)*m.data(12) + m.data(3)*m.data(4)*m.data(13) - m.data(0)*m.data( 7)*m.data(13) - m.data(1)*m.data(4)*m.data(15) + m.data(0)*m.data( 5)*m.data(15); - r.data(11) = m.data(3)*m.data( 5)*m.data( 8) - m.data(1)*m.data( 7)*m.data( 8) - m.data(3)*m.data(4)*m.data( 9) + m.data(0)*m.data( 7)*m.data( 9) + m.data(1)*m.data(4)*m.data(11) - m.data(0)*m.data( 5)*m.data(11); - r.data(12) = m.data(6)*m.data( 9)*m.data(12) - m.data(5)*m.data(10)*m.data(12) - m.data(6)*m.data(8)*m.data(13) + m.data(4)*m.data(10)*m.data(13) + m.data(5)*m.data(8)*m.data(14) - m.data(4)*m.data( 9)*m.data(14); - r.data(13) = m.data(1)*m.data(10)*m.data(12) - m.data(2)*m.data( 9)*m.data(12) + m.data(2)*m.data(8)*m.data(13) - m.data(0)*m.data(10)*m.data(13) - m.data(1)*m.data(8)*m.data(14) + m.data(0)*m.data( 9)*m.data(14); - r.data(14) = m.data(2)*m.data( 5)*m.data(12) - m.data(1)*m.data( 6)*m.data(12) - m.data(2)*m.data(4)*m.data(13) + m.data(0)*m.data( 6)*m.data(13) + m.data(1)*m.data(4)*m.data(14) - m.data(0)*m.data( 5)*m.data(14); - r.data(15) = m.data(1)*m.data( 6)*m.data( 8) - m.data(2)*m.data( 5)*m.data( 8) + m.data(2)*m.data(4)*m.data( 9) - m.data(0)*m.data( 6)*m.data( 9) - m.data(1)*m.data(4)*m.data(10) + m.data(0)*m.data( 5)*m.data(10); - r.scale(1.0f / m.determinant()); - return r; - } - - inline Matrix isometryInverse(Matrix::Arg m) - { - Matrix r(identity); - - // transposed 3x3 upper left matrix - for (int i = 0; i < 3; i++) - { - for (int j = 0; j < 3; j++) - { - r(i, j) = m(j, i); - } - } - - // translate by the negative offsets - r.translate(-Vector3(m.data(12), m.data(13), m.data(14))); - - return r; - } - - // Transform the given 3d point with the given matrix. - inline Vector3 transformPoint(Matrix::Arg m, Vector3::Arg p) - { - return Vector3( - p.x * m(0,0) + p.y * m(0,1) + p.z * m(0,2) + m(0,3), - p.x * m(1,0) + p.y * m(1,1) + p.z * m(1,2) + m(1,3), - p.x * m(2,0) + p.y * m(2,1) + p.z * m(2,2) + m(2,3)); - } - - // Transform the given 3d vector with the given matrix. - inline Vector3 transformVector(Matrix::Arg m, Vector3::Arg p) - { - return Vector3( - p.x * m(0,0) + p.y * m(0,1) + p.z * m(0,2), - p.x * m(1,0) + p.y * m(1,1) + p.z * m(1,2), - p.x * m(2,0) + p.y * m(2,1) + p.z * m(2,2)); - } - - // Transform the given 4d vector with the given matrix. - inline Vector4 transform(Matrix::Arg m, Vector4::Arg p) - { - return Vector4( - p.x * m(0,0) + p.y * m(0,1) + p.z * m(0,2) + p.w * m(0,3), - p.x * m(1,0) + p.y * m(1,1) + p.z * m(1,2) + p.w * m(1,3), - p.x * m(2,0) + p.y * m(2,1) + p.z * m(2,2) + p.w * m(2,3), - p.x * m(3,0) + p.y * m(3,1) + p.z * m(3,2) + p.w * m(3,3)); - } - - inline Matrix mul(Matrix::Arg a, Matrix::Arg b) - { - // @@ Is this the right order? mul(a, b) = b * a - Matrix m = a; - m.apply(b); - return m; - } - - inline void Matrix::operator+=(const Matrix & m) - { - for(int i = 0; i < 16; i++) { - m_data[i] += m.m_data[i]; - } - } - - inline void Matrix::operator-=(const Matrix & m) - { - for(int i = 0; i < 16; i++) { - m_data[i] -= m.m_data[i]; - } - } - - inline Matrix operator+(const Matrix & a, const Matrix & b) - { - Matrix m = a; - m += b; - return m; - } - - inline Matrix operator-(const Matrix & a, const Matrix & b) - { - Matrix m = a; - m -= b; - return m; - } - - -} // nv namespace - - -#if 0 // old code. -/** @name Special matrices. */ -//@{ -/** Generate a translation matrix. */ -void TranslationMatrix(const Vec3 & v) { - data[0] = 1; data[1] = 0; data[2] = 0; data[3] = 0; - data[4] = 0; data[5] = 1; data[6] = 0; data[7] = 0; - data[8] = 0; data[9] = 0; data[10] = 1; data[11] = 0; - data[12] = v.x; data[13] = v.y; data[14] = v.z; data[15] = 1; -} - -/** Rotate theta degrees around v. */ -void RotationMatrix( float theta, float v0, float v1, float v2 ) { - float cost = cos(theta); - float sint = sin(theta); - - if( 1 == v0 && 0 == v1 && 0 == v2 ) { - data[0] = 1.0f; data[1] = 0.0f; data[2] = 0.0f; data[3] = 0.0f; - data[4] = 0.0f; data[5] = cost; data[6] = -sint;data[7] = 0.0f; - data[8] = 0.0f; data[9] = sint; data[10] = cost;data[11] = 0.0f; - data[12] = 0.0f;data[13] = 0.0f;data[14] = 0.0f;data[15] = 1.0f; - } - else if( 0 == v0 && 1 == v1 && 0 == v2 ) { - data[0] = cost; data[1] = 0.0f; data[2] = sint; data[3] = 0.0f; - data[4] = 0.0f; data[5] = 1.0f; data[6] = 0.0f; data[7] = 0.0f; - data[8] = -sint;data[9] = 0.0f;data[10] = cost; data[11] = 0.0f; - data[12] = 0.0f;data[13] = 0.0f;data[14] = 0.0f;data[15] = 1.0f; - } - else if( 0 == v0 && 0 == v1 && 1 == v2 ) { - data[0] = cost; data[1] = -sint;data[2] = 0.0f; data[3] = 0.0f; - data[4] = sint; data[5] = cost; data[6] = 0.0f; data[7] = 0.0f; - data[8] = 0.0f; data[9] = 0.0f; data[10] = 1.0f;data[11] = 0.0f; - data[12] = 0.0f;data[13] = 0.0f;data[14] = 0.0f;data[15] = 1.0f; - } - else { - //we need scale a,b,c to unit length. - float a2, b2, c2; - a2 = v0 * v0; - b2 = v1 * v1; - c2 = v2 * v2; - - float iscale = 1.0f / sqrtf(a2 + b2 + c2); - v0 *= iscale; - v1 *= iscale; - v2 *= iscale; - - float abm, acm, bcm; - float mcos, asin, bsin, csin; - mcos = 1.0f - cost; - abm = v0 * v1 * mcos; - acm = v0 * v2 * mcos; - bcm = v1 * v2 * mcos; - asin = v0 * sint; - bsin = v1 * sint; - csin = v2 * sint; - data[0] = a2 * mcos + cost; - data[1] = abm - csin; - data[2] = acm + bsin; - data[3] = abm + csin; - data[4] = 0.0f; - data[5] = b2 * mcos + cost; - data[6] = bcm - asin; - data[7] = acm - bsin; - data[8] = 0.0f; - data[9] = bcm + asin; - data[10] = c2 * mcos + cost; - data[11] = 0.0f; - data[12] = 0.0f; - data[13] = 0.0f; - data[14] = 0.0f; - data[15] = 1.0f; - } -} - -/* -void SkewMatrix(float angle, const Vec3 & v1, const Vec3 & v2) { -v1.Normalize(); -v2.Normalize(); - -Vec3 v3; -v3.Cross(v1, v2); -v3.Normalize(); - -// Get skew factor. -float costheta = Vec3DotProduct(v1, v2); -float sintheta = Real.Sqrt(1 - costheta * costheta); -float skew = tan(Trig.DegreesToRadians(angle) + acos(sintheta)) * sintheta - costheta; - -// Build orthonormal matrix. -v1 = FXVector3.Cross(v3, v2); -v1.Normalize(); - -Matrix R = Matrix::Identity; -R[0, 0] = v3.X; // Not sure this is in the correct order... -R[1, 0] = v3.Y; -R[2, 0] = v3.Z; -R[0, 1] = v1.X; -R[1, 1] = v1.Y; -R[2, 1] = v1.Z; -R[0, 2] = v2.X; -R[1, 2] = v2.Y; -R[2, 2] = v2.Z; - -// Build skew matrix. -Matrix S = Matrix::Identity; -S[2, 1] = -skew; - -// Return skew transform. -return R * S * R.Transpose; // Not sure this is in the correct order... -} -*/ - -/** -* Generate rotation matrix for the euler angles. This is the same as computing -* 3 rotation matrices and multiplying them together in our custom order. -* -* @todo Have to recompute this code for our new convention. -**/ -void RotationMatrix( float yaw, float pitch, float roll ) { - float sy = sin(yaw+ToRadian(90)); - float cy = cos(yaw+ToRadian(90)); - float sp = sin(pitch-ToRadian(90)); - float cp = cos(pitch-ToRadian(90)); - float sr = sin(roll); - float cr = cos(roll); - - data[0] = cr*cy + sr*sp*sy; - data[1] = cp*sy; - data[2] = -sr*cy + cr*sp*sy; - data[3] = 0; - - data[4] = -cr*sy + sr*sp*cy; - data[5] = cp*cy; - data[6] = sr*sy + cr*sp*cy; - data[7] = 0; - - data[8] = sr*cp; - data[9] = -sp; - data[10] = cr*cp; - data[11] = 0; - - data[12] = 0; - data[13] = 0; - data[14] = 0; - data[15] = 1; -} - -/** Create a frustum matrix with the far plane at the infinity. */ -void Frustum( float xmin, float xmax, float ymin, float ymax, float zNear, float zFar ) { - float one_deltax, one_deltay, one_deltaz, doubleznear; - - doubleznear = 2.0f * zNear; - one_deltax = 1.0f / (xmax - xmin); - one_deltay = 1.0f / (ymax - ymin); - one_deltaz = 1.0f / (zFar - zNear); - - data[0] = (float)(doubleznear * one_deltax); - data[1] = 0.0f; - data[2] = 0.0f; - data[3] = 0.0f; - data[4] = 0.0f; - data[5] = (float)(doubleznear * one_deltay); - data[6] = 0.f; - data[7] = 0.f; - data[8] = (float)((xmax + xmin) * one_deltax); - data[9] = (float)((ymax + ymin) * one_deltay); - data[10] = (float)(-(zFar + zNear) * one_deltaz); - data[11] = -1.f; - data[12] = 0.f; - data[13] = 0.f; - data[14] = (float)(-(zFar * doubleznear) * one_deltaz); - data[15] = 0.f; -} - -/** Create a frustum matrix with the far plane at the infinity. */ -void FrustumInf( float xmin, float xmax, float ymin, float ymax, float zNear ) { - float one_deltax, one_deltay, doubleznear, nudge; - - doubleznear = 2.0f * zNear; - one_deltax = 1.0f / (xmax - xmin); - one_deltay = 1.0f / (ymax - ymin); - nudge = 1.0; // 0.999; - - data[0] = doubleznear * one_deltax; - data[1] = 0.0f; - data[2] = 0.0f; - data[3] = 0.0f; - - data[4] = 0.0f; - data[5] = doubleznear * one_deltay; - data[6] = 0.f; - data[7] = 0.f; - - data[8] = (xmax + xmin) * one_deltax; - data[9] = (ymax + ymin) * one_deltay; - data[10] = -1.0f * nudge; - data[11] = -1.0f; - - data[12] = 0.f; - data[13] = 0.f; - data[14] = -doubleznear * nudge; - data[15] = 0.f; -} - -/** Create an inverse frustum matrix with the far plane at the infinity. */ -void FrustumInfInv( float left, float right, float bottom, float top, float zNear ) { - // this matrix is wrong (not tested floatly) I think it should be transposed. - data[0] = (right - left) / (2 * zNear); - data[1] = 0; - data[2] = 0; - data[3] = (right + left) / (2 * zNear); - data[4] = 0; - data[5] = (top - bottom) / (2 * zNear); - data[6] = 0; - data[7] = (top + bottom) / (2 * zNear); - data[8] = 0; - data[9] = 0; - data[10] = 0; - data[11] = -1; - data[12] = 0; - data[13] = 0; - data[14] = -1 / (2 * zNear); - data[15] = 1 / (2 * zNear); -} - -/** Create an homogeneous projection matrix. */ -void Perspective( float fov, float aspect, float zNear, float zFar ) { - float xmin, xmax, ymin, ymax; - - xmax = zNear * tan( fov/2 ); - xmin = -xmax; - - ymax = xmax / aspect; - ymin = -ymax; - - Frustum(xmin, xmax, ymin, ymax, zNear, zFar); -} - -/** Create a projection matrix with the far plane at the infinity. */ -void PerspectiveInf( float fov, float aspect, float zNear ) { - float x = zNear * tan( fov/2 ); - float y = x / aspect; - FrustumInf( -x, x, -y, y, zNear ); -} - -/** Create an inverse projection matrix with far plane at the infinity. */ -void PerspectiveInfInv( float fov, float aspect, float zNear ) { - float x = zNear * tan( fov/2 ); - float y = x / aspect; - FrustumInfInv( -x, x, -y, y, zNear ); -} - -/** Build bone matrix from quatertion and offset. */ -void BoneMatrix(const Quat & q, const Vec3 & offset) { - float x2, y2, z2, xx, xy, xz, yy, yz, zz, wx, wy, wz; - - // calculate coefficients - x2 = q.x + q.x; - y2 = q.y + q.y; - z2 = q.z + q.z; - - xx = q.x * x2; xy = q.x * y2; xz = q.x * z2; - yy = q.y * y2; yz = q.y * z2; zz = q.z * z2; - wx = q.w * x2; wy = q.w * y2; wz = q.w * z2; - - data[0] = 1.0f - (yy + zz); - data[1] = xy - wz; - data[2] = xz + wy; - data[3] = 0.0f; - - data[4] = xy + wz; - data[5] = 1.0f - (xx + zz); - data[6] = yz - wx; - data[7] = 0.0f; - - data[8] = xz - wy; - data[9] = yz + wx; - data[10] = 1.0f - (xx + yy); - data[11] = 0.0f; - - data[12] = offset.x; - data[13] = offset.y; - data[14] = offset.z; - data[15] = 1.0f; -} - -//@} - - -/** @name Transformations: */ -//@{ - -/** Apply a general scale. */ -void Scale( float x, float y, float z ) { - data[0] *= x; data[4] *= y; data[8] *= z; - data[1] *= x; data[5] *= y; data[9] *= z; - data[2] *= x; data[6] *= y; data[10] *= z; - data[3] *= x; data[7] *= y; data[11] *= z; -} - -/** Apply a rotation of theta degrees around the axis v*/ -void Rotate( float theta, const Vec3 & v ) { - Matrix b; - b.RotationMatrix( theta, v[0], v[1], v[2] ); - Multiply4x3( b ); -} - -/** Apply a rotation of theta degrees around the axis v*/ -void Rotate( float theta, float v0, float v1, float v2 ) { - Matrix b; - b.RotationMatrix( theta, v0, v1, v2 ); - Multiply4x3( b ); -} - -/** -* Translate the matrix by t. This is the same as multiplying by a -* translation matrix with the given offset. -* this = T * this -*/ -void Translate( const Vec3 &t ) { - data[12] = data[0] * t.x + data[4] * t.y + data[8] * t.z + data[12]; - data[13] = data[1] * t.x + data[5] * t.y + data[9] * t.z + data[13]; - data[14] = data[2] * t.x + data[6] * t.y + data[10] * t.z + data[14]; - data[15] = data[3] * t.x + data[7] * t.y + data[11] * t.z + data[15]; -} - -/** -* Translate the matrix by x, y, z. This is the same as multiplying by a -* translation matrix with the given offsets. -*/ -void Translate( float x, float y, float z ) { - data[12] = data[0] * x + data[4] * y + data[8] * z + data[12]; - data[13] = data[1] * x + data[5] * y + data[9] * z + data[13]; - data[14] = data[2] * x + data[6] * y + data[10] * z + data[14]; - data[15] = data[3] * x + data[7] * y + data[11] * z + data[15]; -} - -/** Compute the transposed matrix. */ -void Transpose() { - piSwap(data[1], data[4]); - piSwap(data[2], data[8]); - piSwap(data[6], data[9]); - piSwap(data[3], data[12]); - piSwap(data[7], data[13]); - piSwap(data[11], data[14]); -} - -/** Compute the inverse of a rigid-body/isometry/orthonormal matrix. */ -void IsometryInverse() { - // transposed 3x3 upper left matrix - piSwap(data[1], data[4]); - piSwap(data[2], data[8]); - piSwap(data[6], data[9]); - - // translate by the negative offsets - Vec3 v(-data[12], -data[13], -data[14]); - data[12] = data[13] = data[14] = 0; - Translate(v); -} - -/** Compute the inverse of the affine portion of this matrix. */ -void AffineInverse() { - data[12] = data[13] = data[14] = 0; - Transpose(); -} -//@} - -/** @name Matrix operations: */ -//@{ - -/** Return the determinant of this matrix. */ -float Determinant() const { - return data[0] * data[5] * data[10] * data[15] + - data[1] * data[6] * data[11] * data[12] + - data[2] * data[7] * data[ 8] * data[13] + - data[3] * data[4] * data[ 9] * data[14] - - data[3] * data[6] * data[ 9] * data[12] - - data[2] * data[5] * data[ 8] * data[15] - - data[1] * data[4] * data[11] * data[14] - - data[0] * data[7] * data[10] * data[12]; -} - - -/** Standard matrix product: this *= B. */ -void Multiply4x4( const Matrix & restrict B ) { - Multiply4x4(*this, B); -} - -/** Standard matrix product: this = A * B. this != B*/ -void Multiply4x4( const Matrix & A, const Matrix & restrict B ) { - piDebugCheck(this != &B); - - for(int i = 0; i < 4; i++) { - const float ai0 = A(i,0), ai1 = A(i,1), ai2 = A(i,2), ai3 = A(i,3); - GetElem(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0); - GetElem(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1); - GetElem(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2); - GetElem(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3); - } - - /* Unrolled but does not allow this == A - data[0] = A.data[0] * B.data[0] + A.data[4] * B.data[1] + A.data[8] * B.data[2] + A.data[12] * B.data[3]; - data[1] = A.data[1] * B.data[0] + A.data[5] * B.data[1] + A.data[9] * B.data[2] + A.data[13] * B.data[3]; - data[2] = A.data[2] * B.data[0] + A.data[6] * B.data[1] + A.data[10] * B.data[2] + A.data[14] * B.data[3]; - data[3] = A.data[3] * B.data[0] + A.data[7] * B.data[1] + A.data[11] * B.data[2] + A.data[15] * B.data[3]; - data[4] = A.data[0] * B.data[4] + A.data[4] * B.data[5] + A.data[8] * B.data[6] + A.data[12] * B.data[7]; - data[5] = A.data[1] * B.data[4] + A.data[5] * B.data[5] + A.data[9] * B.data[6] + A.data[13] * B.data[7]; - data[6] = A.data[2] * B.data[4] + A.data[6] * B.data[5] + A.data[10] * B.data[6] + A.data[14] * B.data[7]; - data[7] = A.data[3] * B.data[4] + A.data[7] * B.data[5] + A.data[11] * B.data[6] + A.data[15] * B.data[7]; - data[8] = A.data[0] * B.data[8] + A.data[4] * B.data[9] + A.data[8] * B.data[10] + A.data[12] * B.data[11]; - data[9] = A.data[1] * B.data[8] + A.data[5] * B.data[9] + A.data[9] * B.data[10] + A.data[13] * B.data[11]; - data[10]= A.data[2] * B.data[8] + A.data[6] * B.data[9] + A.data[10] * B.data[10] + A.data[14] * B.data[11]; - data[11]= A.data[3] * B.data[8] + A.data[7] * B.data[9] + A.data[11] * B.data[10] + A.data[15] * B.data[11]; - data[12]= A.data[0] * B.data[12] + A.data[4] * B.data[13] + A.data[8] * B.data[14] + A.data[12] * B.data[15]; - data[13]= A.data[1] * B.data[12] + A.data[5] * B.data[13] + A.data[9] * B.data[14] + A.data[13] * B.data[15]; - data[14]= A.data[2] * B.data[12] + A.data[6] * B.data[13] + A.data[10] * B.data[14] + A.data[14] * B.data[15]; - data[15]= A.data[3] * B.data[12] + A.data[7] * B.data[13] + A.data[11] * B.data[14] + A.data[15] * B.data[15]; - */ -} - -/** Standard matrix product: this *= B. */ -void Multiply4x3( const Matrix & restrict B ) { - Multiply4x3(*this, B); -} - -/** Standard product of matrices, where the last row is [0 0 0 1]. */ -void Multiply4x3( const Matrix & A, const Matrix & restrict B ) { - piDebugCheck(this != &B); - - for(int i = 0; i < 3; i++) { - const float ai0 = A(i,0), ai1 = A(i,1), ai2 = A(i,2), ai3 = A(i,3); - GetElem(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0); - GetElem(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1); - GetElem(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2); - GetElem(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3); - } - data[3] = 0.0f; data[7] = 0.0f; data[11] = 0.0f; data[15] = 1.0f; - - /* Unrolled but does not allow this == A - data[0] = a.data[0] * b.data[0] + a.data[4] * b.data[1] + a.data[8] * b.data[2] + a.data[12] * b.data[3]; - data[1] = a.data[1] * b.data[0] + a.data[5] * b.data[1] + a.data[9] * b.data[2] + a.data[13] * b.data[3]; - data[2] = a.data[2] * b.data[0] + a.data[6] * b.data[1] + a.data[10] * b.data[2] + a.data[14] * b.data[3]; - data[3] = 0.0f; - data[4] = a.data[0] * b.data[4] + a.data[4] * b.data[5] + a.data[8] * b.data[6] + a.data[12] * b.data[7]; - data[5] = a.data[1] * b.data[4] + a.data[5] * b.data[5] + a.data[9] * b.data[6] + a.data[13] * b.data[7]; - data[6] = a.data[2] * b.data[4] + a.data[6] * b.data[5] + a.data[10] * b.data[6] + a.data[14] * b.data[7]; - data[7] = 0.0f; - data[8] = a.data[0] * b.data[8] + a.data[4] * b.data[9] + a.data[8] * b.data[10] + a.data[12] * b.data[11]; - data[9] = a.data[1] * b.data[8] + a.data[5] * b.data[9] + a.data[9] * b.data[10] + a.data[13] * b.data[11]; - data[10]= a.data[2] * b.data[8] + a.data[6] * b.data[9] + a.data[10] * b.data[10] + a.data[14] * b.data[11]; - data[11]= 0.0f; - data[12]= a.data[0] * b.data[12] + a.data[4] * b.data[13] + a.data[8] * b.data[14] + a.data[12] * b.data[15]; - data[13]= a.data[1] * b.data[12] + a.data[5] * b.data[13] + a.data[9] * b.data[14] + a.data[13] * b.data[15]; - data[14]= a.data[2] * b.data[12] + a.data[6] * b.data[13] + a.data[10] * b.data[14] + a.data[14] * b.data[15]; - data[15]= 1.0f; - */ -} -//@} - - -/** @name Vector operations: */ -//@{ - -/** Transform 3d vector (w=0). */ -void TransformVec3(const Vec3 & restrict orig, Vec3 * restrict dest) const { - piDebugCheck(&orig != dest); - dest->x = orig.x * data[0] + orig.y * data[4] + orig.z * data[8]; - dest->y = orig.x * data[1] + orig.y * data[5] + orig.z * data[9]; - dest->z = orig.x * data[2] + orig.y * data[6] + orig.z * data[10]; -} -/** Transform 3d vector by the transpose (w=0). */ -void TransformVec3T(const Vec3 & restrict orig, Vec3 * restrict dest) const { - piDebugCheck(&orig != dest); - dest->x = orig.x * data[0] + orig.y * data[1] + orig.z * data[2]; - dest->y = orig.x * data[4] + orig.y * data[5] + orig.z * data[6]; - dest->z = orig.x * data[8] + orig.y * data[9] + orig.z * data[10]; -} - -/** Transform a 3d homogeneous vector, where the fourth coordinate is assumed to be 1. */ -void TransformPoint(const Vec3 & restrict orig, Vec3 * restrict dest) const { - piDebugCheck(&orig != dest); - dest->x = orig.x * data[0] + orig.y * data[4] + orig.z * data[8] + data[12]; - dest->y = orig.x * data[1] + orig.y * data[5] + orig.z * data[9] + data[13]; - dest->z = orig.x * data[2] + orig.y * data[6] + orig.z * data[10] + data[14]; -} - -/** Transform a point, normalize it, and return w. */ -float TransformPointAndNormalize(const Vec3 & restrict orig, Vec3 * restrict dest) const { - piDebugCheck(&orig != dest); - float w; - dest->x = orig.x * data[0] + orig.y * data[4] + orig.z * data[8] + data[12]; - dest->y = orig.x * data[1] + orig.y * data[5] + orig.z * data[9] + data[13]; - dest->z = orig.x * data[2] + orig.y * data[6] + orig.z * data[10] + data[14]; - w = 1 / (orig.x * data[3] + orig.y * data[7] + orig.z * data[11] + data[15]); - *dest *= w; - return w; -} - -/** Transform a point and return w. */ -float TransformPointReturnW(const Vec3 & restrict orig, Vec3 * restrict dest) const { - piDebugCheck(&orig != dest); - dest->x = orig.x * data[0] + orig.y * data[4] + orig.z * data[8] + data[12]; - dest->y = orig.x * data[1] + orig.y * data[5] + orig.z * data[9] + data[13]; - dest->z = orig.x * data[2] + orig.y * data[6] + orig.z * data[10] + data[14]; - return orig.x * data[3] + orig.y * data[7] + orig.z * data[11] + data[15]; -} - -/** Transform a normalized 3d point by a 4d matrix and return the resulting 4d vector. */ -void TransformVec4(const Vec3 & orig, Vec4 * dest) const { - dest->x = orig.x * data[0] + orig.y * data[4] + orig.z * data[8] + data[12]; - dest->y = orig.x * data[1] + orig.y * data[5] + orig.z * data[9] + data[13]; - dest->z = orig.x * data[2] + orig.y * data[6] + orig.z * data[10] + data[14]; - dest->w = orig.x * data[3] + orig.y * data[7] + orig.z * data[11] + data[15]; -} -//@} - -/** @name Matrix analysis. */ -//@{ - -/** Get the ZYZ euler angles from the matrix. Assumes the matrix is orthonormal. */ -void GetEulerAnglesZYZ(float * s, float * t, float * r) const { - if( GetElem(2,2) < 1.0f ) { - if( GetElem(2,2) > -1.0f ) { - // cs*ct*cr-ss*sr -ss*ct*cr-cs*sr st*cr - // cs*ct*sr+ss*cr -ss*ct*sr+cs*cr st*sr - // -cs*st ss*st ct - *s = atan2(GetElem(1,2), -GetElem(0,2)); - *t = acos(GetElem(2,2)); - *r = atan2(GetElem(2,1), GetElem(2,0)); - } - else { - // -c(s-r) s(s-r) 0 - // s(s-r) c(s-r) 0 - // 0 0 -1 - *s = atan2(GetElem(0, 1), -GetElem(0, 0)); // = s-r - *t = PI; - *r = 0; - } - } - else { - // c(s+r) -s(s+r) 0 - // s(s+r) c(s+r) 0 - // 0 0 1 - *s = atan2(GetElem(0, 1), GetElem(0, 0)); // = s+r - *t = 0; - *r = 0; - } -} - -//@} - -MATHLIB_API friend PiStream & operator<< ( PiStream & s, Matrix & m ); - -/** Print to debug output. */ -void Print() const { - piDebug( "[ %5.2f %5.2f %5.2f %5.2f ]\n", data[0], data[4], data[8], data[12] ); - piDebug( "[ %5.2f %5.2f %5.2f %5.2f ]\n", data[1], data[5], data[9], data[13] ); - piDebug( "[ %5.2f %5.2f %5.2f %5.2f ]\n", data[2], data[6], data[10], data[14] ); - piDebug( "[ %5.2f %5.2f %5.2f %5.2f ]\n", data[3], data[7], data[11], data[15] ); -} - - -public: - - float data[16]; - -}; -#endif - - -#endif // NV_MATH_MATRIX_INL diff --git a/thirdparty/thekla_atlas/nvmath/Morton.h b/thirdparty/thekla_atlas/nvmath/Morton.h deleted file mode 100644 index 10e0d8152a..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Morton.h +++ /dev/null @@ -1,83 +0,0 @@ - -// Code from ryg: -// http://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ - - -// "Insert" a 0 bit after each of the 16 low bits of x -inline uint32 part1By1(uint32 x) -{ - x &= 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210 - x = (x ^ (x << 8)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210 - x = (x ^ (x << 4)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210 - x = (x ^ (x << 2)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10 - x = (x ^ (x << 1)) & 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 - return x; -} - -// "Insert" two 0 bits after each of the 10 low bits of x -inline uint32 part1By2(uint32 x) -{ - x &= 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210 - x = (x ^ (x << 16)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210 - x = (x ^ (x << 8)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210 - x = (x ^ (x << 4)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10 - x = (x ^ (x << 2)) & 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0 - return x; -} - -inline uint32 encodeMorton2(uint32 x, uint32 y) -{ - return (part1By1(y) << 1) + part1By1(x); -} - -inline uint32 encodeMorton3(uint32 x, uint32 y, uint32 z) -{ - return (part1By2(z) << 2) + (part1By2(y) << 1) + part1By2(x); -} - -// Inverse of part1By1 - "delete" all odd-indexed bits -inline uint32 compact1By1(uint32 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 -inline uint32 compact1By2(uint32 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; -} - -inline uint32 decodeMorton2X(uint32 code) -{ - return compact1By1(code >> 0); -} - -inline uint32 decodeMorton2Y(uint32 code) -{ - return compact1By1(code >> 1); -} - -inline uint32 decodeMorton3X(uint32 code) -{ - return compact1By2(code >> 0); -} - -inline uint32 decodeMorton3Y(uint32 code) -{ - return compact1By2(code >> 1); -} - -inline uint32 decodeMorton3Z(uint32 code) -{ - return compact1By2(code >> 2); -}
\ No newline at end of file diff --git a/thirdparty/thekla_atlas/nvmath/Plane.cpp b/thirdparty/thekla_atlas/nvmath/Plane.cpp deleted file mode 100644 index 8b54f829ad..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Plane.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "Plane.h" -#include "Plane.inl" -#include "Matrix.inl" - -namespace nv -{ - Plane transformPlane(const Matrix & m, const Plane & p) - { - Vector3 newVec = transformVector(m, p.vector()); - - Vector3 ptInPlane = p.offset() * p.vector(); - ptInPlane = transformPoint(m, ptInPlane); - - return Plane(newVec, ptInPlane); - } - - Vector3 planeIntersection(const Plane & a, const Plane & b, const Plane & c) - { - return dot(a.vector(), cross(b.vector(), c.vector())) * ( - a.offset() * cross(b.vector(), c.vector()) + - c.offset() * cross(a.vector(), b.vector()) + - b.offset() * cross(c.vector(), a.vector())); - } - -} // nv namespace diff --git a/thirdparty/thekla_atlas/nvmath/Plane.h b/thirdparty/thekla_atlas/nvmath/Plane.h deleted file mode 100644 index dc468b28e2..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Plane.h +++ /dev/null @@ -1,42 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MATH_PLANE_H -#define NV_MATH_PLANE_H - -#include "nvmath.h" -#include "Vector.h" - -namespace nv -{ - class Matrix; - - class NVMATH_CLASS Plane - { - public: - Plane(); - Plane(float x, float y, float z, float w); - Plane(const Vector4 & v); - Plane(const Vector3 & v, float d); - Plane(const Vector3 & normal, const Vector3 & point); - Plane(const Vector3 & v0, const Vector3 & v1, const Vector3 & v2); - - const Plane & operator=(const Plane & v); - - Vector3 vector() const; - float offset() const; - Vector3 normal() const; - - void operator*=(float s); - - Vector4 v; - }; - - Plane transformPlane(const Matrix &, const Plane &); - - Vector3 planeIntersection(const Plane & a, const Plane & b, const Plane & c); - - -} // nv namespace - -#endif // NV_MATH_PLANE_H diff --git a/thirdparty/thekla_atlas/nvmath/Plane.inl b/thirdparty/thekla_atlas/nvmath/Plane.inl deleted file mode 100644 index 2277e38cd5..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Plane.inl +++ /dev/null @@ -1,50 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MATH_PLANE_INL -#define NV_MATH_PLANE_INL - -#include "Plane.h" -#include "Vector.inl" - -namespace nv -{ - inline Plane::Plane() {} - inline Plane::Plane(float x, float y, float z, float w) : v(x, y, z, w) {} - inline Plane::Plane(const Vector4 & v) : v(v) {} - inline Plane::Plane(const Vector3 & v, float d) : v(v, d) {} - inline Plane::Plane(const Vector3 & normal, const Vector3 & point) : v(normal, -dot(normal, point)) {} - inline Plane::Plane(const Vector3 & v0, const Vector3 & v1, const Vector3 & v2) { - Vector3 n = cross(v1-v0, v2-v0); - float d = -dot(n, v0); - v = Vector4(n, d); - } - - inline const Plane & Plane::operator=(const Plane & p) { v = p.v; return *this; } - - inline Vector3 Plane::vector() const { return v.xyz(); } - inline float Plane::offset() const { return v.w; } - inline Vector3 Plane::normal() const { return normalize(vector(), 0.0f); } - - // Normalize plane. - inline Plane normalize(const Plane & plane, float epsilon = NV_EPSILON) - { - const float len = length(plane.vector()); - const float inv = isZero(len, epsilon) ? 0 : 1.0f / len; - return Plane(plane.v * inv); - } - - // Get the signed distance from the given point to this plane. - inline float distance(const Plane & plane, const Vector3 & point) - { - return dot(plane.vector(), point) + plane.offset(); - } - - inline void Plane::operator*=(float s) - { - v *= s; - } - -} // nv namespace - -#endif // NV_MATH_PLANE_H diff --git a/thirdparty/thekla_atlas/nvmath/ProximityGrid.cpp b/thirdparty/thekla_atlas/nvmath/ProximityGrid.cpp deleted file mode 100644 index 3553e48f64..0000000000 --- a/thirdparty/thekla_atlas/nvmath/ProximityGrid.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include "ProximityGrid.h" - -#include "Box.inl" -#include "Morton.h" - - -using namespace nv; - -ProximityGrid::ProximityGrid() { -} - -void ProximityGrid::reset() { - cellArray.clear(); -} - -void ProximityGrid::init(const Array<Vector3> & pointArray) { - - // Compute bounding box. - Box box; - box.clearBounds(); - - const uint count = pointArray.count(); - - for (uint i = 0; i < count; i++) { - box.addPointToBounds(pointArray[i]); - } - - init(box, count); - - // Insert all points. - for (uint i = 0; i < count; i++) { - add(pointArray[i], i); - } -} - - -void ProximityGrid::init(const Box & box, uint count) { - reset(); - - // 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 = pow(cellVolume, 1.0f / 3.0f); - } - - nvDebugCheck(cellWidth != 0); - - sx = max(1, ftoi_ceil(diagonal.x / cellWidth)); - sy = max(1, ftoi_ceil(diagonal.y / cellWidth)); - sz = 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? -} - -// Gather all points inside the given sphere. -// Radius is assumed to be small, so we don't bother culling the cells. -void ProximityGrid::gather(const Vector3 & position, float radius, Array<uint> & 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.append(cellArray[idx].indexArray); - } - } - } -} - - -uint32 ProximityGrid::mortonCount() const { - uint64 s = U64(max3(sx, sy, sz)); - s = nextPowerOfTwo(s); - - if (s > 1024) { - return U32(s * s * min3(sx, sy, sz)); - } - - return U32(s * s * s); -} - -int ProximityGrid::mortonIndex(uint32 code) const { - uint32 x, y, z; - - uint s = U32(max3(sx, sy, sz)); - if (s > 1024) { - // Use layered two-dimensional morton order. - s = nextPowerOfTwo(s); - uint layer = code / (s * s); - code = code % (s * s); - - uint layer_count = U32(min3(sx, sy, sz)); - if (sx == layer_count) { - x = layer; - y = decodeMorton2X(code); - z = decodeMorton2Y(code); - } - else if (sy == layer_count) { - x = decodeMorton2Y(code); - y = layer; - z = decodeMorton2X(code); - } - else /*if (sz == layer_count)*/ { - x = decodeMorton2X(code); - y = decodeMorton2Y(code); - z = layer; - } - } - else { - x = decodeMorton3X(code); - y = decodeMorton3Y(code); - z = decodeMorton3Z(code); - } - - if (x >= U32(sx) || y >= U32(sy) || z >= U32(sz)) { - return -1; - } - - return index(x, y, z); -} diff --git a/thirdparty/thekla_atlas/nvmath/ProximityGrid.h b/thirdparty/thekla_atlas/nvmath/ProximityGrid.h deleted file mode 100644 index a21bb3bd68..0000000000 --- a/thirdparty/thekla_atlas/nvmath/ProximityGrid.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once -#ifndef NV_MATH_PROXIMITYGRID_H -#define NV_MATH_PROXIMITYGRID_H - -#include "Vector.h" -#include "ftoi.h" - -#include "nvcore/Array.inl" - - -// A simple, dynamic proximity grid based on Jon's code. -// Instead of storing pointers here I store indices. - -namespace nv { - - class Box; - - struct Cell { - Array<uint> indexArray; - }; - - struct ProximityGrid { - ProximityGrid(); - - void reset(); - void init(const Array<Vector3> & pointArray); - void init(const Box & box, uint count); - - int index_x(float x) const; - int index_y(float y) const; - int index_z(float z) const; - int index(int x, int y, int z) const; - int index(const Vector3 & pos) const; - - uint32 mortonCount() const; - int mortonIndex(uint32 code) const; - - void add(const Vector3 & pos, uint key); - bool remove(const Vector3 & pos, uint key); - - void gather(const Vector3 & pos, float radius, Array<uint> & indices); - - Array<Cell> cellArray; - - Vector3 corner; - Vector3 invCellSize; - int sx, sy, sz; - }; - - // For morton traversal, do: - // for (int code = 0; code < mortonCount(); code++) { - // int idx = mortonIndex(code); - // if (idx < 0) continue; - // } - - - - inline int ProximityGrid::index_x(float x) const { - return clamp(ftoi_floor((x - corner.x) * invCellSize.x), 0, sx-1); - } - - inline int ProximityGrid::index_y(float y) const { - return clamp(ftoi_floor((y - corner.y) * invCellSize.y), 0, sy-1); - } - - inline int ProximityGrid::index_z(float z) const { - return clamp(ftoi_floor((z - corner.z) * invCellSize.z), 0, sz-1); - } - - inline int ProximityGrid::index(int x, int y, int z) const { - nvDebugCheck(x >= 0 && x < sx); - nvDebugCheck(y >= 0 && y < sy); - nvDebugCheck(z >= 0 && z < sz); - int idx = (z * sy + y) * sx + x; - nvDebugCheck(idx >= 0 && uint(idx) < cellArray.count()); - return idx; - } - - inline int ProximityGrid::index(const Vector3 & pos) const { - int x = index_x(pos.x); - int y = index_y(pos.y); - int z = index_z(pos.z); - return index(x, y, z); - } - - - inline void ProximityGrid::add(const Vector3 & pos, uint key) { - uint idx = index(pos); - cellArray[idx].indexArray.append(key); - } - - inline bool ProximityGrid::remove(const Vector3 & pos, uint key) { - uint idx = index(pos); - return cellArray[idx].indexArray.remove(key); - } - -} // nv namespace - -#endif // NV_MATH_PROXIMITYGRID_H diff --git a/thirdparty/thekla_atlas/nvmath/Quaternion.h b/thirdparty/thekla_atlas/nvmath/Quaternion.h deleted file mode 100644 index dc5219e5e4..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Quaternion.h +++ /dev/null @@ -1,213 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#pragma once -#ifndef NV_MATH_QUATERNION_H -#define NV_MATH_QUATERNION_H - -#include "nvmath/nvmath.h" -#include "nvmath/Vector.inl" // @@ Do not include inl files from header files. -#include "nvmath/Matrix.h" - -namespace nv -{ - - class NVMATH_CLASS Quaternion - { - public: - typedef Quaternion const & Arg; - - Quaternion(); - explicit Quaternion(float f); - Quaternion(float x, float y, float z, float w); - Quaternion(Vector4::Arg v); - - const Quaternion & operator=(Quaternion::Arg v); - - Vector4 asVector() const; - - union { - struct { - float x, y, z, w; - }; - float component[4]; - }; - }; - - inline Quaternion::Quaternion() {} - inline Quaternion::Quaternion(float f) : x(f), y(f), z(f), w(f) {} - inline Quaternion::Quaternion(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} - inline Quaternion::Quaternion(Vector4::Arg v) : x(v.x), y(v.y), z(v.z), w(v.w) {} - - // @@ Move all these to Quaternion.inl! - - inline const Quaternion & Quaternion::operator=(Quaternion::Arg v) { - x = v.x; - y = v.y; - z = v.z; - w = v.w; - return *this; - } - - inline Vector4 Quaternion::asVector() const { return Vector4(x, y, z, w); } - - inline Quaternion mul(Quaternion::Arg a, Quaternion::Arg b) - { - return Quaternion( - + a.x*b.w + a.y*b.z - a.z*b.y + a.w*b.x, - - a.x*b.z + a.y*b.w + a.z*b.x + a.w*b.y, - + a.x*b.y - a.y*b.x + a.z*b.w + a.w*b.z, - - a.x*b.x - a.y*b.y - a.z*b.z + a.w*b.w); - } - - inline Quaternion mul(Quaternion::Arg a, Vector3::Arg b) - { - return Quaternion( - + a.y*b.z - a.z*b.y + a.w*b.x, - - a.x*b.z + a.z*b.x + a.w*b.y, - + a.x*b.y - a.y*b.x + a.w*b.z, - - a.x*b.x - a.y*b.y - a.z*b.z ); - } - - inline Quaternion mul(Vector3::Arg a, Quaternion::Arg b) - { - return Quaternion( - + a.x*b.w + a.y*b.z - a.z*b.y, - - a.x*b.z + a.y*b.w + a.z*b.x, - + a.x*b.y - a.y*b.x + a.z*b.w, - - a.x*b.x - a.y*b.y - a.z*b.z); - } - - inline Quaternion operator *(Quaternion::Arg a, Quaternion::Arg b) - { - return mul(a, b); - } - - inline Quaternion operator *(Quaternion::Arg a, Vector3::Arg b) - { - return mul(a, b); - } - - inline Quaternion operator *(Vector3::Arg a, Quaternion::Arg b) - { - return mul(a, b); - } - - - inline Quaternion scale(Quaternion::Arg q, float s) - { - return scale(q.asVector(), s); - } - inline Quaternion operator *(Quaternion::Arg q, float s) - { - return scale(q, s); - } - inline Quaternion operator *(float s, Quaternion::Arg q) - { - return scale(q, s); - } - - inline Quaternion scale(Quaternion::Arg q, Vector4::Arg s) - { - return scale(q.asVector(), s); - } - /*inline Quaternion operator *(Quaternion::Arg q, Vector4::Arg s) - { - return scale(q, s); - } - inline Quaternion operator *(Vector4::Arg s, Quaternion::Arg q) - { - return scale(q, s); - }*/ - - inline Quaternion conjugate(Quaternion::Arg q) - { - return scale(q, Vector4(-1, -1, -1, 1)); - } - - inline float length(Quaternion::Arg q) - { - return length(q.asVector()); - } - - inline bool isNormalized(Quaternion::Arg q, float epsilon = NV_NORMAL_EPSILON) - { - return equal(length(q), 1, epsilon); - } - - inline Quaternion normalize(Quaternion::Arg q, float epsilon = NV_EPSILON) - { - float l = length(q); - nvDebugCheck(!isZero(l, epsilon)); - Quaternion n = scale(q, 1.0f / l); - nvDebugCheck(isNormalized(n)); - return n; - } - - inline Quaternion inverse(Quaternion::Arg q) - { - return conjugate(normalize(q)); - } - - /// Create a rotation quaternion for @a angle alpha around normal vector @a v. - inline Quaternion axisAngle(Vector3::Arg v, float alpha) - { - float s = sinf(alpha * 0.5f); - float c = cosf(alpha * 0.5f); - return Quaternion(Vector4(v * s, c)); - } - - inline Vector3 imag(Quaternion::Arg q) - { - return q.asVector().xyz(); - } - - inline float real(Quaternion::Arg q) - { - return q.w; - } - - - /// Transform vector. - inline Vector3 transform(Quaternion::Arg q, Vector3::Arg v) - { - //Quaternion t = q * v * conjugate(q); - //return imag(t); - - // Faster method by Fabian Giesen and others: - // http://molecularmusings.wordpress.com/2013/05/24/a-faster-quaternion-vector-multiplication/ - // http://mollyrocket.com/forums/viewtopic.php?t=833&sid=3a84e00a70ccb046cfc87ac39881a3d0 - - Vector3 t = 2 * cross(imag(q), v); - return v + q.w * t + cross(imag(q), t); - } - - // @@ Not tested. - // From Insomniac's Mike Day: - // http://www.insomniacgames.com/converting-a-rotation-matrix-to-a-quaternion/ - inline Quaternion fromMatrix(const Matrix & m) { - if (m(2, 2) < 0) { - if (m(0, 0) < m(1,1)) { - float t = 1 - m(0, 0) - m(1, 1) - m(2, 2); - return Quaternion(t, m(0,1)+m(1,0), m(2,0)+m(0,2), m(1,2)-m(2,1)); - } - else { - float t = 1 - m(0, 0) + m(1, 1) - m(2, 2); - return Quaternion(t, m(0,1) + m(1,0), m(1,2) + m(2,1), m(2,0) - m(0,2)); - } - } - else { - if (m(0, 0) < -m(1, 1)) { - float t = 1 - m(0, 0) - m(1, 1) + m(2, 2); - return Quaternion(t, m(2,0) + m(0,2), m(1,2) + m(2,1), m(0,1) - m(1,0)); - } - else { - float t = 1 + m(0, 0) + m(1, 1) + m(2, 2); - return Quaternion(t, m(1,2) - m(2,1), m(2,0) - m(0,2), m(0,1) - m(1,0)); - } - } - } - - -} // nv namespace - -#endif // NV_MATH_QUATERNION_H diff --git a/thirdparty/thekla_atlas/nvmath/Random.cpp b/thirdparty/thekla_atlas/nvmath/Random.cpp deleted file mode 100644 index 1a60e7f5e7..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Random.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include <nvmath/Random.h> -#include <time.h> - -using namespace nv; - -// Statics -const uint16 Rand48::a0 = 0xE66D; -const uint16 Rand48::a1 = 0xDEEC; -const uint16 Rand48::a2 = 0x0005; -const uint16 Rand48::c0 = 0x000B; - - -/// Get a random seed based on the current time. -uint Rand::randomSeed() -{ - return (uint)time(NULL); -} - - -void MTRand::initialize( uint32 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 *s = state; - uint32 *r = state; - int i = 1; - *s++ = seed & 0xffffffffUL; - for( ; i < N; ++i ) - { - *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; - r++; - } -} - - -void MTRand::reload() -{ - // Generate N new values in state - // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) - uint32 *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; -} - diff --git a/thirdparty/thekla_atlas/nvmath/Random.h b/thirdparty/thekla_atlas/nvmath/Random.h deleted file mode 100644 index 223292706a..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Random.h +++ /dev/null @@ -1,376 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MATH_RANDOM_H -#define NV_MATH_RANDOM_H - -#include "nvmath.h" -#include "nvcore/Utils.h" // nextPowerOfTwo - - -namespace nv -{ - - /// Interface of the random number generators. - class Rand - { - public: - - virtual ~Rand() {} - - enum time_e { Time }; - - /// Provide a new seed. - virtual void seed( uint s ) { /* empty */ }; - - /// Get an integer random number. - virtual uint get() = 0; - - /// Get a random number on [0, max] interval. - uint getRange( uint max ) - { - if (max == 0) return 0; - if (max == NV_UINT32_MAX) return get(); - - const uint np2 = nextPowerOfTwo( max+1 ); // @@ This fails if max == NV_UINT32_MAX - const uint mask = np2 - 1; - uint n; - do { n = get() & mask; } while( n > max ); - return n; - } - - /// Random number on [0.0, 1.0] interval. - float getFloat() - { - union - { - uint32 i; - float f; - } pun; - - pun.i = 0x3f800000UL | (get() & 0x007fffffUL); - return pun.f - 1.0f; - } - - float getFloatRange(float min, float max) { - return getFloat() * (max - min) + min; - } - - /* - /// Random number on [0.0, 1.0] interval. - double getReal() - { - return double(get()) * (1.0/4294967295.0); // 2^32-1 - } - - /// Random number on [0.0, 1.0) interval. - double getRealExclusive() - { - return double(get()) * (1.0/4294967296.0); // 2^32 - } - */ - - /// Get the max value of the random number. - uint max() const { return NV_UINT32_MAX; } - - // Get a random seed. - static uint randomSeed(); - - }; - - - /// Very simple random number generator with low storage requirements. - class SimpleRand : public Rand - { - public: - - /// Constructor that uses the current time as the seed. - SimpleRand( time_e ) - { - seed(randomSeed()); - } - - /// Constructor that uses the given seed. - SimpleRand( uint s = 0 ) - { - seed(s); - } - - /// Set the given seed. - virtual void seed( uint s ) - { - current = s; - } - - /// Get a random number. - virtual uint get() - { - return current = current * 1103515245 + 12345; - } - - private: - - uint current; - - }; - - - /// Mersenne twister random number generator. - class MTRand : public Rand - { - public: - - enum { N = 624 }; // length of state vector - enum { M = 397 }; - - /// Constructor that uses the current time as the seed. - MTRand( time_e ) - { - seed(randomSeed()); - } - - /// Constructor that uses the given seed. - MTRand( uint s = 0 ) - { - seed(s); - } - - /// Constructor that uses the given seeds. - NVMATH_API MTRand( const uint * seed_array, uint length ); - - - /// Provide a new seed. - virtual void seed( uint s ) - { - initialize(s); - reload(); - } - - /// Get a random number between 0 - 65536. - virtual uint 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--; - - uint s1; - s1 = *next++; - s1 ^= (s1 >> 11); - s1 ^= (s1 << 7) & 0x9d2c5680U; - s1 ^= (s1 << 15) & 0xefc60000U; - return ( s1 ^ (s1 >> 18) ); - }; - - - private: - - NVMATH_API void initialize( uint32 seed ); - NVMATH_API void reload(); - - uint hiBit( uint u ) const { return u & 0x80000000U; } - uint loBit( uint u ) const { return u & 0x00000001U; } - uint loBits( uint u ) const { return u & 0x7fffffffU; } - uint mixBits( uint u, uint v ) const { return hiBit(u) | loBits(v); } - uint twist( uint m, uint s0, uint s1 ) const { return m ^ (mixBits(s0,s1)>>1) ^ ((~loBit(s1)+1) & 0x9908b0dfU); } - - private: - - uint state[N]; // internal state - uint * next; // next value to get from state - int left; // number of values left before reload needed - - }; - - - - /** George Marsaglia's random number generator. - * Code based on Thatcher Ulrich public domain source code: - * http://cvs.sourceforge.net/viewcvs.py/tu-testbed/tu-testbed/base/tu_random.cpp?rev=1.7&view=auto - * - * PRNG code adapted from the complimentary-multiply-with-carry - * code in the article: George Marsaglia, "Seeds for Random Number - * Generators", Communications of the ACM, May 2003, Vol 46 No 5, - * pp90-93. - * - * The article says: - * - * "Any one of the choices for seed table size and multiplier will - * provide a RNG that has passed extensive tests of randomness, - * particularly those in [3], yet is simple and fast -- - * approximately 30 million random 32-bit integers per second on a - * 850MHz PC. The period is a*b^n, where a is the multiplier, n - * the size of the seed table and b=2^32-1. (a is chosen so that - * b is a primitive root of the prime a*b^n + 1.)" - * - * [3] Marsaglia, G., Zaman, A., and Tsang, W. Toward a universal - * random number generator. _Statistics and Probability Letters - * 8_ (1990), 35-39. - */ - class GMRand : public Rand - { - public: - - enum { SEED_COUNT = 8 }; - - // const uint64 a = 123471786; // for SEED_COUNT=1024 - // const uint64 a = 123554632; // for SEED_COUNT=512 - // const uint64 a = 8001634; // for SEED_COUNT=255 - // const uint64 a = 8007626; // for SEED_COUNT=128 - // const uint64 a = 647535442; // for SEED_COUNT=64 - // const uint64 a = 547416522; // for SEED_COUNT=32 - // const uint64 a = 487198574; // for SEED_COUNT=16 - // const uint64 a = 716514398U; // for SEED_COUNT=8 - enum { a = 716514398U }; - - - GMRand( time_e ) - { - seed(randomSeed()); - } - - GMRand(uint s = 987654321) - { - seed(s); - } - - - /// Provide a new seed. - virtual void seed( uint s ) - { - c = 362436; - i = SEED_COUNT - 1; - - for(int i = 0; i < SEED_COUNT; i++) { - s = s ^ (s << 13); - s = s ^ (s >> 17); - s = s ^ (s << 5); - Q[i] = s; - } - } - - /// Get a random number between 0 - 65536. - virtual uint get() - { - const uint32 r = 0xFFFFFFFE; - - uint64 t; - uint32 x; - - i = (i + 1) & (SEED_COUNT - 1); - t = a * Q[i] + c; - c = uint32(t >> 32); - x = uint32(t + c); - - if( x < c ) { - x++; - c++; - } - - uint32 val = r - x; - Q[i] = val; - return val; - }; - - - private: - - uint32 c; - uint32 i; - uint32 Q[8]; - - }; - - - /** Random number implementation from the GNU Sci. Lib. (GSL). - * Adapted from Nicholas Chapman version: - * - * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough - * This is the Unix rand48() generator. The generator returns the - * upper 32 bits from each term of the sequence, - * - * x_{n+1} = (a x_n + c) mod m - * - * using 48-bit unsigned arithmetic, with a = 0x5DEECE66D , c = 0xB - * and m = 2^48. The seed specifies the upper 32 bits of the initial - * value, x_1, with the lower 16 bits set to 0x330E. - * - * The theoretical value of x_{10001} is 244131582646046. - * - * The period of this generator is ? FIXME (probably around 2^48). - */ - class Rand48 : public Rand - { - public: - - Rand48( time_e ) - { - seed(randomSeed()); - } - - Rand48( uint s = 0x1234ABCD ) - { - seed(s); - } - - - /** Set the given seed. */ - virtual void seed( uint s ) { - vstate.x0 = 0x330E; - vstate.x1 = uint16(s & 0xFFFF); - vstate.x2 = uint16((s >> 16) & 0xFFFF); - } - - /** Get a random number. */ - virtual uint get() { - - advance(); - - uint x1 = vstate.x1; - uint x2 = vstate.x2; - return (x2 << 16) + x1; - } - - - private: - - void advance() - { - /* work with unsigned long ints throughout to get correct integer - promotions of any unsigned short ints */ - const uint32 x0 = vstate.x0; - const uint32 x1 = vstate.x1; - const uint32 x2 = vstate.x2; - - uint32 a; - a = a0 * x0 + c0; - - vstate.x0 = uint16(a & 0xFFFF); - a >>= 16; - - /* although the next line may overflow we only need the top 16 bits - in the following stage, so it does not matter */ - - a += a0 * x1 + a1 * x0; - vstate.x1 = uint16(a & 0xFFFF); - - a >>= 16; - a += a0 * x2 + a1 * x1 + a2 * x0; - vstate.x2 = uint16(a & 0xFFFF); - } - - - private: - NVMATH_API static const uint16 a0, a1, a2, c0; - - struct rand48_state_t { - uint16 x0, x1, x2; - } vstate; - - }; - -} // nv namespace - -#endif // NV_MATH_RANDOM_H diff --git a/thirdparty/thekla_atlas/nvmath/Solver.cpp b/thirdparty/thekla_atlas/nvmath/Solver.cpp deleted file mode 100644 index 191793ee29..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Solver.cpp +++ /dev/null @@ -1,744 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "Solver.h" -#include "Sparse.h" - -#include "nvcore/Array.inl" - -using namespace nv; - -namespace -{ - class Preconditioner - { - public: - // Virtual dtor. - virtual ~Preconditioner() { } - - // Apply preconditioning step. - virtual void apply(const FullVector & x, FullVector & y) const = 0; - }; - - - // Jacobi preconditioner. - class JacobiPreconditioner : public Preconditioner - { - public: - - JacobiPreconditioner(const SparseMatrix & M, bool symmetric) : m_inverseDiagonal(M.width()) - { - nvCheck(M.isSquare()); - - for(uint x = 0; x < M.width(); x++) - { - float elem = M.getCoefficient(x, x); - //nvDebugCheck( 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 - { - nvDebugCheck(x.dimension() == m_inverseDiagonal.dimension()); - nvDebugCheck(y.dimension() == m_inverseDiagonal.dimension()); - - // @@ Wrap vector component-wise product into a separate function. - const uint D = x.dimension(); - for (uint i = 0; i < D; i++) - { - y[i] = m_inverseDiagonal[i] * x[i]; - } - } - - private: - - FullVector m_inverseDiagonal; - - }; - -} // namespace - - -static bool ConjugateGradientSolver(const SparseMatrix & A, const FullVector & b, FullVector & x, float epsilon); -static bool ConjugateGradientSolver(const Preconditioner & preconditioner, const SparseMatrix & A, const FullVector & b, FullVector & x, float epsilon); - - -// Solve the symmetric system: At·A·x = At·b -bool nv::LeastSquaresSolver(const SparseMatrix & A, const FullVector & b, FullVector & x, float epsilon/*1e-5f*/) -{ - nvDebugCheck(A.width() == x.dimension()); - nvDebugCheck(A.height() == b.dimension()); - nvDebugCheck(A.height() >= A.width()); // @@ If height == width we could solve it directly... - - const uint D = A.width(); - - SparseMatrix At(A.height(), A.width()); - transpose(A, At); - - FullVector Atb(D); - //mult(Transposed, A, b, Atb); - mult(At, b, Atb); - - SparseMatrix AtA(D); - //mult(Transposed, A, NoTransposed, A, AtA); - 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 -bool nv::LeastSquaresSolver(const SparseMatrix & A, const FullVector & b, FullVector & x, const uint * lockedParameters, uint lockedCount, float epsilon/*= 1e-5f*/) -{ - nvDebugCheck(A.width() == x.dimension()); - nvDebugCheck(A.height() == b.dimension()); - nvDebugCheck(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 uint D = A.width() - lockedCount; - nvDebugCheck(D > 0); - - // Compute: b - Al * xl - FullVector b_Alxl(b); - - for (uint y = 0; y < A.height(); y++) - { - const uint count = A.getRow(y).count(); - for (uint e = 0; e < count; e++) - { - uint column = A.getRow(y)[e].x; - - bool isFree = true; - for (uint 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. - SparseMatrix Af(D, A.height()); - - for (uint y = 0; y < A.height(); y++) - { - const uint count = A.getRow(y).count(); - for (uint e = 0; e < count; e++) - { - uint column = A.getRow(y)[e].x; - uint ix = column; - - bool isFree = true; - for (uint 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 (uint i = 0, j = 0; i < A.width(); i++) - { - bool isFree = true; - for (uint 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 (uint i = 0, j = 0; i < A.width(); i++) - { - bool isFree = true; - for (uint l = 0; l < lockedCount; l++) - { - isFree &= (lockedParameters[l] != i); - } - - if (isFree) - { - x[i] = xf[j++]; - } - } - - return result; -} - - -bool nv::SymmetricSolver(const SparseMatrix & A, const FullVector & b, FullVector & x, float epsilon/*1e-5f*/) -{ - nvDebugCheck(A.height() == A.width()); - nvDebugCheck(A.height() == b.dimension()); - nvDebugCheck(b.dimension() == x.dimension()); - - JacobiPreconditioner jacobi(A, true); - return ConjugateGradientSolver(jacobi, A, b, x, epsilon); - - //return ConjugateGradientSolver(A, b, x, epsilon); -} - - -/** -* 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 SparseMatrix & A, const FullVector & b, FullVector & x, float epsilon) -{ - nvDebugCheck( A.isSquare() ); - nvDebugCheck( A.width() == b.dimension() ); - nvDebugCheck( 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; - copy(b, r); - sgemv(-1, A, x, 1, r); - - // p = r; - copy(r, p); - - delta_new = 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 / dot( p, q ); - - // x = alfa·p + x - saxpy(alpha, p, x); - - if ((i & 31) == 0) // recompute r after 32 steps - { - // r = b - A·x - copy(b, r); - sgemv(-1, A, x, 1, r); - } - else - { - // r = r - alpha·q - saxpy(-alpha, q, r); - } - - delta_old = delta_new; - delta_new = dot( r, r ); - - beta = delta_new / delta_old; - - // p = beta·p + r - scal(beta, p); - saxpy(1, r, p); - } - - return delta_new <= epsilon*epsilon*delta_0; -} - - -// Conjugate gradient with preconditioner. -/*static*/ bool ConjugateGradientSolver(const Preconditioner & preconditioner, const SparseMatrix & A, const FullVector & b, FullVector & x, float epsilon) -{ - nvDebugCheck( A.isSquare() ); - nvDebugCheck( A.width() == b.dimension() ); - nvDebugCheck( 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 - copy(b, r); - sgemv(-1, A, x, 1, r); - - - // p = M^-1 · r - preconditioner.apply(r, p); - //copy(r, p); - - - delta_new = 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 / dot(p, q); - - // x = alfa·p + x - saxpy(alpha, p, x); - - if ((i & 31) == 0) // recompute r after 32 steps - { - // r = b - A·x - copy(b, r); - sgemv(-1, A, x, 1, r); - } - else - { - // r = r - alfa·q - saxpy(-alpha, q, r); - } - - // s = M^-1 · r - preconditioner.apply(r, s); - //copy(r, s); - - delta_old = delta_new; - delta_new = dot( r, s ); - - beta = delta_new / delta_old; - - // p = s + beta·p - scal(beta, p); - saxpy(1, s, p); - } - - return delta_new <= epsilon*epsilon*delta_0; -} - - -#if 0 // Nonsymmetric solvers - -/** Bi-conjugate gradient method. */ -MATHLIB_API int BiConjugateGradientSolve( const SparseMatrix &A, const DenseVector &b, DenseVector &x, float epsilon ) { - piDebugCheck( A.IsSquare() ); - piDebugCheck( A.Width() == b.Dim() ); - piDebugCheck( A.Width() == x.Dim() ); - - int i = 0; - const int D = A.Width(); - const int i_max = 4 * D; - - float resid; - float rho_1 = 0; - float rho_2 = 0; - float alpha; - float beta; - - DenseVector r(D); - DenseVector rtilde(D); - DenseVector p(D); - DenseVector ptilde(D); - DenseVector q(D); - DenseVector qtilde(D); - DenseVector tmp(D); // temporal vector. - - // r = b - A·x; - A.Product( x, tmp ); - r.Sub( b, tmp ); - - // rtilde = r - rtilde.Set( r ); - - // p = r; - p.Set( r ); - - // ptilde = rtilde - ptilde.Set( rtilde ); - - - - float normb = b.Norm(); - if( normb == 0.0 ) normb = 1; - - // test convergence - resid = r.Norm() / normb; - if( resid < epsilon ) { - // method converges? - return 0; - } - - - while( i < i_max ) { - - i++; - - rho_1 = DenseVectorDotProduct( r, rtilde ); - - if( rho_1 == 0 ) { - // method fails. - return -i; - } - - if (i == 1) { - p.Set( r ); - ptilde.Set( rtilde ); - } - else { - beta = rho_1 / rho_2; - - // p = r + beta * p; - p.Mad( r, p, beta ); - - // ptilde = ztilde + beta * ptilde; - ptilde.Mad( rtilde, ptilde, beta ); - } - - // q = A * p; - A.Product( p, q ); - - // qtilde = A^t * ptilde; - A.TransProduct( ptilde, qtilde ); - - alpha = rho_1 / DenseVectorDotProduct( ptilde, q ); - - // x += alpha * p; - x.Mad( x, p, alpha ); - - // r -= alpha * q; - r.Mad( r, q, -alpha ); - - // rtilde -= alpha * qtilde; - rtilde.Mad( rtilde, qtilde, -alpha ); - - rho_2 = rho_1; - - // test convergence - resid = r.Norm() / normb; - if( resid < epsilon ) { - // method converges - return i; - } - } - - return i; -} - - -/** Bi-conjugate gradient stabilized method. */ -int BiCGSTABSolve( const SparseMatrix &A, const DenseVector &b, DenseVector &x, float epsilon ) { - piDebugCheck( A.IsSquare() ); - piDebugCheck( A.Width() == b.Dim() ); - piDebugCheck( A.Width() == x.Dim() ); - - int i = 0; - const int D = A.Width(); - const int i_max = 2 * D; - - - float resid; - float rho_1 = 0; - float rho_2 = 0; - float alpha = 0; - float beta = 0; - float omega = 0; - - DenseVector p(D); - DenseVector phat(D); - DenseVector s(D); - DenseVector shat(D); - DenseVector t(D); - DenseVector v(D); - - DenseVector r(D); - DenseVector rtilde(D); - - DenseVector tmp(D); - - // r = b - A·x; - A.Product( x, tmp ); - r.Sub( b, tmp ); - - // rtilde = r - rtilde.Set( r ); - - - float normb = b.Norm(); - if( normb == 0.0 ) normb = 1; - - // test convergence - resid = r.Norm() / normb; - if( resid < epsilon ) { - // method converges? - return 0; - } - - - while( i<i_max ) { - - i++; - - rho_1 = DenseVectorDotProduct( rtilde, r ); - if( rho_1 == 0 ) { - // method fails - return -i; - } - - - if( i == 1 ) { - p.Set( r ); - } - else { - beta = (rho_1 / rho_2) * (alpha / omega); - - // p = r + beta * (p - omega * v); - p.Mad( p, v, -omega ); - p.Mad( r, p, beta ); - } - - //phat = M.solve(p); - phat.Set( p ); - //Precond( &phat, p ); - - //v = A * phat; - A.Product( phat, v ); - - alpha = rho_1 / DenseVectorDotProduct( rtilde, v ); - - // s = r - alpha * v; - s.Mad( r, v, -alpha ); - - - resid = s.Norm() / normb; - if( resid < epsilon ) { - // x += alpha * phat; - x.Mad( x, phat, alpha ); - return i; - } - - //shat = M.solve(s); - shat.Set( s ); - //Precond( &shat, s ); - - //t = A * shat; - A.Product( shat, t ); - - omega = DenseVectorDotProduct( t, s ) / DenseVectorDotProduct( t, t ); - - // x += alpha * phat + omega * shat; - x.Mad( x, shat, omega ); - x.Mad( x, phat, alpha ); - - //r = s - omega * t; - r.Mad( s, t, -omega ); - - rho_2 = rho_1; - - resid = r.Norm() / normb; - if( resid < epsilon ) { - return i; - } - - if( omega == 0 ) { - return -i; // ??? - } - } - - return i; -} - - -/** Bi-conjugate gradient stabilized method. */ -int BiCGSTABPrecondSolve( const SparseMatrix &A, const DenseVector &b, DenseVector &x, const IPreconditioner &M, float epsilon ) { - piDebugCheck( A.IsSquare() ); - piDebugCheck( A.Width() == b.Dim() ); - piDebugCheck( A.Width() == x.Dim() ); - - int i = 0; - const int D = A.Width(); - const int i_max = D; - // const int i_max = 1000; - - - float resid; - float rho_1 = 0; - float rho_2 = 0; - float alpha = 0; - float beta = 0; - float omega = 0; - - DenseVector p(D); - DenseVector phat(D); - DenseVector s(D); - DenseVector shat(D); - DenseVector t(D); - DenseVector v(D); - - DenseVector r(D); - DenseVector rtilde(D); - - DenseVector tmp(D); - - // r = b - A·x; - A.Product( x, tmp ); - r.Sub( b, tmp ); - - // rtilde = r - rtilde.Set( r ); - - - float normb = b.Norm(); - if( normb == 0.0 ) normb = 1; - - // test convergence - resid = r.Norm() / normb; - if( resid < epsilon ) { - // method converges? - return 0; - } - - - while( i<i_max ) { - - i++; - - rho_1 = DenseVectorDotProduct( rtilde, r ); - if( rho_1 == 0 ) { - // method fails - return -i; - } - - - if( i == 1 ) { - p.Set( r ); - } - else { - beta = (rho_1 / rho_2) * (alpha / omega); - - // p = r + beta * (p - omega * v); - p.Mad( p, v, -omega ); - p.Mad( r, p, beta ); - } - - //phat = M.solve(p); - //phat.Set( p ); - M.Precond( &phat, p ); - - //v = A * phat; - A.Product( phat, v ); - - alpha = rho_1 / DenseVectorDotProduct( rtilde, v ); - - // s = r - alpha * v; - s.Mad( r, v, -alpha ); - - - resid = s.Norm() / normb; - - //printf( "--- Iteration %d: residual = %f\n", i, resid ); - - if( resid < epsilon ) { - // x += alpha * phat; - x.Mad( x, phat, alpha ); - return i; - } - - //shat = M.solve(s); - //shat.Set( s ); - M.Precond( &shat, s ); - - //t = A * shat; - A.Product( shat, t ); - - omega = DenseVectorDotProduct( t, s ) / DenseVectorDotProduct( t, t ); - - // x += alpha * phat + omega * shat; - x.Mad( x, shat, omega ); - x.Mad( x, phat, alpha ); - - //r = s - omega * t; - r.Mad( s, t, -omega ); - - rho_2 = rho_1; - - resid = r.Norm() / normb; - if( resid < epsilon ) { - return i; - } - - if( omega == 0 ) { - return -i; // ??? - } - } - - return i; -} - -#endif diff --git a/thirdparty/thekla_atlas/nvmath/Solver.h b/thirdparty/thekla_atlas/nvmath/Solver.h deleted file mode 100644 index 2bbf92736a..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Solver.h +++ /dev/null @@ -1,24 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MATH_SOLVER_H -#define NV_MATH_SOLVER_H - -#include "nvmath.h" - -namespace nv -{ - class SparseMatrix; - class FullVector; - - - // Linear solvers. - NVMATH_API bool LeastSquaresSolver(const SparseMatrix & A, const FullVector & b, FullVector & x, float epsilon = 1e-5f); - NVMATH_API bool LeastSquaresSolver(const SparseMatrix & A, const FullVector & b, FullVector & x, const uint * lockedParameters, uint lockedCount, float epsilon = 1e-5f); - NVMATH_API bool SymmetricSolver(const SparseMatrix & A, const FullVector & b, FullVector & x, float epsilon = 1e-5f); - //NVMATH_API void NonSymmetricSolver(const SparseMatrix & A, const FullVector & b, FullVector & x, float epsilon = 1e-5f); - -} // nv namespace - - -#endif // NV_MATH_SOLVER_H diff --git a/thirdparty/thekla_atlas/nvmath/Sparse.cpp b/thirdparty/thekla_atlas/nvmath/Sparse.cpp deleted file mode 100644 index 421e7ee022..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Sparse.cpp +++ /dev/null @@ -1,889 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castanyo@yahoo.es> - -#include "Sparse.h" -#include "KahanSum.h" - -#include "nvcore/Array.inl" - -#define USE_KAHAN_SUM 0 - - -using namespace nv; - - -FullVector::FullVector(uint dim) -{ - m_array.resize(dim); -} - -FullVector::FullVector(const FullVector & v) : m_array(v.m_array) -{ -} - -const FullVector & FullVector::operator=(const FullVector & v) -{ - nvCheck(dimension() == v.dimension()); - - m_array = v.m_array; - - return *this; -} - - -void FullVector::fill(float f) -{ - const uint dim = dimension(); - for (uint i = 0; i < dim; i++) - { - m_array[i] = f; - } -} - -void FullVector::operator+= (const FullVector & v) -{ - nvDebugCheck(dimension() == v.dimension()); - - const uint dim = dimension(); - for (uint i = 0; i < dim; i++) - { - m_array[i] += v.m_array[i]; - } -} - -void FullVector::operator-= (const FullVector & v) -{ - nvDebugCheck(dimension() == v.dimension()); - - const uint dim = dimension(); - for (uint i = 0; i < dim; i++) - { - m_array[i] -= v.m_array[i]; - } -} - -void FullVector::operator*= (const FullVector & v) -{ - nvDebugCheck(dimension() == v.dimension()); - - const uint dim = dimension(); - for (uint i = 0; i < dim; i++) - { - m_array[i] *= v.m_array[i]; - } -} - -void FullVector::operator+= (float f) -{ - const uint dim = dimension(); - for (uint i = 0; i < dim; i++) - { - m_array[i] += f; - } -} - -void FullVector::operator-= (float f) -{ - const uint dim = dimension(); - for (uint i = 0; i < dim; i++) - { - m_array[i] -= f; - } -} - -void FullVector::operator*= (float f) -{ - const uint dim = dimension(); - for (uint i = 0; i < dim; i++) - { - m_array[i] *= f; - } -} - - -void nv::saxpy(float a, const FullVector & x, FullVector & y) -{ - nvDebugCheck(x.dimension() == y.dimension()); - - const uint dim = x.dimension(); - for (uint i = 0; i < dim; i++) - { - y[i] += a * x[i]; - } -} - -void nv::copy(const FullVector & x, FullVector & y) -{ - nvDebugCheck(x.dimension() == y.dimension()); - - const uint dim = x.dimension(); - for (uint i = 0; i < dim; i++) - { - y[i] = x[i]; - } -} - -void nv::scal(float a, FullVector & x) -{ - const uint dim = x.dimension(); - for (uint i = 0; i < dim; i++) - { - x[i] *= a; - } -} - -float nv::dot(const FullVector & x, const FullVector & y) -{ - nvDebugCheck(x.dimension() == y.dimension()); - - const uint dim = x.dimension(); - -#if USE_KAHAN_SUM - KahanSum kahan; - for (uint i = 0; i < dim; i++) - { - kahan.add(x[i] * y[i]); - } - return kahan.sum(); -#else - float sum = 0; - for (uint i = 0; i < dim; i++) - { - sum += x[i] * y[i]; - } - return sum; -#endif -} - - -FullMatrix::FullMatrix(uint d) : m_width(d), m_height(d) -{ - m_array.resize(d*d, 0.0f); -} - -FullMatrix::FullMatrix(uint w, uint h) : m_width(w), m_height(h) -{ - m_array.resize(w*h, 0.0f); -} - -FullMatrix::FullMatrix(const FullMatrix & m) : m_width(m.m_width), m_height(m.m_height) -{ - m_array = m.m_array; -} - -const FullMatrix & FullMatrix::operator=(const FullMatrix & m) -{ - nvCheck(width() == m.width()); - nvCheck(height() == m.height()); - - m_array = m.m_array; - - return *this; -} - - -float FullMatrix::getCoefficient(uint x, uint y) const -{ - nvDebugCheck( x < width() ); - nvDebugCheck( y < height() ); - - return m_array[y * width() + x]; -} - -void FullMatrix::setCoefficient(uint x, uint y, float f) -{ - nvDebugCheck( x < width() ); - nvDebugCheck( y < height() ); - - m_array[y * width() + x] = f; -} - -void FullMatrix::addCoefficient(uint x, uint y, float f) -{ - nvDebugCheck( x < width() ); - nvDebugCheck( y < height() ); - - m_array[y * width() + x] += f; -} - -void FullMatrix::mulCoefficient(uint x, uint y, float f) -{ - nvDebugCheck( x < width() ); - nvDebugCheck( y < height() ); - - m_array[y * width() + x] *= f; -} - -float FullMatrix::dotRow(uint y, const FullVector & v) const -{ - nvDebugCheck( v.dimension() == width() ); - nvDebugCheck( y < height() ); - - float sum = 0; - - const uint count = v.dimension(); - for (uint i = 0; i < count; i++) - { - sum += m_array[y * count + i] * v[i]; - } - - return sum; -} - -void FullMatrix::madRow(uint y, float alpha, FullVector & v) const -{ - nvDebugCheck( v.dimension() == width() ); - nvDebugCheck( y < height() ); - - const uint count = v.dimension(); - for (uint i = 0; i < count; i++) - { - v[i] += m_array[y * count + i]; - } -} - - -// y = M * x -void nv::mult(const FullMatrix & M, const FullVector & x, FullVector & y) -{ - mult(NoTransposed, M, x, y); -} - -void nv::mult(Transpose TM, const FullMatrix & M, const FullVector & x, FullVector & y) -{ - const uint w = M.width(); - const uint h = M.height(); - - if (TM == Transposed) - { - nvDebugCheck( h == x.dimension() ); - nvDebugCheck( w == y.dimension() ); - - y.fill(0.0f); - - for (uint i = 0; i < h; i++) - { - M.madRow(i, x[i], y); - } - } - else - { - nvDebugCheck( w == x.dimension() ); - nvDebugCheck( h == y.dimension() ); - - for (uint i = 0; i < h; i++) - { - y[i] = M.dotRow(i, x); - } - } -} - -// y = alpha*A*x + beta*y -void nv::sgemv(float alpha, const FullMatrix & A, const FullVector & x, float beta, FullVector & y) -{ - sgemv(alpha, NoTransposed, A, x, beta, y); -} - -void nv::sgemv(float alpha, Transpose TA, const FullMatrix & A, const FullVector & x, float beta, FullVector & y) -{ - const uint w = A.width(); - const uint h = A.height(); - - if (TA == Transposed) - { - nvDebugCheck( h == x.dimension() ); - nvDebugCheck( w == y.dimension() ); - - for (uint i = 0; i < h; i++) - { - A.madRow(i, alpha * x[i], y); - } - } - else - { - nvDebugCheck( w == x.dimension() ); - nvDebugCheck( h == y.dimension() ); - - for (uint i = 0; i < h; i++) - { - y[i] = alpha * A.dotRow(i, x) + beta * y[i]; - } - } -} - - -// Multiply a row of A by a column of B. -static float dot(uint j, Transpose TA, const FullMatrix & A, uint i, Transpose TB, const FullMatrix & B) -{ - const uint w = (TA == NoTransposed) ? A.width() : A.height(); - nvDebugCheck(w == ((TB == NoTransposed) ? B.height() : A.width())); - - float sum = 0.0f; - - for (uint k = 0; k < w; k++) - { - const float a = (TA == NoTransposed) ? A.getCoefficient(k, j) : A.getCoefficient(j, k); // @@ Move branches out of the loop? - const float b = (TB == NoTransposed) ? B.getCoefficient(i, k) : A.getCoefficient(k, i); - sum += a * b; - } - - return sum; -} - - -// C = A * B -void nv::mult(const FullMatrix & A, const FullMatrix & B, FullMatrix & C) -{ - mult(NoTransposed, A, NoTransposed, B, C); -} - -void nv::mult(Transpose TA, const FullMatrix & A, Transpose TB, const FullMatrix & B, FullMatrix & C) -{ - sgemm(1.0f, TA, A, TB, B, 0.0f, C); -} - -// C = alpha*A*B + beta*C -void nv::sgemm(float alpha, const FullMatrix & A, const FullMatrix & B, float beta, FullMatrix & C) -{ - sgemm(alpha, NoTransposed, A, NoTransposed, B, beta, C); -} - -void nv::sgemm(float alpha, Transpose TA, const FullMatrix & A, Transpose TB, const FullMatrix & B, float beta, FullMatrix & C) -{ - const uint w = C.width(); - const uint h = C.height(); - - uint aw = (TA == NoTransposed) ? A.width() : A.height(); - uint ah = (TA == NoTransposed) ? A.height() : A.width(); - uint bw = (TB == NoTransposed) ? B.width() : B.height(); - uint bh = (TB == NoTransposed) ? B.height() : B.width(); - - nvDebugCheck(aw == bh); - nvDebugCheck(bw == ah); - nvDebugCheck(w == bw); - nvDebugCheck(h == ah); - - for (uint y = 0; y < h; y++) - { - for (uint x = 0; x < w; x++) - { - float c = alpha * ::dot(x, TA, A, y, TB, B) + beta * C.getCoefficient(x, y); - C.setCoefficient(x, y, c); - } - } -} - - - - - -/// Ctor. Init the size of the sparse matrix. -SparseMatrix::SparseMatrix(uint d) : m_width(d) -{ - m_array.resize(d); -} - -/// Ctor. Init the size of the sparse matrix. -SparseMatrix::SparseMatrix(uint w, uint h) : m_width(w) -{ - m_array.resize(h); -} - -SparseMatrix::SparseMatrix(const SparseMatrix & m) : m_width(m.m_width) -{ - m_array = m.m_array; -} - -const SparseMatrix & SparseMatrix::operator=(const SparseMatrix & m) -{ - nvCheck(width() == m.width()); - nvCheck(height() == m.height()); - - m_array = m.m_array; - - return *this; -} - - -// x is column, y is row -float SparseMatrix::getCoefficient(uint x, uint y) const -{ - nvDebugCheck( x < width() ); - nvDebugCheck( y < height() ); - - const uint count = m_array[y].count(); - for (uint i = 0; i < count; i++) - { - if (m_array[y][i].x == x) return m_array[y][i].v; - } - - return 0.0f; -} - -void SparseMatrix::setCoefficient(uint x, uint y, float f) -{ - nvDebugCheck( x < width() ); - nvDebugCheck( y < height() ); - - const uint count = m_array[y].count(); - for (uint 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].append( c ); - } -} - -void SparseMatrix::addCoefficient(uint x, uint y, float f) -{ - nvDebugCheck( x < width() ); - nvDebugCheck( y < height() ); - - if (f != 0.0f) - { - const uint count = m_array[y].count(); - for (uint i = 0; i < count; i++) - { - if (m_array[y][i].x == x) - { - m_array[y][i].v += f; - return; - } - } - - Coefficient c = { x, f }; - m_array[y].append( c ); - } -} - -void SparseMatrix::mulCoefficient(uint x, uint y, float f) -{ - nvDebugCheck( x < width() ); - nvDebugCheck( y < height() ); - - const uint count = m_array[y].count(); - for (uint 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].append( c ); - } -} - - -float SparseMatrix::sumRow(uint y) const -{ - nvDebugCheck( y < height() ); - - const uint count = m_array[y].count(); - -#if USE_KAHAN_SUM - KahanSum kahan; - for (uint i = 0; i < count; i++) - { - kahan.add(m_array[y][i].v); - } - return kahan.sum(); -#else - float sum = 0; - for (uint i = 0; i < count; i++) - { - sum += m_array[y][i].v; - } - return sum; -#endif -} - -float SparseMatrix::dotRow(uint y, const FullVector & v) const -{ - nvDebugCheck( y < height() ); - - const uint count = m_array[y].count(); - -#if USE_KAHAN_SUM - KahanSum kahan; - for (uint i = 0; i < count; i++) - { - kahan.add(m_array[y][i].v * v[m_array[y][i].x]); - } - return kahan.sum(); -#else - float sum = 0; - for (uint i = 0; i < count; i++) - { - sum += m_array[y][i].v * v[m_array[y][i].x]; - } - return sum; -#endif -} - -void SparseMatrix::madRow(uint y, float alpha, FullVector & v) const -{ - nvDebugCheck(y < height()); - - const uint count = m_array[y].count(); - for (uint i = 0; i < count; i++) - { - v[m_array[y][i].x] += alpha * m_array[y][i].v; - } -} - - -void SparseMatrix::clearRow(uint y) -{ - nvDebugCheck( y < height() ); - - m_array[y].clear(); -} - -void SparseMatrix::scaleRow(uint y, float f) -{ - nvDebugCheck( y < height() ); - - const uint count = m_array[y].count(); - for (uint i = 0; i < count; i++) - { - m_array[y][i].v *= f; - } -} - -void SparseMatrix::normalizeRow(uint y) -{ - nvDebugCheck( y < height() ); - - float norm = 0.0f; - - const uint count = m_array[y].count(); - for (uint i = 0; i < count; i++) - { - float f = m_array[y][i].v; - norm += f * f; - } - - scaleRow(y, 1.0f / sqrtf(norm)); -} - - -void SparseMatrix::clearColumn(uint x) -{ - nvDebugCheck(x < width()); - - for (uint y = 0; y < height(); y++) - { - const uint count = m_array[y].count(); - for (uint e = 0; e < count; e++) - { - if (m_array[y][e].x == x) - { - m_array[y][e].v = 0.0f; - break; - } - } - } -} - -void SparseMatrix::scaleColumn(uint x, float f) -{ - nvDebugCheck(x < width()); - - for (uint y = 0; y < height(); y++) - { - const uint count = m_array[y].count(); - for (uint e = 0; e < count; e++) - { - if (m_array[y][e].x == x) - { - m_array[y][e].v *= f; - break; - } - } - } -} - -const Array<SparseMatrix::Coefficient> & SparseMatrix::getRow(uint y) const -{ - return m_array[y]; -} - - -bool SparseMatrix::isSymmetric() const -{ - for (uint y = 0; y < height(); y++) - { - const uint count = m_array[y].count(); - for (uint e = 0; e < count; e++) - { - const uint x = m_array[y][e].x; - if (x > y) { - float v = m_array[y][e].v; - - if (!equal(getCoefficient(y, x), v)) { // @@ epsilon - return false; - } - } - } - } - - return true; -} - - -// y = M * x -void nv::mult(const SparseMatrix & M, const FullVector & x, FullVector & y) -{ - mult(NoTransposed, M, x, y); -} - -void nv::mult(Transpose TM, const SparseMatrix & M, const FullVector & x, FullVector & y) -{ - const uint w = M.width(); - const uint h = M.height(); - - if (TM == Transposed) - { - nvDebugCheck( h == x.dimension() ); - nvDebugCheck( w == y.dimension() ); - - y.fill(0.0f); - - for (uint i = 0; i < h; i++) - { - M.madRow(i, x[i], y); - } - } - else - { - nvDebugCheck( w == x.dimension() ); - nvDebugCheck( h == y.dimension() ); - - for (uint i = 0; i < h; i++) - { - y[i] = M.dotRow(i, x); - } - } -} - -// y = alpha*A*x + beta*y -void nv::sgemv(float alpha, const SparseMatrix & A, const FullVector & x, float beta, FullVector & y) -{ - sgemv(alpha, NoTransposed, A, x, beta, y); -} - -void nv::sgemv(float alpha, Transpose TA, const SparseMatrix & A, const FullVector & x, float beta, FullVector & y) -{ - const uint w = A.width(); - const uint h = A.height(); - - if (TA == Transposed) - { - nvDebugCheck( h == x.dimension() ); - nvDebugCheck( w == y.dimension() ); - - for (uint i = 0; i < h; i++) - { - A.madRow(i, alpha * x[i], y); - } - } - else - { - nvDebugCheck( w == x.dimension() ); - nvDebugCheck( h == y.dimension() ); - - for (uint i = 0; i < h; i++) - { - y[i] = alpha * A.dotRow(i, x) + beta * y[i]; - } - } -} - - -// dot y-row of A by x-column of B -static float dotRowColumn(int y, const SparseMatrix & A, int x, const SparseMatrix & B) -{ - const Array<SparseMatrix::Coefficient> & row = A.getRow(y); - - const uint count = row.count(); - -#if USE_KAHAN_SUM - KahanSum kahan; - for (uint i = 0; i < count; i++) - { - const SparseMatrix::Coefficient & c = row[i]; - kahan.add(c.v * B.getCoefficient(x, c.x)); - } - return kahan.sum(); -#else - float sum = 0.0f; - for (uint i = 0; i < count; i++) - { - const SparseMatrix::Coefficient & c = row[i]; - sum += c.v * B.getCoefficient(x, c.x); - } - return sum; -#endif -} - -// dot y-row of A by x-row of B -static float dotRowRow(int y, const SparseMatrix & A, int x, const SparseMatrix & B) -{ - const Array<SparseMatrix::Coefficient> & row = A.getRow(y); - - const uint count = row.count(); - -#if USE_KAHAN_SUM - KahanSum kahan; - for (uint i = 0; i < count; i++) - { - const SparseMatrix::Coefficient & c = row[i]; - kahan.add(c.v * B.getCoefficient(c.x, x)); - } - return kahan.sum(); -#else - float sum = 0.0f; - for (uint i = 0; i < count; i++) - { - const SparseMatrix::Coefficient & c = row[i]; - sum += c.v * B.getCoefficient(c.x, x); - } - return sum; -#endif -} - -// dot y-column of A by x-column of B -static float dotColumnColumn(int y, const SparseMatrix & A, int x, const SparseMatrix & B) -{ - nvDebugCheck(A.height() == B.height()); - - const uint h = A.height(); - -#if USE_KAHAN_SUM - KahanSum kahan; - for (uint i = 0; i < h; i++) - { - kahan.add(A.getCoefficient(y, i) * B.getCoefficient(x, i)); - } - return kahan.sum(); -#else - float sum = 0.0f; - for (uint i = 0; i < h; i++) - { - sum += A.getCoefficient(y, i) * B.getCoefficient(x, i); - } - return sum; -#endif -} - - -void nv::transpose(const SparseMatrix & A, SparseMatrix & B) -{ - nvDebugCheck(A.width() == B.height()); - nvDebugCheck(B.width() == A.height()); - - const uint w = A.width(); - for (uint x = 0; x < w; x++) - { - B.clearRow(x); - } - - const uint h = A.height(); - for (uint y = 0; y < h; y++) - { - const Array<SparseMatrix::Coefficient> & row = A.getRow(y); - - const uint count = row.count(); - for (uint i = 0; i < count; i++) - { - const SparseMatrix::Coefficient & c = row[i]; - nvDebugCheck(c.x < w); - - B.setCoefficient(y, c.x, c.v); - } - } -} - -// C = A * B -void nv::mult(const SparseMatrix & A, const SparseMatrix & B, SparseMatrix & C) -{ - mult(NoTransposed, A, NoTransposed, B, C); -} - -void nv::mult(Transpose TA, const SparseMatrix & A, Transpose TB, const SparseMatrix & B, SparseMatrix & C) -{ - sgemm(1.0f, TA, A, TB, B, 0.0f, C); -} - -// C = alpha*A*B + beta*C -void nv::sgemm(float alpha, const SparseMatrix & A, const SparseMatrix & B, float beta, SparseMatrix & C) -{ - sgemm(alpha, NoTransposed, A, NoTransposed, B, beta, C); -} - -void nv::sgemm(float alpha, Transpose TA, const SparseMatrix & A, Transpose TB, const SparseMatrix & B, float beta, SparseMatrix & C) -{ - const uint w = C.width(); - const uint h = C.height(); - - uint aw = (TA == NoTransposed) ? A.width() : A.height(); - uint ah = (TA == NoTransposed) ? A.height() : A.width(); - uint bw = (TB == NoTransposed) ? B.width() : B.height(); - uint bh = (TB == NoTransposed) ? B.height() : B.width(); - - nvDebugCheck(aw == bh); - nvDebugCheck(bw == ah); - nvDebugCheck(w == bw); - nvDebugCheck(h == ah); - - - for (uint y = 0; y < h; y++) - { - for (uint 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); - } - } -} - -// C = At * A -void nv::sqm(const SparseMatrix & A, SparseMatrix & C) -{ - // This is quite expensive... - mult(Transposed, A, NoTransposed, A, C); -} diff --git a/thirdparty/thekla_atlas/nvmath/Sparse.h b/thirdparty/thekla_atlas/nvmath/Sparse.h deleted file mode 100644 index 6b03ed51f3..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Sparse.h +++ /dev/null @@ -1,204 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MATH_SPARSE_H -#define NV_MATH_SPARSE_H - -#include "nvmath.h" -#include "nvcore/Array.h" - - -// Full and sparse vector and matrix classes. BLAS subset. - -namespace nv -{ - class FullVector; - class FullMatrix; - class SparseMatrix; - - - /// Fixed size vector class. - class FullVector - { - public: - - FullVector(uint dim); - FullVector(const FullVector & v); - - const FullVector & operator=(const FullVector & v); - - uint dimension() const { return m_array.count(); } - - const float & operator[]( uint index ) const { return m_array[index]; } - float & operator[] ( uint index ) { return m_array[index]; } - - void fill(float f); - - void operator+= (const FullVector & v); - void operator-= (const FullVector & v); - void operator*= (const FullVector & v); - - void operator+= (float f); - void operator-= (float f); - void operator*= (float f); - - - private: - - Array<float> m_array; - - }; - - // Pseudo-BLAS interface. - NVMATH_API void saxpy(float a, const FullVector & x, FullVector & y); // y = a * x + y - NVMATH_API void copy(const FullVector & x, FullVector & y); - NVMATH_API void scal(float a, FullVector & x); - NVMATH_API float dot(const FullVector & x, const FullVector & y); - - - enum Transpose - { - NoTransposed = 0, - Transposed = 1 - }; - - /// Full matrix class. - class FullMatrix - { - public: - - FullMatrix(uint d); - FullMatrix(uint w, uint h); - FullMatrix(const FullMatrix & m); - - const FullMatrix & operator=(const FullMatrix & m); - - uint width() const { return m_width; } - uint height() const { return m_height; } - bool isSquare() const { return m_width == m_height; } - - float getCoefficient(uint x, uint y) const; - - void setCoefficient(uint x, uint y, float f); - void addCoefficient(uint x, uint y, float f); - void mulCoefficient(uint x, uint y, float f); - - float dotRow(uint y, const FullVector & v) const; - void madRow(uint y, float alpha, FullVector & v) const; - - protected: - - bool isValid() const { - return m_array.size() == (m_width * m_height); - } - - private: - - const uint m_width; - const uint m_height; - Array<float> m_array; - - }; - - NVMATH_API void mult(const FullMatrix & M, const FullVector & x, FullVector & y); - NVMATH_API void mult(Transpose TM, const FullMatrix & M, const FullVector & x, FullVector & y); - - // y = alpha*A*x + beta*y - NVMATH_API void sgemv(float alpha, const FullMatrix & A, const FullVector & x, float beta, FullVector & y); - NVMATH_API void sgemv(float alpha, Transpose TA, const FullMatrix & A, const FullVector & x, float beta, FullVector & y); - - NVMATH_API void mult(const FullMatrix & A, const FullMatrix & B, FullMatrix & C); - NVMATH_API void mult(Transpose TA, const FullMatrix & A, Transpose TB, const FullMatrix & B, FullMatrix & C); - - // C = alpha*A*B + beta*C - NVMATH_API void sgemm(float alpha, const FullMatrix & A, const FullMatrix & B, float beta, FullMatrix & C); - NVMATH_API void sgemm(float alpha, Transpose TA, const FullMatrix & A, Transpose TB, const FullMatrix & B, float beta, FullMatrix & C); - - - /** - * 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 SparseMatrix - { - friend class FullMatrix; - public: - - // An element of the sparse array. - struct Coefficient { - uint x; // column - float v; // value - }; - - - public: - - SparseMatrix(uint d); - SparseMatrix(uint w, uint h); - SparseMatrix(const SparseMatrix & m); - - const SparseMatrix & operator=(const SparseMatrix & m); - - - uint width() const { return m_width; } - uint height() const { return m_array.count(); } - bool isSquare() const { return width() == height(); } - - float getCoefficient(uint x, uint y) const; // x is column, y is row - - void setCoefficient(uint x, uint y, float f); - void addCoefficient(uint x, uint y, float f); - void mulCoefficient(uint x, uint y, float f); - - float sumRow(uint y) const; - float dotRow(uint y, const FullVector & v) const; - void madRow(uint y, float alpha, FullVector & v) const; - - void clearRow(uint y); - void scaleRow(uint y, float f); - void normalizeRow(uint y); - - void clearColumn(uint x); - void scaleColumn(uint x, float f); - - const Array<Coefficient> & getRow(uint y) const; - - bool isSymmetric() const; - - private: - - /// Number of columns. - const uint m_width; - - /// Array of matrix elements. - Array< Array<Coefficient> > m_array; - - }; - - NVMATH_API void transpose(const SparseMatrix & A, SparseMatrix & B); - - NVMATH_API void mult(const SparseMatrix & M, const FullVector & x, FullVector & y); - NVMATH_API void mult(Transpose TM, const SparseMatrix & M, const FullVector & x, FullVector & y); - - // y = alpha*A*x + beta*y - NVMATH_API void sgemv(float alpha, const SparseMatrix & A, const FullVector & x, float beta, FullVector & y); - NVMATH_API void sgemv(float alpha, Transpose TA, const SparseMatrix & A, const FullVector & x, float beta, FullVector & y); - - NVMATH_API void mult(const SparseMatrix & A, const SparseMatrix & B, SparseMatrix & C); - NVMATH_API void mult(Transpose TA, const SparseMatrix & A, Transpose TB, const SparseMatrix & B, SparseMatrix & C); - - // C = alpha*A*B + beta*C - NVMATH_API void sgemm(float alpha, const SparseMatrix & A, const SparseMatrix & B, float beta, SparseMatrix & C); - NVMATH_API void sgemm(float alpha, Transpose TA, const SparseMatrix & A, Transpose TB, const SparseMatrix & B, float beta, SparseMatrix & C); - - // C = At * A - NVMATH_API void sqm(const SparseMatrix & A, SparseMatrix & C); - -} // nv namespace - - -#endif // NV_MATH_SPARSE_H diff --git a/thirdparty/thekla_atlas/nvmath/Sphere.cpp b/thirdparty/thekla_atlas/nvmath/Sphere.cpp deleted file mode 100644 index e0c1ad652c..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Sphere.cpp +++ /dev/null @@ -1,431 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "Sphere.h" -#include "Vector.inl" -#include "Box.inl" - -#include <float.h> // FLT_MAX - -using namespace nv; - -const float radiusEpsilon = 1e-4f; - -Sphere::Sphere(Vector3::Arg p0, Vector3::Arg p1) -{ - if (p0 == p1) *this = Sphere(p0); - else { - center = (p0 + p1) * 0.5f; - radius = length(p0 - center) + radiusEpsilon; - - float d0 = length(p0 - center); - float d1 = length(p1 - center); - nvDebugCheck(equal(d0, radius - radiusEpsilon)); - nvDebugCheck(equal(d1, radius - radiusEpsilon)); - } -} - -Sphere::Sphere(Vector3::Arg p0, Vector3::Arg p1, Vector3::Arg p2) -{ - if (p0 == p1 || p0 == p2) *this = Sphere(p1, p2); - else if (p1 == p2) *this = Sphere(p0, p2); - else { - Vector3 a = p1 - p0; - Vector3 b = p2 - p0; - Vector3 c = cross(a, b); - - float denominator = 2.0f * lengthSquared(c); - - if (!isZero(denominator)) { - Vector3 d = (lengthSquared(b) * cross(c, a) + lengthSquared(a) * cross(b, c)) / denominator; - - center = p0 + d; - radius = length(d) + radiusEpsilon; - - float d0 = length(p0 - center); - float d1 = length(p1 - center); - float d2 = length(p2 - center); - nvDebugCheck(equal(d0, radius - radiusEpsilon)); - nvDebugCheck(equal(d1, radius - radiusEpsilon)); - nvDebugCheck(equal(d2, radius - radiusEpsilon)); - } - else { - // @@ This is a specialization of the code below, but really, the only thing we need to do here is to find the two most distant points. - // Compute all possible spheres, invalidate those that do not contain the four points, keep the smallest. - Sphere s0(p1, p2); - float d0 = distanceSquared(s0, p0); - if (d0 > 0) s0.radius = NV_FLOAT_MAX; - - Sphere s1(p0, p2); - float d1 = distanceSquared(s1, p1); - if (d1 > 0) s1.radius = NV_FLOAT_MAX; - - Sphere s2(p0, p1); - float d2 = distanceSquared(s2, p2); - if (d2 > 0) s1.radius = NV_FLOAT_MAX; - - if (s0.radius < s1.radius && s0.radius < s2.radius) { - center = s0.center; - radius = s0.radius; - } - else if (s1.radius < s2.radius) { - center = s1.center; - radius = s1.radius; - } - else { - center = s2.center; - radius = s2.radius; - } - } - } -} - -Sphere::Sphere(Vector3::Arg p0, Vector3::Arg p1, Vector3::Arg p2, Vector3::Arg p3) -{ - if (p0 == p1 || p0 == p2 || p0 == p3) *this = Sphere(p1, p2, p3); - else if (p1 == p2 || p1 == p3) *this = Sphere(p0, p2, p3); - else if (p2 == p3) *this = Sphere(p0, p1, p2); - else { - // @@ This only works if the points are not coplanar! - Vector3 a = p1 - p0; - Vector3 b = p2 - p0; - Vector3 c = p3 - p0; - - float denominator = 2.0f * dot(c, cross(a, b)); // triple product. - - if (!isZero(denominator)) { - Vector3 d = (lengthSquared(c) * cross(a, b) + lengthSquared(b) * cross(c, a) + lengthSquared(a) * cross(b, c)) / denominator; - - center = p0 + d; - radius = length(d) + radiusEpsilon; - - float d0 = length(p0 - center); - float d1 = length(p1 - center); - float d2 = length(p2 - center); - float d3 = length(p3 - center); - nvDebugCheck(equal(d0, radius - radiusEpsilon)); - nvDebugCheck(equal(d1, radius - radiusEpsilon)); - nvDebugCheck(equal(d2, radius - radiusEpsilon)); - nvDebugCheck(equal(d3, radius - radiusEpsilon)); - } - else { - // Compute all possible spheres, invalidate those that do not contain the four points, keep the smallest. - Sphere s0(p1, p2, p3); - float d0 = distanceSquared(s0, p0); - if (d0 > 0) s0.radius = NV_FLOAT_MAX; - - Sphere s1(p0, p2, p3); - float d1 = distanceSquared(s1, p1); - if (d1 > 0) s1.radius = NV_FLOAT_MAX; - - Sphere s2(p0, p1, p3); - float d2 = distanceSquared(s2, p2); - if (d2 > 0) s2.radius = NV_FLOAT_MAX; - - Sphere s3(p0, p1, p2); - float d3 = distanceSquared(s3, p3); - if (d3 > 0) s2.radius = NV_FLOAT_MAX; - - if (s0.radius < s1.radius && s0.radius < s2.radius && s0.radius < s3.radius) { - center = s0.center; - radius = s0.radius; - } - else if (s1.radius < s2.radius && s1.radius < s3.radius) { - center = s1.center; - radius = s1.radius; - } - else if (s1.radius < s3.radius) { - center = s2.center; - radius = s2.radius; - } - else { - center = s3.center; - radius = s3.radius; - } - } - } -} - - -float nv::distanceSquared(const Sphere & sphere, const Vector3 & point) -{ - return lengthSquared(sphere.center - point) - square(sphere.radius); -} - - - -// Implementation of "MiniBall" based on: -// http://www.flipcode.com/archives/Smallest_Enclosing_Spheres.shtml - -static Sphere recurseMini(const Vector3 *P[], uint p, uint b = 0) -{ - Sphere MB; - - switch(b) - { - case 0: - MB = Sphere(*P[0]); - break; - case 1: - MB = Sphere(*P[-1]); - break; - case 2: - MB = Sphere(*P[-1], *P[-2]); - break; - case 3: - MB = Sphere(*P[-1], *P[-2], *P[-3]); - break; - case 4: - MB = Sphere(*P[-1], *P[-2], *P[-3], *P[-4]); - return MB; - } - - for (uint i = 0; i < p; i++) - { - if (distanceSquared(MB, *P[i]) > 0) // Signed square distance to sphere - { - for (uint j = i; j > 0; j--) - { - swap(P[j], P[j-1]); - } - - MB = recurseMini(P + 1, i, b + 1); - } - } - - return MB; -} - -static bool allInside(const Sphere & sphere, const Vector3 * pointArray, const uint pointCount) { - for (uint i = 0; i < pointCount; i++) { - if (distanceSquared(sphere, pointArray[i]) >= NV_EPSILON) { - return false; - } - } - return true; -} - - -Sphere nv::miniBall(const Vector3 * pointArray, const uint pointCount) -{ - nvDebugCheck(pointArray != NULL); - nvDebugCheck(pointCount > 0); - - const Vector3 **L = new const Vector3*[pointCount]; - - for (uint i = 0; i < pointCount; i++) { - L[i] = &pointArray[i]; - } - - Sphere sphere = recurseMini(L, pointCount); - - delete [] L; - - nvDebugCheck(allInside(sphere, pointArray, pointCount)); - - return sphere; -} - - -// Approximate bounding sphere, based on "An Efficient Bounding Sphere" by Jack Ritter, from "Graphics Gems" -Sphere nv::approximateSphere_Ritter(const Vector3 * pointArray, const uint pointCount) -{ - nvDebugCheck(pointArray != NULL); - nvDebugCheck(pointCount > 0); - - Vector3 xmin, xmax, ymin, ymax, zmin, zmax; - - xmin = xmax = ymin = ymax = zmin = zmax = pointArray[0]; - - // FIRST PASS: find 6 minima/maxima points - xmin.x = ymin.y = zmin.z = FLT_MAX; - xmax.x = ymax.y = zmax.z = -FLT_MAX; - - for (uint i = 0; i < pointCount; i++) - { - const Vector3 & p = pointArray[i]; - if (p.x < xmin.x) xmin = p; - if (p.x > xmax.x) xmax = p; - if (p.y < ymin.y) ymin = p; - if (p.y > ymax.y) ymax = p; - if (p.z < zmin.z) zmin = p; - if (p.z > zmax.z) zmax = p; - } - - float xspan = lengthSquared(xmax - xmin); - float yspan = lengthSquared(ymax - ymin); - float zspan = lengthSquared(zmax - zmin); - - // Set points dia1 & dia2 to the maximally separated pair. - Vector3 dia1 = xmin; - Vector3 dia2 = xmax; - float maxspan = xspan; - if (yspan > maxspan) { - maxspan = yspan; - dia1 = ymin; - dia2 = ymax; - } - if (zspan > maxspan) { - dia1 = zmin; - dia2 = zmax; - } - - // |dia1-dia2| is a diameter of initial sphere - - // calc initial center - Sphere sphere; - sphere.center = (dia1 + dia2) / 2.0f; - - // calculate initial radius**2 and radius - float rad_sq = lengthSquared(dia2 - sphere.center); - sphere.radius = sqrtf(rad_sq); - - - // SECOND PASS: increment current sphere - for (uint i = 0; i < pointCount; i++) - { - const Vector3 & p = pointArray[i]; - - float old_to_p_sq = lengthSquared(p - sphere.center); - - if (old_to_p_sq > rad_sq) // do r**2 test first - { - // this point is outside of current sphere - float old_to_p = sqrtf(old_to_p_sq); - - // calc radius of new sphere - sphere.radius = (sphere.radius + old_to_p) / 2.0f; - rad_sq = sphere.radius * sphere.radius; // for next r**2 compare - - float old_to_new = old_to_p - sphere.radius; - - // calc center of new sphere - sphere.center = (sphere.radius * sphere.center + old_to_new * p) / old_to_p; - } - } - - nvDebugCheck(allInside(sphere, pointArray, pointCount)); - - return sphere; -} - - -static float computeSphereRadius(const Vector3 & center, const Vector3 * pointArray, const uint pointCount) { - - float maxRadius2 = 0; - - for (uint i = 0; i < pointCount; i++) - { - const Vector3 & p = pointArray[i]; - - float r2 = lengthSquared(center - p); - - if (r2 > maxRadius2) { - maxRadius2 = r2; - } - } - - return sqrtf(maxRadius2) + radiusEpsilon; -} - - -Sphere nv::approximateSphere_AABB(const Vector3 * pointArray, const uint pointCount) -{ - nvDebugCheck(pointArray != NULL); - nvDebugCheck(pointCount > 0); - - Box box; - box.clearBounds(); - - for (uint i = 0; i < pointCount; i++) { - box.addPointToBounds(pointArray[i]); - } - - Sphere sphere; - sphere.center = box.center(); - sphere.radius = computeSphereRadius(sphere.center, pointArray, pointCount); - - nvDebugCheck(allInside(sphere, pointArray, pointCount)); - - return sphere; -} - - -static void computeExtremalPoints(const Vector3 & dir, const Vector3 * pointArray, uint pointCount, Vector3 * minPoint, Vector3 * maxPoint) { - nvDebugCheck(pointCount > 0); - - uint mini = 0; - uint maxi = 0; - float minDist = FLT_MAX; - float maxDist = -FLT_MAX; - - for (uint i = 0; i < pointCount; i++) { - float d = dot(dir, pointArray[i]); - - if (d < minDist) { - minDist = d; - mini = i; - } - if (d > maxDist) { - maxDist = d; - maxi = i; - } - } - nvDebugCheck(minDist != FLT_MAX); - nvDebugCheck(maxDist != -FLT_MAX); - - *minPoint = pointArray[mini]; - *maxPoint = pointArray[maxi]; -} - -// EPOS algorithm based on: -// http://www.ep.liu.se/ecp/034/009/ecp083409.pdf -Sphere nv::approximateSphere_EPOS6(const Vector3 * pointArray, uint pointCount) -{ - nvDebugCheck(pointArray != NULL); - nvDebugCheck(pointCount > 0); - - Vector3 extremalPoints[6]; - - // Compute 6 extremal points. - computeExtremalPoints(Vector3(1, 0, 0), pointArray, pointCount, extremalPoints+0, extremalPoints+1); - computeExtremalPoints(Vector3(0, 1, 0), pointArray, pointCount, extremalPoints+2, extremalPoints+3); - computeExtremalPoints(Vector3(0, 0, 1), pointArray, pointCount, extremalPoints+4, extremalPoints+5); - - Sphere sphere = miniBall(extremalPoints, 6); - sphere.radius = computeSphereRadius(sphere.center, pointArray, pointCount); - - nvDebugCheck(allInside(sphere, pointArray, pointCount)); - - return sphere; -} - -Sphere nv::approximateSphere_EPOS14(const Vector3 * pointArray, uint pointCount) -{ - nvDebugCheck(pointArray != NULL); - nvDebugCheck(pointCount > 0); - - Vector3 extremalPoints[14]; - - // Compute 14 extremal points. - computeExtremalPoints(Vector3(1, 0, 0), pointArray, pointCount, extremalPoints+0, extremalPoints+1); - computeExtremalPoints(Vector3(0, 1, 0), pointArray, pointCount, extremalPoints+2, extremalPoints+3); - computeExtremalPoints(Vector3(0, 0, 1), pointArray, pointCount, extremalPoints+4, extremalPoints+5); - - float d = sqrtf(1.0f/3.0f); - - computeExtremalPoints(Vector3(d, d, d), pointArray, pointCount, extremalPoints+6, extremalPoints+7); - computeExtremalPoints(Vector3(-d, d, d), pointArray, pointCount, extremalPoints+8, extremalPoints+9); - computeExtremalPoints(Vector3(-d, -d, d), pointArray, pointCount, extremalPoints+10, extremalPoints+11); - computeExtremalPoints(Vector3(d, -d, d), pointArray, pointCount, extremalPoints+12, extremalPoints+13); - - - Sphere sphere = miniBall(extremalPoints, 14); - sphere.radius = computeSphereRadius(sphere.center, pointArray, pointCount); - - nvDebugCheck(allInside(sphere, pointArray, pointCount)); - - return sphere; -} - - - diff --git a/thirdparty/thekla_atlas/nvmath/Sphere.h b/thirdparty/thekla_atlas/nvmath/Sphere.h deleted file mode 100644 index 300731af44..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Sphere.h +++ /dev/null @@ -1,43 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MATH_SPHERE_H -#define NV_MATH_SPHERE_H - -#include "Vector.h" - -namespace nv -{ - - class Sphere - { - public: - Sphere() {} - Sphere(Vector3::Arg center, float radius) : center(center), radius(radius) {} - - Sphere(Vector3::Arg center) : center(center), radius(0.0f) {} - Sphere(Vector3::Arg p0, Vector3::Arg p1); - Sphere(Vector3::Arg p0, Vector3::Arg p1, Vector3::Arg p2); - Sphere(Vector3::Arg p0, Vector3::Arg p1, Vector3::Arg p2, Vector3::Arg p3); - - Vector3 center; - float radius; - }; - - // Returns negative values if point is inside. - float distanceSquared(const Sphere & sphere, const Vector3 &point); - - - // Welz's algorithm. Fairly slow, recursive implementation uses large stack. - Sphere miniBall(const Vector3 * pointArray, uint pointCount); - - Sphere approximateSphere_Ritter(const Vector3 * pointArray, uint pointCount); - Sphere approximateSphere_AABB(const Vector3 * pointArray, uint pointCount); - Sphere approximateSphere_EPOS6(const Vector3 * pointArray, uint pointCount); - Sphere approximateSphere_EPOS14(const Vector3 * pointArray, uint pointCount); - - -} // nv namespace - - -#endif // NV_MATH_SPHERE_H diff --git a/thirdparty/thekla_atlas/nvmath/TypeSerialization.cpp b/thirdparty/thekla_atlas/nvmath/TypeSerialization.cpp deleted file mode 100644 index 72fa678f47..0000000000 --- a/thirdparty/thekla_atlas/nvmath/TypeSerialization.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "TypeSerialization.h" - -#include "nvcore/Stream.h" - -#include "nvmath/Vector.h" -#include "nvmath/Matrix.h" -#include "nvmath/Quaternion.h" -#include "nvmath/Basis.h" -#include "nvmath/Box.h" -#include "nvmath/Plane.inl" - -using namespace nv; - -Stream & nv::operator<< (Stream & s, Vector2 & v) -{ - return s << v.x << v.y; -} - -Stream & nv::operator<< (Stream & s, Vector3 & v) -{ - return s << v.x << v.y << v.z; -} - -Stream & nv::operator<< (Stream & s, Vector4 & v) -{ - return s << v.x << v.y << v.z << v.w; -} - -Stream & nv::operator<< (Stream & s, Matrix & m) -{ - return s; -} - -Stream & nv::operator<< (Stream & s, Quaternion & q) -{ - return s << q.x << q.y << q.z << q.w; -} - -Stream & nv::operator<< (Stream & s, Basis & basis) -{ - return s << basis.tangent << basis.bitangent << basis.normal; -} - -Stream & nv::operator<< (Stream & s, Box & box) -{ - return s << box.minCorner << box.maxCorner; -} - -Stream & nv::operator<< (Stream & s, Plane & plane) -{ - return s << plane.v; -} diff --git a/thirdparty/thekla_atlas/nvmath/TypeSerialization.h b/thirdparty/thekla_atlas/nvmath/TypeSerialization.h deleted file mode 100644 index 32d6de827e..0000000000 --- a/thirdparty/thekla_atlas/nvmath/TypeSerialization.h +++ /dev/null @@ -1,35 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MATH_TYPESERIALIZATION_H -#define NV_MATH_TYPESERIALIZATION_H - -#include "nvmath.h" - -namespace nv -{ - class Stream; - - class Vector2; - class Vector3; - class Vector4; - - class Matrix; - class Quaternion; - class Basis; - class Box; - class Plane; - - NVMATH_API Stream & operator<< (Stream & s, Vector2 & obj); - NVMATH_API Stream & operator<< (Stream & s, Vector3 & obj); - NVMATH_API Stream & operator<< (Stream & s, Vector4 & obj); - - NVMATH_API Stream & operator<< (Stream & s, Matrix & obj); - NVMATH_API Stream & operator<< (Stream & s, Quaternion & obj); - NVMATH_API Stream & operator<< (Stream & s, Basis & obj); - NVMATH_API Stream & operator<< (Stream & s, Box & obj); - NVMATH_API Stream & operator<< (Stream & s, Plane & obj); - -} // nv namespace - -#endif // NV_MATH_TYPESERIALIZATION_H diff --git a/thirdparty/thekla_atlas/nvmath/Vector.cpp b/thirdparty/thekla_atlas/nvmath/Vector.cpp deleted file mode 100644 index 9122a1b0e9..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Vector.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "Vector.h" -#include "Vector.inl" diff --git a/thirdparty/thekla_atlas/nvmath/Vector.h b/thirdparty/thekla_atlas/nvmath/Vector.h deleted file mode 100644 index ad18672a8a..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Vector.h +++ /dev/null @@ -1,149 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MATH_VECTOR_H -#define NV_MATH_VECTOR_H - -#include "nvmath.h" - -namespace nv -{ - class NVMATH_CLASS Vector2 - { - public: - typedef Vector2 const & Arg; - - Vector2(); - explicit Vector2(float f); - Vector2(float x, float y); - Vector2(Vector2::Arg v); - - //template <typename T> explicit Vector2(const T & v) : x(v.x), y(v.y) {} - //template <typename T> operator T() const { return T(x, y); } - - const Vector2 & operator=(Vector2::Arg v); - - const float * ptr() const; - - void set(float x, float y); - - Vector2 operator-() const; - void operator+=(Vector2::Arg v); - void operator-=(Vector2::Arg v); - void operator*=(float s); - void operator*=(Vector2::Arg v); - - friend bool operator==(Vector2::Arg a, Vector2::Arg b); - friend bool operator!=(Vector2::Arg a, Vector2::Arg b); - - union { - struct { - float x, y; - }; - float component[2]; - }; - }; - - class NVMATH_CLASS Vector3 - { - public: - typedef Vector3 const & Arg; - - Vector3(); - explicit Vector3(float x); - //explicit Vector3(int x) : x(float(x)), y(float(x)), z(float(x)) {} - Vector3(float x, float y, float z); - Vector3(Vector2::Arg v, float z); - Vector3(Vector3::Arg v); - - //template <typename T> explicit Vector3(const T & v) : x(v.x), y(v.y), z(v.z) {} - //template <typename T> operator T() const { return T(x, y, z); } - - const Vector3 & operator=(Vector3::Arg v); - - Vector2 xy() const; - - const float * ptr() const; - - void set(float x, float y, float z); - - Vector3 operator-() const; - void operator+=(Vector3::Arg v); - void operator-=(Vector3::Arg v); - void operator*=(float s); - void operator/=(float s); - void operator*=(Vector3::Arg v); - void operator/=(Vector3::Arg v); - - friend bool operator==(Vector3::Arg a, Vector3::Arg b); - friend bool operator!=(Vector3::Arg a, Vector3::Arg b); - - union { - struct { - float x, y, z; - }; - float component[3]; - }; - }; - - class NVMATH_CLASS Vector4 - { - public: - typedef Vector4 const & Arg; - - Vector4(); - explicit Vector4(float x); - Vector4(float x, float y, float z, float w); - Vector4(Vector2::Arg v, float z, float w); - Vector4(Vector2::Arg v, Vector2::Arg u); - Vector4(Vector3::Arg v, float w); - Vector4(Vector4::Arg v); - // Vector4(const Quaternion & v); - - //template <typename T> explicit Vector4(const T & v) : x(v.x), y(v.y), z(v.z), w(v.w) {} - //template <typename T> operator T() const { return T(x, y, z, w); } - - const Vector4 & operator=(Vector4::Arg v); - - Vector2 xy() const; - Vector2 zw() const; - Vector3 xyz() const; - - const float * ptr() const; - - void set(float x, float y, float z, float w); - - Vector4 operator-() const; - void operator+=(Vector4::Arg v); - void operator-=(Vector4::Arg v); - void operator*=(float s); - void operator/=(float s); - void operator*=(Vector4::Arg v); - void operator/=(Vector4::Arg v); - - friend bool operator==(Vector4::Arg a, Vector4::Arg b); - friend bool operator!=(Vector4::Arg a, Vector4::Arg b); - - union { - struct { - float x, y, z, w; - }; - float component[4]; - }; - }; - -} // nv namespace - -// If we had these functions, they would be ambiguous, the compiler would not know which one to pick: -//template <typename T> Vector2 to(const T & v) { return Vector2(v.x, v.y); } -//template <typename T> Vector3 to(const T & v) { return Vector3(v.x, v.y, v.z); } -//template <typename T> Vector4 to(const T & v) { return Vector4(v.x, v.y, v.z, v.z); } - -// We could use a cast operator so that we could infer the expected type, but that doesn't work the same way in all compilers and produces horrible error messages. - -// Instead we simply have explicit casts: -template <typename T> T to(const nv::Vector2 & v) { NV_COMPILER_CHECK(sizeof(T) == sizeof(nv::Vector2)); return T(v.x, v.y); } -template <typename T> T to(const nv::Vector3 & v) { NV_COMPILER_CHECK(sizeof(T) == sizeof(nv::Vector3)); return T(v.x, v.y, v.z); } -template <typename T> T to(const nv::Vector4 & v) { NV_COMPILER_CHECK(sizeof(T) == sizeof(nv::Vector4)); return T(v.x, v.y, v.z, v.w); } - -#endif // NV_MATH_VECTOR_H diff --git a/thirdparty/thekla_atlas/nvmath/Vector.inl b/thirdparty/thekla_atlas/nvmath/Vector.inl deleted file mode 100644 index bcaec7bf2a..0000000000 --- a/thirdparty/thekla_atlas/nvmath/Vector.inl +++ /dev/null @@ -1,919 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MATH_VECTOR_INL -#define NV_MATH_VECTOR_INL - -#include "Vector.h" -#include "nvcore/Utils.h" // min, max -#include "nvcore/Hash.h" // hash - -namespace nv -{ - - // Helpers to convert vector types. Assume T has x,y members and 2 argument constructor. - //template <typename T> T to(Vector2::Arg v) { return T(v.x, v.y); } - - // Helpers to convert vector types. Assume T has x,y,z members and 3 argument constructor. - //template <typename T> T to(Vector3::Arg v) { return T(v.x, v.y, v.z); } - - // Helpers to convert vector types. Assume T has x,y,z members and 3 argument constructor. - //template <typename T> T to(Vector4::Arg v) { return T(v.x, v.y, v.z, v.w); } - - - // Vector2 - inline Vector2::Vector2() {} - inline Vector2::Vector2(float f) : x(f), y(f) {} - inline Vector2::Vector2(float x, float y) : x(x), y(y) {} - inline Vector2::Vector2(Vector2::Arg v) : x(v.x), y(v.y) {} - - inline const Vector2 & Vector2::operator=(Vector2::Arg v) - { - x = v.x; - y = v.y; - return *this; - } - - inline const float * Vector2::ptr() const - { - return &x; - } - - inline void Vector2::set(float x, float y) - { - this->x = x; - this->y = y; - } - - inline Vector2 Vector2::operator-() const - { - return Vector2(-x, -y); - } - - inline void Vector2::operator+=(Vector2::Arg v) - { - x += v.x; - y += v.y; - } - - inline void Vector2::operator-=(Vector2::Arg v) - { - x -= v.x; - y -= v.y; - } - - inline void Vector2::operator*=(float s) - { - x *= s; - y *= s; - } - - inline void Vector2::operator*=(Vector2::Arg v) - { - x *= v.x; - y *= v.y; - } - - inline bool operator==(Vector2::Arg a, Vector2::Arg b) - { - return a.x == b.x && a.y == b.y; - } - inline bool operator!=(Vector2::Arg a, Vector2::Arg b) - { - return a.x != b.x || a.y != b.y; - } - - - // Vector3 - inline Vector3::Vector3() {} - inline Vector3::Vector3(float f) : x(f), y(f), z(f) {} - inline Vector3::Vector3(float x, float y, float z) : x(x), y(y), z(z) {} - inline Vector3::Vector3(Vector2::Arg v, float z) : x(v.x), y(v.y), z(z) {} - inline Vector3::Vector3(Vector3::Arg v) : x(v.x), y(v.y), z(v.z) {} - - inline const Vector3 & Vector3::operator=(Vector3::Arg v) - { - x = v.x; - y = v.y; - z = v.z; - return *this; - } - - - inline Vector2 Vector3::xy() const - { - return Vector2(x, y); - } - - inline const float * Vector3::ptr() const - { - return &x; - } - - inline void Vector3::set(float x, float y, float z) - { - this->x = x; - this->y = y; - this->z = z; - } - - inline Vector3 Vector3::operator-() const - { - return Vector3(-x, -y, -z); - } - - inline void Vector3::operator+=(Vector3::Arg v) - { - x += v.x; - y += v.y; - z += v.z; - } - - inline void Vector3::operator-=(Vector3::Arg v) - { - x -= v.x; - y -= v.y; - z -= v.z; - } - - inline void Vector3::operator*=(float s) - { - x *= s; - y *= s; - z *= s; - } - - inline void Vector3::operator/=(float s) - { - float is = 1.0f / s; - x *= is; - y *= is; - z *= is; - } - - inline void Vector3::operator*=(Vector3::Arg v) - { - x *= v.x; - y *= v.y; - z *= v.z; - } - - inline void Vector3::operator/=(Vector3::Arg v) - { - x /= v.x; - y /= v.y; - z /= v.z; - } - - inline bool operator==(Vector3::Arg a, Vector3::Arg b) - { - return a.x == b.x && a.y == b.y && a.z == b.z; - } - inline bool operator!=(Vector3::Arg a, Vector3::Arg b) - { - return a.x != b.x || a.y != b.y || a.z != b.z; - } - - - // Vector4 - inline Vector4::Vector4() {} - inline Vector4::Vector4(float f) : x(f), y(f), z(f), w(f) {} - inline Vector4::Vector4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} - inline Vector4::Vector4(Vector2::Arg v, float z, float w) : x(v.x), y(v.y), z(z), w(w) {} - inline Vector4::Vector4(Vector2::Arg v, Vector2::Arg u) : x(v.x), y(v.y), z(u.x), w(u.y) {} - inline Vector4::Vector4(Vector3::Arg v, float w) : x(v.x), y(v.y), z(v.z), w(w) {} - inline Vector4::Vector4(Vector4::Arg v) : x(v.x), y(v.y), z(v.z), w(v.w) {} - - inline const Vector4 & Vector4::operator=(const Vector4 & v) - { - x = v.x; - y = v.y; - z = v.z; - w = v.w; - return *this; - } - - inline Vector2 Vector4::xy() const - { - return Vector2(x, y); - } - - inline Vector2 Vector4::zw() const - { - return Vector2(z, w); - } - - inline Vector3 Vector4::xyz() const - { - return Vector3(x, y, z); - } - - inline const float * Vector4::ptr() const - { - return &x; - } - - inline void Vector4::set(float x, float y, float z, float w) - { - this->x = x; - this->y = y; - this->z = z; - this->w = w; - } - - inline Vector4 Vector4::operator-() const - { - return Vector4(-x, -y, -z, -w); - } - - inline void Vector4::operator+=(Vector4::Arg v) - { - x += v.x; - y += v.y; - z += v.z; - w += v.w; - } - - inline void Vector4::operator-=(Vector4::Arg v) - { - x -= v.x; - y -= v.y; - z -= v.z; - w -= v.w; - } - - inline void Vector4::operator*=(float s) - { - x *= s; - y *= s; - z *= s; - w *= s; - } - - inline void Vector4::operator/=(float s) - { - x /= s; - y /= s; - z /= s; - w /= s; - } - - inline void Vector4::operator*=(Vector4::Arg v) - { - x *= v.x; - y *= v.y; - z *= v.z; - w *= v.w; - } - - inline void Vector4::operator/=(Vector4::Arg v) - { - x /= v.x; - y /= v.y; - z /= v.z; - w /= v.w; - } - - inline bool operator==(Vector4::Arg a, Vector4::Arg b) - { - return a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w; - } - inline bool operator!=(Vector4::Arg a, Vector4::Arg b) - { - return a.x != b.x || a.y != b.y || a.z != b.z || a.w != b.w; - } - - - - // Functions - - - // Vector2 - - inline Vector2 add(Vector2::Arg a, Vector2::Arg b) - { - return Vector2(a.x + b.x, a.y + b.y); - } - inline Vector2 operator+(Vector2::Arg a, Vector2::Arg b) - { - return add(a, b); - } - - inline Vector2 sub(Vector2::Arg a, Vector2::Arg b) - { - return Vector2(a.x - b.x, a.y - b.y); - } - inline Vector2 operator-(Vector2::Arg a, Vector2::Arg b) - { - return sub(a, b); - } - - inline Vector2 scale(Vector2::Arg v, float s) - { - return Vector2(v.x * s, v.y * s); - } - - inline Vector2 scale(Vector2::Arg v, Vector2::Arg s) - { - return Vector2(v.x * s.x, v.y * s.y); - } - - inline Vector2 operator*(Vector2::Arg v, float s) - { - return scale(v, s); - } - - inline Vector2 operator*(Vector2::Arg v1, Vector2::Arg v2) - { - return Vector2(v1.x*v2.x, v1.y*v2.y); - } - - inline Vector2 operator*(float s, Vector2::Arg v) - { - return scale(v, s); - } - - inline Vector2 operator/(Vector2::Arg v, float s) - { - return scale(v, 1.0f/s); - } - - inline 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); - } - - inline float dot(Vector2::Arg a, Vector2::Arg b) - { - return a.x * b.x + a.y * b.y; - } - - inline float lengthSquared(Vector2::Arg v) - { - return v.x * v.x + v.y * v.y; - } - - inline float length(Vector2::Arg v) - { - return sqrtf(lengthSquared(v)); - } - - inline float distance(Vector2::Arg a, Vector2::Arg b) - { - return length(a - b); - } - - inline float inverseLength(Vector2::Arg v) - { - return 1.0f / sqrtf(lengthSquared(v)); - } - - inline bool isNormalized(Vector2::Arg v, float epsilon = NV_NORMAL_EPSILON) - { - return equal(length(v), 1, epsilon); - } - - inline Vector2 normalize(Vector2::Arg v, float epsilon = NV_EPSILON) - { - float l = length(v); - nvDebugCheck(!isZero(l, epsilon)); - Vector2 n = scale(v, 1.0f / l); - nvDebugCheck(isNormalized(n)); - return n; - } - - inline Vector2 normalizeSafe(Vector2::Arg v, Vector2::Arg fallback, float epsilon = NV_EPSILON) - { - float l = length(v); - if (isZero(l, epsilon)) { - return fallback; - } - return scale(v, 1.0f / l); - } - - // Safe, branchless normalization from Andy Firth. All error checking ommitted. - // http://altdevblogaday.com/2011/08/21/practical-flt-point-tricks/ - inline Vector2 normalizeFast(Vector2::Arg v) - { - const float very_small_float = 1.0e-037f; - float l = very_small_float + length(v); - return scale(v, 1.0f / l); - } - - inline 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); - } - - inline Vector2 min(Vector2::Arg a, Vector2::Arg b) - { - return Vector2(min(a.x, b.x), min(a.y, b.y)); - } - - inline Vector2 max(Vector2::Arg a, Vector2::Arg b) - { - return Vector2(max(a.x, b.x), max(a.y, b.y)); - } - - inline Vector2 clamp(Vector2::Arg v, float min, float max) - { - return Vector2(clamp(v.x, min, max), clamp(v.y, min, max)); - } - - inline Vector2 saturate(Vector2::Arg v) - { - return Vector2(saturate(v.x), saturate(v.y)); - } - - inline bool isFinite(Vector2::Arg v) - { - return isFinite(v.x) && isFinite(v.y); - } - - inline Vector2 validate(Vector2::Arg v, Vector2::Arg fallback = Vector2(0.0f)) - { - if (!isFinite(v)) return fallback; - Vector2 vf = v; - nv::floatCleanup(vf.component, 2); - return vf; - } - - // Note, this is the area scaled by 2! - inline float triangleArea(Vector2::Arg v0, Vector2::Arg v1) - { - return (v0.x * v1.y - v0.y * v1.x); // * 0.5f; - } - inline 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); - } - - - template <> - inline uint hash(const Vector2 & v, uint h) - { - return sdbmFloatHash(v.component, 2, h); - } - - - - // Vector3 - - inline Vector3 add(Vector3::Arg a, Vector3::Arg b) - { - return Vector3(a.x + b.x, a.y + b.y, a.z + b.z); - } - inline Vector3 add(Vector3::Arg a, float b) - { - return Vector3(a.x + b, a.y + b, a.z + b); - } - inline Vector3 operator+(Vector3::Arg a, Vector3::Arg b) - { - return add(a, b); - } - inline Vector3 operator+(Vector3::Arg a, float b) - { - return add(a, b); - } - - inline Vector3 sub(Vector3::Arg a, Vector3::Arg b) - { - return Vector3(a.x - b.x, a.y - b.y, a.z - b.z); - } - inline Vector3 sub(Vector3::Arg a, float b) - { - return Vector3(a.x - b, a.y - b, a.z - b); - } - inline Vector3 operator-(Vector3::Arg a, Vector3::Arg b) - { - return sub(a, b); - } - inline Vector3 operator-(Vector3::Arg a, float b) - { - return sub(a, b); - } - - inline 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); - } - - inline Vector3 scale(Vector3::Arg v, float s) - { - return Vector3(v.x * s, v.y * s, v.z * s); - } - - inline Vector3 scale(Vector3::Arg v, Vector3::Arg s) - { - return Vector3(v.x * s.x, v.y * s.y, v.z * s.z); - } - - inline Vector3 operator*(Vector3::Arg v, float s) - { - return scale(v, s); - } - - inline Vector3 operator*(float s, Vector3::Arg v) - { - return scale(v, s); - } - - inline Vector3 operator*(Vector3::Arg v, Vector3::Arg s) - { - return scale(v, s); - } - - inline Vector3 operator/(Vector3::Arg v, float s) - { - return scale(v, 1.0f/s); - } - - /*inline Vector3 add_scaled(Vector3::Arg a, Vector3::Arg b, float s) - { - return Vector3(a.x + b.x * s, a.y + b.y * s, a.z + b.z * s); - }*/ - - inline 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); - } - - inline float dot(Vector3::Arg a, Vector3::Arg b) - { - return a.x * b.x + a.y * b.y + a.z * b.z; - } - - inline float lengthSquared(Vector3::Arg v) - { - return v.x * v.x + v.y * v.y + v.z * v.z; - } - - inline float length(Vector3::Arg v) - { - return sqrtf(lengthSquared(v)); - } - - inline float distance(Vector3::Arg a, Vector3::Arg b) - { - return length(a - b); - } - - inline float distanceSquared(Vector3::Arg a, Vector3::Arg b) - { - return lengthSquared(a - b); - } - - inline float inverseLength(Vector3::Arg v) - { - return 1.0f / sqrtf(lengthSquared(v)); - } - - inline bool isNormalized(Vector3::Arg v, float epsilon = NV_NORMAL_EPSILON) - { - return equal(length(v), 1, epsilon); - } - - inline Vector3 normalize(Vector3::Arg v, float epsilon = NV_EPSILON) - { - float l = length(v); - nvDebugCheck(!isZero(l, epsilon)); - Vector3 n = scale(v, 1.0f / l); - nvDebugCheck(isNormalized(n)); - return n; - } - - inline Vector3 normalizeSafe(Vector3::Arg v, Vector3::Arg fallback, float epsilon = NV_EPSILON) - { - float l = length(v); - if (isZero(l, epsilon)) { - return fallback; - } - return scale(v, 1.0f / l); - } - - // Safe, branchless normalization from Andy Firth. All error checking ommitted. - // http://altdevblogaday.com/2011/08/21/practical-flt-point-tricks/ - inline Vector3 normalizeFast(Vector3::Arg v) - { - const float very_small_float = 1.0e-037f; - float l = very_small_float + length(v); - return scale(v, 1.0f / l); - } - - inline 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); - } - - inline Vector3 min(Vector3::Arg a, Vector3::Arg b) - { - return Vector3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)); - } - - inline Vector3 max(Vector3::Arg a, Vector3::Arg b) - { - return Vector3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); - } - - inline 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)); - } - - inline Vector3 saturate(Vector3::Arg v) - { - return Vector3(saturate(v.x), saturate(v.y), saturate(v.z)); - } - - inline Vector3 floor(Vector3::Arg v) - { - return Vector3(floorf(v.x), floorf(v.y), floorf(v.z)); - } - - inline Vector3 ceil(Vector3::Arg v) - { - return Vector3(ceilf(v.x), ceilf(v.y), ceilf(v.z)); - } - - inline bool isFinite(Vector3::Arg v) - { - return isFinite(v.x) && isFinite(v.y) && isFinite(v.z); - } - - inline Vector3 validate(Vector3::Arg v, Vector3::Arg fallback = Vector3(0.0f)) - { - if (!isFinite(v)) return fallback; - Vector3 vf = v; - nv::floatCleanup(vf.component, 3); - return vf; - } - - inline Vector3 reflect(Vector3::Arg v, Vector3::Arg n) - { - return v - (2 * dot(v, n)) * n; - } - - template <> - inline uint hash(const Vector3 & v, uint h) - { - return sdbmFloatHash(v.component, 3, h); - } - - - // Vector4 - - inline Vector4 add(Vector4::Arg a, Vector4::Arg b) - { - return Vector4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); - } - inline Vector4 operator+(Vector4::Arg a, Vector4::Arg b) - { - return add(a, b); - } - - inline Vector4 sub(Vector4::Arg a, Vector4::Arg b) - { - return Vector4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); - } - inline Vector4 operator-(Vector4::Arg a, Vector4::Arg b) - { - return sub(a, b); - } - - inline Vector4 scale(Vector4::Arg v, float s) - { - return Vector4(v.x * s, v.y * s, v.z * s, v.w * s); - } - - inline Vector4 scale(Vector4::Arg v, Vector4::Arg s) - { - return Vector4(v.x * s.x, v.y * s.y, v.z * s.z, v.w * s.w); - } - - inline Vector4 operator*(Vector4::Arg v, float s) - { - return scale(v, s); - } - - inline Vector4 operator*(float s, Vector4::Arg v) - { - return scale(v, s); - } - - inline Vector4 operator*(Vector4::Arg v, Vector4::Arg s) - { - return scale(v, s); - } - - inline Vector4 operator/(Vector4::Arg v, float s) - { - return scale(v, 1.0f/s); - } - - /*inline Vector4 add_scaled(Vector4::Arg a, Vector4::Arg b, float s) - { - return Vector4(a.x + b.x * s, a.y + b.y * s, a.z + b.z * s, a.w + b.w * s); - }*/ - - inline Vector4 lerp(Vector4::Arg v1, Vector4::Arg v2, float t) - { - const float s = 1.0f - t; - return Vector4(v1.x * s + t * v2.x, v1.y * s + t * v2.y, v1.z * s + t * v2.z, v1.w * s + t * v2.w); - } - - inline float dot(Vector4::Arg a, Vector4::Arg b) - { - return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; - } - - inline float lengthSquared(Vector4::Arg v) - { - return v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w; - } - - inline float length(Vector4::Arg v) - { - return sqrtf(lengthSquared(v)); - } - - inline float inverseLength(Vector4::Arg v) - { - return 1.0f / sqrtf(lengthSquared(v)); - } - - inline bool isNormalized(Vector4::Arg v, float epsilon = NV_NORMAL_EPSILON) - { - return equal(length(v), 1, epsilon); - } - - inline Vector4 normalize(Vector4::Arg v, float epsilon = NV_EPSILON) - { - float l = length(v); - nvDebugCheck(!isZero(l, epsilon)); - Vector4 n = scale(v, 1.0f / l); - nvDebugCheck(isNormalized(n)); - return n; - } - - inline Vector4 normalizeSafe(Vector4::Arg v, Vector4::Arg fallback, float epsilon = NV_EPSILON) - { - float l = length(v); - if (isZero(l, epsilon)) { - return fallback; - } - return scale(v, 1.0f / l); - } - - // Safe, branchless normalization from Andy Firth. All error checking ommitted. - // http://altdevblogaday.com/2011/08/21/practical-flt-point-tricks/ - inline Vector4 normalizeFast(Vector4::Arg v) - { - const float very_small_float = 1.0e-037f; - float l = very_small_float + length(v); - return scale(v, 1.0f / l); - } - - inline bool equal(Vector4::Arg v1, Vector4::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) && equal(v1.w, v2.w, epsilon); - } - - inline Vector4 min(Vector4::Arg a, Vector4::Arg b) - { - return Vector4(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w)); - } - - inline Vector4 max(Vector4::Arg a, Vector4::Arg b) - { - return Vector4(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z), max(a.w, b.w)); - } - - inline Vector4 clamp(Vector4::Arg v, float min, float max) - { - return Vector4(clamp(v.x, min, max), clamp(v.y, min, max), clamp(v.z, min, max), clamp(v.w, min, max)); - } - - inline Vector4 saturate(Vector4::Arg v) - { - return Vector4(saturate(v.x), saturate(v.y), saturate(v.z), saturate(v.w)); - } - - inline bool isFinite(Vector4::Arg v) - { - return isFinite(v.x) && isFinite(v.y) && isFinite(v.z) && isFinite(v.w); - } - - inline Vector4 validate(Vector4::Arg v, Vector4::Arg fallback = Vector4(0.0f)) - { - if (!isFinite(v)) return fallback; - Vector4 vf = v; - nv::floatCleanup(vf.component, 4); - return vf; - } - - template <> - inline uint hash(const Vector4 & v, uint h) - { - return sdbmFloatHash(v.component, 4, h); - } - - -#if NV_OS_IOS // LLVM is not happy with implicit conversion of immediate constants to float - - //int: - - inline Vector2 scale(Vector2::Arg v, int s) - { - return Vector2(v.x * s, v.y * s); - } - - inline Vector2 operator*(Vector2::Arg v, int s) - { - return scale(v, s); - } - - inline Vector2 operator*(int s, Vector2::Arg v) - { - return scale(v, s); - } - - inline Vector2 operator/(Vector2::Arg v, int s) - { - return scale(v, 1.0f/s); - } - - inline Vector3 scale(Vector3::Arg v, int s) - { - return Vector3(v.x * s, v.y * s, v.z * s); - } - - inline Vector3 operator*(Vector3::Arg v, int s) - { - return scale(v, s); - } - - inline Vector3 operator*(int s, Vector3::Arg v) - { - return scale(v, s); - } - - inline Vector3 operator/(Vector3::Arg v, int s) - { - return scale(v, 1.0f/s); - } - - inline Vector4 scale(Vector4::Arg v, int s) - { - return Vector4(v.x * s, v.y * s, v.z * s, v.w * s); - } - - inline Vector4 operator*(Vector4::Arg v, int s) - { - return scale(v, s); - } - - inline Vector4 operator*(int s, Vector4::Arg v) - { - return scale(v, s); - } - - inline Vector4 operator/(Vector4::Arg v, int s) - { - return scale(v, 1.0f/s); - } - - //double: - - inline Vector3 operator*(Vector3::Arg v, double s) - { - return scale(v, (float)s); - } - - inline Vector3 operator*(double s, Vector3::Arg v) - { - return scale(v, (float)s); - } - - inline Vector3 operator/(Vector3::Arg v, double s) - { - return scale(v, 1.f/((float)s)); - } - -#endif //NV_OS_IOS - -} // nv namespace - -#endif // NV_MATH_VECTOR_INL diff --git a/thirdparty/thekla_atlas/nvmath/ftoi.h b/thirdparty/thekla_atlas/nvmath/ftoi.h deleted file mode 100644 index 182c56d1c3..0000000000 --- a/thirdparty/thekla_atlas/nvmath/ftoi.h +++ /dev/null @@ -1,261 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#pragma once -#ifndef NV_MATH_FTOI_H -#define NV_MATH_FTOI_H - -#include "nvmath/nvmath.h" - -#include <math.h> - -namespace nv -{ - // Optimized float to int conversions. See: - // http://cbloomrants.blogspot.com/2009/01/01-17-09-float-to-int.html - // http://www.stereopsis.com/sree/fpu2006.html - // http://assemblyrequired.crashworks.org/2009/01/12/why-you-should-never-cast-floats-to-ints/ - // http://chrishecker.com/Miscellaneous_Technical_Articles#Floating_Point - - - union DoubleAnd64 { - uint64 i; - double d; - }; - - static const double floatutil_xs_doublemagic = (6755399441055744.0); // 2^52 * 1.5 - static const double floatutil_xs_doublemagicdelta = (1.5e-8); // almost .5f = .5f + 1e^(number of exp bit) - static const double floatutil_xs_doublemagicroundeps = (0.5f - floatutil_xs_doublemagicdelta); // almost .5f = .5f - 1e^(number of exp bit) - - NV_FORCEINLINE int ftoi_round_xs(double val, double magic) { -#if 1 - DoubleAnd64 dunion; - dunion.d = val + magic; - return (int32) dunion.i; // just cast to grab the bottom bits -#else - val += magic; - return ((int*)&val)[0]; // @@ Assumes little endian. -#endif - } - - NV_FORCEINLINE int ftoi_round_xs(float val) { - return ftoi_round_xs(val, floatutil_xs_doublemagic); - } - - NV_FORCEINLINE int ftoi_floor_xs(float val) { - return ftoi_round_xs(val - floatutil_xs_doublemagicroundeps, floatutil_xs_doublemagic); - } - - NV_FORCEINLINE int ftoi_ceil_xs(float val) { - return ftoi_round_xs(val + floatutil_xs_doublemagicroundeps, floatutil_xs_doublemagic); - } - - NV_FORCEINLINE int ftoi_trunc_xs(float val) { - return (val<0) ? ftoi_ceil_xs(val) : ftoi_floor_xs(val); - } - -// -- GODOT start -- -//#if NV_CPU_X86 || NV_CPU_X86_64 -#if NV_USE_SSE -// -- GODOT end -- - - NV_FORCEINLINE int ftoi_round_sse(float f) { - return _mm_cvt_ss2si(_mm_set_ss(f)); - } - - NV_FORCEINLINE int ftoi_trunc_sse(float f) { - return _mm_cvtt_ss2si(_mm_set_ss(f)); - } - -#endif - - - -#if NV_USE_SSE - - NV_FORCEINLINE int ftoi_round(float val) { - return ftoi_round_sse(val); - } - - NV_FORCEINLINE int ftoi_trunc(float f) { - return ftoi_trunc_sse(f); - } - - // We can probably do better than this. See for example: - // http://dss.stephanierct.com/DevBlog/?p=8 - NV_FORCEINLINE int ftoi_floor(float val) { - return ftoi_round(floorf(val)); - } - - NV_FORCEINLINE int ftoi_ceil(float val) { - return ftoi_round(ceilf(val)); - } - -#else - - // In theory this should work with any double floating point math implementation, but it appears that MSVC produces incorrect code - // when SSE2 is targeted and fast math is enabled (/arch:SSE2 & /fp:fast). These problems go away with /fp:precise, which is the default mode. - - NV_FORCEINLINE int ftoi_round(float val) { - return ftoi_round_xs(val); - } - - NV_FORCEINLINE int ftoi_floor(float val) { - return ftoi_floor_xs(val); - } - - NV_FORCEINLINE int ftoi_ceil(float val) { - return ftoi_ceil_xs(val); - } - - NV_FORCEINLINE int ftoi_trunc(float f) { - return ftoi_trunc_xs(f); - } - -#endif - - - inline void test_ftoi() { - - // Round to nearest integer. - nvCheck(ftoi_round(0.1f) == 0); - nvCheck(ftoi_round(0.6f) == 1); - nvCheck(ftoi_round(-0.2f) == 0); - nvCheck(ftoi_round(-0.7f) == -1); - nvCheck(ftoi_round(10.1f) == 10); - nvCheck(ftoi_round(10.6f) == 11); - nvCheck(ftoi_round(-90.1f) == -90); - nvCheck(ftoi_round(-90.6f) == -91); - - nvCheck(ftoi_round(0) == 0); - nvCheck(ftoi_round(1) == 1); - nvCheck(ftoi_round(-1) == -1); - - nvCheck(ftoi_round(0.5f) == 0); // How are midpoints rounded? Bankers rounding. - nvCheck(ftoi_round(1.5f) == 2); - nvCheck(ftoi_round(2.5f) == 2); - nvCheck(ftoi_round(3.5f) == 4); - nvCheck(ftoi_round(4.5f) == 4); - nvCheck(ftoi_round(-0.5f) == 0); - nvCheck(ftoi_round(-1.5f) == -2); - - - // Truncation (round down if > 0, round up if < 0). - nvCheck(ftoi_trunc(0.1f) == 0); - nvCheck(ftoi_trunc(0.6f) == 0); - nvCheck(ftoi_trunc(-0.2f) == 0); - nvCheck(ftoi_trunc(-0.7f) == 0); // @@ When using /arch:SSE2 in Win32, msvc produce wrong code for this one. It is skipping the addition. - nvCheck(ftoi_trunc(1.99f) == 1); - nvCheck(ftoi_trunc(-1.2f) == -1); - - // Floor (round down). - nvCheck(ftoi_floor(0.1f) == 0); - nvCheck(ftoi_floor(0.6f) == 0); - nvCheck(ftoi_floor(-0.2f) == -1); - nvCheck(ftoi_floor(-0.7f) == -1); - nvCheck(ftoi_floor(1.99f) == 1); - nvCheck(ftoi_floor(-1.2f) == -2); - - nvCheck(ftoi_floor(0) == 0); - nvCheck(ftoi_floor(1) == 1); - nvCheck(ftoi_floor(-1) == -1); - nvCheck(ftoi_floor(2) == 2); - nvCheck(ftoi_floor(-2) == -2); - - // Ceil (round up). - nvCheck(ftoi_ceil(0.1f) == 1); - nvCheck(ftoi_ceil(0.6f) == 1); - nvCheck(ftoi_ceil(-0.2f) == 0); - nvCheck(ftoi_ceil(-0.7f) == 0); - nvCheck(ftoi_ceil(1.99f) == 2); - nvCheck(ftoi_ceil(-1.2f) == -1); - - nvCheck(ftoi_ceil(0) == 0); - nvCheck(ftoi_ceil(1) == 1); - nvCheck(ftoi_ceil(-1) == -1); - nvCheck(ftoi_ceil(2) == 2); - nvCheck(ftoi_ceil(-2) == -2); - } - - - - - - // Safe versions using standard casts. - - inline int iround(float f) - { - return ftoi_round(f); - //return int(floorf(f + 0.5f)); - } - - inline int iround(double f) - { - return int(::floor(f + 0.5)); - } - - inline int ifloor(float f) - { - return ftoi_floor(f); - //return int(floorf(f)); - } - - inline int iceil(float f) - { - return int(ceilf(f)); - } - - - - // I'm always confused about which quantizer to use. I think we should choose a quantizer based on how the values are expanded later and this is generally using the 'exact endpoints' rule. - // Some notes from cbloom: http://cbloomrants.blogspot.com/2011/07/07-26-11-pixel-int-to-float-options.html - - // Quantize a float in the [0,1] range, using exact end points or uniform bins. - inline float quantizeFloat(float x, uint bits, bool exactEndPoints = true) { - nvDebugCheck(bits <= 16); - - float range = float(1 << bits); - if (exactEndPoints) { - return floorf(x * (range-1) + 0.5f) / (range-1); - } - else { - return (floorf(x * range) + 0.5f) / range; - } - } - - - // This is the most common rounding mode: - // - // 0 1 2 3 - // |___|_______|_______|___| - // 0 1 - // - // You get that if you take the unit floating point number multiply by 'N-1' and round to nearest. That is, `i = round(f * (N-1))`. - // You reconstruct the original float dividing by 'N-1': `f = i / (N-1)` - - - // 0 1 2 3 - // |_____|_____|_____|_____| - // 0 1 - - /*enum BinningMode { - RoundMode_ExactEndPoints, - RoundMode_UniformBins, - };*/ - - template <int N> - inline uint unitFloatToFixed(float f) { - return ftoi_round(f * ((1<<N)-1)); - } - - inline uint8 unitFloatToFixed8(float f) { - return (uint8)unitFloatToFixed<8>(f); - } - - inline uint16 unitFloatToFixed16(float f) { - return (uint16)unitFloatToFixed<16>(f); - } - - -} // nv - -#endif // NV_MATH_FTOI_H diff --git a/thirdparty/thekla_atlas/nvmath/nvmath.h b/thirdparty/thekla_atlas/nvmath/nvmath.h deleted file mode 100644 index a697f9293d..0000000000 --- a/thirdparty/thekla_atlas/nvmath/nvmath.h +++ /dev/null @@ -1,342 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MATH_H -#define NV_MATH_H - -#include "nvcore/nvcore.h" -#include "nvcore/Debug.h" // nvDebugCheck -#include "nvcore/Utils.h" // max, clamp - -#include <math.h> - -#if NV_OS_WIN32 || NV_OS_XBOX || NV_OS_DURANGO -#include <float.h> // finite, isnan -#endif - -// -- GODOT start -- -//#if NV_CPU_X86 || NV_CPU_X86_64 -// //#include <intrin.h> -// #include <xmmintrin.h> -//#endif -// -- GODOT end -- - - - -// Function linkage -#if NVMATH_SHARED -#ifdef NVMATH_EXPORTS -#define NVMATH_API DLL_EXPORT -#define NVMATH_CLASS DLL_EXPORT_CLASS -#else -#define NVMATH_API DLL_IMPORT -#define NVMATH_CLASS DLL_IMPORT -#endif -#else // NVMATH_SHARED -#define NVMATH_API -#define NVMATH_CLASS -#endif // NVMATH_SHARED - -// Set some reasonable defaults. -#ifndef NV_USE_ALTIVEC -# define NV_USE_ALTIVEC NV_CPU_PPC -//# define NV_USE_ALTIVEC defined(__VEC__) -#endif - -#ifndef NV_USE_SSE -# if NV_CPU_X86_64 - // x64 always supports at least SSE2 -# define NV_USE_SSE 2 -# elif NV_CC_MSVC && defined(_M_IX86_FP) - // Also on x86 with the /arch:SSE flag in MSVC. -# define NV_USE_SSE _M_IX86_FP // 1=SSE, 2=SS2 -# elif defined(__SSE__) -# define NV_USE_SSE 1 -# elif defined(__SSE2__) -# define NV_USE_SSE 2 -# else - // Otherwise we assume no SSE. -# define NV_USE_SSE 0 -# endif -#endif - - -// Internally set NV_USE_SIMD when either altivec or sse is available. -#if NV_USE_ALTIVEC && NV_USE_SSE -# error "Cannot enable both altivec and sse!" -#endif - - -// -- GODOT start -- -#if NV_USE_SSE - //#include <intrin.h> - #include <xmmintrin.h> -#endif -// -- GODOT end -- - - -#ifndef PI -#define PI float(3.1415926535897932384626433833) -#endif - -#define NV_EPSILON (0.0001f) -#define NV_NORMAL_EPSILON (0.001f) - -/* -#define SQ(r) ((r)*(r)) - -#define SIGN_BITMASK 0x80000000 - -/// Integer representation of a floating-point value. -#define IR(x) ((uint32 &)(x)) - -/// Absolute integer representation of a floating-point value -#define AIR(x) (IR(x) & 0x7fffffff) - -/// Floating-point representation of an integer value. -#define FR(x) ((float&)(x)) - -/// Integer-based comparison of a floating point value. -/// Don't use it blindly, it can be faster or slower than the FPU comparison, depends on the context. -#define IS_NEGATIVE_FLOAT(x) (IR(x)&SIGN_BITMASK) -*/ - -extern "C" inline double sqrt_assert(const double f) -{ - nvDebugCheck(f >= 0.0f); - return sqrt(f); -} - -inline float sqrtf_assert(const float f) -{ - nvDebugCheck(f >= 0.0f); - return sqrtf(f); -} - -extern "C" inline double acos_assert(const double f) -{ - nvDebugCheck(f >= -1.0f && f <= 1.0f); - return acos(f); -} - -inline float acosf_assert(const float f) -{ - nvDebugCheck(f >= -1.0f && f <= 1.0f); - return acosf(f); -} - -extern "C" inline double asin_assert(const double f) -{ - nvDebugCheck(f >= -1.0f && f <= 1.0f); - return asin(f); -} - -inline float asinf_assert(const float f) -{ - nvDebugCheck(f >= -1.0f && f <= 1.0f); - return asinf(f); -} - -// Replace default functions with asserting ones. -#if !NV_CC_MSVC || (NV_CC_MSVC && (_MSC_VER < 1700)) // IC: Apparently this was causing problems in Visual Studio 2012. See Issue 194: https://code.google.com/p/nvidia-texture-tools/issues/detail?id=194 -#define sqrt sqrt_assert -#define sqrtf sqrtf_assert -#define acos acos_assert -#define acosf acosf_assert -#define asin asin_assert -#define asinf asinf_assert -#endif - -#if NV_CC_MSVC -NV_FORCEINLINE float log2f(float x) -{ - nvCheck(x >= 0); - return logf(x) / logf(2.0f); -} -NV_FORCEINLINE float exp2f(float x) -{ - return powf(2.0f, x); -} -#endif - -namespace nv -{ - inline float toRadian(float degree) { return degree * (PI / 180.0f); } - inline float toDegree(float radian) { return radian * (180.0f / PI); } - - // Robust floating point comparisons: - // http://realtimecollisiondetection.net/blog/?p=89 - inline 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)); - } - - inline bool isZero(const float f, const float epsilon = NV_EPSILON) - { - return fabs(f) <= epsilon; - } - - inline bool isFinite(const float f) - { -#if NV_OS_WIN32 || NV_OS_XBOX || NV_OS_DURANGO - return _finite(f) != 0; -#elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD || NV_OS_ORBIS || NV_OS_LINUX - return isfinite(f); -#else -# error "isFinite not supported" -#endif - //return std::isfinite (f); - //return finite (f); - } - - inline bool isNan(const float f) - { -#if NV_OS_WIN32 || NV_OS_XBOX || NV_OS_DURANGO - return _isnan(f) != 0; -#elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD || NV_OS_ORBIS || NV_OS_LINUX - return isnan(f); -#else -# error "isNan not supported" -#endif - } - - inline uint log2(uint32 i) - { - uint32 value = 0; - while( i >>= 1 ) value++; - return value; - } - - inline uint log2(uint64 i) - { - uint64 value = 0; - while (i >>= 1) value++; - return U32(value); - } - - inline float lerp(float f0, float f1, float t) - { - const float s = 1.0f - t; - return f0 * s + f1 * t; - } - - inline float square(float f) { return f * f; } - inline int square(int i) { return i * i; } - - inline float cube(float f) { return f * f * f; } - inline int cube(int i) { return i * i * i; } - - inline float frac(float f) - { - return f - floor(f); - } - - inline float floatRound(float f) - { - return floorf(f + 0.5f); - } - - // Eliminates negative zeros from a float array. - inline void floatCleanup(float * fp, int n) - { - for (int i = 0; i < n; i++) { - //nvDebugCheck(isFinite(fp[i])); - union { float f; uint32 i; } x = { fp[i] }; - if (x.i == 0x80000000) fp[i] = 0.0f; - } - } - - inline float saturate(float f) { - return clamp(f, 0.0f, 1.0f); - } - - inline float linearstep(float edge0, float edge1, float x) { - // Scale, bias and saturate x to 0..1 range - return saturate((x - edge0) / (edge1 - edge0)); - } - - inline float smoothstep(float edge0, float edge1, float x) { - x = linearstep(edge0, edge1, x); - - // Evaluate polynomial - return x*x*(3 - 2*x); - } - - inline int sign(float a) - { - return (a > 0) - (a < 0); - //if (a > 0.0f) return 1; - //if (a < 0.0f) return -1; - //return 0; - } - - union Float754 { - unsigned int raw; - float value; - struct { - #if NV_BIG_ENDIAN - unsigned int negative:1; - unsigned int biasedexponent:8; - unsigned int mantissa:23; - #else - unsigned int mantissa:23; - unsigned int biasedexponent:8; - unsigned int negative:1; - #endif - } field; - }; - - // Return the exponent of x ~ Floor(Log2(x)) - inline int floatExponent(float x) - { - Float754 f; - f.value = x; - return (f.field.biasedexponent - 127); - } - - - // FloatRGB9E5 - union Float3SE { - uint32 v; - struct { - #if NV_BIG_ENDIAN - uint32 e : 5; - uint32 zm : 9; - uint32 ym : 9; - uint32 xm : 9; - #else - uint32 xm : 9; - uint32 ym : 9; - uint32 zm : 9; - uint32 e : 5; - #endif - }; - }; - - // FloatR11G11B10 - union Float3PK { - uint32 v; - struct { - #if NV_BIG_ENDIAN - uint32 ze : 5; - uint32 zm : 5; - uint32 ye : 5; - uint32 ym : 6; - uint32 xe : 5; - uint32 xm : 6; - #else - uint32 xm : 6; - uint32 xe : 5; - uint32 ym : 6; - uint32 ye : 5; - uint32 zm : 5; - uint32 ze : 5; - #endif - }; - }; - - -} // nv - -#endif // NV_MATH_H diff --git a/thirdparty/thekla_atlas/nvmesh/BaseMesh.cpp b/thirdparty/thekla_atlas/nvmesh/BaseMesh.cpp deleted file mode 100644 index f17d3b46fd..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/BaseMesh.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "BaseMesh.h" -#include "Stream.h" -#include "nvmath/TypeSerialization.h" - - -namespace nv -{ - static Stream & operator<< (Stream & s, BaseMesh::Vertex & vertex) - { - return s << vertex.id << vertex.pos << vertex.nor << vertex.tex; - } - - Stream & operator<< (Stream & s, BaseMesh & mesh) - { - return s << mesh.m_vertexArray; - } -} diff --git a/thirdparty/thekla_atlas/nvmesh/BaseMesh.h b/thirdparty/thekla_atlas/nvmesh/BaseMesh.h deleted file mode 100644 index c8559511f1..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/BaseMesh.h +++ /dev/null @@ -1,72 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MESH_BASEMESH_H -#define NV_MESH_BASEMESH_H - -#include "nvmesh.h" -#include "nvmath/Vector.h" -#include "nvcore/Array.h" -#include "nvcore/Hash.h" - -namespace nv -{ - - /// Base mesh without connectivity. - class BaseMesh - { - public: - struct Vertex; - - BaseMesh() {} - - BaseMesh(uint vertexNum) : - m_vertexArray(vertexNum) {} - - // Vertex methods. - uint vertexCount() const { return m_vertexArray.count(); } - const Vertex & vertexAt(uint i) const { return m_vertexArray[i]; } - Vertex & vertexAt(uint i) { return m_vertexArray[i]; } - const Array<Vertex> & vertices() const { return m_vertexArray; } - Array<Vertex> & vertices() { return m_vertexArray; } - - friend Stream & operator<< (Stream & s, BaseMesh & obj); - - protected: - - Array<Vertex> m_vertexArray; - }; - - - /// BaseMesh vertex. - struct BaseMesh::Vertex - { - Vertex() : id(NIL), pos(0.0f), nor(0.0f), tex(0.0f) {} - - uint id; // @@ Vertex should be an index into the vertex data. - Vector3 pos; - Vector3 nor; - Vector2 tex; - }; - - inline bool operator==(const BaseMesh::Vertex & a, const BaseMesh::Vertex & b) - { - return a.pos == b.pos && a.nor == b.nor && a.tex == b.tex; - } - - inline bool operator!=(const BaseMesh::Vertex & a, const BaseMesh::Vertex & b) - { - return a.pos != b.pos && a.nor != b.nor && a.tex != b.tex; - } - - template <> struct Hash<BaseMesh::Vertex> - { - uint operator()(const BaseMesh::Vertex & v) const - { - return Hash<Vector3>()(v.pos); - } - }; - -} // nv namespace - -#endif // NV_MESH_BASEMESH_H diff --git a/thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp b/thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp deleted file mode 100644 index 24d8ddff89..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp +++ /dev/null @@ -1,1000 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "nvmesh.h" // pch - -#include "MeshBuilder.h" -#include "TriMesh.h" -#include "QuadTriMesh.h" -#include "halfedge/Mesh.h" -#include "halfedge/Vertex.h" -#include "halfedge/Face.h" - -#include "weld/Weld.h" - -#include "nvmath/Box.h" -#include "nvmath/Vector.inl" - -#include "nvcore/StrLib.h" -#include "nvcore/RadixSort.h" -#include "nvcore/Ptr.h" -#include "nvcore/Array.inl" -#include "nvcore/HashMap.inl" - - -using namespace nv; - -/* -By default the mesh builder creates 3 streams (position, normal, texcoord), I'm planning to add support for extra streams as follows: - -enum StreamType { StreamType_Float, StreamType_Vector2, StreamType_Vector3, StreamType_Vector4 }; - -uint addStream(const char *, uint idx, StreamType); - -uint addAttribute(float) -uint addAttribute(Vector2) -uint addAttribute(Vector3) -uint addAttribute(Vector4) - -struct Vertex -{ - uint pos; - uint nor; - uint tex; - uint * attribs; // NULL or NIL terminated array? -}; - -All streams must be added before hand, so that you know the size of the attribs array. - -The vertex hash function could be kept as is, but the == operator should be extended to test -the extra atributes when available. - -That might require a custom hash implementation, or an extension of the current one. How to -handle the variable number of attributes in the attribs array? - -bool operator()(const Vertex & a, const Vertex & b) const -{ - if (a.pos != b.pos || a.nor != b.nor || a.tex != b.tex) return false; - if (a.attribs == NULL && b.attribs == NULL) return true; - return 0 == memcmp(a.attribs, b.attribs, ???); -} - -We could use a NIL terminated array, or provide custom user data to the equals functor. - -vertexMap.setUserData((void *)vertexAttribCount); - -bool operator()(const Vertex & a, const Vertex & b, void * userData) const { ... } - -*/ - - - -namespace -{ - struct Material - { - Material() : faceCount(0) {} - Material(const String & str) : name(str), faceCount(0) {} - - String name; - uint faceCount; - }; - - struct Vertex - { - //Vertex() {} - //Vertex(uint p, uint n, uint t0, uint t1, uint c) : pos(p), nor(n), tex0(t0), tex1(t1), col(c) {} - - friend bool operator==(const Vertex & a, const Vertex & b) - { - return a.pos == b.pos && a.nor == b.nor && a.tex[0] == b.tex[0] && a.tex[1] == b.tex[1] && a.col[0] == b.col[0] && a.col[1] == b.col[1] && a.col[2] == b.col[2]; - } - - uint pos; - uint nor; - uint tex[2]; - uint col[3]; - }; - - struct Face - { - uint id; - uint firstIndex; - uint indexCount; - uint material; - uint group; - }; - -} // namespace - - -namespace nv -{ - // This is a much better hash than the default and greatly improves performance! - template <> struct Hash<Vertex> - { - uint operator()(const Vertex & v) const { return v.pos + v.nor + v.tex[0]/* + v.col*/; } - }; -} - -struct MeshBuilder::PrivateData -{ - PrivateData() : currentGroup(NIL), currentMaterial(NIL), maxFaceIndexCount(0) {} - - uint pushVertex(uint p, uint n, uint t0, uint t1, uint c0, uint c1, uint c2); - uint pushVertex(const Vertex & v); - - Array<Vector3> posArray; - Array<Vector3> norArray; - Array<Vector2> texArray[2]; - Array<Vector4> colArray[3]; - - Array<Vertex> vertexArray; - HashMap<Vertex, uint> vertexMap; - - HashMap<String, uint> materialMap; - Array<Material> materialArray; - - uint currentGroup; - uint currentMaterial; - - Array<uint> indexArray; - Array<Face> faceArray; - - uint maxFaceIndexCount; -}; - - -uint MeshBuilder::PrivateData::pushVertex(uint p, uint n, uint t0, uint t1, uint c0, uint c1, uint c2) -{ - Vertex v; - v.pos = p; - v.nor = n; - v.tex[0] = t0; - v.tex[1] = t1; - v.col[0] = c0; - v.col[1] = c1; - v.col[2] = c2; - return pushVertex(v); -} - -uint MeshBuilder::PrivateData::pushVertex(const Vertex & v) -{ - // Lookup vertex v in map. - uint idx; - if (vertexMap.get(v, &idx)) - { - return idx; - } - - idx = vertexArray.count(); - vertexArray.pushBack(v); - vertexMap.add(v, idx); - - return idx; -} - - -MeshBuilder::MeshBuilder() : d(new PrivateData()) -{ -} - -MeshBuilder::~MeshBuilder() -{ - nvDebugCheck(d != NULL); - delete d; -} - - -// Builder methods. -uint MeshBuilder::addPosition(const Vector3 & v) -{ - d->posArray.pushBack(validate(v)); - return d->posArray.count() - 1; -} - -uint MeshBuilder::addNormal(const Vector3 & v) -{ - d->norArray.pushBack(validate(v)); - return d->norArray.count() - 1; -} - -uint MeshBuilder::addTexCoord(const Vector2 & v, uint set/*=0*/) -{ - d->texArray[set].pushBack(validate(v)); - return d->texArray[set].count() - 1; -} - -uint MeshBuilder::addColor(const Vector4 & v, uint set/*=0*/) -{ - d->colArray[set].pushBack(validate(v)); - return d->colArray[set].count() - 1; -} - -void MeshBuilder::beginGroup(uint id) -{ - d->currentGroup = id; -} - -void MeshBuilder::endGroup() -{ - d->currentGroup = NIL; -} - -// Add named material, check for uniquenes. -uint MeshBuilder::addMaterial(const char * name) -{ - uint index; - if (d->materialMap.get(name, &index)) { - nvDebugCheck(d->materialArray[index].name == name); - } - else { - index = d->materialArray.count(); - d->materialMap.add(name, index); - - Material material(name); - d->materialArray.append(material); - } - return index; -} - -void MeshBuilder::beginMaterial(uint id) -{ - d->currentMaterial = id; -} - -void MeshBuilder::endMaterial() -{ - d->currentMaterial = NIL; -} - -void MeshBuilder::beginPolygon(uint id/*=0*/) -{ - Face face; - face.id = id; - face.firstIndex = d->indexArray.count(); - face.indexCount = 0; - face.material = d->currentMaterial; - face.group = d->currentGroup; - - d->faceArray.pushBack(face); -} - -uint MeshBuilder::addVertex(uint p, uint n/*= NIL*/, uint t0/*= NIL*/, uint t1/*= NIL*/, uint c0/*= NIL*/, uint c1/*= NIL*/, uint c2/*= NIL*/) -{ - // @@ In theory there's no need to add vertices before faces, but I'm adding this to debug problems in our maya exporter: - nvDebugCheck(p < d->posArray.count()); - nvDebugCheck(n == NIL || n < d->norArray.count()); - nvDebugCheck(t0 == NIL || t0 < d->texArray[0].count()); - nvDebugCheck(t1 == NIL || t1 < d->texArray[1].count()); - //nvDebugCheck(c0 == NIL || c0 < d->colArray[0].count()); - if (c0 > d->colArray[0].count()) c0 = NIL; // @@ This seems to be happening in loc_swamp_catwalk.mb! No idea why. - nvDebugCheck(c1 == NIL || c1 < d->colArray[1].count()); - nvDebugCheck(c2 == NIL || c2 < d->colArray[2].count()); - - uint idx = d->pushVertex(p, n, t0, t1, c0, c1, c2); - d->indexArray.pushBack(idx); - d->faceArray.back().indexCount++; - return idx; -} - -uint MeshBuilder::addVertex(const Vector3 & pos) -{ - uint p = addPosition(pos); - return addVertex(p); -} - -#if 0 -uint MeshBuilder::addVertex(const Vector3 & pos, const Vector3 & nor, const Vector2 & tex0, const Vector2 & tex1, const Vector4 & col0, const Vector4 & col1) -{ - uint p = addPosition(pos); - uint n = addNormal(nor); - uint t0 = addTexCoord(tex0, 0); - uint t1 = addTexCoord(tex1, 1); - uint c0 = addColor(col0); - uint c1 = addColor(col1); - return addVertex(p, n, t0, t1, c0, c1); -} -#endif - -// Return true if the face is valid and was added to the mesh. -bool MeshBuilder::endPolygon() -{ - const Face & face = d->faceArray.back(); - const uint count = face.indexCount; - - // Validate polygon here. - bool invalid = count <= 2; - - if (!invalid) { - // Skip zero area polygons. Or polygons with degenerate edges (which will result in zero-area triangles). - const uint first = face.firstIndex; - for (uint j = count - 1, i = 0; i < count; j = i, i++) { - uint v0 = d->indexArray[first + i]; - uint v1 = d->indexArray[first + j]; - - uint p0 = d->vertexArray[v0].pos; - uint p1 = d->vertexArray[v1].pos; - - if (p0 == p1) { - invalid = true; - break; - } - - if (equal(d->posArray[p0], d->posArray[p1], FLT_EPSILON)) { - invalid = true; - break; - } - } - - uint v0 = d->indexArray[first]; - uint p0 = d->vertexArray[v0].pos; - Vector3 x0 = d->posArray[p0]; - - float area = 0.0f; - for (uint j = 1, i = 2; i < count; j = i, i++) { - uint v1 = d->indexArray[first + i]; - uint v2 = d->indexArray[first + j]; - - uint p1 = d->vertexArray[v1].pos; - uint p2 = d->vertexArray[v2].pos; - - Vector3 x1 = d->posArray[p1]; - Vector3 x2 = d->posArray[p2]; - - area += length(cross(x1-x0, x2-x0)); - } - - if (0.5 * area < 1e-6) { // Reduce this threshold if artists have legitimate complains. - invalid = true; - } - - // @@ This is not complete. We may still get zero area triangles after triangulation. - // However, our plugin triangulates before building the mesh, so hopefully that's not a problem. - - } - - if (invalid) - { - d->indexArray.resize(d->indexArray.size() - count); - d->faceArray.popBack(); - return false; - } - else - { - if (d->currentMaterial != NIL) { - d->materialArray[d->currentMaterial].faceCount++; - } - - d->maxFaceIndexCount = max(d->maxFaceIndexCount, count); - return true; - } -} - - -uint MeshBuilder::weldPositions() -{ - Array<uint> xrefs; - Weld<Vector3> weldVector3; - - if (d->posArray.count()) { - // Weld vertex attributes. - weldVector3(d->posArray, xrefs); - - // Remap vertex indices. - const uint vertexCount = d->vertexArray.count(); - for (uint v = 0; v < vertexCount; v++) - { - Vertex & vertex = d->vertexArray[v]; - if (vertex.pos != NIL) vertex.pos = xrefs[vertex.pos]; - } - } - - return d->posArray.count(); -} - -uint MeshBuilder::weldNormals() -{ - Array<uint> xrefs; - Weld<Vector3> weldVector3; - - if (d->norArray.count()) { - // Weld vertex attributes. - weldVector3(d->norArray, xrefs); - - // Remap vertex indices. - const uint vertexCount = d->vertexArray.count(); - for (uint v = 0; v < vertexCount; v++) - { - Vertex & vertex = d->vertexArray[v]; - if (vertex.nor != NIL) vertex.nor = xrefs[vertex.nor]; - } - } - - return d->norArray.count(); -} - -uint MeshBuilder::weldTexCoords(uint set/*=0*/) -{ - Array<uint> xrefs; - Weld<Vector2> weldVector2; - - if (d->texArray[set].count()) { - // Weld vertex attributes. - weldVector2(d->texArray[set], xrefs); - - // Remap vertex indices. - const uint vertexCount = d->vertexArray.count(); - for (uint v = 0; v < vertexCount; v++) - { - Vertex & vertex = d->vertexArray[v]; - if (vertex.tex[set] != NIL) vertex.tex[set] = xrefs[vertex.tex[set]]; - } - } - - return d->texArray[set].count(); -} - -uint MeshBuilder::weldColors(uint set/*=0*/) -{ - Array<uint> xrefs; - Weld<Vector4> weldVector4; - - if (d->colArray[set].count()) { - // Weld vertex attributes. - weldVector4(d->colArray[set], xrefs); - - // Remap vertex indices. - const uint vertexCount = d->vertexArray.count(); - for (uint v = 0; v < vertexCount; v++) - { - Vertex & vertex = d->vertexArray[v]; - if (vertex.col[set] != NIL) vertex.col[set] = xrefs[vertex.col[set]]; - } - } - - return d->colArray[set].count(); -} - -void MeshBuilder::weldVertices() { - - if (d->vertexArray.count() == 0) { - // Nothing to do. - return; - } - - Array<uint> xrefs; - Weld<Vertex> weldVertex; - - // Weld vertices. - weldVertex(d->vertexArray, xrefs); - - // Remap face indices. - const uint indexCount = d->indexArray.count(); - for (uint i = 0; i < indexCount; i++) - { - d->indexArray[i] = xrefs[d->indexArray[i]]; - } - - // Remap vertex map. - foreach(i, d->vertexMap) - { - d->vertexMap[i].value = xrefs[d->vertexMap[i].value]; - } -} - - -void MeshBuilder::optimize() -{ - if (d->vertexArray.count() == 0) - { - return; - } - - weldPositions(); - weldNormals(); - weldTexCoords(0); - weldTexCoords(1); - weldColors(); - - weldVertices(); -} - - - - - - -void MeshBuilder::removeUnusedMaterials(Array<uint> & newMaterialId) -{ - uint materialCount = d->materialArray.count(); - - // Reset face counts. - for (uint i = 0; i < materialCount; i++) { - d->materialArray[i].faceCount = 0; - } - - // Count faces. - foreach(i, d->faceArray) { - Face & face = d->faceArray[i]; - - if (face.material != NIL) { - nvDebugCheck(face.material < materialCount); - - d->materialArray[face.material].faceCount++; - } - } - - // Remove unused materials. - newMaterialId.resize(materialCount); - - for (uint i = 0, m = 0; i < materialCount; i++) - { - if (d->materialArray[m].faceCount > 0) - { - newMaterialId[i] = m++; - } - else - { - newMaterialId[i] = NIL; - d->materialArray.removeAt(m); - } - } - - materialCount = d->materialArray.count(); - - // Update face material ids. - foreach(i, d->faceArray) { - Face & face = d->faceArray[i]; - - if (face.material != NIL) { - uint id = newMaterialId[face.material]; - nvDebugCheck(id != NIL && id < materialCount); - - face.material = id; - } - } -} - -void MeshBuilder::sortFacesByGroup() -{ - const uint faceCount = d->faceArray.count(); - - Array<uint> faceGroupArray; - faceGroupArray.resize(faceCount); - - for (uint i = 0; i < faceCount; i++) { - faceGroupArray[i] = d->faceArray[i].group; - } - - RadixSort radix; - radix.sort(faceGroupArray); - - Array<Face> newFaceArray; - newFaceArray.resize(faceCount); - - for (uint i = 0; i < faceCount; i++) { - newFaceArray[i] = d->faceArray[radix.rank(i)]; - } - - swap(newFaceArray, d->faceArray); -} - -void MeshBuilder::sortFacesByMaterial() -{ - const uint faceCount = d->faceArray.count(); - - Array<uint> faceMaterialArray; - faceMaterialArray.resize(faceCount); - - for (uint i = 0; i < faceCount; i++) { - faceMaterialArray[i] = d->faceArray[i].material; - } - - RadixSort radix; - radix.sort(faceMaterialArray); - - Array<Face> newFaceArray; - newFaceArray.resize(faceCount); - - for (uint i = 0; i < faceCount; i++) { - newFaceArray[i] = d->faceArray[radix.rank(i)]; - } - - swap(newFaceArray, d->faceArray); -} - - -void MeshBuilder::reset() -{ - nvDebugCheck(d != NULL); - delete d; - d = new PrivateData(); -} - -void MeshBuilder::done() -{ - if (d->currentGroup != NIL) { - endGroup(); - } - - if (d->currentMaterial != NIL) { - endMaterial(); - } -} - -// Hints. -void MeshBuilder::hintTriangleCount(uint count) -{ - d->indexArray.reserve(d->indexArray.count() + count * 4); -} - -void MeshBuilder::hintVertexCount(uint count) -{ - d->vertexArray.reserve(d->vertexArray.count() + count); - d->vertexMap.resize(d->vertexMap.count() + count); -} - -void MeshBuilder::hintPositionCount(uint count) -{ - d->posArray.reserve(d->posArray.count() + count); -} - -void MeshBuilder::hintNormalCount(uint count) -{ - d->norArray.reserve(d->norArray.count() + count); -} - -void MeshBuilder::hintTexCoordCount(uint count, uint set/*=0*/) -{ - d->texArray[set].reserve(d->texArray[set].count() + count); -} - -void MeshBuilder::hintColorCount(uint count, uint set/*=0*/) -{ - d->colArray[set].reserve(d->colArray[set].count() + count); -} - - -// Helpers. -void MeshBuilder::addTriangle(uint v0, uint v1, uint v2) -{ - beginPolygon(); - addVertex(v0); - addVertex(v1); - addVertex(v2); - endPolygon(); -} - -void MeshBuilder::addQuad(uint v0, uint v1, uint v2, uint v3) -{ - beginPolygon(); - addVertex(v0); - addVertex(v1); - addVertex(v2); - addVertex(v3); - endPolygon(); -} - - -// Get tri mesh. -TriMesh * MeshBuilder::buildTriMesh() const -{ - const uint faceCount = d->faceArray.count(); - uint triangleCount = 0; - for (uint f = 0; f < faceCount; f++) { - triangleCount += d->faceArray[f].indexCount - 2; - } - - const uint vertexCount = d->vertexArray.count(); - TriMesh * mesh = new TriMesh(triangleCount, vertexCount); - - // Build faces. - Array<TriMesh::Face> & faces = mesh->faces(); - - for(uint f = 0; f < faceCount; f++) - { - int firstIndex = d->faceArray[f].firstIndex; - int indexCount = d->faceArray[f].indexCount; - - int v0 = d->indexArray[firstIndex + 0]; - int v1 = d->indexArray[firstIndex + 1]; - - for(int t = 0; t < indexCount - 2; t++) { - int v2 = d->indexArray[firstIndex + t + 2]; - - TriMesh::Face face; - face.id = faces.count(); - face.v[0] = v0; - face.v[1] = v1; - face.v[2] = v2; - faces.append(face); - - v1 = v2; - } - } - - // Build vertices. - Array<BaseMesh::Vertex> & vertices = mesh->vertices(); - - for(uint i = 0; i < vertexCount; i++) - { - BaseMesh::Vertex vertex; - vertex.id = i; - if (d->vertexArray[i].pos != NIL) vertex.pos = d->posArray[d->vertexArray[i].pos]; - if (d->vertexArray[i].nor != NIL) vertex.nor = d->norArray[d->vertexArray[i].nor]; - if (d->vertexArray[i].tex[0] != NIL) vertex.tex = d->texArray[0][d->vertexArray[i].tex[0]]; - - vertices.append(vertex); - } - - return mesh; -} - -// Get quad/tri mesh. -QuadTriMesh * MeshBuilder::buildQuadTriMesh() const -{ - const uint faceCount = d->faceArray.count(); - const uint vertexCount = d->vertexArray.count(); - QuadTriMesh * mesh = new QuadTriMesh(faceCount, vertexCount); - - // Build faces. - Array<QuadTriMesh::Face> & faces = mesh->faces(); - - for (uint f = 0; f < faceCount; f++) - { - int firstIndex = d->faceArray[f].firstIndex; - int indexCount = d->faceArray[f].indexCount; - - QuadTriMesh::Face face; - face.id = f; - - face.v[0] = d->indexArray[firstIndex + 0]; - face.v[1] = d->indexArray[firstIndex + 1]; - face.v[2] = d->indexArray[firstIndex + 2]; - - // Only adds triangles and quads. Ignores polygons. - if (indexCount == 3) { - face.v[3] = NIL; - faces.append(face); - } - else if (indexCount == 4) { - face.v[3] = d->indexArray[firstIndex + 3]; - faces.append(face); - } - } - - // Build vertices. - Array<BaseMesh::Vertex> & vertices = mesh->vertices(); - - for(uint i = 0; i < vertexCount; i++) - { - BaseMesh::Vertex vertex; - vertex.id = i; - if (d->vertexArray[i].pos != NIL) vertex.pos = d->posArray[d->vertexArray[i].pos]; - if (d->vertexArray[i].nor != NIL) vertex.nor = d->norArray[d->vertexArray[i].nor]; - if (d->vertexArray[i].tex[0] != NIL) vertex.tex = d->texArray[0][d->vertexArray[i].tex[0]]; - - vertices.append(vertex); - } - - return mesh; -} - -// Get half edge mesh. -HalfEdge::Mesh * MeshBuilder::buildHalfEdgeMesh(bool weldPositions, Error * error/*=NULL*/, Array<uint> * badFaces/*=NULL*/) const -{ - if (error != NULL) *error = Error_None; - - const uint vertexCount = d->vertexArray.count(); - AutoPtr<HalfEdge::Mesh> mesh(new HalfEdge::Mesh()); - - for(uint v = 0; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->addVertex(d->posArray[d->vertexArray[v].pos]); - if (d->vertexArray[v].nor != NIL) vertex->nor = d->norArray[d->vertexArray[v].nor]; - if (d->vertexArray[v].tex[0] != NIL) vertex->tex = Vector2(d->texArray[0][d->vertexArray[v].tex[0]]); - if (d->vertexArray[v].col[0] != NIL) vertex->col = d->colArray[0][d->vertexArray[v].col[0]]; - } - - if (weldPositions) { - mesh->linkColocals(); - } - else { - // Build canonical map from position indices. - Array<uint> canonicalMap(vertexCount); - - foreach (i, d->vertexArray) { - canonicalMap.append(d->vertexArray[i].pos); - } - - mesh->linkColocalsWithCanonicalMap(canonicalMap); - } - - const uint faceCount = d->faceArray.count(); - for (uint f = 0; f < faceCount; f++) - { - const uint firstIndex = d->faceArray[f].firstIndex; - const uint indexCount = d->faceArray[f].indexCount; - - HalfEdge::Face * face = mesh->addFace(d->indexArray, firstIndex, indexCount); - - // @@ This is too late, removing the face here will leave the mesh improperly connected. - /*if (face->area() <= FLT_EPSILON) { - mesh->remove(face); - face = NULL; - }*/ - - if (face == NULL) { - // Non manifold mesh. - if (error != NULL) *error = Error_NonManifoldEdge; - if (badFaces != NULL) { - badFaces->append(d->faceArray[f].id); - } - //return NULL; // IC: Ignore error and continue building the mesh. - } - - if (face != NULL) { - face->group = d->faceArray[f].group; - face->material = d->faceArray[f].material; - } - } - - mesh->linkBoundary(); - - // We cannot fix functions here, because this would introduce new vertices and these vertices won't have the corresponding builder data. - - // Maybe the builder should perform the search for T-junctions and update the vertex data directly. - - // For now, we don't fix T-junctions at export time, but only during parameterization. - - //mesh->fixBoundaryJunctions(); - - //mesh->sewBoundary(); - - return mesh.release(); -} - - -bool MeshBuilder::buildPositions(Array<Vector3> & positionArray) -{ - const uint vertexCount = d->vertexArray.count(); - positionArray.resize(vertexCount); - - for (uint v = 0; v < vertexCount; v++) - { - nvDebugCheck(d->vertexArray[v].pos != NIL); - positionArray[v] = d->posArray[d->vertexArray[v].pos]; - } - - return true; -} - -bool MeshBuilder::buildNormals(Array<Vector3> & normalArray) -{ - bool anyNormal = false; - - const uint vertexCount = d->vertexArray.count(); - normalArray.resize(vertexCount); - - for (uint v = 0; v < vertexCount; v++) - { - if (d->vertexArray[v].nor == NIL) { - normalArray[v] = Vector3(0, 0, 1); - } - else { - anyNormal = true; - normalArray[v] = d->norArray[d->vertexArray[v].nor]; - } - } - - return anyNormal; -} - -bool MeshBuilder::buildTexCoords(Array<Vector2> & texCoordArray, uint set/*=0*/) -{ - bool anyTexCoord = false; - - const uint vertexCount = d->vertexArray.count(); - texCoordArray.resize(vertexCount); - - for (uint v = 0; v < vertexCount; v++) - { - if (d->vertexArray[v].tex[set] == NIL) { - texCoordArray[v] = Vector2(0, 0); - } - else { - anyTexCoord = true; - texCoordArray[v] = d->texArray[set][d->vertexArray[v].tex[set]]; - } - } - - return anyTexCoord; -} - -bool MeshBuilder::buildColors(Array<Vector4> & colorArray, uint set/*=0*/) -{ - bool anyColor = false; - - const uint vertexCount = d->vertexArray.count(); - colorArray.resize(vertexCount); - - for (uint v = 0; v < vertexCount; v++) - { - if (d->vertexArray[v].col[set] == NIL) { - colorArray[v] = Vector4(0, 0, 0, 1); - } - else { - anyColor = true; - colorArray[v] = d->colArray[set][d->vertexArray[v].col[set]]; - } - } - - return anyColor; -} - -void MeshBuilder::buildVertexToPositionMap(Array<int> &map) -{ - const uint vertexCount = d->vertexArray.count(); - map.resize(vertexCount); - - foreach (i, d->vertexArray) { - map[i] = d->vertexArray[i].pos; - } -} - - - -uint MeshBuilder::vertexCount() const -{ - return d->vertexArray.count(); -} - - -uint MeshBuilder::positionCount() const -{ - return d->posArray.count(); -} - -uint MeshBuilder::normalCount() const -{ - return d->norArray.count(); -} - -uint MeshBuilder::texCoordCount(uint set/*=0*/) const -{ - return d->texArray[set].count(); -} - -uint MeshBuilder::colorCount(uint set/*=0*/) const -{ - return d->colArray[set].count(); -} - - -uint MeshBuilder::materialCount() const -{ - return d->materialArray.count(); -} - -const char * MeshBuilder::material(uint i) const -{ - return d->materialArray[i].name; -} - - -uint MeshBuilder::positionIndex(uint vertex) const -{ - return d->vertexArray[vertex].pos; -} -uint MeshBuilder::normalIndex(uint vertex) const -{ - return d->vertexArray[vertex].nor; -} -uint MeshBuilder::texCoordIndex(uint vertex, uint set/*=0*/) const -{ - return d->vertexArray[vertex].tex[set]; -} -uint MeshBuilder::colorIndex(uint vertex, uint set/*=0*/) const -{ - return d->vertexArray[vertex].col[set]; -} diff --git a/thirdparty/thekla_atlas/nvmesh/MeshBuilder.h b/thirdparty/thekla_atlas/nvmesh/MeshBuilder.h deleted file mode 100644 index 5b3af3fc1d..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/MeshBuilder.h +++ /dev/null @@ -1,119 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_MESHBUILDER_H -#define NV_MESH_MESHBUILDER_H - -#include "nvmesh.h" -#include "nvcore/Array.h" -#include "nvmath/Vector.h" - -namespace nv -{ - class String; - class TriMesh; - class QuadTriMesh; - namespace HalfEdge { class Mesh; } - - - /// Mesh builder is a helper class for importers. - /// Ideally it should handle any vertex data, but for now it only accepts positions, - /// normals and texcoords. - class MeshBuilder - { - NV_FORBID_COPY(MeshBuilder); - NV_FORBID_HEAPALLOC(); - public: - MeshBuilder(); - ~MeshBuilder(); - - // Builder methods. - uint addPosition(const Vector3 & v); - uint addNormal(const Vector3 & v); - uint addTexCoord(const Vector2 & v, uint set = 0); - uint addColor(const Vector4 & v, uint set = 0); - - void beginGroup(uint id); - void endGroup(); - - uint addMaterial(const char * name); - void beginMaterial(uint id); - void endMaterial(); - - void beginPolygon(uint id = 0); - uint addVertex(uint p, uint n = NIL, uint t0 = NIL, uint t1 = NIL, uint c0 = NIL, uint c1 = NIL, uint c2 = NIL); - uint addVertex(const Vector3 & p); - //uint addVertex(const Vector3 & p, const Vector3 & n, const Vector2 & t0 = Vector2(0), const Vector2 & t1 = Vector2(0), const Vector4 & c0 = Vector4(0), const Vector4 & c1 = Vector4(0)); - bool endPolygon(); - - uint weldPositions(); - uint weldNormals(); - uint weldTexCoords(uint set = 0); - uint weldColors(uint set = 0); - void weldVertices(); - - void optimize(); // eliminate duplicate components and duplicate vertices. - void removeUnusedMaterials(Array<uint> & newMaterialId); - void sortFacesByGroup(); - void sortFacesByMaterial(); - - void done(); - void reset(); - - // Hints. - void hintTriangleCount(uint count); - void hintVertexCount(uint count); - void hintPositionCount(uint count); - void hintNormalCount(uint count); - void hintTexCoordCount(uint count, uint set = 0); - void hintColorCount(uint count, uint set = 0); - - // Helpers. - void addTriangle(uint v0, uint v1, uint v2); - void addQuad(uint v0, uint v1, uint v2, uint v3); - - // Get result. - TriMesh * buildTriMesh() const; - QuadTriMesh * buildQuadTriMesh() const; - - enum Error { - Error_None, - Error_NonManifoldEdge, - Error_NonManifoldVertex, - }; - - HalfEdge::Mesh * buildHalfEdgeMesh(bool weldPositions, Error * error = NULL, Array<uint> * badFaces = NULL) const; - - bool buildPositions(Array<Vector3> & positionArray); - bool buildNormals(Array<Vector3> & normalArray); - bool buildTexCoords(Array<Vector2> & texCoordArray, uint set = 0); - bool buildColors(Array<Vector4> & colorArray, uint set = 0); - void buildVertexToPositionMap(Array<int> & map); - - - // Expose attribute indices of the unified vertex array. - uint vertexCount() const; - - uint positionCount() const; - uint normalCount() const; - uint texCoordCount(uint set = 0) const; - uint colorCount(uint set = 0) const; - - uint materialCount() const; - const char * material(uint i) const; - - uint positionIndex(uint vertex) const; - uint normalIndex(uint vertex) const; - uint texCoordIndex(uint vertex, uint set = 0) const; - uint colorIndex(uint vertex, uint set = 0) const; - - private: - - struct PrivateData; - PrivateData * d; - - }; - -} // nv namespace - -#endif // NV_MESH_MESHBUILDER_H diff --git a/thirdparty/thekla_atlas/nvmesh/MeshTopology.cpp b/thirdparty/thekla_atlas/nvmesh/MeshTopology.cpp deleted file mode 100644 index e7e1dce421..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/MeshTopology.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "nvmesh.h" // pch - -#include "nvcore/Array.h" -#include "nvcore/BitArray.h" - -#include "nvmesh/MeshTopology.h" -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Edge.h" -#include "nvmesh/halfedge/Face.h" - -using namespace nv; - -void MeshTopology::buildTopologyInfo(const HalfEdge::Mesh * mesh) -{ - const uint vertexCount = mesh->colocalVertexCount(); - const uint faceCount = mesh->faceCount(); - const uint edgeCount = mesh->edgeCount(); - - nvDebug( "--- Building mesh topology:\n" ); - - Array<uint> stack(faceCount); - - BitArray bitFlags(faceCount); - bitFlags.clearAll(); - - // Compute connectivity. - nvDebug( "--- Computing connectivity.\n" ); - - m_connectedCount = 0; - - for(uint f = 0; f < faceCount; f++ ) { - if( bitFlags.bitAt(f) == false ) { - m_connectedCount++; - - stack.pushBack( f ); - while( !stack.isEmpty() ) { - - const uint top = stack.back(); - nvCheck(top != NIL); - stack.popBack(); - - if( bitFlags.bitAt(top) == false ) { - bitFlags.setBitAt(top); - - const HalfEdge::Face * face = mesh->faceAt(top); - const HalfEdge::Edge * firstEdge = face->edge; - const HalfEdge::Edge * edge = firstEdge; - - do { - const HalfEdge::Face * neighborFace = edge->pair->face; - if (neighborFace != NULL) { - stack.pushBack(neighborFace->id); - } - edge = edge->next; - } while(edge != firstEdge); - } - } - } - } - nvCheck(stack.isEmpty()); - nvDebug( "--- %d connected components.\n", m_connectedCount ); - - - // Count boundary loops. - nvDebug( "--- 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 (uint e = 0; e < edgeCount; e++) - { - const HalfEdge::Edge * startEdge = mesh->edgeAt(e); - if (startEdge != NULL && startEdge->isBoundary() && bitFlags.bitAt(e) == false) - { - nvDebugCheck(startEdge->face != NULL); - nvDebugCheck(startEdge->pair->face == NULL); - - startEdge = startEdge->pair; - - m_boundaryCount++; - - const HalfEdge::Edge * edge = startEdge; - do { - bitFlags.setBitAt(edge->id / 2); - edge = edge->next; - } while(startEdge != edge); - } - } - nvDebug("--- %d boundary loops found.\n", m_boundaryCount ); - - - // Compute euler number. - m_eulerNumber = vertexCount - edgeCount + faceCount; - nvDebug("--- 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; - nvDebug("--- Genus: %d.\n", m_genus); - } -} - - -/*static*/ bool MeshTopology::isQuadOnly(const HalfEdge::Mesh * mesh) -{ - const uint faceCount = mesh->faceCount(); - for(uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = mesh->faceAt(f); - if (face->edgeCount() != 4) { - return false; - } - } - - return true; -} diff --git a/thirdparty/thekla_atlas/nvmesh/MeshTopology.h b/thirdparty/thekla_atlas/nvmesh/MeshTopology.h deleted file mode 100644 index c3d7477b15..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/MeshTopology.h +++ /dev/null @@ -1,66 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_MESHTOPOLOGY_H -#define NV_MESH_MESHTOPOLOGY_H - -#include <nvmesh/nvmesh.h> - -namespace nv -{ - namespace HalfEdge { class Mesh; } - class MeshAdjacency; - - /// Mesh topology information. - class MeshTopology - { - public: - MeshTopology(const HalfEdge::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*/; } - - /// Return the number of connected components. - int connectedCount() const { return m_connectedCount; } - - /// Return the number of open holes. - int holeCount() const { return m_boundaryCount; } - - /// Return the genus of the mesh. - int genus() const { return m_genus; } - - /// Return the euler number of the mesh. - int euler() const { return m_eulerNumber; } - - - static bool isQuadOnly(const HalfEdge::Mesh * mesh); - - - private: - - NVMESH_API void buildTopologyInfo(const HalfEdge::Mesh * mesh); - - 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; - }; - -} // nv namespace - -#endif // NV_MESH_MESHTOPOLOGY_H diff --git a/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.cpp b/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.cpp deleted file mode 100644 index 64a071abe9..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "QuadTriMesh.h" -#include "Stream.h" - -using namespace nv; - - -bool QuadTriMesh::isQuadFace(uint i) const -{ - return m_faceArray[i].isQuadFace(); -} - -const QuadTriMesh::Vertex & QuadTriMesh::faceVertex(uint f, uint v) const -{ - if (isQuadFace(f)) nvDebugCheck(v < 4); - else nvDebugCheck(v < 3); - - const Face & face = this->faceAt(f); - return this->vertexAt(face.v[v]); -} - - -namespace nv -{ - static Stream & operator<< (Stream & s, QuadTriMesh::Face & face) - { - return s << face.id << face.v[0] << face.v[1] << face.v[2] << face.v[3]; - } - - Stream & operator<< (Stream & s, QuadTriMesh & mesh) - { - return s << mesh.m_faceArray << (BaseMesh &) mesh; - } -} - diff --git a/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.h b/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.h deleted file mode 100644 index b8465f2db0..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.h +++ /dev/null @@ -1,60 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MESH_QUADTRIMESH_H -#define NV_MESH_QUADTRIMESH_H - -#include "nvcore/Array.h" -#include "nvmath/Vector.h" -#include "nvmesh/nvmesh.h" -#include "nvmesh/BaseMesh.h" - -namespace nv -{ - class Stream; - - /// Mixed quad/triangle mesh. - class QuadTriMesh : public BaseMesh - { - public: - struct Face; - typedef BaseMesh::Vertex Vertex; - - QuadTriMesh() {}; - QuadTriMesh(uint faceCount, uint vertexCount) : BaseMesh(vertexCount), m_faceArray(faceCount) {} - - // Face methods. - uint faceCount() const { return m_faceArray.count(); } - - const Face & faceAt(uint i) const { return m_faceArray[i]; } - Face & faceAt(uint i) { return m_faceArray[i]; } - - const Array<Face> & faces() const { return m_faceArray; } - Array<Face> & faces() { return m_faceArray; } - - bool isQuadFace(uint i) const; - - const Vertex & faceVertex(uint f, uint v) const; - - friend Stream & operator<< (Stream & s, QuadTriMesh & obj); - - private: - - Array<Face> m_faceArray; - - }; - - - /// QuadTriMesh face. - struct QuadTriMesh::Face - { - uint id; - uint v[4]; - - bool isQuadFace() const { return v[3] != NIL; } - }; - -} // nv namespace - - -#endif // NV_MESH_QUADTRIMESH_H diff --git a/thirdparty/thekla_atlas/nvmesh/TriMesh.cpp b/thirdparty/thekla_atlas/nvmesh/TriMesh.cpp deleted file mode 100644 index bf10a474fb..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/TriMesh.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "TriMesh.h" - -using namespace nv; - - -/// Triangle mesh. -Vector3 TriMesh::faceNormal(uint f) const -{ - const Face & face = this->faceAt(f); - const Vector3 & p0 = this->vertexAt(face.v[0]).pos; - const Vector3 & p1 = this->vertexAt(face.v[1]).pos; - const Vector3 & p2 = this->vertexAt(face.v[2]).pos; - return normalizeSafe(cross(p1 - p0, p2 - p0), Vector3(0.0f), 0.0f); -} - -/// Get face vertex. -const TriMesh::Vertex & TriMesh::faceVertex(uint f, uint v) const -{ - nvDebugCheck(v < 3); - const Face & face = this->faceAt(f); - return this->vertexAt(face.v[v]); -} - diff --git a/thirdparty/thekla_atlas/nvmesh/TriMesh.h b/thirdparty/thekla_atlas/nvmesh/TriMesh.h deleted file mode 100644 index bc5672c1ac..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/TriMesh.h +++ /dev/null @@ -1,51 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MESH_TRIMESH_H -#define NV_MESH_TRIMESH_H - -#include "nvcore/Array.h" -#include "nvmath/Vector.inl" -#include "nvmesh/nvmesh.h" -#include "nvmesh/BaseMesh.h" - -namespace nv -{ - /// Triangle mesh. - class TriMesh : public BaseMesh - { - public: - struct Face; - typedef BaseMesh::Vertex Vertex; - - TriMesh(uint faceCount, uint vertexCount) : BaseMesh(vertexCount), m_faceArray(faceCount) {} - - // Face methods. - uint faceCount() const { return m_faceArray.count(); } - const Face & faceAt(uint i) const { return m_faceArray[i]; } - Face & faceAt(uint i) { return m_faceArray[i]; } - const Array<Face> & faces() const { return m_faceArray; } - Array<Face> & faces() { return m_faceArray; } - - NVMESH_API Vector3 faceNormal(uint f) const; - NVMESH_API const Vertex & faceVertex(uint f, uint v) const; - - friend Stream & operator<< (Stream & s, BaseMesh & obj); - - private: - - Array<Face> m_faceArray; - - }; - - - /// TriMesh face. - struct TriMesh::Face - { - uint id; - uint v[3]; - }; - -} // nv namespace - -#endif // NV_MESH_TRIMESH_H diff --git a/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.cpp b/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.cpp deleted file mode 100644 index 69fd1deb24..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#include "nvmesh.h" // pch - -#include "Bounds.h" - -#include "nvmesh/BaseMesh.h" -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Vertex.h" - -#include "nvmath/Box.inl" - -using namespace nv; - -Box MeshBounds::box(const BaseMesh * mesh) -{ - nvCheck(mesh != NULL); - - Box bounds; - bounds.clearBounds(); - - const uint vertexCount = mesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) - { - const BaseMesh::Vertex & vertex = mesh->vertexAt(v); - bounds.addPointToBounds( vertex.pos ); - } - - return bounds; -} - -Box MeshBounds::box(const HalfEdge::Mesh * mesh) -{ - nvCheck(mesh != NULL); - - Box bounds; - bounds.clearBounds(); - - const uint vertexCount = mesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) - { - const HalfEdge::Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(vertex != NULL); - bounds.addPointToBounds( vertex->pos ); - } - - return bounds; -} - -/*Sphere MeshBounds::sphere(const HalfEdge::Mesh * mesh) -{ - // @@ TODO - return Sphere(); -}*/ diff --git a/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.h b/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.h deleted file mode 100644 index 1cb5b7b905..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.h +++ /dev/null @@ -1,28 +0,0 @@ -// This code is in the public domain -- Ignacio Castaño <castano@gmail.com> - -#pragma once -#ifndef NV_MESH_MESHBOUNDS_H -#define NV_MESH_MESHBOUNDS_H - -#include <nvmath/Sphere.h> -#include <nvmath/Box.h> - -#include <nvmesh/nvmesh.h> - -namespace nv -{ - class BaseMesh; - namespace HalfEdge { class Mesh; } - - // Bounding volumes computation. - namespace MeshBounds - { - Box box(const BaseMesh * mesh); - Box box(const HalfEdge::Mesh * mesh); - - Sphere sphere(const HalfEdge::Mesh * mesh); - } - -} // nv namespace - -#endif // NV_MESH_MESHBOUNDS_H diff --git a/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.cpp b/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.cpp deleted file mode 100644 index e0c271663b..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "Measurements.h" -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Face.h" - -using namespace nv; - -float nv::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(); - } - nvDebugCheck(area >= 0); - - return area; -} - -float nv::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; -} diff --git a/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.h b/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.h deleted file mode 100644 index 0be863b79e..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.h +++ /dev/null @@ -1,18 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#pragma once -#ifndef NV_MESH_MESHMEASUREMENTS_H -#define NV_MESH_MESHMEASUREMENTS_H - -#include "nvmesh/nvmesh.h" - -namespace nv -{ - namespace HalfEdge { class Mesh; } - - float computeSurfaceArea(const HalfEdge::Mesh * mesh); - float computeParametricArea(const HalfEdge::Mesh * mesh); - -} // nv namespace - -#endif // NV_MESH_MESHMEASUREMENTS_H diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.cpp b/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.cpp deleted file mode 100644 index 671650296c..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "nvmesh.h" // pch - -#include "Edge.h" -#include "Vertex.h" - -#include "nvmath/Vector.inl" - -using namespace nv; -using namespace HalfEdge; - -Vector3 Edge::midPoint() const -{ - return (to()->pos + from()->pos) * 0.5f; -} - -float Edge::length() const -{ - return ::length(to()->pos - from()->pos); -} - -// Return angle between this edge and the previous one. -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) / (nv::length(v0) * nv::length(v1))); -} - -bool Edge::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; -} - -/* -Edge * Edge::nextBoundary() { - nvDebugCheck(this->m_pair == NULL); - -} - -Edge * Edge::prevBoundary() { - nvDebugCheck(this->m_pair == NULL); - -} -*/ - - diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.h b/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.h deleted file mode 100644 index 25c47f4860..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.h +++ /dev/null @@ -1,70 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_HALFEDGE_EDGE_H -#define NV_MESH_HALFEDGE_EDGE_H - -#include "nvmath/Vector.h" - -namespace nv -{ - namespace HalfEdge { class Vertex; class Face; class Edge; } - - /// Half edge edge. - class HalfEdge::Edge - { - NV_FORBID_COPY(Edge); - public: - - uint 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(uint 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; } - - // @@ Add these helpers: - //Edge * nextBoundary(); - //Edge * prevBoundary(); - - - // @@ 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 isValid() const; - - // Geometric queries. - Vector3 midPoint() const; - float length() const; - float angle() const; - - }; - -} // nv namespace - - -#endif // NV_MESH_HALFEDGE_EDGE_H diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Face.cpp b/thirdparty/thekla_atlas/nvmesh/halfedge/Face.cpp deleted file mode 100644 index 9f6987154e..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Face.cpp +++ /dev/null @@ -1,268 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "nvmesh.h" // pch - -#include "Face.h" -#include "Vertex.h" - -#include "nvmath/Fitting.h" -#include "nvmath/Plane.h" -#include "nvmath/Vector.inl" - -#include "nvcore/Array.h" - - -using namespace nv; -using namespace HalfEdge; - -/// Get face area. -float Face::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 Face::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; -} - - -/// Get boundary length. -float Face::boundaryLength() const -{ - float bl = 0; - - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - bl += edge->length(); - } - - return bl; -} - - -/// Get face normal. -Vector3 Face::normal() const -{ - Vector3 n(0); - - const Vertex * vertex0 = NULL; - - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - nvCheck(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(); - - 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); - - - // Get face points eliminating duplicates. - /*Array<Vector3> points(4); - - points.append(m_edge->prev()->from()->pos); - - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - nvDebugCheck(edge != NULL); - - const Vector3 & p = edge->from()->pos; - if (points.back() != p) - { - points.append(edge->from()->pos); - } - } - - points.popBack(); - - if (points.count() < 3) - { - // Invalid normal. - return Vector3(0.0f); - } - else - { - // Compute regular normal. - Vector3 normal = normalizeSafe(cross(points[1] - points[0], points[2] - points[0]), Vector3(0.0f), 0.0f); - -#pragma NV_MESSAGE("TODO: make sure these three points are not colinear") - - if (points.count() > 3) - { - // Compute best fitting plane to the points. - Plane plane = Fit::bestPlane(points.count(), points.buffer()); - - // Adjust normal orientation. - if (dot(normal, plane.vector()) > 0) { - normal = plane.vector(); - } - else { - normal = -plane.vector(); - } - } - - nvDebugCheck(isNormalized(normal)); - return normal; - }*/ -} - -Vector3 Face::centroid() const -{ - Vector3 sum(0.0f); - uint count = 0; - - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - sum += edge->from()->pos; - count++; - } - - return sum / float(count); -} - - -bool Face::isValid() const -{ - uint count = 0; - - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - if (edge->face != this) return false; - if (!edge->isValid()) return false; - if (!edge->pair->isValid()) return false; - count++; - } - - if (count < 3) return false; - - return true; -} - - -// Determine if this face contains the given edge. -bool Face::contains(const Edge * e) const -{ - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - if(it.current() == e) return true; - } - return false; -} - -// Returns index in this face of the given edge. -uint Face::edgeIndex(const Edge * e) const -{ - int i = 0; - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance(), i++) - { - if(it.current() == e) return i; - } - return NIL; -} - - -Edge * Face::edgeAt(uint idx) -{ - int i = 0; - for(EdgeIterator it(edges()); !it.isDone(); it.advance(), i++) { - if (i == idx) return it.current(); - } - return NULL; -} -const Edge * Face::edgeAt(uint idx) const -{ - int i = 0; - for(ConstEdgeIterator it(edges()); !it.isDone(); it.advance(), i++) { - if (i == idx) return it.current(); - } - return NULL; -} - - -// Count the number of edges in this face. -uint Face::edgeCount() const -{ - uint count = 0; - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { ++count; } - return count; -} - -// Determine if this is a boundary face. -bool Face::isBoundary() const -{ - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - nvDebugCheck(edge->pair != NULL); - - if (edge->pair->face == NULL) { - return true; - } - } - return false; -} - -// Count the number of boundary edges in the face. -uint Face::boundaryCount() const -{ - uint count = 0; - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - nvDebugCheck(edge->pair != NULL); - - if (edge->pair->face == NULL) { - count++; - } - } - return count; -} diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Face.h b/thirdparty/thekla_atlas/nvmesh/halfedge/Face.h deleted file mode 100644 index 677f8666f0..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Face.h +++ /dev/null @@ -1,106 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_HALFEDGE_FACE_H -#define NV_MESH_HALFEDGE_FACE_H - -#include <nvmesh/halfedge/Edge.h> - -namespace nv -{ - namespace HalfEdge { class Vertex; class Face; class Edge; } - - /// Face of a half-edge mesh. - class HalfEdge::Face - { - NV_FORBID_COPY(Face); - public: - - uint id; - uint16 group; - uint16 material; - Edge * edge; - - - Face(uint id) : id(id), group(~0), material(~0), edge(NULL) {} - - float area() const; - float parametricArea() const; - float boundaryLength() const; - Vector3 normal() const; - Vector3 centroid() const; - - bool isValid() const; - - bool contains(const Edge * e) const; - uint edgeIndex(const Edge * e) const; - - Edge * edgeAt(uint idx); - const Edge * edgeAt(uint idx) const; - - uint edgeCount() const; - bool isBoundary() const; - uint boundaryCount() const; - - - // 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) - { - nvDebugCheck(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 - { - nvDebugCheck(contains(e)); - return ConstEdgeIterator(e); - } - }; - -} // nv namespace - -#endif // NV_MESH_HALFEDGE_FACE_H diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.cpp b/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.cpp deleted file mode 100644 index 0012513bce..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.cpp +++ /dev/null @@ -1,1284 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "nvmesh.h" // pch - -#include "Mesh.h" -#include "Edge.h" -#include "Vertex.h" -#include "Face.h" - -#include "nvmesh/TriMesh.h" -#include "nvmesh/QuadTriMesh.h" -#include "nvmesh/MeshBuilder.h" - -#include "nvmath/Vector.inl" -#include "nvcore/Array.inl" -#include "nvcore/HashMap.inl" - - -using namespace nv; -using namespace HalfEdge; - -Mesh::Mesh() : m_colocalVertexCount(0) -{ - errorCount = 0; -} - -Mesh::Mesh(const Mesh * mesh) -{ - errorCount = 0; - - // Copy mesh vertices. - const uint vertexCount = mesh->vertexCount(); - m_vertexArray.resize(vertexCount); - - for (uint v = 0; v < vertexCount; v++) - { - const Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(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 uint faceCount = mesh->faceCount(); - - Array<uint> indexArray(3); - - for (uint 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.append(vertex->id); - } - - addFace(indexArray); - indexArray.clear(); - } -} - -Mesh::~Mesh() -{ - clear(); -} - - -void Mesh::clear() -{ - deleteAll(m_vertexArray); - m_vertexArray.clear(); - - foreach(i, m_edgeMap) - { - delete m_edgeMap[i].value; - } - //deleteAll(m_edgeArray); // edgeArray only contains 1/2 of the edges! - m_edgeArray.clear(); - m_edgeMap.clear(); - - deleteAll(m_faceArray); - m_faceArray.clear(); -} - - -Vertex * Mesh::addVertex(const Vector3 & pos) -{ - nvDebugCheck(isFinite(pos)); - - Vertex * v = new Vertex(m_vertexArray.count()); - v->pos = pos; - m_vertexArray.append(v); - - return v; - -// return addVertex(m_vertexArray.count(), pos); -} - -/*Vertex * Mesh::addVertex(uint id, const Vector3 & pos) -{ - nvDebugCheck(isFinite(pos)); - - Vertex * v = new Vertex(id); - v->pos = pos; - m_vertexArray.append(v); - - return v; -}*/ - -/*void Mesh::addVertices(const Mesh * mesh) -{ -nvCheck(mesh != NULL); - -// Add mesh vertices -for (uint v = 0; v < vertexCount; v++) -{ -const Vertex * vertex = mesh->vertexAt(v); -nvDebugCheck(vertex != NULL); - -Vertex * v = addVertex(vertex->pos()); -nvDebugCheck(v != NULL); - -v->setNor(vertex->nor()); -v->setTex(vertex->tex()); -} -}*/ - - -/// Link colocal vertices based on geometric location only. -void Mesh::linkColocals() -{ - nvDebug("--- Linking colocals:\n"); - - const uint vertexCount = this->vertexCount(); - HashMap<Vector3, Vertex *> vertexMap(vertexCount); - - for (uint v = 0; v < vertexCount; v++) - { - Vertex * vertex = vertexAt(v); - - Vertex * colocal; - if (vertexMap.get(vertex->pos, &colocal)) - { - colocal->linkColocal(vertex); - } - else - { - vertexMap.add(vertex->pos, vertex); - } - } - - m_colocalVertexCount = vertexMap.count(); - - nvDebug("--- %d vertex positions.\n", m_colocalVertexCount); - - // @@ Remove duplicated vertices? or just leave them as colocals? -} - -void Mesh::linkColocalsWithCanonicalMap(const Array<uint> & canonicalMap) -{ - nvDebug("--- Linking colocals:\n"); - - uint vertexMapSize = 0; - foreach(i, canonicalMap) { - vertexMapSize = max(vertexMapSize, canonicalMap[i] + 1); - } - - Array<Vertex *> vertexMap; - vertexMap.resize(vertexMapSize, NULL); - - m_colocalVertexCount = 0; - - const uint vertexCount = this->vertexCount(); - for (uint v = 0; v < vertexCount; v++) - { - Vertex * vertex = vertexAt(v); - - Vertex * colocal = vertexMap[canonicalMap[v]]; - if (colocal != NULL) - { - nvDebugCheck(vertex->pos == colocal->pos); - colocal->linkColocal(vertex); - } - else - { - vertexMap[canonicalMap[v]] = vertex; - m_colocalVertexCount++; - } - } - - nvDebug("--- %d vertex positions.\n", m_colocalVertexCount); -} - - -Face * Mesh::addFace() -{ - Face * f = new Face(m_faceArray.count()); - m_faceArray.append(f); - return f; -} - -Face * Mesh::addFace(uint v0, uint v1, uint v2) -{ - Array<uint> indexArray(3); - indexArray << v0 << v1 << v2; - return addFace(indexArray, 0, 3); -} - -Face * Mesh::addFace(uint v0, uint v1, uint v2, uint v3) -{ - Array<uint> indexArray(4); - indexArray << v0 << v1 << v2 << v3; - return addFace(indexArray, 0, 4); -} - -Face * Mesh::addFace(const Array<uint> & indexArray) -{ - return addFace(indexArray, 0, indexArray.count()); -} - - -Face * Mesh::addFace(const Array<uint> & indexArray, uint first, uint num) -{ - nvDebugCheck(first < indexArray.count()); - nvDebugCheck(num <= indexArray.count()-first); - nvDebugCheck(num > 2); - - if (!canAddFace(indexArray, first, num)) { - errorCount++; - return NULL; - } - - Face * f = new Face(m_faceArray.count()); - - Edge * firstEdge = NULL; - Edge * last = NULL; - Edge * current = NULL; - - for(uint i = 0; i < num-1; i++) - { - current = addEdge(indexArray[first+i], indexArray[first+i+1]); - nvCheck(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]); - nvCheck(current != NULL && current->face == NULL); - - current->face = f; - - last->setNext(current); - current->setNext(firstEdge); - - f->edge = firstEdge; - m_faceArray.append(f); - - return f; -} - -/*void Mesh::addFaces(const Mesh * mesh) -{ -nvCheck(mesh != NULL); - -Array indexArray; -// Add faces - -}*/ - - -// Return true if the face can be added to the manifold mesh. -bool Mesh::canAddFace(const Array<uint> & indexArray, uint first, uint num) const -{ - for (uint 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 (uint i = 0; i < num; i++) { - - int i0 = indexArray[first + i + 0]; - int i1 = indexArray[first + (i + 1)%num]; - - for (uint j = i + 1; j < num; j++) { - int j0 = indexArray[first + j + 0]; - int j1 = indexArray[first + (j + 1)%num]; - - if (i0 == j0 && i1 == j1) { - return false; - } - } - } - - return true; -} - -// Return true if the edge doesn't exist or doesn't have any adjacent face. -bool Mesh::canAddEdge(uint i, uint j) const -{ - if (i == j) { - // Skip degenerate edges. - 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. - return false; - } - } - - // Make sure edge has not been added yet. - Edge * edge = findEdge(i, j); - - return edge == NULL || edge->face == NULL; // We ignore edges that don't have an adjacent face yet, since this face could become the edge's face. -} - -Edge * Mesh::addEdge(uint i, uint j) -{ - nvCheck(i != j); - - Edge * edge = findEdge(i, j); - - if (edge != NULL) { - // Edge may already exist, but its face must not be set. - nvDebugCheck(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.count()); - - // Add only unpaired edges. - m_edgeArray.append(edge); - } - - edge->vertex = m_vertexArray[i]; - m_edgeMap.add(Key(i,j), edge); - } - - // Face and Next are set by addFace. - - return edge; -} - - -/// Find edge, test all colocals. -Edge * Mesh::findEdge(uint i, uint 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) { - m_edgeMap.get(key, &edge); -#if !defined(_DEBUG) - if (edge != NULL) return edge; -#endif - } - else { - // Make sure that only one edge is found. - nvDebugCheck(!m_edgeMap.get(key)); - } - } - } - - return edge; -} - -/// Link boundary edges once the mesh has been created. -void Mesh::linkBoundary() -{ - nvDebug("--- Linking boundaries:\n"); - - int num = 0; - - // Create boundary edges. - uint edgeCount = this->edgeCount(); - for(uint e = 0; e < edgeCount; e++) - { - Edge * edge = edgeAt(e); - if (edge != NULL && edge->pair == NULL) { - Edge * pair = new Edge(edge->id + 1); - - uint i = edge->from()->id; - uint j = edge->next->from()->id; - - Key key(j,i); - nvCheck(!m_edgeMap.get(key)); - - pair->vertex = m_vertexArray[j]; - m_edgeMap.add(key, pair); - - edge->pair = pair; - pair->pair = edge; - - num++; - } - } - - // Link boundary edges. - for (uint e = 0; e < edgeCount; e++) { - Edge * edge = edgeAt(e); - if (edge != NULL && edge->pair->face == NULL) { - linkBoundaryEdge(edge->pair); - } - } - - nvDebug("--- %d boundary edges.\n", num); -} - -/// Link this boundary edge. -void Mesh::linkBoundaryEdge(Edge * edge) -{ - nvCheck(edge->face == NULL); - - // Make sure next pointer has not been set. @@ We want to be able to relink boundary edges after mesh changes. - //nvCheck(edge->next() == NULL); - - 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. - //nvCheck( edge->vertex()->edge() == NULL || edge->vertex()->edge()->face() != NULL ); - edge->vertex->edge = edge; - } -} - - -/// Convert to tri mesh. -TriMesh * Mesh::toTriMesh() const -{ - uint triangleCount = 0; - - // Count triangle faces. - const uint faceCount = this->faceCount(); - for(uint f = 0; f < faceCount; f++) - { - const Face * face = faceAt(f); - triangleCount += face->edgeCount() - 2; - } - - TriMesh * triMesh = new TriMesh(triangleCount, vertexCount()); - - // Add vertices. - Array<TriMesh::Vertex> & vertices = triMesh->vertices(); - - const uint vertexCount = this->vertexCount(); - for(uint v = 0; v < vertexCount; v++) - { - const Vertex * vertex = vertexAt(v); - - TriMesh::Vertex triVertex; - triVertex.id = vertices.count(); - triVertex.pos = vertex->pos; - triVertex.nor = vertex->nor; - triVertex.tex = vertex->tex; - - vertices.append(triVertex); - } - - // Add triangles. - Array<TriMesh::Face> & triangles = triMesh->faces(); - - for(uint f = 0; f < faceCount; f++) - { - const Face * face = faceAt(f); - - // @@ Triangulate arbitrary polygons correctly. - const uint v0 = face->edge->vertex->id; - uint v1 = face->edge->next->vertex->id; - - for(Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - uint v2 = it.current()->vertex->id; - - // Skip the first two vertices. - if (v2 == v0 || v2 == v1) continue; - - TriMesh::Face triangle; - triangle.id = triangles.count(); - triangle.v[0] = v0; - triangle.v[1] = v1; - triangle.v[2] = v2; - - v1 = v2; - - triangles.append(triangle); - } - } - - return triMesh; -} - -QuadTriMesh * Mesh::toQuadTriMesh() const -{ - MeshBuilder builder; - - const uint vertexCount = this->vertexCount(); - builder.hintVertexCount(vertexCount); - - for(uint v = 0; v < vertexCount; v++) - { - const Vertex * vertex = vertexAt(v); - - builder.addPosition(vertex->pos); - builder.addNormal(vertex->nor); - builder.addTexCoord(vertex->tex); - } - - const uint faceCount = this->faceCount(); - builder.hintTriangleCount(faceCount); - - for(uint f = 0; f < faceCount; f++) - { - const Face * face = faceAt(f); - - builder.beginPolygon(); - - for(Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - uint v = it.current()->vertex->id; - builder.addVertex(v, v, v); - } - - builder.endPolygon(); - } - - builder.done(); - - return builder.buildQuadTriMesh(); -} - - -// Triangulate in place. -void Mesh::triangulate() { - - bool all_triangles = true; - - const uint faceCount = m_faceArray.count(); - for (uint 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. - Array<Edge *> edgeArray; - Array<Face *> faceArray; - - swap(edgeArray, m_edgeArray); - swap(faceArray, m_faceArray); - m_edgeMap.clear(); - - for (uint f = 0; f < faceCount; f++) { - Face * face = faceArray[f]; - - // Trivial fan-like triangulation. - const uint v0 = face->edge->vertex->id; - uint v2, v1 = -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; - } - } - - nvDebugCheck(m_faceArray.count() > faceCount); // triangle count > face count - - linkBoundary(); - - deleteAll(edgeArray); - deleteAll(faceArray); -} - - -/* -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 Mesh::splitBoundaryEdges() { - - Array<Vertex *> boundaryVertices; - - foreach(i, m_vertexArray) { - Vertex * v = m_vertexArray[i]; - if (v->isBoundary()) { - boundaryVertices.append(v); - } - } - - nvDebug("Fixing T-junctions:\n"); - - int splitCount = 0; - - foreach(v, boundaryVertices) { - Vertex * vertex = boundaryVertices[v]; - - Vector3 x0 = vertex->pos; - - // Find edges that this vertex overlaps with. - foreach(e, m_edgeArray) { - //for (uint e = 0; e < m_edgeArray.count(); 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); - - // @@ Snap x0 to x1 or x2, if too close? No, do vertex snapping elsewhere. - /*if (equal(t, 0.0f, 0.01f)) { - //vertex->setPos(x1); - } - else if (equal(t, 1.0f, 0.01f)) { - //vertex->setPos(x2); - } - else*/ - if (t > 0.0f + NV_EPSILON && t < 1.0f - NV_EPSILON) { - nvDebugCheck(equal(lerp(x1, x2, t), x0)); - - Vertex * splitVertex = splitBoundaryEdge(edge, t, x0); - vertex->linkColocal(splitVertex); // @@ Should we do this here? - splitCount++; - } - } - } - } - } - - nvDebug(" - %d edges split.\n", splitCount); - - nvDebugCheck(isValid()); - - return splitCount != 0; -} - - -// For this to be effective, we have to fix the boundary junctions first. -Edge * Mesh::sewBoundary(Edge * startEdge) { - nvDebugCheck(startEdge->face == NULL); - - // @@ We may want to be more conservative linking colocals in order to preserve the input topology. One way of doing that is by linking colocals only - // if the vertices next to them are linked as well. That is, by sewing boundaries after detecting them. If any pair of consecutive edges have their first - // and last vertex in the same position, then it can be linked. - - Edge * lastBoundarySeen = startEdge; - - nvDebug("Sewing Boundary:\n"); - - int count = 0; - int sewnCount = 0; - - Edge * edge = startEdge; - do { - nvDebugCheck(edge->face == NULL); - - Edge * edge_a = edge; - Edge * edge_b = edge->prev; - - Edge * pair_a = edge_a->pair; - Edge * pair_b = edge_b->pair; - - Vertex * v0a = edge_a->to(); - Vertex * v0b = edge_b->from(); - Vertex * v1a = edge_a->from(); - Vertex * v1b = edge_b->to(); - - nvDebugCheck(v1a->isColocal(v1b)); - - /* - v0b + _+ v0a - \ / - b \ / a - \|/ - v1b + v1a - */ - - // @@ This should not happen while sewing, but it may be produced somewhere else. - nvDebugCheck(edge_a != edge_b); - - if (v0a->pos == v0b->pos) { - - // Link vertices. - v0a->linkColocal(v0b); - - // Remove edges to be collapsed. - disconnect(edge_a); - disconnect(edge_b); - disconnect(pair_a); - disconnect(pair_b); - - // Link new boundary edges. - Edge * prevBoundary = edge_b->prev; - Edge * nextBoundary = edge_a->next; - if (nextBoundary != NULL) { - nvDebugCheck(nextBoundary->face == NULL); - nvDebugCheck(prevBoundary->face == NULL); - nextBoundary->setPrev(prevBoundary); - - // Make sure boundary vertex points to boundary edge. - v0a->setEdge(nextBoundary); // This updates all colocals. - } - lastBoundarySeen = prevBoundary; - - // Creat new edge. - Edge * newEdge_a = addEdge(v0a->id, v1a->id); // pair_a->from()->id, pair_a->to()->id - Edge * newEdge_b = addEdge(v1b->id, v0b->id); - - newEdge_a->pair = newEdge_b; - newEdge_b->pair = newEdge_a; - - newEdge_a->face = pair_a->face; - newEdge_b->face = pair_b->face; - - newEdge_a->setNext(pair_a->next); - newEdge_a->setPrev(pair_a->prev); - - newEdge_b->setNext(pair_b->next); - newEdge_b->setPrev(pair_b->prev); - - delete edge_a; - delete edge_b; - delete pair_a; - delete pair_b; - - edge = nextBoundary; // If nextBoundary is NULL we have closed the loop. - sewnCount++; - } - else { - edge = edge->next; - } - - count++; - } while(edge != NULL && edge != lastBoundarySeen); - - nvDebug(" - Sewn %d out of %d.\n", sewnCount, count); - - if (lastBoundarySeen != NULL) { - nvDebugCheck(lastBoundarySeen->face == NULL); - } - - return lastBoundarySeen; -} - - -// @@ We must always disconnect edge pairs simultaneously. -void Mesh::disconnect(Edge * edge) { - nvDebugCheck(edge != NULL); - - // Remove from edge list. - if ((edge->id & 1) == 0) { - nvDebugCheck(m_edgeArray[edge->id / 2] == edge); - m_edgeArray[edge->id / 2] = NULL; - } - - // Remove edge from map. @@ Store map key inside edge? - nvDebugCheck(edge->from() != NULL && edge->to() != NULL); - bool removed = m_edgeMap.remove(Key(edge->from()->id, edge->to()->id)); - nvDebugCheck(removed == true); - - // 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? - } - } - //edge->setVertex(NULL); - } - - // 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? - } - } - //edge->setFace(NULL); - } - - // @@ Hack, we don't disconnect from pair, because pair needs us to remove itself from the map. - // Disconect from pair. - /*if (edge->pair != NULL) { - if (edge->pair->pair == edge) { - edge->pair->setPair(NULL); - } - //edge->setPair(NULL); - }*/ - - // 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 Mesh::remove(Edge * edge) { - nvDebugCheck(edge != NULL); - - disconnect(edge); - - delete edge; -} - -void Mesh::remove(Vertex * vertex) { - nvDebugCheck(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 Mesh::remove(Face * face) { - nvDebugCheck(face != NULL); - - // Remove from face list. - m_faceArray[face->id] = NULL; - - // Disconnect from edges. - if (face->edge != NULL) { - nvDebugCheck(face->edge->face == face); - - face->edge->face = NULL; - - face->edge = NULL; - } - - delete face; -} - - -void Mesh::compactEdges() { - const uint edgeCount = m_edgeArray.count(); - - uint c = 0; - for (uint i = 0; i < edgeCount; i++) { - if (m_edgeArray[i] != NULL) { - if (i != c) { - m_edgeArray[c] = m_edgeArray[i]; - m_edgeArray[c]->id = 2 * c; - if (m_edgeArray[c]->pair != NULL) { - m_edgeArray[c]->pair->id = 2 * c + 1; - } - } - c++; - } - } - - m_edgeArray.resize(c); -} - - -void Mesh::compactVertices() { - const uint vertexCount = m_vertexArray.count(); - - uint c = 0; - for (uint i = 0; i < vertexCount; i++) { - if (m_vertexArray[i] != NULL) { - if (i != c) { - m_vertexArray[c] = m_vertexArray[i]; - m_vertexArray[c]->id = c; - } - c++; - } - } - - m_vertexArray.resize(c); - - // @@ Generate xref array for external attributes. -} - - -void Mesh::compactFaces() { - const uint faceCount = m_faceArray.count(); - - uint c = 0; - for (uint i = 0; i < faceCount; i++) { - if (m_faceArray[i] != NULL) { - if (i != c) { - m_faceArray[c] = m_faceArray[i]; - m_faceArray[c]->id = c; - } - c++; - } - } - - m_faceArray.resize(c); -} - - -Vertex * Mesh::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. - nvDebugCheck(pair != NULL); - - // Make sure edge is a boundary edge. - nvDebugCheck(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); - vertex->col = lerp(edge->from()->col, edge->to()->col, 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); - - nvDebugCheck(e0->next == e1); - nvDebugCheck(e1->prev == e0); - - nvDebugCheck(p1->next == p0); - nvDebugCheck(p0->prev == p1); - - nvDebugCheck(p0->pair == e0); - nvDebugCheck(e0->pair == p0); - - nvDebugCheck(p1->pair == e1); - nvDebugCheck(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; -} - -#if 0 -// Without introducing new vertices. -void Mesh::splitBoundaryEdge(Edge * edge, Vertex * vertex) { - - /* - We want to go from this configuration: - - | | pn - + + - | ^ - | | - edge |<->| pair - | | - v | - + + - | | pp - - To this one: - \ / - \ / - + + - | ^ - e0 |<->| p0 - v | - vertex + + - | ^ - e1 |<->| p1 - v | - + + - / \ - / \ - */ - - - Edge * pair = edge->pair; - Edge * pn = pair->next(); - Edge * pp = pair->prev(); - - // Make sure boundaries are linked. - nvDebugCheck(pair != NULL); - - // Make sure edge is a boundary edge. - nvDebugCheck(pair->face() == NULL); - - nvDebugCheck(edge->isValid()); - nvDebugCheck(pair->isValid()); - - disconnect(edge); - disconnect(pair); - - // Add edges. - Edge * e0 = addEdge(edge->from()->id(), vertex->id()); - Edge * e1 = addEdge(vertex->id(), edge->to()->id()); - - // Link faces. - e0->setFace(edge->face()); - e1->setFace(edge->face()); - - // Link pairs. - Edge * p0 = findEdge(vertex->id(), pair->to()->id()); - if (p0 == NULL) { - p0 = addEdge(vertex->id(), pair->to()->id()); - pn->setPrev(p0); - } - else { - nvDebugCheck(p0->face() != NULL); - if (e0->prev() != NULL) { - pn->setPrev(e0->prev()); - } - else { - nvDebugCheck(pn == e0); - } - } - - Edge * p1 = findEdge(pair->from()->id(), vertex->id()); - if (p1 == NULL) { - p1 = addEdge(pair->from()->id(), vertex->id()); - pp->setNext(p1); - } - else { - nvDebugCheck(p1->face() != NULL); - if (e1->next() != NULL) { - pp->setPrev(e1->next()); - } - else { - nvDebugCheck(pp == e1); - } - } - - // Link edges. - e0->setNext(e1); // e1->setPrev(e0) - - if (p0->face() == p1->face()) { // can be null - p1->setNext(p0); // p0->setPrev(p1) - } - else { - //if (p1->face() == NULL) p1->setNext( - } - - - e0->setPrev(edge->prev()); - e1->setNext(edge->next()); - - nvDebugCheck(e0->pair == p0); - nvDebugCheck(e1->pair == p1); - nvDebugCheck(p0->pair == e0); - nvDebugCheck(p1->pair == e1); - - nvDebugCheck(e0->isValid()); - nvDebugCheck(e1->isValid()); - nvDebugCheck(pp->isValid()); - nvDebugCheck(pn->isValid()); - - nvDebugCheck(e0->pair->isValid()); - nvDebugCheck(e1->pair->isValid()); - nvDebugCheck(pp->pair->isValid()); - nvDebugCheck(pn->pair->isValid()); - - nvDebugCheck(edge->face->isValid()); - - if (pn->pair->face != NULL) { - nvDebugCheck(pn->pair->face->isValid()); - } - - if (pp->pair->face() != NULL) { - nvDebugCheck(pn->pair->face->isValid()); - } - - if (p0->face != NULL) { - nvDebugCheck(p0->face->isValid()); - } - - if (p1->face() != NULL) { - nvDebugCheck(p1->face()->isValid()); - } - - nvDebugCheck(isValid()); // Only for extreme debugging. - - // Link vertices. - edge->from()->setEdge(e0); - vertex->setEdge(p0); - - delete edge; - delete pair; -} -#endif - -bool Mesh::isValid() const -{ - // Make sure all edges are valid. - const uint edgeCount = m_edgeArray.count(); - for (uint 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; -} diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.h b/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.h deleted file mode 100644 index c202c2ef9a..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.h +++ /dev/null @@ -1,274 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_HALFEDGE_MESH_H -#define NV_MESH_HALFEDGE_MESH_H - -#include "nvmesh/nvmesh.h" -#include "nvcore/Array.h" -#include "nvcore/HashMap.h" - -/* -If I were to redo this again, there are a number of things that I would do differently. -- Edge map is only useful when importing a mesh to guarantee the result is two-manifold. However, when manipulating the mesh - it's a pain to maintain the map up to date. -- Edge array only points to the even vertices. There's no good reason for that. The map becomes required to traverse all edges - or you have to make sure edges are properly paired. -- Linked boundaries. It's cleaner to assume a NULL pair means a boundary edge. Makes easier to seal boundaries. The only reason - why we link boundaries is to simplify traversal, but that could be done with two helper functions (nextBoundary, prevBoundary). -- Minimize the amount of state that needs to be set in a certain way: - - boundary vertices point to boundary edge. -- Remove parenthesis! Make some members public. -- Remove member functions with side effects: - - e->setNext(n) modifies e->next and n->prev, instead use "link(e, n)", or "e->next = n, n->prev = e" -*/ - - -namespace nv -{ - class Vector3; - class TriMesh; - class QuadTriMesh; - //template <typename T> struct Hash<Mesh::Key>; - - namespace HalfEdge - { - class Edge; - class Face; - class Vertex; - - /// Simple half edge mesh designed for dynamic mesh manipulation. - class Mesh - { - public: - - Mesh(); - Mesh(const Mesh * mesh); - ~Mesh(); - - void clear(); - - Vertex * addVertex(const Vector3 & pos); - //Vertex * addVertex(uint id, const Vector3 & pos); - //void addVertices(const Mesh * mesh); - - void linkColocals(); - void linkColocalsWithCanonicalMap(const Array<uint> & canonicalMap); - void resetColocalLinks(); - - Face * addFace(); - Face * addFace(uint v0, uint v1, uint v2); - Face * addFace(uint v0, uint v1, uint v2, uint v3); - Face * addFace(const Array<uint> & indexArray); - Face * addFace(const Array<uint> & indexArray, uint first, uint num); - //void addFaces(const Mesh * mesh); - - // These functions disconnect the given element from the mesh and delete it. - void disconnect(Edge * edge); - void disconnectPair(Edge * edge); - void disconnect(Vertex * vertex); - void disconnect(Face * face); - - void remove(Edge * edge); - void remove(Vertex * vertex); - void remove(Face * face); - - // Remove holes from arrays and reassign indices. - void compactEdges(); - void compactVertices(); - void compactFaces(); - - void triangulate(); - - void linkBoundary(); - - bool splitBoundaryEdges(); // Returns true if any split was made. - - // Sew the boundary that starts at the given edge, returns one edge that still belongs to boundary, or NULL if boundary closed. - HalfEdge::Edge * sewBoundary(Edge * startEdge); - - - // Vertices - uint vertexCount() const { return m_vertexArray.count(); } - const Vertex * vertexAt(int i) const { return m_vertexArray[i]; } - Vertex * vertexAt(int i) { return m_vertexArray[i]; } - - uint colocalVertexCount() const { return m_colocalVertexCount; } - - // Faces - uint faceCount() const { return m_faceArray.count(); } - const Face * faceAt(int i) const { return m_faceArray[i]; } - Face * faceAt(int i) { return m_faceArray[i]; } - - // Edges - uint edgeCount() const { return m_edgeArray.count(); } - 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; - uint 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; - uint 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; - uint 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; - uint 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; - uint 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; - uint m_current; - }; - ConstEdgeIterator edges() const { return ConstEdgeIterator(this); } - - // @@ Add half-edge iterator. - - - - // Convert to tri mesh. - TriMesh * toTriMesh() const; - QuadTriMesh * toQuadTriMesh() const; - - bool isValid() const; - - public: - - // Error status: - mutable uint errorCount; - mutable uint errorIndex0; - mutable uint errorIndex1; - - private: - - bool canAddFace(const Array<uint> & indexArray, uint first, uint num) const; - bool canAddEdge(uint i, uint j) const; - Edge * addEdge(uint i, uint j); - - Edge * findEdge(uint i, uint j) const; - - void linkBoundaryEdge(Edge * edge); - Vertex * splitBoundaryEdge(Edge * edge, float t, const Vector3 & pos); - void splitBoundaryEdge(Edge * edge, Vertex * vertex); - - private: - - Array<Vertex *> m_vertexArray; - Array<Edge *> m_edgeArray; - Array<Face *> m_faceArray; - - struct Key { - Key() {} - Key(const Key & k) : p0(k.p0), p1(k.p1) {} - Key(uint v0, uint 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; } - - uint p0; - uint p1; - }; - friend struct Hash<Mesh::Key>; - - HashMap<Key, Edge *> m_edgeMap; - - uint m_colocalVertexCount; - - }; - /* - // This is a much better hash than the default and greatly improves performance! - template <> struct hash<Mesh::Key> - { - uint operator()(const Mesh::Key & k) const { return k.p0 + k.p1; } - }; - */ - - } // HalfEdge namespace - -} // nv namespace - -#endif // NV_MESH_HALFEDGE_MESH_H diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.cpp b/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.cpp deleted file mode 100644 index 66dad69f8a..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "Vertex.h" - -#include "nvmath/Vector.inl" - -using namespace nv; -using namespace HalfEdge; - - -// Set first edge of all colocals. -void Vertex::setEdge(Edge * e) -{ - for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { - it.current()->edge = e; - } -} - -// Update position of all colocals. -void Vertex::setPos(const Vector3 & p) -{ - for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { - it.current()->pos = p; - } -} - - -uint HalfEdge::Vertex::colocalCount() const -{ - uint count = 0; - for (ConstVertexIterator it(colocals()); !it.isDone(); it.advance()) { ++count; } - return count; -} - -uint HalfEdge::Vertex::valence() const -{ - uint count = 0; - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { ++count; } - return count; -} - -const HalfEdge::Vertex * HalfEdge::Vertex::firstColocal() const -{ - uint 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; -} - -HalfEdge::Vertex * HalfEdge::Vertex::firstColocal() -{ - Vertex * vertex = this; - uint firstId = id; - - for (VertexIterator it(colocals()); !it.isDone(); it.advance()) - { - if (it.current()->id < firstId) { - firstId = vertex->id; - vertex = it.current(); - } - } - - return vertex; -} - -bool HalfEdge::Vertex::isFirstColocal() const -{ - return firstColocal() == this; -} - -bool HalfEdge::Vertex::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; -} - diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.h b/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.h deleted file mode 100644 index 1c5c8d7141..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.h +++ /dev/null @@ -1,221 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_HALFEDGE_VERTEX_H -#define NV_MESH_HALFEDGE_VERTEX_H - -#include "nvmesh/halfedge/Edge.h" - -namespace nv -{ - namespace HalfEdge { class Vertex; class Face; class Edge; } - - // Half edge vertex. - class HalfEdge::Vertex - { - NV_FORBID_COPY(Vertex); - public: - - uint id; - - Edge * edge; - Vertex * next; - Vertex * prev; - - Vector3 pos; - Vector3 nor; - Vector2 tex; - Vector4 col; - - - Vertex(uint id) : id(id), edge(NULL), pos(0.0f), nor(0.0f), tex(0.0f), col(0.0f) { - next = this; - prev = this; - } - - - void setEdge(Edge * e); - void setPos(const Vector3 & p); - - uint colocalCount() const; - uint valence() const; - bool isFirstColocal() const; - const Vertex * firstColocal() const; - Vertex * firstColocal(); - - bool isColocal(const Vertex * v) const; - - - 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); - } - - - // for(EdgeIterator it(iterator()); !it.isDone(); it.advance()) { ... } - // - // EdgeIterator it(iterator()); - // while(!it.isDone()) { - // ... - // id.advance(); - // } - - // 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 the edges around this vertex in counterclockwise order. - class ReverseEdgeIterator //: public Iterator<Edge *> - { - public: - ReverseEdgeIterator(Edge * e) : m_end(NULL), m_current(e) { } - - virtual void advance() - { - if (m_end == NULL) m_end = m_current; - 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; - }; - - // Iterator that visits the edges around this vertex in counterclockwise order. - class ReverseConstEdgeIterator //: public Iterator<Edge *> - { - public: - ReverseConstEdgeIterator(const Edge * e) : m_end(NULL), m_current(e) { } - - virtual void advance() - { - if (m_end == NULL) m_end = m_current; - 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; - }; - - - - // 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); } - - }; - -} // nv namespace - -#endif // NV_MESH_HALFEDGE_VERTEX_H diff --git a/thirdparty/thekla_atlas/nvmesh/nvmesh.cpp b/thirdparty/thekla_atlas/nvmesh/nvmesh.cpp deleted file mode 100644 index d007eda332..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/nvmesh.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "nvmesh.h" // pch - diff --git a/thirdparty/thekla_atlas/nvmesh/nvmesh.h b/thirdparty/thekla_atlas/nvmesh/nvmesh.h deleted file mode 100644 index eb6819675d..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/nvmesh.h +++ /dev/null @@ -1,34 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_H -#define NV_MESH_H - -#include "nvcore/nvcore.h" - -// Function linkage -#if NVMESH_SHARED -#ifdef NVMESH_EXPORTS -#define NVMESH_API DLL_EXPORT -#define NVMESH_CLASS DLL_EXPORT_CLASS -#else -#define NVMESH_API DLL_IMPORT -#define NVMESH_CLASS DLL_IMPORT -#endif -#else -#define NVMESH_API -#define NVMESH_CLASS -#endif - -#if 1 //USE_PRECOMPILED_HEADERS // If using precompiled headers: -//#include <string.h> // strlen, strcmp, etc. -//#include "nvcore/StrLib.h" -//#include "nvcore/StdStream.h" -//#include "nvcore/Memory.h" -//#include "nvcore/Debug.h" -//#include "nvmath/Vector.h" -//#include "nvcore/Array.h" -//#include "nvcore/HashMap.h" -#endif - -#endif // NV_MESH_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/Atlas.cpp b/thirdparty/thekla_atlas/nvmesh/param/Atlas.cpp deleted file mode 100644 index 98f92cef96..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/Atlas.cpp +++ /dev/null @@ -1,1519 +0,0 @@ -// Copyright NVIDIA Corporation 2006 -- Ignacio Castano <icastano@nvidia.com> - -#include "nvmesh.h" // pch - -#include "Atlas.h" -#include "Util.h" -#include "AtlasBuilder.h" -#include "AtlasPacker.h" -#include "SingleFaceMap.h" -#include "OrthogonalProjectionMap.h" -#include "LeastSquaresConformalMap.h" -#include "ParameterizationQuality.h" - -//#include "nvmesh/export/MeshExportOBJ.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/halfedge/Vertex.h" - -#include "nvmesh/MeshBuilder.h" -#include "nvmesh/MeshTopology.h" -#include "nvmesh/param/Util.h" -#include "nvmesh/geometry/Measurements.h" - -#include "nvmath/Vector.inl" -#include "nvmath/Fitting.h" -#include "nvmath/Box.inl" -#include "nvmath/ProximityGrid.h" -#include "nvmath/Morton.h" - -#include "nvcore/StrLib.h" -#include "nvcore/Array.inl" -#include "nvcore/HashMap.inl" - -using namespace nv; - - -/// Ctor. -Atlas::Atlas() -{ - failed=false; -} - -// Dtor. -Atlas::~Atlas() -{ - deleteAll(m_meshChartsArray); -} - -uint Atlas::chartCount() const -{ - uint count = 0; - foreach(c, m_meshChartsArray) { - count += m_meshChartsArray[c]->chartCount(); - } - return count; -} - -const Chart * Atlas::chartAt(uint i) const -{ - foreach(c, m_meshChartsArray) { - uint count = m_meshChartsArray[c]->chartCount(); - - if (i < count) { - return m_meshChartsArray[c]->chartAt(i); - } - - i -= count; - } - - return NULL; -} - -Chart * Atlas::chartAt(uint i) -{ - foreach(c, m_meshChartsArray) { - uint count = m_meshChartsArray[c]->chartCount(); - - if (i < count) { - return m_meshChartsArray[c]->chartAt(i); - } - - i -= count; - } - - return NULL; -} - -// Extract the charts and add to this atlas. -void Atlas::addMeshCharts(MeshCharts * meshCharts) -{ - m_meshChartsArray.append(meshCharts); -} - -void Atlas::extractCharts(const HalfEdge::Mesh * mesh) -{ - MeshCharts * meshCharts = new MeshCharts(mesh); - meshCharts->extractCharts(); - addMeshCharts(meshCharts); -} - -void Atlas::computeCharts(const HalfEdge::Mesh * mesh, const SegmentationSettings & settings, const Array<uint> & unchartedMaterialArray) -{ - failed=false; - MeshCharts * meshCharts = new MeshCharts(mesh); - meshCharts->computeCharts(settings, unchartedMaterialArray); - addMeshCharts(meshCharts); -} - - - - -#if 0 - -/// Compute a seamless texture atlas. -bool Atlas::computeSeamlessTextureAtlas(bool groupFaces/*= true*/, bool scaleTiles/*= false*/, uint w/*= 1024*/, uint h/* = 1024*/) -{ - // Implement seamless texture atlas similar to what ZBrush does. See also: - // "Meshed Atlases for Real-Time Procedural Solid Texturing" - // http://graphics.cs.uiuc.edu/~jch/papers/rtpst.pdf - - // Other methods that we should experiment with: - // - // Seamless Texture Atlases: - // http://www.cs.jhu.edu/~bpurnomo/STA/index.html - // - // Rectangular Multi-Chart Geometry Images: - // http://graphics.cs.uiuc.edu/~jch/papers/rmcgi.pdf - // - // Discrete differential geometry also provide a way of constructing - // seamless quadrangulations as shown in: - // http://www.geometry.caltech.edu/pubs/TACD06.pdf - // - -#pragma message(NV_FILE_LINE "TODO: Implement seamless texture atlas.") - - if (groupFaces) - { - // @@ TODO. - } - else - { - // @@ Create one atlas per face. - } - - if (scaleTiles) - { - // @@ TODO - } - - /* - if (!isQuadMesh(m_mesh)) { - // Only handle quads for now. - return false; - } - - // Each face is a chart. - const uint faceCount = m_mesh->faceCount(); - m_chartArray.resize(faceCount); - - for(uint f = 0; f < faceCount; f++) { - m_chartArray[f].faceArray.clear(); - m_chartArray[f].faceArray.append(f); - } - - // Map each face to a separate square. - - // Determine face layout according to width and height. - float aspect = float(m_width) / float(m_height); - - uint i = 2; - uint total = (m_width / (i+1)) * (m_height / (i+1)); - while(total > faceCount) { - i *= 2; - total = (m_width / (i+1)) * (m_height / (i+1)); - } - - uint tileSize = i / 2; - - int x = 0; - int y = 0; - - m_result = new HalfEdge::Mesh(); - - // Once you have that it's just matter of traversing the faces. - for(uint f = 0; f < faceCount; f++) { - // Compute texture coordinates. - Vector2 tex[4]; - tex[0] = Vector2(float(x), float(y)); - tex[1] = Vector2(float(x+tileSize), float(y)); - tex[2] = Vector2(float(x+tileSize), float(y+tileSize)); - tex[3] = Vector2(float(x), float(y+tileSize)); - - Array<uint> indexArray(4); - - const HalfEdge::Face * face = m_mesh->faceAt(f); - - int i = 0; - for(HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance(), i++) { - const HalfEdge::Edge * edge = it.current(); - const HalfEdge::Vertex * vertex = edge->from(); - - HalfEdge::Vertex * newVertex = m_result->addVertex(vertex->id(), vertex->pos()); - - newVertex->setTex(Vector3(tex[i], 0)); - newVertex->setNor(vertex->nor()); - - indexArray.append(m_result->vertexCount() + 1); - } - - m_result->addFace(indexArray); - - // Move to the next tile. - x += tileSize + 1; - if (x + tileSize > m_width) { - x = 0; - y += tileSize + 1; - } - } - */ - - return false; -} - -#endif - - -void Atlas::parameterizeCharts() -{ - foreach(i, m_meshChartsArray) { - m_meshChartsArray[i]->parameterizeCharts(); - } -} - - -float Atlas::packCharts(int quality, float texelsPerUnit, bool blockAlign, bool conservative) -{ - AtlasPacker packer(this); - packer.packCharts(quality, texelsPerUnit, blockAlign, conservative); - if (hasFailed()) - return 0; - return packer.computeAtlasUtilization(); -} - - - - -/// Ctor. -MeshCharts::MeshCharts(const HalfEdge::Mesh * mesh) : m_mesh(mesh) -{ -} - -// Dtor. -MeshCharts::~MeshCharts() -{ - deleteAll(m_chartArray); -} - - -void MeshCharts::extractCharts() -{ - const uint faceCount = m_mesh->faceCount(); - - int first = 0; - Array<uint> queue(faceCount); - - BitArray bitFlags(faceCount); - bitFlags.clearAll(); - - for (uint f = 0; f < faceCount; f++) - { - if (bitFlags.bitAt(f) == false) - { - // Start new patch. Reset queue. - first = 0; - queue.clear(); - queue.append(f); - bitFlags.setBitAt(f); - - while (first != queue.count()) - { - 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(); - nvDebugCheck(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; - nvDebugCheck(neighborFace != NULL); - - if (bitFlags.bitAt(neighborFace->id) == false) - { - queue.append(neighborFace->id); - bitFlags.setBitAt(neighborFace->id); - } - } - } - - first++; - } - - Chart * chart = new Chart(); - chart->build(m_mesh, queue); - - m_chartArray.append(chart); - } - } -} - - -/* -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. - -*/ - - -SegmentationSettings::SegmentationSettings() -{ - // Charts have no area or boundary limits right now. - maxChartArea = NV_FLOAT_MAX; - maxBoundaryLength = NV_FLOAT_MAX; - - proxyFitMetricWeight = 1.0f; - roundnessMetricWeight = 0.1f; - straightnessMetricWeight = 0.25f; - normalSeamMetricWeight = 1.0f; - textureSeamMetricWeight = 0.1f; -} - - - -void MeshCharts::computeCharts(const SegmentationSettings & settings, const Array<uint> & unchartedMaterialArray) -{ - Chart * vertexMap = NULL; - - if (unchartedMaterialArray.count() != 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.append(vertexMap); - } - - if (builder.facesLeft != 0) { - - // Tweak these values: - const float maxThreshold = 2; - const uint growFaceCount = 32; - const uint maxIterations = 4; - - builder.settings = settings; - - //builder.settings.proxyFitMetricWeight *= 0.75; // relax proxy fit weight during initial seed placement. - //builder.settings.roundnessMetricWeight = 0; - //builder.settings.straightnessMetricWeight = 0; - - // This seems a reasonable estimate. - uint maxSeedCount = max(6U, builder.facesLeft); - - // Create initial charts greedely. - nvDebug("### Placing seeds\n"); - builder.placeSeeds(maxThreshold, maxSeedCount); - nvDebug("### Placed %d seeds (max = %d)\n", builder.chartCount(), maxSeedCount); - - builder.updateProxies(); - - builder.mergeCharts(); - - #if 1 - nvDebug("### Relocating seeds\n"); - builder.relocateSeeds(); - - nvDebug("### Reset charts\n"); - builder.resetCharts(); - - if (vertexMap != NULL) { - builder.markUnchartedFaces(vertexMap->faceArray()); - } - - builder.settings = settings; - - nvDebug("### Growing charts\n"); - - // Restart process growing charts in parallel. - uint iteration = 0; - while (true) - { - if (!builder.growCharts(maxThreshold, growFaceCount)) - { - nvDebug("### Can't grow anymore\n"); - - // If charts cannot grow more: fill holes, merge charts, relocate seeds and start new iteration. - - nvDebug("### Filling holes\n"); - builder.fillHoles(maxThreshold); - nvDebug("### Using %d charts now\n", builder.chartCount()); - - builder.updateProxies(); - - nvDebug("### Merging charts\n"); - builder.mergeCharts(); - nvDebug("### Using %d charts now\n", builder.chartCount()); - - nvDebug("### Reseeding\n"); - if (!builder.relocateSeeds()) - { - nvDebug("### Cannot relocate seeds anymore\n"); - - // Done! - break; - } - - if (iteration == maxIterations) - { - nvDebug("### Reached iteration limit\n"); - break; - } - iteration++; - - nvDebug("### Reset charts\n"); - builder.resetCharts(); - - if (vertexMap != NULL) { - builder.markUnchartedFaces(vertexMap->faceArray()); - } - - nvDebug("### Growing charts\n"); - } - }; - #endif - - // Make sure no holes are left! - nvDebugCheck(builder.facesLeft == 0); - - const uint chartCount = builder.chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - Chart * chart = new Chart(); - m_chartArray.append(chart); - - chart->build(m_mesh, builder.chartFaces(i)); - } - } - - - const uint chartCount = m_chartArray.count(); - - // Build face indices. - m_faceChart.resize(m_mesh->faceCount()); - m_faceIndex.resize(m_mesh->faceCount()); - - for (uint i = 0; i < chartCount; i++) - { - const Chart * chart = m_chartArray[i]; - - const uint faceCount = chart->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - uint 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 (uint 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 MeshCharts::parameterizeCharts() -{ - ParameterizationQuality globalParameterizationQuality; - - // Parameterize the charts. - uint diskCount = 0; - const uint chartCount = m_chartArray.count(); - for (uint 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()); - - // If the orthogonal projection produces better results, just use that. - // @@ It may be dangerous to do this, because isValid() does not detect self-overlaps. - // @@ Another problem is that with very thin patches with nearly zero parametric area, the results of our metric are not accurate. - /*if (orthogonalQuality.isValid() && orthogonalQuality.rmsStretchMetric() < lscmQuality.rmsStretchMetric()) { - computeOrthogonalProjectionMap(chart->unifiedMesh()); - chartParameterizationQuality = orthogonalQuality; - } - else*/ { - chartParameterizationQuality = lscmQuality; - } - - // If conformal map failed, - - // @@ Experiment with other parameterization methods. - //computeCircularBoundaryMap(chart->unifiedMesh()); - //computeConformalMap(chart->unifiedMesh()); - //computeNaturalConformalMap(chart->unifiedMesh()); - //computeGuidanceGradientMap(chart->unifiedMesh()); - } - - //ParameterizationQuality chartParameterizationQuality(chart->unifiedMesh()); - - isValid = chartParameterizationQuality.isValid(); - - if (!isValid) - { - nvDebug("*** Invalid parameterization.\n"); -#if 0 - // Dump mesh to inspect problem: - static int pieceCount = 0; - - StringBuilder fileName; - fileName.format("invalid_chart_%d.obj", pieceCount++); - exportMesh(chart->unifiedMesh(), fileName.str()); -#endif - } - - // @@ Check that parameterization quality is above a certain threshold. - - // @@ Detect boundary self-intersections. - - globalParameterizationQuality += chartParameterizationQuality; - } - - if (!isValid) - { - //nvDebugBreak(); - // @@ Run the builder again, but only on this chart. - //AtlasBuilder builder(chart->chartMesh()); - } - - // Transfer parameterization from unified mesh to chart mesh. - chart->transferParameterization(); - - } - - nvDebug(" Parameterized %d/%d charts.\n", diskCount, chartCount); - nvDebug(" RMS stretch metric: %f\n", globalParameterizationQuality.rmsStretchMetric()); - nvDebug(" MAX stretch metric: %f\n", globalParameterizationQuality.maxStretchMetric()); - nvDebug(" RMS conformal metric: %f\n", globalParameterizationQuality.rmsConformalMetric()); - nvDebug(" RMS authalic metric: %f\n", globalParameterizationQuality.maxAuthalicMetric()); -} - - - -Chart::Chart() : m_chartMesh(NULL), m_unifiedMesh(NULL), m_isDisk(false), m_isVertexMapped(false) -{ -} - -void Chart::build(const HalfEdge::Mesh * originalMesh, const Array<uint> & faceArray) -{ - // Copy face indices. - m_faceArray = faceArray; - - const uint meshVertexCount = originalMesh->vertexCount(); - - m_chartMesh = new HalfEdge::Mesh(); - m_unifiedMesh = new HalfEdge::Mesh(); - - Array<uint> chartMeshIndices; - chartMeshIndices.resize(meshVertexCount, ~0); - - Array<uint> unifiedMeshIndices; - unifiedMeshIndices.resize(meshVertexCount, ~0); - - // Add vertices. - const uint faceCount = faceArray.count(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = originalMesh->faceAt(faceArray[f]); - nvDebugCheck(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(); - - nvDebugCheck(vertex->pos == unifiedVertex->pos); - m_unifiedMesh->addVertex(vertex->pos); - } - - if (chartMeshIndices[vertex->id] == ~0) - { - chartMeshIndices[vertex->id] = m_chartMesh->vertexCount(); - m_chartToOriginalMap.append(vertex->id); - m_chartToUnifiedMap.append(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. - //nvCheck(m_chartMesh->colocalVertexCount() == m_unifiedMesh->vertexCount()); - - // Is that OK? What happens in meshes were that happens? Does anything break? Apparently not... - - - - Array<uint> faceIndices(7); - - // Add faces. - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = originalMesh->faceAt(faceArray[f]); - nvDebugCheck(face != NULL); - - faceIndices.clear(); - - for(HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Vertex * vertex = it.current()->vertex; - nvDebugCheck(vertex != NULL); - - faceIndices.append(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; - nvDebugCheck(vertex != NULL); - - vertex = vertex->firstColocal(); - - faceIndices.append(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 = unifyVertices(m_unifiedMesh.ptr()); - } - - //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 = triangulate(m_unifiedMesh.ptr()); - - //exportMesh(m_unifiedMesh.ptr(), "debug_triangulated.obj"); - - - // Analyze chart topology. - MeshTopology topology(m_unifiedMesh.ptr()); - m_isDisk = topology.isDisk(); - - // This is sometimes failing, when triangulate fails to add a triangle, it generates a hole in the mesh. - //nvDebugCheck(m_isDisk); - - /*if (!m_isDisk) { - static int pieceCount = 0; - StringBuilder fileName; - fileName.format("debug_hole_%d.obj", pieceCount++); - exportMesh(m_unifiedMesh.ptr(), fileName.str()); - }*/ - - -#if 0 - if (!m_isDisk) { - nvDebugBreak(); - - static int pieceCount = 0; - - StringBuilder fileName; - fileName.format("debug_nodisk_%d.obj", pieceCount++); - exportMesh(m_chartMesh.ptr(), fileName.str()); - } -#endif - -} - - -void Chart::buildVertexMap(const HalfEdge::Mesh * originalMesh, const Array<uint> & unchartedMaterialArray) -{ - nvCheck(m_chartMesh == NULL && m_unifiedMesh == NULL); - - m_isVertexMapped = true; - - // Build face indices. - m_faceArray.clear(); - - const uint meshFaceCount = originalMesh->faceCount(); - for (uint f = 0; f < meshFaceCount; f++) { - const HalfEdge::Face * face = originalMesh->faceAt(f); - - if (unchartedMaterialArray.contains(face->material)) { - m_faceArray.append(f); - } - } - - const uint faceCount = m_faceArray.count(); - - if (faceCount == 0) { - return; - } - - - // @@ The chartMesh construction is basically the same as with regular charts, don't duplicate! - - const uint meshVertexCount = originalMesh->vertexCount(); - - m_chartMesh = new HalfEdge::Mesh(); - - Array<uint> chartMeshIndices; - chartMeshIndices.resize(meshVertexCount, ~0); - - // Vertex map mesh only has disconnected vertices. - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = originalMesh->faceAt(m_faceArray[f]); - nvDebugCheck(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.append(vertex->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(); - - Array<uint> faceIndices(7); - - // Add faces. - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = originalMesh->faceAt(m_faceArray[f]); - nvDebugCheck(face != NULL); - - faceIndices.clear(); - - for(HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Vertex * vertex = it.current()->vertex; - nvDebugCheck(vertex != NULL); - nvDebugCheck(chartMeshIndices[vertex->id] != ~0); - - faceIndices.append(chartMeshIndices[vertex->id]); - } - - HalfEdge::Face * new_face = m_chartMesh->addFace(faceIndices); - nvDebugCheck(new_face != NULL); - } - - m_chartMesh->linkBoundary(); - - - const uint chartVertexCount = m_chartMesh->vertexCount(); - - Box bounds; - bounds.clearBounds(); - - for (uint i = 0; i < chartVertexCount; i++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(i); - bounds.addPointToBounds(vertex->pos); - } - - ProximityGrid grid; - grid.init(bounds, chartVertexCount); - - for (uint i = 0; i < chartVertexCount; i++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(i); - grid.add(vertex->pos, i); - } - - -#if 0 - // Arrange vertices in a rectangle. - vertexMapWidth = ftoi_ceil(sqrtf(float(chartVertexCount))); - vertexMapHeight = (chartVertexCount + vertexMapWidth - 1) / vertexMapWidth; - nvDebugCheck(vertexMapWidth >= vertexMapHeight); - - int x = 0, y = 0; - for (uint i = 0; i < chartVertexCount; i++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(i); - - vertex->tex.x = float(x); - vertex->tex.y = float(y); - - x++; - if (x == vertexMapWidth) { - x = 0; - y++; - nvCheck(y < vertexMapHeight); - } - } - -#elif 0 - // Arrange vertices in a rectangle, traversing grid in 3D morton order and laying them down in 2D morton order. - vertexMapWidth = ftoi_ceil(sqrtf(float(chartVertexCount))); - vertexMapHeight = (chartVertexCount + vertexMapWidth - 1) / vertexMapWidth; - nvDebugCheck(vertexMapWidth >= vertexMapHeight); - - int n = 0; - uint32 texelCode = 0; - - uint cellsVisited = 0; - - const uint32 cellCodeCount = grid.mortonCount(); - for (uint32 cellCode = 0; cellCode < cellCodeCount; cellCode++) { - int cell = grid.mortonIndex(cellCode); - if (cell < 0) continue; - - cellsVisited++; - - const Array<uint> & indexArray = grid.cellArray[cell].indexArray; - - foreach(i, indexArray) { - uint idx = indexArray[i]; - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(idx); - - //vertex->tex.x = float(n % rectangleWidth) + 0.5f; - //vertex->tex.y = float(n / rectangleWidth) + 0.5f; - - // Lay down the points in z order too. - uint x, y; - do { - x = decodeMorton2X(texelCode); - y = decodeMorton2Y(texelCode); - texelCode++; - } while (x >= U32(vertexMapWidth) || y >= U32(vertexMapHeight)); - - vertex->tex.x = float(x); - vertex->tex.y = float(y); - - n++; - } - } - - nvDebugCheck(cellsVisited == grid.cellArray.count()); - nvDebugCheck(n == chartVertexCount); - -#else - - uint texelCount = 0; - - const float positionThreshold = 0.01f; - const float normalThreshold = 0.01f; - - uint verticesVisited = 0; - uint cellsVisited = 0; - - Array<int> vertexIndexArray; - vertexIndexArray.resize(chartVertexCount, -1); // Init all indices to -1. - - // Traverse vertices in morton order. @@ It may be more interesting to sort them based on orientation. - const uint cellCodeCount = grid.mortonCount(); - for (uint cellCode = 0; cellCode < cellCodeCount; cellCode++) { - int cell = grid.mortonIndex(cellCode); - if (cell < 0) continue; - - cellsVisited++; - - const Array<uint> & indexArray = grid.cellArray[cell].indexArray; - - foreach(i, indexArray) { - uint idx = indexArray[i]; - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(idx); - - nvDebugCheck(vertexIndexArray[idx] == -1); - - Array<uint> neighbors; - grid.gather(vertex->pos, positionThreshold, /*ref*/neighbors); - - // Compare against all nearby vertices, cluster greedily. - foreach(j, neighbors) { - uint 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++; - } - } - - nvDebugCheck(cellsVisited == grid.cellArray.count()); - nvDebugCheck(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. - nvDebugCheck(vertexMapWidth >= vertexMapHeight); - - nvDebug("Reduced vertex count from %d to %d.\n", chartVertexCount, texelCount); - -#if 0 - // This lays down the clustered vertices linearly. - for (uint i = 0; i < chartVertexCount; i++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(i); - - int idx = vertexIndexArray[i]; - - vertex->tex.x = float(idx % vertexMapWidth); - vertex->tex.y = float(idx / vertexMapWidth); - } -#else - // Lay down the clustered vertices in morton order. - - Array<uint> texelCodes; - texelCodes.resize(texelCount); - - // For each texel, assign one morton code. - uint texelCode = 0; - for (uint i = 0; i < texelCount; i++) { - uint x, y; - do { - x = decodeMorton2X(texelCode); - y = decodeMorton2Y(texelCode); - texelCode++; - } while (x >= U32(vertexMapWidth) || y >= U32(vertexMapHeight)); - - texelCodes[i] = texelCode - 1; - } - - for (uint i = 0; i < chartVertexCount; i++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(i); - - int idx = vertexIndexArray[i]; - if (idx != -1) { - uint texelCode = texelCodes[idx]; - uint x = decodeMorton2X(texelCode); - uint y = decodeMorton2Y(texelCode); - - vertex->tex.x = float(x); - vertex->tex.y = float(y); - } - } - -#endif - -#endif - -} - - - -static void getBoundaryEdges(HalfEdge::Mesh * mesh, Array<HalfEdge::Edge *> & boundaryEdges) -{ - nvDebugCheck(mesh != NULL); - - const uint 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 (uint e = 0; e < edgeCount; e++) - { - HalfEdge::Edge * startEdge = mesh->edgeAt(e); - - if (startEdge != NULL && startEdge->isBoundary() && bitFlags.bitAt(e) == false) - { - nvDebugCheck(startEdge->face != NULL); - nvDebugCheck(startEdge->pair->face == NULL); - - startEdge = startEdge->pair; - - const HalfEdge::Edge * edge = startEdge; - do { - nvDebugCheck(edge->face == NULL); - nvDebugCheck(bitFlags.bitAt(edge->id/2) == false); - - bitFlags.setBitAt(edge->id / 2); - edge = edge->next; - } while(startEdge != edge); - - boundaryEdges.append(startEdge); - } - } -} - - -bool Chart::closeLoop(uint start, const Array<HalfEdge::Edge *> & loop) -{ - const uint vertexCount = loop.count() - start; - - nvDebugCheck(vertexCount >= 3); - if (vertexCount < 3) return false; - - nvDebugCheck(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. - - Array<Vector3> points; - points.resize(vertexCount); - for (uint i = 0; i < vertexCount; i++) { - points[i] = loop[start+i]->vertex->pos; - } - - bool isPlanar = Fit::isPlanar(vertexCount, points.buffer()); - - if (isPlanar) { - // Add face and connect edges. - HalfEdge::Face * face = m_unifiedMesh->addFace(); - for (uint 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]; - - nvDebugCheck(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 (uint 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 (uint 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); - nvDebugCheck(face != NULL); - } - } - - return true; -} - - -bool Chart::closeHoles() -{ - nvDebugCheck(!m_isVertexMapped); - - Array<HalfEdge::Edge *> boundaryEdges; - getBoundaryEdges(m_unifiedMesh.ptr(), boundaryEdges); - - uint boundaryCount = boundaryEdges.count(); - if (boundaryCount <= 1) - { - // Nothing to close. - return true; - } - - // Compute lengths and areas. - Array<float> boundaryLengths; - //Array<Vector3> boundaryCentroids; - - for (uint i = 0; i < boundaryCount; i++) - { - const HalfEdge::Edge * startEdge = boundaryEdges[i]; - nvCheck(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.append(boundaryLength); - //boundaryCentroids.append(boundaryCentroid / boundaryEdgeCount); - } - - - // Find disk boundary. - uint diskBoundary = 0; - float maxLength = boundaryLengths[0]; - - for (uint i = 1; i < boundaryCount; i++) - { - if (boundaryLengths[i] > maxLength) - { - maxLength = boundaryLengths[i]; - diskBoundary = i; - } - } - - - // Sew holes. - /*for (uint i = 0; i < boundaryCount; i++) - { - if (diskBoundary == i) - { - // Skip disk boundary. - continue; - } - - HalfEdge::Edge * startEdge = boundaryEdges[i]; - nvCheck(startEdge->face() == NULL); - - boundaryEdges[i] = m_unifiedMesh->sewBoundary(startEdge); - } - - exportMesh(m_unifiedMesh.ptr(), "debug_sewn.obj");*/ - - //bool hasNewHoles = false; - - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // @@ Close loop is wrong, after closing a loop, we do not only have to add the face, but make sure that every edge in he loop is pointing to the right place. - - // Close holes. - for (uint i = 0; i < boundaryCount; i++) - { - if (diskBoundary == i) - { - // Skip disk boundary. - continue; - } - - HalfEdge::Edge * startEdge = boundaryEdges[i]; - nvDebugCheck(startEdge != NULL); - nvDebugCheck(startEdge->face == NULL); - -#if 1 - Array<HalfEdge::Vertex *> vertexLoop; - Array<HalfEdge::Edge *> edgeLoop; - - HalfEdge::Edge * edge = startEdge; - do { - HalfEdge::Vertex * vertex = edge->next->vertex; // edge->to() - - uint i; - for (i = 0; i < vertexLoop.count(); i++) { - if (vertex->isColocal(vertexLoop[i])) { - break; - } - } - - bool isCrossing = (i != vertexLoop.count()); - - if (isCrossing) { - - HalfEdge::Edge * prev = edgeLoop[i]; // Previous edge before the loop. - HalfEdge::Edge * next = edge->next; // Next edge after the loop. - - nvDebugCheck(prev->to()->isColocal(next->from())); - - // Close loop. - edgeLoop.append(edge); - closeLoop(i+1, edgeLoop); - - // Link boundary loop. - prev->setNext(next); - vertex->setEdge(next); - - // Start over again. - vertexLoop.clear(); - edgeLoop.clear(); - - edge = startEdge; - vertex = edge->to(); - } - - vertexLoop.append(vertex); - edgeLoop.append(edge); - - edge = edge->next; - } while(edge != startEdge); - - closeLoop(0, edgeLoop); -#endif - - /* - - // Add face and connect boundary edges. - HalfEdge::Face * face = m_unifiedMesh->addFace(); - face->setEdge(startEdge); - - HalfEdge::Edge * edge = startEdge; - do { - edge->setFace(face); - - edge = edge->next(); - } while(edge != startEdge); - - */ - - - /* - uint edgeCount = 0; - HalfEdge::Edge * edge = startEdge; - do { - edgeCount++; - edge = edge->next(); - } while(edge != startEdge); - - - - // Count edges in this boundary. - uint edgeCount = 0; - HalfEdge::Edge * edge = startEdge; - do { - edgeCount++; - edge = edge->next(); - } while(edge != startEdge); - - // Trivial hole, fill with one triangle. This actually works for all convex boundaries with non colinear vertices. - if (edgeCount == 3) { - // Add face and connect boundary edges. - HalfEdge::Face * face = m_unifiedMesh->addFace(); - face->setEdge(startEdge); - - edge = startEdge; - do { - edge->setFace(face); - - edge = edge->next(); - } while(edge != startEdge); - - // @@ Implement the above using addFace, it should now work with existing edges, as long as their face pointers is zero. - - } - else { - // Ideally we should: - // - compute best fit plane of boundary vertices. - // - project boundary polygon onto plane. - // - triangulate boundary polygon. - // - add faces of the resulting triangulation. - - // I don't have a good triangulator available. A more simple solution that works in more (but not all) cases: - // - compute boundary centroid. - // - add vertex centroid. - // - connect centroid vertex with boundary vertices. - // - connect radial edges with boundary edges. - - // This should work for non-convex boundaries with colinear vertices as long as the kernel of the polygon is not empty. - - // Compute boundary centroid: - Vector3 centroid_pos(0); - Vector2 centroid_tex(0); - - HalfEdge::Edge * edge = startEdge; - do { - centroid_pos += edge->vertex()->pos; - centroid_tex += edge->vertex()->tex; - edge = edge->next(); - } while(edge != startEdge); - - centroid_pos *= (1.0f / edgeCount); - centroid_tex *= (1.0f / edgeCount); - - HalfEdge::Vertex * centroid = m_unifiedMesh->addVertex(centroid_pos); - centroid->tex = centroid_tex; - - // Add one pair of edges for each boundary vertex. - edge = startEdge; - do { - HalfEdge::Edge * next = edge->next(); - - nvCheck(edge->face() == NULL); - HalfEdge::Face * face = m_unifiedMesh->addFace(centroid->id(), edge->from()->id(), edge->to()->id()); - - if (face != NULL) { - nvCheck(edge->face() == face); - } - else { - hasNewHoles = true; - } - - edge = next; - } while(edge != startEdge); - } - */ - } - - /*nvDebugCheck(!hasNewHoles); - - if (hasNewHoles) { - // Link boundary again, in case closeHoles created new holes! - m_unifiedMesh->linkBoundary(); - }*/ - - // Because some algorithms do not expect sparse edge buffers. - //m_unifiedMesh->compactEdges(); - - // In case we messed up: - //m_unifiedMesh->linkBoundary(); - - getBoundaryEdges(m_unifiedMesh.ptr(), boundaryEdges); - - boundaryCount = boundaryEdges.count(); - nvDebugCheck(boundaryCount == 1); - - //exportMesh(m_unifiedMesh.ptr(), "debug_hole_filled.obj"); - - return boundaryCount == 1; -} - - -// Transfer parameterization from unified mesh to chart mesh. -void Chart::transferParameterization() { - nvDebugCheck(!m_isVertexMapped); - - uint vertexCount = m_chartMesh->vertexCount(); - for (uint 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 Chart::computeSurfaceArea() const { - return nv::computeSurfaceArea(m_chartMesh.ptr()) * scale; -} - -float Chart::computeParametricArea() const { - // This only makes sense in parameterized meshes. - nvDebugCheck(m_isDisk); - nvDebugCheck(!m_isVertexMapped); - - return nv::computeParametricArea(m_chartMesh.ptr()); -} - -Vector2 Chart::computeParametricBounds() const { - // This only makes sense in parameterized meshes. - nvDebugCheck(m_isDisk); - nvDebugCheck(!m_isVertexMapped); - - Box bounds; - bounds.clearBounds(); - - uint vertexCount = m_chartMesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(v); - bounds.addPointToBounds(Vector3(vertex->tex, 0)); - } - - return bounds.extents().xy(); -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/Atlas.h b/thirdparty/thekla_atlas/nvmesh/param/Atlas.h deleted file mode 100644 index 41cfaea9cb..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/Atlas.h +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright NVIDIA Corporation 2006 -- Ignacio Castano <icastano@nvidia.com> - -#pragma once -#ifndef NV_MESH_ATLAS_H -#define NV_MESH_ATLAS_H - -#include "nvcore/Array.h" -#include "nvcore/Ptr.h" -#include "nvmath/Vector.h" -#include "nvmesh/nvmesh.h" -#include "nvmesh/halfedge/Mesh.h" - - -namespace nv -{ - namespace HalfEdge { class Mesh; } - - class Chart; - class MeshCharts; - class VertexMap; - - struct SegmentationSettings - { - SegmentationSettings(); - - float maxChartArea; - float maxBoundaryLength; - - float proxyFitMetricWeight; - float roundnessMetricWeight; - float straightnessMetricWeight; - float normalSeamMetricWeight; - float textureSeamMetricWeight; - }; - - - /// An atlas is a set of charts. - class Atlas - { - public: - - Atlas(); - ~Atlas(); - - uint meshCount() const { return m_meshChartsArray.count(); } - const MeshCharts * meshAt(uint i) const { return m_meshChartsArray[i]; } - MeshCharts * meshAt(uint i) { return m_meshChartsArray[i]; } - - uint chartCount() const; - const Chart * chartAt(uint i) const; - Chart * chartAt(uint i); - - // Add mesh charts and takes ownership. - void addMeshCharts(MeshCharts * meshCharts); - - void extractCharts(const HalfEdge::Mesh * mesh); - void computeCharts(const HalfEdge::Mesh * mesh, const SegmentationSettings & settings, const Array<uint> & unchartedMaterialArray); - - - // Compute a trivial seamless texture similar to ZBrush. - //bool computeSeamlessTextureAtlas(bool groupFaces = true, bool scaleTiles = false, uint w = 1024, uint h = 1024); - - void parameterizeCharts(); - - // Pack charts in the smallest possible rectangle. - float packCharts(int quality, float texelArea, bool blockAlign, bool conservative); - void setFailed() { failed = true; } - bool hasFailed() const { return failed; } - - private: - - bool failed; - Array<MeshCharts *> m_meshChartsArray; - - }; - - - // Set of charts corresponding to a single mesh. - class MeshCharts - { - public: - MeshCharts(const HalfEdge::Mesh * mesh); - ~MeshCharts(); - - uint chartCount() const { return m_chartArray.count(); } - uint vertexCount () const { return m_totalVertexCount; } - - const Chart * chartAt(uint i) const { return m_chartArray[i]; } - Chart * chartAt(uint i) { return m_chartArray[i]; } - - void computeVertexMap(const Array<uint> & unchartedMaterialArray); - - // Extract the charts of the input mesh. - void extractCharts(); - - // Compute charts using a simple segmentation algorithm. - void computeCharts(const SegmentationSettings & settings, const Array<uint> & unchartedMaterialArray); - - void parameterizeCharts(); - - uint faceChartAt(uint i) const { return m_faceChart[i]; } - uint faceIndexWithinChartAt(uint i) const { return m_faceIndex[i]; } - - uint vertexCountBeforeChartAt(uint i) const { return m_chartVertexCountPrefixSum[i]; } - - private: - - const HalfEdge::Mesh * m_mesh; - - Array<Chart *> m_chartArray; - - Array<uint> m_chartVertexCountPrefixSum; - uint m_totalVertexCount; - - Array<uint> m_faceChart; // the chart of every face of the input mesh. - Array<uint> m_faceIndex; // the index within the chart for every face of the input mesh. - }; - - - /// A chart is a connected set of faces with a certain topology (usually a disk). - class Chart - { - public: - - Chart(); - - void build(const HalfEdge::Mesh * originalMesh, const Array<uint> & faceArray); - void buildVertexMap(const HalfEdge::Mesh * originalMesh, const Array<uint> & unchartedMaterialArray); - - bool closeHoles(); - - bool isDisk() const { return m_isDisk; } - bool isVertexMapped() const { return m_isVertexMapped; } - - uint vertexCount() const { return m_chartMesh->vertexCount(); } - uint colocalVertexCount() const { return m_unifiedMesh->vertexCount(); } - - uint faceCount() const { return m_faceArray.count(); } - uint faceAt(uint i) const { return m_faceArray[i]; } - - const HalfEdge::Mesh * chartMesh() const { return m_chartMesh.ptr(); } - HalfEdge::Mesh * chartMesh() { return m_chartMesh.ptr(); } - const HalfEdge::Mesh * unifiedMesh() const { return m_unifiedMesh.ptr(); } - HalfEdge::Mesh * unifiedMesh() { return m_unifiedMesh.ptr(); } - - //uint vertexIndex(uint i) const { return m_vertexIndexArray[i]; } - - uint mapChartVertexToOriginalVertex(uint i) const { return m_chartToOriginalMap[i]; } - uint mapChartVertexToUnifiedVertex(uint i) const { return m_chartToUnifiedMap[i]; } - - const Array<uint> & faceArray() const { return m_faceArray; } - - void transferParameterization(); - - float computeSurfaceArea() const; - float computeParametricArea() const; - Vector2 computeParametricBounds() const; - - - float scale = 1.0f; - uint vertexMapWidth; - uint vertexMapHeight; - - private: - - bool closeLoop(uint start, const Array<HalfEdge::Edge *> & loop); - - // Chart mesh. - AutoPtr<HalfEdge::Mesh> m_chartMesh; - AutoPtr<HalfEdge::Mesh> m_unifiedMesh; - - bool m_isDisk; - bool m_isVertexMapped; - - // List of faces of the original mesh that belong to this chart. - Array<uint> m_faceArray; - - // Map vertices of the chart mesh to vertices of the original mesh. - Array<uint> m_chartToOriginalMap; - - Array<uint> m_chartToUnifiedMap; - }; - -} // nv namespace - -#endif // NV_MESH_ATLAS_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.cpp b/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.cpp deleted file mode 100644 index bd2140c2f3..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.cpp +++ /dev/null @@ -1,1320 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "AtlasBuilder.h" -#include "Util.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/halfedge/Vertex.h" - -#include "nvmath/Matrix.inl" -#include "nvmath/Vector.inl" - -//#include "nvcore/IntroSort.h" -#include "nvcore/Array.inl" - -#include <algorithm> // std::sort - -#include <float.h> // FLT_MAX -#include <limits.h> // UINT_MAX - -using namespace nv; - -namespace -{ - - // 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(uint size = UINT_MAX) : maxSize(size) {} - - void push(float priority, uint face) { - uint i = 0; - const uint count = pairs.count(); - for (; i < count; i++) { - if (pairs[i].priority > priority) break; - } - - Pair p = { priority, face }; - pairs.insertAt(i, p); - - if (pairs.count() > maxSize) { - pairs.removeAt(0); - } - } - - // push face out of order, to be sorted later. - void push(uint face) { - Pair p = { 0.0f, face }; - pairs.append(p); - } - - uint pop() { - uint f = pairs.back().face; - pairs.pop_back(); - return f; - } - - void sort() { - //nv::sort(pairs); // @@ My intro sort appears to be much slower than it should! - std::sort(pairs.buffer(), pairs.buffer() + pairs.count()); - } - - void clear() { - pairs.clear(); - } - - uint count() const { return pairs.count(); } - - float firstPriority() const { return pairs.back().priority; } - - - const uint maxSize; - - struct Pair { - bool operator <(const Pair & p) const { return priority > p.priority; } // !! Sort in inverse priority order! - float priority; - uint face; - }; - - - Array<Pair> pairs; - }; - - static bool isNormalSeam(const HalfEdge::Edge * edge) { - return (edge->vertex->nor != edge->pair->next->vertex->nor || edge->next->vertex->nor != edge->pair->vertex->nor); - } - - static bool isTextureSeam(const HalfEdge::Edge * edge) { - return (edge->vertex->tex != edge->pair->next->vertex->tex || edge->next->vertex->tex != edge->pair->vertex->tex); - } - -} // namespace - - -struct nv::ChartBuildData -{ - ChartBuildData(int id) : id(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; - - Array<uint> seeds; // @@ These could be a pointers to the HalfEdge faces directly. - Array<uint> faces; - PriorityQueue candidates; -}; - - - -AtlasBuilder::AtlasBuilder(const HalfEdge::Mesh * m) : mesh(m), facesLeft(m->faceCount()) -{ - const uint faceCount = m->faceCount(); - faceChartArray.resize(faceCount, -1); - faceCandidateArray.resize(faceCount, -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. - uint edgeCount = m->edgeCount(); - edgeLengths.resize(edgeCount); - - for (uint i = 0; i < edgeCount; i++) { - uint id = m->edgeAt(i)->id; - nvDebugCheck(id / 2 == i); - - edgeLengths[i] = m->edgeAt(i)->length(); - } - - faceAreas.resize(faceCount); - for (uint i = 0; i < faceCount; i++) { - faceAreas[i] = m->faceAt(i)->area(); - } -} - -AtlasBuilder::~AtlasBuilder() -{ - const uint chartCount = chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - delete chartArray[i]; - } -} - - -void AtlasBuilder::markUnchartedFaces(const Array<uint> & unchartedFaces) -{ - const uint unchartedFaceCount = unchartedFaces.count(); - for (uint i = 0; i < unchartedFaceCount; i++){ - uint f = unchartedFaces[i]; - faceChartArray[f] = -2; - //faceCandidateArray[f] = -2; // @@ ? - - removeCandidate(f); - } - - nvDebugCheck(facesLeft >= unchartedFaceCount); - facesLeft -= unchartedFaceCount; -} - - -void AtlasBuilder::computeShortestPaths() -{ - const uint faceCount = mesh->faceCount(); - shortestPaths.resize(faceCount*faceCount, FLT_MAX); - - // Fill edges: - for (uint 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; - - uint 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 (uint k = 0; k < faceCount; k++) - { - for (uint i = 0; i < faceCount; i++) - { - for (uint j = 0; j < faceCount; j++) - { - shortestPaths[i*faceCount + j] = min(shortestPaths[i*faceCount + j], shortestPaths[i*faceCount + k]+shortestPaths[k*faceCount + j]); - } - } - } -} - - -void AtlasBuilder::placeSeeds(float threshold, uint 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 (uint i = 0; i < maxSeedCount; i++) - { - if (facesLeft == 0) { - // No faces left, stop creating seeds. - break; - } - - createRandomChart(threshold); - } -} - - -void AtlasBuilder::createRandomChart(float threshold) -{ - ChartBuildData * chart = new ChartBuildData(chartArray.count()); - chartArray.append(chart); - - // Pick random face that is not used by any chart yet. - uint randomFaceIdx = rand.getRange(facesLeft - 1); - uint i = 0; - for (uint f = 0; f != randomFaceIdx; f++, i++) - { - while (faceChartArray[i] != -1) i++; - } - while (faceChartArray[i] != -1) i++; - - chart->seeds.append(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 AtlasBuilder::addFaceToChart(ChartBuildData * chart, uint f, bool recomputeProxy) -{ - // Add face to chart. - chart->faces.append(f); - - nvDebugCheck(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); -} - -// @@ Get N best candidates in one pass. -const AtlasBuilder::Candidate & AtlasBuilder::getBestCandidate() const -{ - uint best = 0; - float bestCandidateMetric = FLT_MAX; - - const uint candidateCount = candidateArray.count(); - nvCheck(candidateCount > 0); - - for (uint i = 0; i < candidateCount; i++) - { - const Candidate & candidate = candidateArray[i]; - - if (candidate.metric < bestCandidateMetric) { - bestCandidateMetric = candidate.metric; - best = i; - } - } - - return candidateArray[best]; -} - - -// Returns true if any of the charts can grow more. -bool AtlasBuilder::growCharts(float threshold, uint faceCount) -{ -#if 1 // Using one global list. - - faceCount = min(faceCount, facesLeft); - - for (uint 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. - -#else // Using one list per chart. - bool canGrowMore = false; - - const uint chartCount = chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - if (growChart(chartArray[i], threshold, faceCount)) - { - canGrowMore = true; - } - } - - return canGrowMore; -#endif -} - -bool AtlasBuilder::growChart(ChartBuildData * chart, float threshold, uint faceCount) -{ - // Try to add faceCount faces within threshold to chart. - for (uint i = 0; i < faceCount; ) - { - if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold) - { - return false; - } - - uint 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 AtlasBuilder::resetCharts() -{ - const uint faceCount = mesh->faceCount(); - for (uint i = 0; i < faceCount; i++) - { - faceChartArray[i] = -1; - faceCandidateArray[i] = -1; - } - - facesLeft = faceCount; - - candidateArray.clear(); - - const uint chartCount = chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - ChartBuildData * chart = chartArray[i]; - - const uint 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 AtlasBuilder::updateCandidates(ChartBuildData * chart, uint 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()) - { - uint f = edge->face->id; - - if (faceChartArray[f] == -1) - { - chart->candidates.push(f); - } - } - } -} - - -void AtlasBuilder::updateProxies() -{ - const uint chartCount = chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - updateProxy(chartArray[i]); - } -} - - -namespace { - - float absoluteSum(Vector4::Arg v) - { - return fabs(v.x) + fabs(v.y) + fabs(v.z) + fabs(v.w); - } - - //#pragma message(NV_FILE_LINE "FIXME: Using the c=cos(teta) substitution, the equation system becomes linear and we can avoid the newton solver.") - - struct ConeFitting - { - ConeFitting(const HalfEdge::Mesh * m, float g, float tf, float tx) : mesh(m), gamma(g), tolf(tf), tolx(tx), F(0), D(0), H(0) { - } - - void addTerm(Vector3 N, float A) - { - const float c = cosf(X.w); - const float s = sinf(X.w); - const float tmp = dot(X.xyz(), N) - c; - - F += tmp * tmp; - - D.x += 2 * X.x * tmp; - D.y += 2 * X.y * tmp; - D.z += 2 * X.z * tmp; - D.w += 2 * s * tmp; - - H(0,0) = 2 * X.x * N.x + 2 * tmp; - H(0,1) = 2 * X.x * N.y; - H(0,2) = 2 * X.x * N.z; - H(0,3) = 2 * X.x * s; - - H(1,0) = 2 * X.y * N.x; - H(1,1) = 2 * X.y * N.y + 2 * tmp; - H(1,2) = 2 * X.y * N.z; - H(1,3) = 2 * X.y * s; - - H(2,0) = 2 * X.z * N.x; - H(2,1) = 2 * X.z * N.y; - H(2,2) = 2 * X.z * N.z + 2 * tmp; - H(2,3) = 2 * X.z * s; - - H(3,0) = 2 * s * N.x; - H(3,1) = 2 * s * N.y; - H(3,2) = 2 * s * N.z; - H(3,3) = 2 * s * s + 2 * c * tmp; - } - - Vector4 solve(ChartBuildData * chart, Vector4 start) - { - const uint faceCount = chart->faces.count(); - - X = start; - - Vector4 dX; - - do { - for (uint i = 0; i < faceCount; i++) - { - const HalfEdge::Face * face = mesh->faceAt(chart->faces[i]); - - addTerm(face->normal(), face->area()); - } - - Vector4 dX; - //solveKramer(H, D, &dX); - solveLU(H, D, &dX); - - // @@ Do a full newton step and reduce by half if F doesn't decrease. - X -= gamma * dX; - - // Constrain normal to be normalized. - X = Vector4(normalize(X.xyz()), X.w); - - } while(absoluteSum(D) > tolf || absoluteSum(dX) > tolx); - - return X; - } - - HalfEdge::Mesh const * const mesh; - const float gamma; - const float tolf; - const float tolx; - - Vector4 X; - - float F; - Vector4 D; - Matrix H; - }; - - // Unnormalized face normal assuming it's a triangle. - static Vector3 triangleNormal(const HalfEdge::Face * face) - { - Vector3 p0 = face->edge->vertex->pos; - Vector3 p1 = face->edge->next->vertex->pos; - Vector3 p2 = face->edge->next->next->vertex->pos; - - Vector3 e0 = p2 - p0; - Vector3 e1 = p1 - p0; - - return normalizeSafe(cross(e0, e1), Vector3(0), 0.0f); - } - - static Vector3 triangleNormalAreaScaled(const HalfEdge::Face * face) - { - Vector3 p0 = face->edge->vertex->pos; - Vector3 p1 = face->edge->next->vertex->pos; - Vector3 p2 = face->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. - static Vector3 triangleCenter(const HalfEdge::Face * face) - { - Vector3 p0 = face->edge->vertex->pos; - Vector3 p1 = face->edge->next->vertex->pos; - Vector3 p2 = face->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; - } - -} // namespace - -void AtlasBuilder::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.count()); - - //#pragma message(NV_FILE_LINE "TODO: Experiment with conic fitting.") - - // F = (Nc*Nt - cos Oc)^2 = (x*Nt_x + y*Nt_y + z*Nt_z - cos w)^2 - // dF/dx = 2 * x * (x*Nt_x + y*Nt_y + z*Nt_z - cos w) - // dF/dy = 2 * y * (x*Nt_x + y*Nt_y + z*Nt_z - cos w) - // dF/dz = 2 * z * (x*Nt_x + y*Nt_y + z*Nt_z - cos w) - // dF/dw = 2 * sin w * (x*Nt_x + y*Nt_y + z*Nt_z - cos w) - - // JacobianMatrix({ - // 2 * x * (x*Nt_x + y*Nt_y + z*Nt_z - Cos(w)), - // 2 * y * (x*Nt_x + y*Nt_y + z*Nt_z - Cos(w)), - // 2 * z * (x*Nt_x + y*Nt_y + z*Nt_z - Cos(w)), - // 2 * Sin(w) * (x*Nt_x + y*Nt_y + z*Nt_z - Cos(w))}, {x,y,z,w}) - - // H[0,0] = 2 * x * Nt_x + 2 * (x*Nt_x + y*Nt_y + z*Nt_z - cos(w)); - // H[0,1] = 2 * x * Nt_y; - // H[0,2] = 2 * x * Nt_z; - // H[0,3] = 2 * x * sin(w); - - // H[1,0] = 2 * y * Nt_x; - // H[1,1] = 2 * y * Nt_y + 2 * (x*Nt_x + y*Nt_y + z*Nt_z - cos(w)); - // H[1,2] = 2 * y * Nt_z; - // H[1,3] = 2 * y * sin(w); - - // H[2,0] = 2 * z * Nt_x; - // H[2,1] = 2 * z * Nt_y; - // H[2,2] = 2 * z * Nt_z + 2 * (x*Nt_x + y*Nt_y + z*Nt_z - cos(w)); - // H[2,3] = 2 * z * sin(w); - - // H[3,0] = 2 * sin(w) * Nt_x; - // H[3,1] = 2 * sin(w) * Nt_y; - // H[3,2] = 2 * sin(w) * Nt_z; - // H[3,3] = 2 * sin(w) * sin(w) + 2 * cos(w) * (x*Nt_x + y*Nt_y + z*Nt_z - cos(w)); - - // @@ Cone fitting might be quite slow. - - /*ConeFitting coneFitting(mesh, 0.1f, 0.001f, 0.001f); - - Vector4 start = Vector4(chart->coneAxis, chart->coneAngle); - Vector4 solution = coneFitting.solve(chart, start); - - chart->coneAxis = solution.xyz(); - chart->coneAngle = solution.w;*/ -} - - - -bool AtlasBuilder::relocateSeeds() -{ - bool anySeedChanged = false; - - const uint chartCount = chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - if (relocateSeed(chartArray[i])) - { - anySeedChanged = true; - } - } - - return anySeedChanged; -} - - -bool AtlasBuilder::relocateSeed(ChartBuildData * chart) -{ - Vector3 centroid = computeChartCentroid(chart); - - const uint N = 10; // @@ Hardcoded to 10? - PriorityQueue bestTriangles(N); - - // Find the first N triangles that fit the proxy best. - const uint faceCount = chart->faces.count(); - for (uint 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. - uint mostCentral; - float maxDistance = -1; - - const uint bestCount = bestTriangles.count(); - for (uint i = 0; i < bestCount; i++) - { - const HalfEdge::Face * face = mesh->faceAt(bestTriangles.pairs[i].face); - Vector3 faceCentroid = triangleCenter(face); - - float distance = length(centroid - faceCentroid); - - /*#pragma message(NV_FILE_LINE "TODO: Implement evaluateDistanceToBoundary.") - float distance = evaluateDistanceToBoundary(chart, bestTriangles.pairs[i].face);*/ - - if (distance > maxDistance) - { - maxDistance = distance; - mostCentral = bestTriangles.pairs[i].face; - } - } - nvDebugCheck(maxDistance >= 0); - - // In order to prevent k-means cyles we record all the previously chosen seeds. - uint index; - if (chart->seeds.find(mostCentral, &index)) - { - // Move new seed to the end of the seed array. - uint last = chart->seeds.count() - 1; - swap(chart->seeds[index], chart->seeds[last]); - return false; - } - else - { - // Append new seed. - chart->seeds.append(mostCentral); - return true; - } -} - -void AtlasBuilder::removeCandidate(uint f) -{ - int c = faceCandidateArray[f]; - if (c != -1) { - faceCandidateArray[f] = -1; - - if (c == candidateArray.count() - 1) { - candidateArray.popBack(); - } - else { - candidateArray.replaceWithLast(c); - faceCandidateArray[candidateArray[c].face] = c; - } - } -} - -void AtlasBuilder::updateCandidate(ChartBuildData * chart, uint f, float metric) -{ - if (faceCandidateArray[f] == -1) { - const uint index = candidateArray.count(); - faceCandidateArray[f] = index; - candidateArray.resize(index + 1); - candidateArray[index].face = f; - candidateArray[index].chart = chart; - candidateArray[index].metric = metric; - } - else { - int c = faceCandidateArray[f]; - nvDebugCheck(c != -1); - - Candidate & candidate = candidateArray[c]; - nvDebugCheck(candidate.face == f); - - if (metric < candidate.metric || chart == candidate.chart) { - candidate.metric = metric; - candidate.chart = chart; - } - } - -} - - -void AtlasBuilder::updatePriorities(ChartBuildData * chart) -{ - // Re-evaluate candidate priorities. - uint candidateCount = chart->candidates.count(); - for (uint 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 AtlasBuilder::evaluatePriority(ChartBuildData * chart, uint 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( - settings.proxyFitMetricWeight * F + - settings.roundnessMetricWeight * C + - settings.straightnessMetricWeight * P + - settings.normalSeamMetricWeight * N + - settings.textureSeamMetricWeight * T); - - /*cost = settings.proxyFitMetricWeight * powf(F, settings.proxyFitMetricExponent); - cost = max(cost, settings.roundnessMetricWeight * powf(C, settings.roundnessMetricExponent)); - cost = max(cost, settings.straightnessMetricWeight * pow(P, settings.straightnessMetricExponent)); - cost = max(cost, settings.normalSeamMetricWeight * N); - cost = max(cost, settings.textureSeamMetricWeight * T);*/ - - // Enforce limits strictly: - if (newChartArea > settings.maxChartArea) cost = FLT_MAX; - if (newBoundaryLength > settings.maxBoundaryLength) cost = FLT_MAX; - - // Make sure normal seams are fully respected: - if (settings.normalSeamMetricWeight >= 1000 && N != 0) cost = FLT_MAX; - - nvCheck(isFinite(cost)); - return cost; -} - - -// Returns a value in [0-1]. -float AtlasBuilder::evaluateProxyFitMetric(ChartBuildData * chart, uint f) -{ - const HalfEdge::Face * face = mesh->faceAt(f); - Vector3 faceNormal = triangleNormal(face); - //return square(dot(chart->coneAxis, faceNormal) - cosf(chart->coneAngle)); - - // Use plane fitting metric for now: - //return square(1 - dot(faceNormal, chart->planeNormal)); // @@ normal deviations should be weighted by face area - return 1 - dot(faceNormal, chart->planeNormal); // @@ normal deviations should be weighted by face area - - // Find distance to chart. - /*Vector3 faceCentroid = face->centroid(); - - float dist = 0; - int count = 0; - - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - - if (!edge->isBoundary()) { - const HalfEdge::Face * neighborFace = edge->pair()->face(); - if (faceChartArray[neighborFace->id()] == chart->id) { - dist += length(neighborFace->centroid() - faceCentroid); - count++; - } - } - } - - dist /= (count * count); - - return (1 - dot(faceNormal, chart->planeNormal)) * dist;*/ - - //return (1 - dot(faceNormal, chart->planeNormal)); -} - -float AtlasBuilder::evaluateDistanceToBoundary(ChartBuildData * chart, uint face) -{ -//#pragma message(NV_FILE_LINE "TODO: Evaluate distance to boundary metric.") - - // @@ This is needed for the seed relocation code. - // @@ This could provide a better roundness metric. - - return 0.0f; -} - -float AtlasBuilder::evaluateDistanceToSeed(ChartBuildData * chart, uint f) -{ - //const uint seed = chart->seeds.back(); - //const uint faceCount = mesh->faceCount(); - //return shortestPaths[seed * faceCount + f]; - - const HalfEdge::Face * seed = mesh->faceAt(chart->seeds.back()); - const HalfEdge::Face * face = mesh->faceAt(f); - return length(triangleCenter(seed) - triangleCenter(face)); -} - - -float AtlasBuilder::evaluateRoundnessMetric(ChartBuildData * chart, uint face, float newBoundaryLength, float newChartArea) -{ - // @@ D-charts use distance to seed. - // C(c,t) = pi * D(S_c,t)^2 / A_c - //return PI * square(evaluateDistanceToSeed(chart, face)) / chart->area; - //return PI * square(evaluateDistanceToSeed(chart, face)) / chart->area; - //return 2 * PI * evaluateDistanceToSeed(chart, face) / chart->boundaryLength; - - // Garland's Hierarchical Face Clustering paper uses ratio between boundary and area, which is easier to compute and might work as well: - // roundness = D^2/4*pi*A -> circle = 1, non circle greater than 1 - - //return square(newBoundaryLength) / (newChartArea * 4 * PI); - 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; - } - - //return square(newBoundaryLength) / (4 * PI * newChartArea); - //return clamp(1 - (4 * PI * newChartArea) / square(newBoundaryLength), 0.0f, 1.0f); - - // Use the ratio between the new roundness vs. the previous roundness. - // - If we use the absolute metric, when the initial face is very long, then it's hard to make any progress. - //return (square(newBoundaryLength) * chart->area) / (square(chart->boundaryLength) * newChartArea); - //return (4 * PI * newChartArea) / square(newBoundaryLength) - (4 * PI * chart->area) / square(chart->boundaryLength); - - //if (square(newBoundaryLength) * chart->area) / (square(chart->boundaryLength) * newChartArea); - -} - -float AtlasBuilder::evaluateStraightnessMetric(ChartBuildData * chart, uint 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 = edge->length(); - float l = edgeLengths[edge->id/2]; - - if (edge->isBoundary()) - { - l_out += l; - } - else - { - uint neighborFaceId = edge->pair->face->id; - if (faceChartArray[neighborFaceId] != chart->id) { - l_out += l; - } - else { - l_in += l; - } - } - } - nvDebugCheck(l_in != 0.0f); // Candidate face must be adjacent to chart. @@ This is not true if the input mesh has zero-length edges. - - //return l_out / l_in; - float ratio = (l_out - l_in) / (l_out + l_in); - //if (ratio < 0) ratio *= 10; // Encourage closing gaps. - return min(ratio, 0.0f); // Only use the straightness metric to close gaps. - //return ratio; -} - - -float AtlasBuilder::evaluateNormalSeamMetric(ChartBuildData * chart, uint 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 uint 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 (isNormalSeam(edge)) - { - 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); - //float a0 = clamp(acosf(d0) / (PI/2), 0.0f, 1.0f); - //float a1 = clamp(acosf(d1) / (PI/2), 0.0f, 1.0f); - //l *= (a0 + a1) * 0.5f; - - l *= 1 - (d0 + d1) * 0.5f; - - seamFactor += l; - } - } - - if (seamFactor == 0) return 0.0f; - return seamFactor / totalLength; -} - - -float AtlasBuilder::evaluateTextureSeamMetric(ChartBuildData * chart, uint f) -{ - float seamLength = 0.0f; - //float newSeamLength = 0.0f; - //float oldSeamLength = 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(); - - /*float l = edge->length(); - totalLength += l; - - if (edge->isBoundary() || !edge->isSeam()) { - continue; - } - - // Make sure it's a texture seam. - if (isTextureSeam(edge)) - { - uint neighborFaceId = edge->pair()->face()->id(); - if (faceChartArray[neighborFaceId] != chart->id) { - newSeamLength += l; - } - else { - oldSeamLength += l; - } - }*/ - - if (edge->isBoundary()) { - continue; - } - - const uint 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 (isTextureSeam(edge)) - { - seamLength += l; - } - } - - if (seamLength == 0.0f) { - return 0.0f; // Avoid division by zero. - } - - return seamLength / totalLength; -} - - -float AtlasBuilder::evaluateSeamMetric(ChartBuildData * chart, uint f) -{ - float newSeamLength = 0.0f; - float oldSeamLength = 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(); - - //float l = edge->length(); - float l = edgeLengths[edge->id/2]; - - if (edge->isBoundary()) - { - newSeamLength += l; - } - else - { - if (edge->isSeam()) - { - uint neighborFaceId = edge->pair->face->id; - if (faceChartArray[neighborFaceId] != chart->id) { - newSeamLength += l; - } - else { - oldSeamLength += l; - } - } - } - - totalLength += l; - } - - return (newSeamLength - oldSeamLength) / totalLength; -} - - -float AtlasBuilder::evaluateChartArea(ChartBuildData * chart, uint f) -{ - const HalfEdge::Face * face = mesh->faceAt(f); - //return chart->area + face->area(); - return chart->area + faceAreas[face->id]; -} - - -float AtlasBuilder::evaluateBoundaryLength(ChartBuildData * chart, uint 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 - { - uint neighborFaceId = edge->pair->face->id; - if (faceChartArray[neighborFaceId] != chart->id) { - boundaryLength += edgeLength; - } - else { - boundaryLength -= edgeLength; - } - } - } - //nvDebugCheck(boundaryLength >= 0); - - return max(0.0f, boundaryLength); // @@ Hack! -} - -Vector3 AtlasBuilder::evaluateChartNormalSum(ChartBuildData * chart, uint f) -{ - const HalfEdge::Face * face = mesh->faceAt(f); - return chart->normalSum + triangleNormalAreaScaled(face); -} - -Vector3 AtlasBuilder::evaluateChartCentroidSum(ChartBuildData * chart, uint f) -{ - const HalfEdge::Face * face = mesh->faceAt(f); - return chart->centroidSum + face->centroid(); -} - - -Vector3 AtlasBuilder::computeChartCentroid(const ChartBuildData * chart) -{ - Vector3 centroid(0); - - const uint faceCount = chart->faces.count(); - for (uint i = 0; i < faceCount; i++) - { - const HalfEdge::Face * face = mesh->faceAt(chart->faces[i]); - centroid += triangleCenter(face); - } - - return centroid / float(faceCount); -} - - -void AtlasBuilder::fillHoles(float threshold) -{ - while (facesLeft > 0) - { - createRandomChart(threshold); - } -} - - -void AtlasBuilder::mergeChart(ChartBuildData * owner, ChartBuildData * chart, float sharedBoundaryLength) -{ - const uint faceCount = chart->faces.count(); - for (uint i = 0; i < faceCount; i++) - { - uint f = chart->faces[i]; - - nvDebugCheck(faceChartArray[f] == chart->id); - faceChartArray[f] = owner->id; - - owner->faces.append(f); - } - - // Update adjacencies? - - owner->area += chart->area; - owner->boundaryLength += chart->boundaryLength - sharedBoundaryLength; - - owner->normalSum += chart->normalSum; - owner->centroidSum += chart->centroidSum; - - updateProxy(owner); -} - -void AtlasBuilder::mergeCharts() -{ - Array<float> sharedBoundaryLengths; - - const uint chartCount = chartArray.count(); - for (int c = chartCount-1; c >= 0; c--) - { - sharedBoundaryLengths.clear(); - sharedBoundaryLengths.resize(chartCount, 0.0f); - - ChartBuildData * chart = chartArray[c]; - - float externalBoundary = 0.0f; - - const uint faceCount = chart->faces.count(); - for (uint i = 0; i < faceCount; i++) - { - uint 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 { - uint neighborFace = edge->pair->face->id; - uint neighborChart = faceChartArray[neighborFace]; - - if (neighborChart != c) { - if ((edge->isSeam() && (isNormalSeam(edge) || isTextureSeam(edge))) || 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 * 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 * 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 < I32(chartArray.count()); /*do not increment if removed*/) - { - if (chartArray[c] == NULL) { - chartArray.removeAt(c); - - // Update faceChartArray. - const uint faceCount = faceChartArray.count(); - for (uint i = 0; i < faceCount; i++) { - nvDebugCheck (faceChartArray[i] != -1); - nvDebugCheck (faceChartArray[i] != c); - nvDebugCheck (faceChartArray[i] <= I32(chartArray.count())); - - if (faceChartArray[i] > c) { - faceChartArray[i]--; - } - } - } - else { - chartArray[c]->id = c; - c++; - } - } -} - - - -const Array<uint> & AtlasBuilder::chartFaces(uint i) const -{ - return chartArray[i]->faces; -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.h b/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.h deleted file mode 100644 index f25c724f7e..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.h +++ /dev/null @@ -1,111 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#pragma once -#ifndef NV_MESH_ATLASBUILDER_H -#define NV_MESH_ATLASBUILDER_H - -#include "Atlas.h" - -#include "nvmath/Vector.h" -#include "nvmath/Random.h" -#include "nvmesh/nvmesh.h" - -#include "nvcore/Array.h" -#include "nvcore/BitArray.h" - - - -namespace nv -{ - namespace HalfEdge { class Mesh; } - - struct ChartBuildData; - - struct AtlasBuilder - { - AtlasBuilder(const HalfEdge::Mesh * m); - ~AtlasBuilder(); - - void markUnchartedFaces(const Array<uint> & unchartedFaces); - - void computeShortestPaths(); - - void placeSeeds(float threshold, uint maxSeedCount); - void createRandomChart(float threshold); - - void addFaceToChart(ChartBuildData * chart, uint f, bool recomputeProxy=false); - - bool growCharts(float threshold, uint faceCount); - bool growChart(ChartBuildData * chart, float threshold, uint faceCount); - - void resetCharts(); - - void updateCandidates(ChartBuildData * chart, uint face); - - void updateProxies(); - void updateProxy(ChartBuildData * chart); - - bool relocateSeeds(); - bool relocateSeed(ChartBuildData * chart); - - void updatePriorities(ChartBuildData * chart); - - float evaluatePriority(ChartBuildData * chart, uint face); - float evaluateProxyFitMetric(ChartBuildData * chart, uint face); - float evaluateDistanceToBoundary(ChartBuildData * chart, uint face); - float evaluateDistanceToSeed(ChartBuildData * chart, uint face); - float evaluateRoundnessMetric(ChartBuildData * chart, uint face, float newBoundaryLength, float newChartArea); - float evaluateStraightnessMetric(ChartBuildData * chart, uint face); - - float evaluateNormalSeamMetric(ChartBuildData * chart, uint f); - float evaluateTextureSeamMetric(ChartBuildData * chart, uint f); - float evaluateSeamMetric(ChartBuildData * chart, uint f); - - float evaluateChartArea(ChartBuildData * chart, uint f); - float evaluateBoundaryLength(ChartBuildData * chart, uint f); - Vector3 evaluateChartNormalSum(ChartBuildData * chart, uint f); - Vector3 evaluateChartCentroidSum(ChartBuildData * chart, uint f); - - Vector3 computeChartCentroid(const ChartBuildData * chart); - - - void fillHoles(float threshold); - void mergeCharts(); - - // @@ Cleanup. - struct Candidate { - uint face; - ChartBuildData * chart; - float metric; - }; - - const Candidate & getBestCandidate() const; - void removeCandidate(uint f); - void updateCandidate(ChartBuildData * chart, uint f, float metric); - - void mergeChart(ChartBuildData * owner, ChartBuildData * chart, float sharedBoundaryLength); - - - uint chartCount() const { return chartArray.count(); } - const Array<uint> & chartFaces(uint i) const; - - const HalfEdge::Mesh * mesh; - uint facesLeft; - Array<int> faceChartArray; - Array<ChartBuildData *> chartArray; - Array<float> shortestPaths; - - Array<float> edgeLengths; - Array<float> faceAreas; - - Array<Candidate> candidateArray; // - Array<uint> faceCandidateArray; // Map face index to candidate index. - - MTRand rand; - - SegmentationSettings settings; - }; - -} // nv namespace - -#endif // NV_MESH_ATLASBUILDER_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp b/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp deleted file mode 100644 index 11e635db17..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp +++ /dev/null @@ -1,1401 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "AtlasPacker.h" -#include "nvmesh/halfedge/Vertex.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/param/Atlas.h" -#include "nvmesh/param/Util.h" -#include "nvmesh/raster/Raster.h" - -#include "nvmath/Vector.inl" -#include "nvmath/ConvexHull.h" -#include "nvmath/Color.h" -#include "nvmath/ftoi.h" - -#include "nvcore/StrLib.h" // debug -#include "nvcore/StdStream.h" // fileOpen - -#include <float.h> // FLT_MAX -#include <limits.h> // UINT_MAX - -using namespace nv; - -#define DEBUG_OUTPUT 0 - -#if DEBUG_OUTPUT - -#include "nvimage/ImageIO.h" - -namespace -{ - const uint TGA_TYPE_GREY = 3; - const uint TGA_TYPE_RGB = 2; - const uint TGA_ORIGIN_UPPER = 0x20; - -#pragma pack(push, 1) - struct TgaHeader { - uint8 id_length; - uint8 colormap_type; - uint8 image_type; - uint16 colormap_index; - uint16 colormap_length; - uint8 colormap_size; - uint16 x_origin; - uint16 y_origin; - uint16 width; - uint16 height; - uint8 pixel_size; - uint8 flags; - - enum { Size = 18 }; //const static int SIZE = 18; - }; -#pragma pack(pop) - - static void outputDebugBitmap(const char * fileName, const BitMap & bitmap, int w, int h) - { - FILE * fp = fileOpen(fileName, "wb"); - if (fp == NULL) return; - - nvStaticCheck(sizeof(TgaHeader) == TgaHeader::Size); - TgaHeader tga; - tga.id_length = 0; - tga.colormap_type = 0; - tga.image_type = TGA_TYPE_GREY; - - tga.colormap_index = 0; - tga.colormap_length = 0; - tga.colormap_size = 0; - - tga.x_origin = 0; - tga.y_origin = 0; - tga.width = w; - tga.height = h; - tga.pixel_size = 8; - tga.flags = TGA_ORIGIN_UPPER; - - fwrite(&tga, sizeof(TgaHeader), 1, fp); - - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - uint8 color = bitmap.bitAt(i, j) ? 0xFF : 0x0; - fwrite(&color, 1, 1, fp); - } - } - - fclose(fp); - } - - static void outputDebugImage(const char * fileName, const Image & bitmap, int w, int h) - { - FILE * fp = fileOpen(fileName, "wb"); - if (fp == NULL) return; - - nvStaticCheck(sizeof(TgaHeader) == TgaHeader::Size); - TgaHeader tga; - tga.id_length = 0; - tga.colormap_type = 0; - tga.image_type = TGA_TYPE_RGB; - - tga.colormap_index = 0; - tga.colormap_length = 0; - tga.colormap_size = 0; - - tga.x_origin = 0; - tga.y_origin = 0; - tga.width = w; - tga.height = h; - tga.pixel_size = 24; - tga.flags = TGA_ORIGIN_UPPER; - - fwrite(&tga, sizeof(TgaHeader), 1, fp); - - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - Color32 color = bitmap.pixel(i, j); - fwrite(&color.r, 1, 1, fp); - fwrite(&color.g, 1, 1, fp); - fwrite(&color.b, 1, 1, fp); - } - } - - fclose(fp); - } -} - -#endif // DEBUG_OUTPUT - -inline int align(int x, int a) { - //return a * ((x + a - 1) / a); - //return (x + a - 1) & -a; - return (x + a - 1) & ~(a - 1); -} - -inline bool isAligned(int x, int a) { - return (x & (a - 1)) == 0; -} - - - -AtlasPacker::AtlasPacker(Atlas * atlas) : m_atlas(atlas), m_bitmap(256, 256) -{ - m_width = 0; - m_height = 0; - - // -- GODOT start -- - //m_debug_bitmap.allocate(256, 256); - //m_debug_bitmap.fill(Color32(0,0,0,0)); - // -- GODOT end -- -} - -AtlasPacker::~AtlasPacker() -{ -} - -// This should compute convex hull and use rotating calipers to find the best box. Currently it uses a brute force method. -static bool computeBoundingBox(Chart * chart, Vector2 * majorAxis, Vector2 * minorAxis, Vector2 * minCorner, Vector2 * maxCorner) -{ - // Compute list of boundary points. - Array<Vector2> points(16); - - HalfEdge::Mesh * mesh = chart->chartMesh(); - const uint vertexCount = mesh->vertexCount(); - - for (uint i = 0; i < vertexCount; i++) { - HalfEdge::Vertex * vertex = mesh->vertexAt(i); - if (vertex->isBoundary()) { - points.append(vertex->tex); - } - } - - // This is not valid anymore. The chart mesh may have multiple boundaries! - /*const HalfEdge::Vertex * vertex = findBoundaryVertex(chart->chartMesh()); - - // Traverse boundary. - const HalfEdge::Edge * const firstEdge = vertex->edge(); - const HalfEdge::Edge * edge = firstEdge; - do { - vertex = edge->vertex(); - - nvDebugCheck (vertex->isBoundary()); - points.append(vertex->tex); - - edge = edge->next(); - } while (edge != firstEdge);*/ - -#if 1 - Array<Vector2> hull; - if (points.size()==0) { - return false; - } - - 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 uint hullCount = hull.count(); - for (uint 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); - nvDebugCheck(isFinite(axis)); - - // Compute bounding box. - Vector2 box_min(FLT_MAX, FLT_MAX); - Vector2 box_max(-FLT_MAX, -FLT_MAX); - - for (uint 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; - } - } - - // Make sure the box contains all the input points since the convex hull is not 100% accurate. - /*const uint pointCount = points.count(); - for (uint v = 0; v < pointCount; v++) { - - Vector2 point = points[v]; - - float x = dot(best_axis, point); - if (x < best_min.x) best_min.x = x; - - float y = dot(Vector2(-best_axis.y, best_axis.x), point); - if (y < best_min.y) best_min.y = y; - }*/ - - // Consider all points, not only boundary points, in case the input chart is malformed. - for (uint 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; - -#else - // Approximate implementation: try 16 different directions and keep the best. - - const uint N = 16; - Vector2 axis[N]; - - float minAngle = 0; - float maxAngle = PI / 2; - - int best; - Vector2 mins[N]; - Vector2 maxs[N]; - - const int iterationCount = 1; - for (int j = 0; j < iterationCount; j++) - { - // Init predefined directions. - for (int i = 0; i < N; i++) - { - float angle = lerp(minAngle, maxAngle, float(i)/N); - axis[i].set(cosf(angle), sinf(angle)); - } - - // Compute box for each direction. - for (int i = 0; i < N; i++) - { - mins[i].set(FLT_MAX, FLT_MAX); - maxs[i].set(-FLT_MAX, -FLT_MAX); - } - - for (uint p = 0; p < points.count(); p++) - { - Vector2 point = points[p]; - - for (int i = 0; i < N; i++) - { - float x = dot(axis[i], point); - if (x < mins[i].x) mins[i].x = x; - if (x > maxs[i].x) maxs[i].x = x; - - float y = dot(Vector2(-axis[i].y, axis[i].x), point); - if (y < mins[i].y) mins[i].y = y; - if (y > maxs[i].y) maxs[i].y = y; - } - } - - // Find box with minimum area. - best = -1; - int second_best = -1; - float best_area = FLT_MAX; - float second_best_area = FLT_MAX; - - for (int i = 0; i < N; i++) - { - float area = (maxs[i].x - mins[i].x) * (maxs[i].y - mins[i].y); - - if (area < best_area) - { - second_best_area = best_area; - second_best = best; - - best_area = area; - best = i; - } - else if (area < second_best_area) - { - second_best_area = area; - second_best = i; - } - } - nvDebugCheck(best != -1); - nvDebugCheck(second_best != -1); - nvDebugCheck(best != second_best); - - if (j != iterationCount-1) - { - // Handle wrap-around during the first iteration. - if (j == 0) { - if (best == 0 && second_best == N-1) best = N; - if (best == N-1 && second_best == 0) second_best = N; - } - - if (best < second_best) swap(best, second_best); - - // Update angles. - float deltaAngle = (maxAngle - minAngle) / N; - maxAngle = minAngle + (best - 0.5f) * deltaAngle; - minAngle = minAngle + (second_best + 0.5f) * deltaAngle; - } - } - - // Compute major and minor axis, and origin. - *majorAxis = axis[best]; - *minorAxis = Vector2(-axis[best].y, axis[best].x); - *origin = mins[best]; - - // @@ If the parameterization is invalid, we could have an interior vertex outside the boundary. - // @@ In that case the returned bounding box would be incorrect. Compute updated bounds here. - /*for (uint p = 0; p < points.count(); p++) - { - Vector2 point = points[p]; - - for (int i = 0; i < N; i++) - { - float x = dot(*majorAxis, point); - float y = dot(*minorAxis, point); - } - }*/ -#endif - - return true; -} - - -void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned, bool conservative) -{ - const uint chartCount = m_atlas->chartCount(); - if (chartCount == 0) return; - - Array<float> chartOrderArray; - chartOrderArray.resize(chartCount); - - Array<Vector2> chartExtents; - chartExtents.resize(chartCount); - - float meshArea = 0; - for (uint 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()) { - // Let's assume vertex maps are arranged in a rectangle. - //HalfEdge::Mesh * mesh = chart->chartMesh(); - - // 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 = fabs(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; - } - nvCheck(isFinite(scale)); - - // Compute bounding box of chart. - Vector2 majorAxis, minorAxis, origin, end; - if (!computeBoundingBox(chart, &majorAxis, &minorAxis, &origin, &end)) { - m_atlas->setFailed(); - return; - } - - nvCheck(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 uint vertexCount = mesh->vertexCount(); - for (uint 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) { - nvDebug("tmp: %f %f\n", tmp.x, tmp.y); - nvDebug("scale: %f\n", scale); - nvDebug("origin: %f %f\n", origin.x, origin.y); - nvDebug("majorAxis: %f %f\n", majorAxis.x, majorAxis.y); - nvDebug("minorAxis: %f %f\n", minorAxis.x, minorAxis.y); - // -- GODOT start -- - //nvDebugBreak(); - m_atlas->setFailed(); - return; - // -- GODOT end -- - } - //nvCheck(tmp.x >= 0 && tmp.y >= 0); - - vertex->tex = tmp; - - nvCheck(isFinite(vertex->tex.x) && isFinite(vertex->tex.y)); - - extents = max(extents, tmp); - } - nvDebugCheck(extents.x >= 0 && extents.y >= 0); - - // Limit chart size. - if (extents.x > 1024 || extents.y > 1024) { - float limit = max(extents.x, extents.y); - - scale = 1024 / (limit + 1); - - for (uint i = 0; i < vertexCount; i++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(i); - vertex->tex *= scale; - } - - extents *= scale; - - nvDebugCheck(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 (blockAligned) { - // Align all chart extents to 4x4 blocks, but taking padding into account. - if (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 (blockAligned) { - // Align all chart extents to 4x4 blocks, but taking padding into account. - if (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 (uint 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; - - nvCheck(isFinite(vertex->tex.x) && 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. - - //nvDebug("Sorting charts.\n"); - - // Sort charts by area. - m_radix.sort(chartOrderArray); - const uint32 * ranks = m_radix.ranks(); - - // Estimate size of the map based on the mesh surface area and given texel scale. - float texelCount = meshArea * square(texelsPerUnit) / 0.75f; // Assume 75% utilization. - if (texelCount < 1) texelCount = 1; - uint approximateExtent = nextPowerOfTwo(uint(sqrtf(texelCount))); - - //nvDebug("Init bitmap.\n"); - - // @@ Pack all charts smaller than a texel into a compact rectangle. - // @@ Start considering only 1x1 charts. Extend to 1xn charts later. - - /*for (uint i = 0; i < chartCount; i++) - { - uint c = ranks[chartCount - i - 1]; // largest chart first - - Chart * chart = m_atlas->chartAt(c); - - if (!chart->isDisk()) continue; - - if (iceil(chartExtents[c].x) == 1 && iceil(chartExtents[c].x) == 1) { - // @@ Add to - } - }*/ - - - - // Init bit map. - m_bitmap.clearAll(); - if (approximateExtent > m_bitmap.width()) { - m_bitmap.resize(approximateExtent, approximateExtent, false); - // -- GODOT start -- - //m_debug_bitmap.resize(approximateExtent, approximateExtent); - //m_debug_bitmap.fill(Color32(0,0,0,0)); - // -- GODOT end -- - } - - - int w = 0; - int h = 0; - -#if 1 - // Add sorted charts to bitmap. - for (uint i = 0; i < chartCount; i++) - { - uint 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()) { - // 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 (conservative) { - // Init all bits to 0. - chart_bitmap.resize(ftoi_ceil(chartExtents[c].x) + 2, ftoi_ceil(chartExtents[c].y) + 2, /*initValue=*/false); // + 2 to add padding on both sides. - - // Rasterize chart and dilate. - drawChartBitmapDilate(chart, &chart_bitmap, /*padding=*/1); - } - 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(quality, &chart_bitmap, chartExtents[c], w, h, &best_x, &best_y, &best_cw, &best_ch, &best_r); - - /*if (w < best_x + best_cw || h < best_y + best_ch) - { - nvDebug("Resize extents to (%d, %d).\n", best_x + best_cw, best_y + best_ch); - }*/ - - // Update parametric extents. - w = max(w, best_x + best_cw); - h = max(h, best_y + best_ch); - - w = align(w, 4); - h = align(h, 4); - - // Resize bitmap if necessary. - if (uint(w) > m_bitmap.width() || uint(h) > m_bitmap.height()) - { - //nvDebug("Resize bitmap (%d, %d).\n", nextPowerOfTwo(w), nextPowerOfTwo(h)); - m_bitmap.resize(nextPowerOfTwo(U32(w)), nextPowerOfTwo(U32(h)), false); - // -- GODOT start -- - //m_debug_bitmap.resize(nextPowerOfTwo(U32(w)), nextPowerOfTwo(U32(h))); - // -- GODOT end -- - } - - //nvDebug("Add chart at (%d, %d).\n", best_x, best_y); - - addChart(&chart_bitmap, w, h, best_x, best_y, best_r, /*debugOutput=*/NULL); - - // -- GODOT start -- - // IC: Output chart again to debug bitmap. - /*if (chart->isVertexMapped()) { - addChart(&chart_bitmap, w, h, best_x, best_y, best_r, &m_debug_bitmap); - } - else { - addChart(chart, w, h, best_x, best_y, best_r, &m_debug_bitmap); - }*/ - // -- GODOT end -- - - //float best_angle = 2 * PI * best_r; - - // Translate and rotate chart texture coordinates. - HalfEdge::Mesh * mesh = chart->chartMesh(); - const uint vertexCount = mesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - - Vector2 t = vertex->tex; - if (best_r) 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; - - nvCheck(vertex->tex.x >= 0 && vertex->tex.y >= 0); - nvCheck(isFinite(vertex->tex.x) && isFinite(vertex->tex.y)); - } - -#if DEBUG_OUTPUT && 0 - StringBuilder fileName; - fileName.format("debug_packer_%d.tga", i); - //outputDebugBitmap(fileName.str(), m_bitmap, w, h); - outputDebugImage(fileName.str(), m_debug_bitmap, w, h); -#endif - } - -#else // 0 - - // Add sorted charts to bitmap. - for (uint i = 0; i < chartCount; i++) - { - uint c = ranks[chartCount - i - 1]; // largest chart first - - Chart * chart = m_atlas->chartAt(c); - - if (!chart->isDisk()) continue; - - Vector2 scale(1, 1); - -#if 0 // old method. - //m_padding_x = 2*padding; - //m_padding_y = 2*padding; -#else - //m_padding_x = 0; //padding; - //m_padding_y = 0; //padding; -#endif - - int bw = ftoi_ceil(chartExtents[c].x + 1); - int bh = ftoi_ceil(chartExtents[c].y + 1); - - if (chartExtents[c].x < 1.0f) { - scale.x = 0.01f; // @@ Ideally we would like to scale it to 0, but then our rasterizer would not touch any pixels. - bw = 1; - } - if (chartExtents[c].y < 1.0f) { - scale.y = 0.01f; - bh = 1; - } - - //BitMap chart_bitmap(iceil(chartExtents[c].x) + 1 + m_padding_x * 2, iceil(chartExtents[c].y) + 1 + m_padding_y * 2); - //BitMap chart_bitmap(ftoi_ceil(chartExtents[c].x/2)*2, ftoi_ceil(chartExtents[c].y/2)*2); - BitMap chart_bitmap(bw, bh); - chart_bitmap.clearAll(); - - Vector2 offset; - offset.x = 0; // (chart_bitmap.width() - chartExtents[c].x) * 0.5f; - offset.y = 0; // (chart_bitmap.height() - chartExtents[c].y) * 0.5f; - - drawChartBitmap(chart, &chart_bitmap, scale, offset); - - int best_x, best_y; - int best_cw, best_ch; - int best_r; - findChartLocation(quality, &chart_bitmap, chartExtents[c], w, h, &best_x, &best_y, &best_cw, &best_ch, &best_r); - - /*if (w < best_x + best_cw || h < best_y + best_ch) - { - nvDebug("Resize extents to (%d, %d).\n", best_x + best_cw, best_y + best_ch); - }*/ - - // Update parametric extents. - w = max(w, best_x + best_cw); - h = max(h, best_y + best_ch); - - // Resize bitmap if necessary. - if (uint(w) > m_bitmap.width() || uint(h) > m_bitmap.height()) - { - //nvDebug("Resize bitmap (%d, %d).\n", nextPowerOfTwo(w), nextPowerOfTwo(h)); - m_bitmap.resize(nextPowerOfTwo(w), nextPowerOfTwo(h), false); - m_debug_bitmap.resize(nextPowerOfTwo(w), nextPowerOfTwo(h)); - } - - //nvDebug("Add chart at (%d, %d).\n", best_x, best_y); - -#if 0 // old method. -#if _DEBUG - checkCanAddChart(chart, w, h, best_x, best_y, best_r); -#endif - - // Add chart. - addChart(chart, w, h, best_x, best_y, best_r); -#else - // Add chart reusing its bitmap. - addChart(&chart_bitmap, w, h, best_x, best_y, best_r); -#endif - - //float best_angle = 2 * PI * best_r; - - // Translate and rotate chart texture coordinates. - HalfEdge::Mesh * mesh = chart->chartMesh(); - const uint vertexCount = mesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - - Vector2 t = vertex->tex * scale + offset; - if (best_r) 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; - - nvCheck(vertex->tex.x >= 0 && vertex->tex.y >= 0); - } - -#if DEBUG_OUTPUT && 0 - StringBuilder fileName; - fileName.format("debug_packer_%d.tga", i); - //outputDebugBitmap(fileName.str(), m_bitmap, w, h); - outputDebugImage(fileName.str(), m_debug_bitmap, w, h); -#endif - } - -#endif // 0 - - //w -= padding - 1; // Leave one pixel border! - //h -= padding - 1; - - m_width = max(0, w); - m_height = max(0, h); - - nvCheck(isAligned(m_width, 4)); - nvCheck(isAligned(m_height, 4)); - - // -- GODOT start -- - //m_debug_bitmap.resize(m_width, m_height); - //m_debug_bitmap.setFormat(Image::Format_ARGB); - // -- GODOT end -- - -#if DEBUG_OUTPUT - //outputDebugBitmap("debug_packer_final.tga", m_bitmap, w, h); - //outputDebugImage("debug_packer_final.tga", m_debug_bitmap, w, h); - ImageIO::save("debug_packer_final.tga", &m_debug_bitmap); -#endif -} - - -// 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 AtlasPacker::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) -{ - 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); - } - else - { - findChartLocation_random(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r, attempts); - } -} - -#define BLOCK_SIZE 4 - -void AtlasPacker::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) -{ - int best_metric = INT_MAX; - - // Try two different orientations. - for (int r = 0; r < 2; r++) - { - int cw = bitmap->width(); - int ch = bitmap->height(); - if (r & 1) swap(cw, ch); - - for (int y = 0; y <= h + 1; y += BLOCK_SIZE) // + 1 to extend atlas in case atlas full. - { - for (int x = 0; x <= w + 1; x += BLOCK_SIZE) // + 1 not really necessary here. - { - // Early out. - int area = max(w, x+cw) * max(h, y+ch); - //int perimeter = max(w, x+cw) + max(h, y+ch); - int extents = max(max(w, x+cw), max(h, y+ch)); - - int metric = extents*extents + area; - - if (metric > best_metric) { - continue; - } - if (metric == best_metric && max(x, y) >= 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: - nvDebugCheck (best_metric != INT_MAX); -} - - -void AtlasPacker::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) -{ - 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. - - x = align(x, BLOCK_SIZE); - y = align(y, BLOCK_SIZE); - - int cw = bitmap->width(); - int ch = bitmap->height(); - if (r & 1) swap(cw, ch); - - // Early out. - int area = max(w, x+cw) * max(h, y+ch); - //int perimeter = max(w, x+cw) + max(h, y+ch); - int extents = max(max(w, x+cw), max(h, y+ch)); - - int metric = extents*extents + area; - - if (metric > best_metric) { - continue; - } - if (metric == best_metric && min(x, y) > 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 AtlasPacker::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 uint faceCount = chart->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = chart->chartMesh()->faceAt(f); - - Vector2 vertices[4]; - - uint 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); - } - } - - swap(tmp, *bitmap); - } -} - - -void AtlasPacker::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) - }; - /*static const Vector2 pad[4] = { - Vector2(-1, -1), - Vector2(1, -1), - Vector2(-1, 1), - Vector2(1, 1) - };*/ - - // 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 uint faceCount = chart->chartMesh()->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = chart->chartMesh()->faceAt(f); - - Vector2 vertices[4]; - - uint 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]; - nvCheck(ftoi_ceil(vertices[edgeCount].x) >= 0); - nvCheck(ftoi_ceil(vertices[edgeCount].y) >= 0); - nvCheck(ftoi_ceil(vertices[edgeCount].x) <= w); - nvCheck(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); - } - } - } - - // @@ This only allows us to expand the size in texel intervals. - /*if (m_padding_x != 0 && m_padding_y != 0)*/ { - - // Expand chart by padding pixels. (dilation) - BitMap tmp(w, h); - //for (int i = 0; i < 1; 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); - } - } - - swap(tmp, *bitmap); - //} - } -} - -bool AtlasPacker::canAddChart(const BitMap * bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r) -{ - nvDebugCheck(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; -} - -#if 0 -void AtlasPacker::checkCanAddChart(const Chart * chart, int w, int h, int x, int y, int r) -{ - nvDebugCheck(r == 0 || r == 1); - Vector2 extents = Vector2(float(w), float(h)); - Vector2 offset = Vector2(float(x), float(y)); - - // Rasterize chart faces, set bits. - const uint faceCount = chart->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = chart->chartMesh()->faceAt(f); - - Vector2 vertices[4]; - - uint edgeCount = 0; - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - if (edgeCount < 4) - { - Vector2 t = it.vertex()->tex; - if (r == 1) swap(t.x, t.y); - vertices[edgeCount] = t + offset; - } - edgeCount++; - } - - if (edgeCount == 3) - { - Raster::drawTriangle(Raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::checkBitsCallback, &m_bitmap); - } - else - { - Raster::drawQuad(Raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::checkBitsCallback, &m_bitmap); - } - } -} -#endif // 0 - - -static Color32 chartColor = Color32(0); -static void selectRandomColor(MTRand & rand) { - // Pick random color for this chart. @@ Select random hue, but fixed saturation/luminance? - chartColor.r = 128 + rand.getRange(127); - chartColor.g = 128 + rand.getRange(127); - chartColor.b = 128 + rand.getRange(127); - chartColor.a = 255; -} -static bool debugDrawCallback(void * param, int x, int y, Vector3::Arg, Vector3::Arg, Vector3::Arg, float area) -{ - Image * image = (Image *)param; - - if (area > 0.0) { - Color32 c = image->pixel(x, y); - c.r = chartColor.r; - c.g = chartColor.g; - c.b = chartColor.b; - c.a += U8(ftoi_round(0.5f * area * 255)); - image->pixel(x, y) = c; - } - - return true; -} - -void AtlasPacker::addChart(const Chart * chart, int w, int h, int x, int y, int r, Image * debugOutput) -{ - nvDebugCheck(r == 0 || r == 1); - - nvDebugCheck(debugOutput != NULL); - selectRandomColor(m_rand); - - Vector2 extents = Vector2(float(w), float(h)); - Vector2 offset = Vector2(float(x), float(y)) + Vector2(0.5); - - // Rasterize chart faces, set bits. - const uint faceCount = chart->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = chart->chartMesh()->faceAt(f); - - Vector2 vertices[4]; - - uint edgeCount = 0; - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - if (edgeCount < 4) - { - Vector2 t = it.vertex()->tex; - if (r == 1) swap(t.x, t.y); - vertices[edgeCount] = t + offset; - } - edgeCount++; - } - - if (edgeCount == 3) - { - Raster::drawTriangle(Raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, debugDrawCallback, debugOutput); - } - else - { - Raster::drawQuad(Raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, debugDrawCallback, debugOutput); - } - } -} - - -void AtlasPacker::addChart(const BitMap * bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r, Image * debugOutput) -{ - nvDebugCheck(r == 0 || r == 1); - - // Check whether the two bitmaps overlap. - - const int w = bitmap->width(); - const int h = bitmap->height(); - - if (debugOutput != NULL) { - selectRandomColor(m_rand); - } - - 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 (debugOutput) debugOutput->pixel(xx, yy) = chartColor; - else { - nvDebugCheck(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) { - if (debugOutput) debugOutput->pixel(xx, yy) = chartColor; - else { - nvDebugCheck(m_bitmap.bitAt(xx, yy) == false); - m_bitmap.setBitAt(xx, yy); - } - } - } - } - } - } - } - } -} - - - -/*static*/ bool AtlasPacker::checkBitsCallback(void * param, int x, int y, Vector3::Arg, Vector3::Arg, Vector3::Arg, float) -{ - BitMap * bitmap = (BitMap * )param; - - nvDebugCheck(bitmap->bitAt(x, y) == false); - - return true; -} - -/*static*/ bool AtlasPacker::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; -} - - - -float AtlasPacker::computeAtlasUtilization() const { - const uint w = m_width; - const uint h = m_height; - nvDebugCheck(w <= m_bitmap.width()); - nvDebugCheck(h <= m_bitmap.height()); - - uint count = 0; - for (uint y = 0; y < h; y++) { - for (uint x = 0; x < w; x++) { - count += m_bitmap.bitAt(x, y); - } - } - - return float(count) / (w * h); -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.h b/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.h deleted file mode 100644 index 845dbfb6f3..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.h +++ /dev/null @@ -1,65 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#pragma once -#ifndef NV_MESH_ATLASPACKER_H -#define NV_MESH_ATLASPACKER_H - -#include "nvcore/RadixSort.h" -#include "nvmath/Vector.h" -#include "nvmath/Random.h" -#include "nvimage/BitMap.h" -#include "nvimage/Image.h" - -#include "nvmesh/nvmesh.h" - - -namespace nv -{ - class Atlas; - class Chart; - - struct AtlasPacker - { - AtlasPacker(Atlas * atlas); - ~AtlasPacker(); - - void packCharts(int quality, float texelArea, bool blockAligned, bool conservative); - float computeAtlasUtilization() const; - - private: - - 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); - 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); - 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); - - void drawChartBitmapDilate(const Chart * chart, BitMap * bitmap, int padding); - void drawChartBitmap(const Chart * chart, BitMap * bitmap, const Vector2 & scale, const Vector2 & offset); - - bool canAddChart(const BitMap * bitmap, int w, int h, int x, int y, int r); - void addChart(const BitMap * bitmap, int w, int h, int x, int y, int r, Image * debugOutput); - //void checkCanAddChart(const Chart * chart, int w, int h, int x, int y, int r); - void addChart(const Chart * chart, int w, int h, int x, int y, int r, Image * debugOutput); - - - static bool checkBitsCallback(void * param, int x, int y, Vector3::Arg bar, Vector3::Arg dx, Vector3::Arg dy, float coverage); - static bool setBitsCallback(void * param, int x, int y, Vector3::Arg bar, Vector3::Arg dx, Vector3::Arg dy, float coverage); - - private: - - Atlas * m_atlas; - BitMap m_bitmap; - // -- GODOT start -- - //Image m_debug_bitmap; - // -- GODOT end -- - RadixSort m_radix; - - uint m_width; - uint m_height; - - MTRand m_rand; - - }; - -} // nv namespace - -#endif // NV_MESH_ATLASPACKER_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.cpp b/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.cpp deleted file mode 100644 index cd1e8bbb7b..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.cpp +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#include "nvmesh.h" // pch - -#include "LeastSquaresConformalMap.h" -#include "ParameterizationQuality.h" -#include "Util.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Vertex.h" -#include "nvmesh/halfedge/Face.h" - -#include "nvmath/Sparse.h" -#include "nvmath/Solver.h" -#include "nvmath/Vector.inl" - -#include "nvcore/Array.inl" - - -using namespace nv; -using namespace HalfEdge; - -namespace -{ - - // Test all pairs of vertices in the boundary and check distance. - static void findDiameterVertices(HalfEdge::Mesh * mesh, HalfEdge::Vertex ** a, HalfEdge::Vertex ** b) - { - nvDebugCheck(mesh != NULL); - nvDebugCheck(a != NULL); - nvDebugCheck(b != NULL); - - const uint vertexCount = mesh->vertexCount(); - - float maxLength = 0.0f; - - for (uint v0 = 1; v0 < vertexCount; v0++) - { - HalfEdge::Vertex * vertex0 = mesh->vertexAt(v0); - nvDebugCheck(vertex0 != NULL); - - if (!vertex0->isBoundary()) continue; - - for (uint v1 = 0; v1 < v0; v1++) - { - HalfEdge::Vertex * vertex1 = mesh->vertexAt(v1); - nvDebugCheck(vertex1 != NULL); - - if (!vertex1->isBoundary()) continue; - - float len = length(vertex0->pos - vertex1->pos); - - if (len > maxLength) - { - maxLength = len; - - *a = vertex0; - *b = vertex1; - } - } - } - - nvDebugCheck(*a != NULL && *b != NULL); - } - - // Fast sweep in 3 directions - static bool findApproximateDiameterVertices(HalfEdge::Mesh * mesh, HalfEdge::Vertex ** a, HalfEdge::Vertex ** b) - { - nvDebugCheck(mesh != NULL); - nvDebugCheck(a != NULL); - nvDebugCheck(b != NULL); - - const uint 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 (uint v = 1; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(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 (uint v = 1; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(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 Bruno Levy: - - // Computes the coordinates of the vertices of a triangle - // in a local 2D orthonormal basis of the triangle's plane. - static void project_triangle(Vector3::Arg p0, Vector3::Arg p1, Vector3::Arg p2, Vector2 * z0, Vector2 * z1, Vector2 * z2) - { - Vector3 X = normalize(p1 - p0, 0.0f); - Vector3 Z = normalize(cross(X, (p2 - p0)), 0.0f); - Vector3 Y = normalize(cross(Z, X), 0.0f); - - float x0 = 0.0f; - float y0 = 0.0f; - float x1 = length(p1 - p0); - float y1 = 0.0f; - float x2 = dot((p2 - p0), X); - float y2 = dot((p2 - p0), Y); - - *z0 = Vector2(x0, y0); - *z1 = Vector2(x1, y1); - *z2 = Vector2(x2, y2); - } - - // LSCM equation, geometric form : - // (Z1 - Z0)(U2 - U0) = (Z2 - Z0)(U1 - U0) - // Where Uk = uk + i.vk is the complex number - // corresponding to (u,v) coords - // Zk = xk + i.yk is the complex number - // corresponding to local (x,y) coords - // cool: no divide with this expression, - // makes it more numerically stable in - // the presence of degenerate triangles. - - static void setup_conformal_map_relations(SparseMatrix & 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; - - Vector2 z0, z1, z2; - project_triangle(p0, p1, p2, &z0, &z1, &z2); - - Vector2 z01 = z1 - z0; - Vector2 z02 = z2 - z0; - - float a = z01.x; - float b = z01.y; - float c = z02.x; - float d = z02.y; - nvCheck(b == 0.0f); - - // 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; - - // Note : b = 0 - - // Real part - A.setCoefficient(u0_id, 2 * row + 0, -a+c); - A.setCoefficient(v0_id, 2 * row + 0, b-d); - A.setCoefficient(u1_id, 2 * row + 0, -c); - A.setCoefficient(v1_id, 2 * row + 0, d); - A.setCoefficient(u2_id, 2 * row + 0, a); - - // Imaginary part - A.setCoefficient(u0_id, 2 * row + 1, -b+d); - A.setCoefficient(v0_id, 2 * row + 1, -a+c); - A.setCoefficient(u1_id, 2 * row + 1, -d); - A.setCoefficient(v1_id, 2 * row + 1, -c); - A.setCoefficient(v2_id, 2 * row + 1, a); - } - - - // 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 triangle_cosines(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3, float *a1, float *a2, float *a3) - { - *a1 = vec_angle_cos(v3, v1, v2); - *a2 = vec_angle_cos(v1, v2, v3); - *a3 = vec_angle_cos(v2, v3, v1); - } - - static void setup_abf_relations(SparseMatrix & 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; - -#if 1 - // @@ 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); - - /*// Hack for degenerate triangles. - if (equal(s0, 0) && equal(s1, 0) && equal(s2, 0)) { - if (equal(a0, 0)) a0 += 0.001f; - if (equal(a1, 0)) a1 += 0.001f; - if (equal(a2, 0)) a2 += 0.001f; - - if (equal(a0, PI)) a0 = PI - a1 - a2; - if (equal(a1, PI)) a1 = PI - a0 - a2; - if (equal(a2, PI)) a2 = PI - a0 - a1; - - s0 = sinf(a0); - s1 = sinf(a1); - s2 = sinf(a2); - }*/ - - if (s1 > s0 && s1 > s2) - { - swap(s1, s2); - swap(s0, s1); - - swap(a1, a2); - swap(a0, a1); - - swap(id1, id2); - swap(id0, id1); - } - else if (s0 > s1 && s0 > s2) - { - swap(s0, s2); - swap(s0, s1); - - swap(a0, a2); - swap(a0, a1); - - swap(id0, id2); - swap(id0, id1); - } - - float c0 = cosf(a0); -#else - float c0, c1, c2; - triangle_cosines(p0, p1, p2, &c0, &c1, &c2); - - float s0 = 1 - c0*c0; - float s1 = 1 - c1*c1; - float s2 = 1 - c2*c2; - - nvDebugCheck(s0 != 0 || s1 != 0 || s2 != 0); - - if (s1 > s0 && s1 > s2) - { - swap(s1, s2); - swap(s0, s1); - - swap(c1, c2); - swap(c0, c1); - - swap(id1, id2); - swap(id0, id1); - } - else if (s0 > s1 && s0 > s2) - { - swap(s0, s2); - swap(s0, s1); - - swap(c0, c2); - swap(c0, c1); - - swap(id0, id2); - swap(id0, id1); - } -#endif - - 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); - } - -} // namespace - - -bool nv::computeLeastSquaresConformalMap(HalfEdge::Mesh * mesh) -{ - nvDebugCheck(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 uint vertexCount = mesh->vertexCount(); - const uint D = 2 * vertexCount; - const uint N = 2 * 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; - } - - SparseMatrix 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 (uint v = 0; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(vertex != NULL); - - // Initial solution. - x[2 * v + 0] = vertex->tex.x; - x[2 * v + 1] = vertex->tex.y; - } - - // Fill A: - const uint faceCount = mesh->faceCount(); - for (uint f = 0, t = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = mesh->faceAt(f); - nvDebugCheck(face != NULL); - nvDebugCheck(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(); - nvCheck(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 uint lockedParameters[] = - { - 2 * v0->id + 0, - 2 * v0->id + 1, - 2 * v1->id + 0, - 2 * v1->id + 1 - }; - - // Solve - LeastSquaresSolver(A, b, x, lockedParameters, 4, 0.000001f); - - // Map x back to texcoords: - for (uint v = 0; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(vertex != NULL); - - vertex->tex = Vector2(x[2 * v + 0], x[2 * v + 1]); - } - - return true; -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.h b/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.h deleted file mode 100644 index 51fbf193c8..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#pragma once -#ifndef NV_MESH_LEASTSQUARESCONFORMALMAP_H -#define NV_MESH_LEASTSQUARESCONFORMALMAP_H - -namespace nv -{ - namespace HalfEdge { class Mesh; } - - bool computeLeastSquaresConformalMap(HalfEdge::Mesh * mesh); - -} // nv namespace - -#endif // NV_MESH_LEASTSQUARESCONFORMALMAP_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.cpp b/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.cpp deleted file mode 100644 index d6e5e30561..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "OrthogonalProjectionMap.h" - -#include "nvcore/Array.inl" - -#include "nvmath/Fitting.h" -#include "nvmath/Vector.inl" -#include "nvmath/Box.inl" -#include "nvmath/Plane.inl" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Vertex.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/geometry/Bounds.h" - - -using namespace nv; - -bool nv::computeOrthogonalProjectionMap(HalfEdge::Mesh * mesh) -{ - Vector3 axis[2]; - -#if 1 - - uint vertexCount = mesh->vertexCount(); - Array<Vector3> points(vertexCount); - points.resize(vertexCount); - - for (uint i = 0; i < vertexCount; i++) - { - points[i] = mesh->vertexAt(i)->pos; - } - -#if 0 - axis[0] = Fit::computePrincipalComponent_EigenSolver(vertexCount, points.buffer()); - axis[0] = normalize(axis[0]); - - Plane plane = Fit::bestPlane(vertexCount, points.buffer()); - - Vector3 n = plane.vector(); - - axis[1] = cross(axis[0], n); - axis[1] = normalize(axis[1]); -#else - // Avoid redundant computations. - float matrix[6]; - Fit::computeCovariance(vertexCount, points.buffer(), matrix); - - if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0) { - return false; - } - - float eigenValues[3]; - Vector3 eigenVectors[3]; - if (!nv::Fit::eigenSolveSymmetric3(matrix, eigenValues, eigenVectors)) { - return false; - } - - axis[0] = normalize(eigenVectors[0]); - axis[1] = normalize(eigenVectors[1]); -#endif - - -#else - - // IC: I thought this was generally more robust, but turns out it's not even guaranteed to return a valid projection. Imagine a narrow quad perpendicular to one plane, but rotated so that the shortest axis of - // the bounding box is in the direction of that plane. - - // Use the shortest box axis - Box box = MeshBounds::box(mesh); - Vector3 dir = box.extents(); - - if (fabs(dir.x) <= fabs(dir.y) && fabs(dir.x) <= fabs(dir.z)) { - axis[0] = Vector3(0, 1, 0); - axis[1] = Vector3(0, 0, 1); - } - else if (fabs(dir.y) <= fabs(dir.z)) { - axis[0] = Vector3(1, 0, 0); - axis[1] = Vector3(0, 0, 1); - } - else { - axis[0] = Vector3(1, 0, 0); - axis[1] = Vector3(0, 1, 0); - } -#endif - - // 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; -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.h b/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.h deleted file mode 100644 index 54920413d5..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.h +++ /dev/null @@ -1,15 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#pragma once -#ifndef NV_MESH_ORTHOGONALPROJECTIONMAP_H -#define NV_MESH_ORTHOGONALPROJECTIONMAP_H - -namespace nv -{ - namespace HalfEdge { class Mesh; } - - bool computeOrthogonalProjectionMap(HalfEdge::Mesh * mesh); - -} // nv namespace - -#endif // NV_MESH_ORTHOGONALPROJECTIONMAP_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp b/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp deleted file mode 100644 index 683ee603cd..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#include "nvmesh.h" // pch - -#include "ParameterizationQuality.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/halfedge/Vertex.h" -#include "nvmesh/halfedge/Edge.h" - -#include "nvmath/Vector.inl" - -#include "nvcore/Debug.h" - -#include <float.h> - - -using namespace nv; - -#if 0 -/* -float triangleConformalEnergy(Vector3 q[3], Vector2 p[3]) -{ -const Vector3 v1 = q[0]; -const Vector3 v2 = q[1]; -const Vector3 v3 = q[2]; - -const Vector2 w1 = p[0]; -const Vector2 w2 = p[1]; -const Vector2 w3 = p[2]; - -float x1 = v2.x() - v1.x(); -float x2 = v3.x() - v1.x(); -float y1 = v2.y() - v1.y(); -float y2 = v3.y() - v1.y(); -float z1 = v2.z() - v1.z(); -float z2 = v3.z() - v1.z(); - -float s1 = w2.x() - w1.x(); -float s2 = w3.x() - w1.x(); -float t1 = w2.y() - w1.y(); -float t2 = w3.y() - w1.y(); - -float r = 1.0f / (s1 * t2 - s2 * t1); -Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); -Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); - -Vector3 N = cross(v3-v1, v2-v1); - -// Rotate 90 around N. -} -*/ - -static float triangleConformalEnergy(Vector3 q[3], Vector2 p[3]) -{ - // Using Denis formulas: - Vector3 c0 = q[1] - q[2]; - Vector3 c1 = q[2] - q[0]; - Vector3 c2 = q[0] - q[1]; - - Vector3 N = cross(-c0, c1); - float T = length(N); // 2T - N = normalize(N, 0); - - float cot_alpha0 = dot(-c1, c2) / length(cross(-c1, c2)); - float cot_alpha1 = dot(-c2, c0) / length(cross(-c2, c0)); - float cot_alpha2 = dot(-c0, c1) / length(cross(-c0, c1)); - - Vector3 t0 = -cot_alpha1 * c1 + cot_alpha2 * c2; - Vector3 t1 = -cot_alpha2 * c2 + cot_alpha0 * c0; - Vector3 t2 = -cot_alpha0 * c0 + cot_alpha1 * c1; - - nvCheck(equal(length(t0), length(c0))); - nvCheck(equal(length(t1), length(c1))); - nvCheck(equal(length(t2), length(c2))); - nvCheck(equal(dot(t0, c0), 0)); - nvCheck(equal(dot(t1, c1), 0)); - nvCheck(equal(dot(t2, c2), 0)); - - // Gradients - Vector3 grad_u = 1.0f / T * (p[0].x * t0 + p[1].x * t1 + p[2].x * t2); - Vector3 grad_v = 1.0f / T * (p[0].y * t0 + p[1].y * t1 + p[2].y * t2); - - // Rotated gradients - Vector3 Jgrad_u = 1.0f / T * (p[0].x * c0 + p[1].x * c1 + p[2].x * c2); - Vector3 Jgrad_v = 1.0f / T * (p[0].y * c0 + p[1].y * c1 + p[2].y * c2); - - // Using Lengyel's formulas: - { - const Vector3 v1 = q[0]; - const Vector3 v2 = q[1]; - const Vector3 v3 = q[2]; - - const Vector2 w1 = p[0]; - const Vector2 w2 = p[1]; - const Vector2 w3 = p[2]; - - float x1 = v2.x - v1.x; - float x2 = v3.x - v1.x; - float y1 = v2.y - v1.y; - float y2 = v3.y - v1.y; - float z1 = v2.z - v1.z; - float z2 = v3.z - v1.z; - - float s1 = w2.x - w1.x; - float s2 = w3.x - w1.x; - float t1 = w2.y - w1.y; - float t2 = w3.y - w1.y; - - float r = 1.0f / (s1 * t2 - s2 * t1); - Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); - Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); - - Vector3 Jsdir = cross(N, sdir); - Vector3 Jtdir = cross(N, tdir); - - float x = 3; - } - - // check: sdir == grad_u - // check: tdir == grad_v - - return length(grad_u - Jgrad_v); -} -#endif // 0 - - -ParameterizationQuality::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::ParameterizationQuality(const HalfEdge::Mesh * mesh) -{ - nvDebugCheck(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 uint faceCount = mesh->faceCount(); - for (uint 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; - } - - nvDebugCheck(isFinite(m_parametricArea) && m_parametricArea >= 0); - nvDebugCheck(isFinite(m_geometricArea) && m_geometricArea >= 0); - nvDebugCheck(isFinite(m_stretchMetric)); - nvDebugCheck(isFinite(m_maxStretchMetric)); - nvDebugCheck(isFinite(m_conformalMetric)); - nvDebugCheck(isFinite(m_authalicMetric)); -} - -bool ParameterizationQuality::isValid() const -{ - return m_flippedTriangleCount == 0; // @@ Does not test for self-overlaps. -} - -float ParameterizationQuality::rmsStretchMetric() const -{ - if (m_geometricArea == 0) return 0.0f; - float normFactor = sqrtf(m_parametricArea / m_geometricArea); - return sqrtf(m_stretchMetric / m_geometricArea) * normFactor; -} - -float ParameterizationQuality::maxStretchMetric() const -{ - if (m_geometricArea == 0) return 0.0f; - float normFactor = sqrtf(m_parametricArea / m_geometricArea); - return m_maxStretchMetric * normFactor; -} - -float ParameterizationQuality::rmsConformalMetric() const -{ - if (m_geometricArea == 0) return 0.0f; - return sqrtf(m_conformalMetric / m_geometricArea); -} - -float ParameterizationQuality::maxAuthalicMetric() const -{ - if (m_geometricArea == 0) return 0.0f; - return sqrtf(m_authalicMetric / m_geometricArea); -} - -void ParameterizationQuality::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 = max(m_maxStretchMetric, pq.m_maxStretchMetric); - - m_conformalMetric += pq.m_conformalMetric; - m_authalicMetric += pq.m_authalicMetric; -} - - -void ParameterizationQuality::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 * max(0.0f, a + c - sqrtf(square(a - c) + 4 * square(b)))); // gamma uppercase, min eigenvalue. - float sigma2 = sqrtf(0.5f * max(0.0f, a + c + sqrtf(square(a - c) + 4 * square(b)))); // gamma lowercase, max eigenvalue. - nvCheck(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); - nvDebugCheck(equal(rmsStretch, rmsStretch2, 0.01f)); - - if (parametricArea < 0.0f) - { - // Count flipped triangles. - m_flippedTriangleCount++; - - parametricArea = fabsf(parametricArea); - } - - m_stretchMetric += square(rmsStretch) * geometricArea; - m_maxStretchMetric = 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); -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.h b/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.h deleted file mode 100644 index 342e26b889..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#pragma once -#ifndef NV_MESH_PARAMETERIZATIONQUALITY_H -#define NV_MESH_PARAMETERIZATIONQUALITY_H - -#include <nvmesh/nvmesh.h> - -namespace nv -{ - class Vector2; - class Vector3; - - namespace HalfEdge { class Mesh; } - - // Estimate quality of existing parameterization. - NVMESH_CLASS class ParameterizationQuality - { - public: - ParameterizationQuality(); - ParameterizationQuality(const HalfEdge::Mesh * mesh); - - bool isValid() const; - - float rmsStretchMetric() const; - float maxStretchMetric() const; - - float rmsConformalMetric() const; - float maxAuthalicMetric() const; - - void operator += (const ParameterizationQuality & pq); - - private: - - void processTriangle(Vector3 p[3], Vector2 t[3]); - - private: - - uint m_totalTriangleCount; - uint m_flippedTriangleCount; - uint m_zeroAreaTriangleCount; - - float m_parametricArea; - float m_geometricArea; - - float m_stretchMetric; - float m_maxStretchMetric; - - float m_conformalMetric; - float m_authalicMetric; - - }; - -} // nv namespace - -#endif // NV_MESH_PARAMETERIZATIONQUALITY_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.cpp b/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.cpp deleted file mode 100644 index 4b205de8bf..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#include "nvmesh.h" // pch - -#include "SingleFaceMap.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Vertex.h" -#include "nvmesh/halfedge/Face.h" - -#include "nvmath/Vector.inl" - -using namespace nv; - - - -void nv::computeSingleFaceMap(HalfEdge::Mesh * mesh) -{ - nvDebugCheck(mesh != NULL); - nvDebugCheck(mesh->faceCount() == 1); - - HalfEdge::Face * face = mesh->faceAt(0); - nvCheck(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); - - uint i = 0; - for (HalfEdge::Face::EdgeIterator it(face->edges()); !it.isDone(); it.advance(), i++) - { - HalfEdge::Vertex * vertex = it.vertex(); - nvCheck(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); - } - } -} - diff --git a/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.h b/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.h deleted file mode 100644 index b70719f5d8..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#pragma once -#ifndef NV_MESH_SINGLEFACEMAP_H -#define NV_MESH_SINGLEFACEMAP_H - -namespace nv -{ - namespace HalfEdge - { - class Mesh; - } - - void computeSingleFaceMap(HalfEdge::Mesh * mesh); - -} // nv namespace - -#endif // NV_MESH_SINGLEFACEMAP_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/Util.cpp b/thirdparty/thekla_atlas/nvmesh/param/Util.cpp deleted file mode 100644 index fe7b58edf8..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/Util.cpp +++ /dev/null @@ -1,326 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "Util.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/halfedge/Vertex.h" - -#include "nvmath/Vector.inl" - -#include "nvcore/Array.inl" - - -using namespace nv; - -// Determine if the given mesh is a quad mesh. -bool nv::isQuadMesh(const HalfEdge::Mesh * mesh) -{ - nvDebugCheck(mesh != NULL); - - const uint faceCount = mesh->faceCount(); - for(uint i = 0; i < faceCount; i++) { - const HalfEdge::Face * face = mesh->faceAt(i); - if (face->edgeCount() != 4) { - return false; - } - } - - return true; -} - -bool nv::isTriangularMesh(const HalfEdge::Mesh * mesh) -{ - for (HalfEdge::Mesh::ConstFaceIterator it(mesh->faces()); !it.isDone(); it.advance()) - { - const HalfEdge::Face * face = it.current(); - if (face->edgeCount() != 3) return false; - } - return true; -} - - -uint nv::countMeshTriangles(const HalfEdge::Mesh * mesh) -{ - const uint faceCount = mesh->faceCount(); - - uint triangleCount = 0; - - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = mesh->faceAt(f); - - uint edgeCount = face->edgeCount(); - nvDebugCheck(edgeCount > 2); - - triangleCount += edgeCount - 2; - } - - return triangleCount; -} - -const HalfEdge::Vertex * nv::findBoundaryVertex(const HalfEdge::Mesh * mesh) -{ - const uint vertexCount = mesh->vertexCount(); - - for (uint v = 0; v < vertexCount; v++) - { - const HalfEdge::Vertex * vertex = mesh->vertexAt(v); - if (vertex->isBoundary()) return vertex; - } - - return NULL; -} - - -HalfEdge::Mesh * nv::unifyVertices(const HalfEdge::Mesh * inputMesh) -{ - HalfEdge::Mesh * mesh = new HalfEdge::Mesh; - - // Only add the first colocal. - const uint vertexCount = inputMesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) { - const HalfEdge::Vertex * vertex = inputMesh->vertexAt(v); - - if (vertex->isFirstColocal()) { - mesh->addVertex(vertex->pos); - } - } - - nv::Array<uint> indexArray; - - // Add new faces pointing to first colocals. - uint faceCount = inputMesh->faceCount(); - for (uint f = 0; f < faceCount; f++) { - const HalfEdge::Face * face = inputMesh->faceAt(f); - - indexArray.clear(); - - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { - const HalfEdge::Edge * edge = it.current(); - const HalfEdge::Vertex * vertex = edge->vertex->firstColocal(); - - indexArray.append(vertex->id); - } - - mesh->addFace(indexArray); - } - - mesh->linkBoundary(); - - return mesh; -} - -#include "nvmath/Basis.h" - -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. -HalfEdge::Mesh * nv::triangulate(const HalfEdge::Mesh * inputMesh) -{ - HalfEdge::Mesh * mesh = new HalfEdge::Mesh; - - // Add all vertices. - const uint vertexCount = inputMesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) { - const HalfEdge::Vertex * vertex = inputMesh->vertexAt(v); - mesh->addVertex(vertex->pos); - } - - Array<int> polygonVertices; - Array<float> polygonAngles; - Array<Vector2> polygonPoints; - - const uint faceCount = inputMesh->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = inputMesh->faceAt(f); - nvDebugCheck(face != NULL); - - const uint edgeCount = face->edgeCount(); - nvDebugCheck(edgeCount >= 3); - - polygonVertices.clear(); - polygonVertices.reserve(edgeCount); - - if (edgeCount == 3) { - // Simple case for triangles. - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - const HalfEdge::Vertex * vertex = edge->vertex; - polygonVertices.append(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 (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - const HalfEdge::Vertex * vertex = edge->vertex; - polygonVertices.append(vertex->id); - - Vector2 p; - p.x = dot(basis.tangent, vertex->pos); - p.y = dot(basis.bitangent, vertex->pos); - - polygonPoints.append(p); - } - polygonAngles.resize(edgeCount); - - while (polygonVertices.size() > 2) { - uint size = polygonVertices.size(); - - // Update polygon angles. @@ Update only those that have changed. - float minAngle = 2 * PI; - uint bestEar = 0; // Use first one if none of them is valid. - bool bestIsValid = false; - for (uint i = 0; i < size; i++) { - uint i0 = i; - uint i1 = (i+1) % size; // Use Sean's polygon interation trick. - uint i2 = (i+2) % size; - - Vector2 p0 = polygonPoints[i0]; - Vector2 p1 = polygonPoints[i1]; - Vector2 p2 = polygonPoints[i2]; - - 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 (uint 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; - } - } - } - - nvDebugCheck(minAngle <= 2 * PI); - - // Clip best ear: - - uint i0 = (bestEar+size-1) % size; - uint i1 = (bestEar+0) % size; - uint i2 = (bestEar+1) % size; - - int v0 = polygonVertices[i0]; - int v1 = polygonVertices[i1]; - int v2 = polygonVertices[i2]; - - mesh->addFace(v0, v1, v2); - - polygonVertices.removeAt(i1); - polygonPoints.removeAt(i1); - polygonAngles.removeAt(i1); - } - } - -#if 0 - - uint i = 0; - while (polygonVertices.size() > 2 && i < polygonVertices.size()) { - uint size = polygonVertices.size(); - uint i0 = (i+0) % size; - uint i1 = (i+1) % size; - uint i2 = (i+2) % size; - - const HalfEdge::Vertex * v0 = polygonVertices[i0]; - const HalfEdge::Vertex * v1 = polygonVertices[i1]; - const HalfEdge::Vertex * v2 = polygonVertices[i2]; - - const Vector3 p0 = v0->pos; - const Vector3 p1 = v1->pos; - const Vector3 p2 = v2->pos; - - const Vector3 e0 = p2 - p1; - const Vector3 e1 = p0 - p1; - - // If this ear forms a valid triangle, setup relations, remove v1 and repeat. - Vector3 n = cross(e0, e1); - float len = dot(fn, n); // = sin(angle) - - float angle = asin(len); - - - if (len > 0.0f) { - mesh->addFace(v0->id(), v1->id(), v2->id()); - polygonVertices.removeAt(i1); - polygonAngles.removeAt(i1); - if (i2 > i1) i2--; - // @@ Update angles at i0 and i2 - } - else { - i++; - } - } - - // @@ Create a few degenerate triangles to avoid introducing holes. - i = 0; - const uint size = polygonVertices.size(); - while (i < size - 2) { - uint i0 = (i+0) % size; - uint i1 = (i+1) % size; - uint i2 = (i+2) % size; - - const HalfEdge::Vertex * v0 = polygonVertices[i0]; - const HalfEdge::Vertex * v1 = polygonVertices[i1]; - const HalfEdge::Vertex * v2 = polygonVertices[i2]; - - mesh->addFace(v0->id(), v1->id(), v2->id()); - i++; - } -#endif - } - - mesh->linkBoundary(); - - return mesh; -} - - diff --git a/thirdparty/thekla_atlas/nvmesh/param/Util.h b/thirdparty/thekla_atlas/nvmesh/param/Util.h deleted file mode 100644 index 774563ac0b..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/Util.h +++ /dev/null @@ -1,18 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh/nvmesh.h" - -namespace nv { - - namespace HalfEdge { class Mesh; class Vertex; } - - bool isQuadMesh(const HalfEdge::Mesh * mesh); - bool isTriangularMesh(const HalfEdge::Mesh * mesh); - - uint countMeshTriangles(const HalfEdge::Mesh * mesh); - const HalfEdge::Vertex * findBoundaryVertex(const HalfEdge::Mesh * mesh); - - HalfEdge::Mesh * unifyVertices(const HalfEdge::Mesh * inputMesh); - HalfEdge::Mesh * triangulate(const HalfEdge::Mesh * inputMesh); - -} // nv namespace diff --git a/thirdparty/thekla_atlas/nvmesh/raster/ClippedTriangle.h b/thirdparty/thekla_atlas/nvmesh/raster/ClippedTriangle.h deleted file mode 100644 index 0947d4851c..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/raster/ClippedTriangle.h +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright NVIDIA Corporation 2007 -- Denis Kovacs <den.kovacs@gmail.com> - -#pragma once -#ifndef NV_MESH_CLIPPEDTRIANGLE_H -#define NV_MESH_CLIPPEDTRIANGLE_H - -#include <nvmath/Vector.h> - -namespace nv -{ - - 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; - } - - uint vertexCount() - { - return m_numVertices; - } - - const Vector2 * vertices() - { - return m_vertexBuffers[m_activeVertexBuffer]; - } - - inline 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; - uint p=0; - - for (uint 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 (uint k=0; k<m_numVertices; k++) printf("(%f, %f)\n", v2[k].x, v2[k].y); printf("\n"); - } - - inline 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; - uint p=0; - - for (uint 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; - - //for (uint k=0; k<m_numVertices; k++) printf("(%f, %f)\n", v2[k].x, v2[k].y); printf("\n"); - } - - void computeAreaCentroid() - { - Vector2 * v = m_vertexBuffers[m_activeVertexBuffer]; - v[m_numVertices] = v[0]; - - m_area = 0; - float centroidx=0, centroidy=0; - for (uint 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 * fabs(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]; - uint m_numVertices; - uint m_activeVertexBuffer; - float m_area; - Vector2 m_centroid; - }; - -} // nv namespace - -#endif // NV_MESH_CLIPPEDTRIANGLE_H diff --git a/thirdparty/thekla_atlas/nvmesh/raster/Raster.cpp b/thirdparty/thekla_atlas/nvmesh/raster/Raster.cpp deleted file mode 100644 index d46b34f045..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/raster/Raster.cpp +++ /dev/null @@ -1,626 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -/** @file Raster.cpp - * @brief Triangle rasterization library using affine interpolation. Not - * specially optimized, but enough for my purposes. -**/ - -#include "nvmesh.h" // pch - -#include "Raster.h" -#include "ClippedTriangle.h" - -#include "nvcore/Utils.h" // min, max - -#include "nvmath/Vector.inl" -#include "nvmath/ftoi.h" - - -#define RA_EPSILON 0.00001f - -using namespace nv; -using namespace nv::Raster; - -namespace -{ - static inline float delta(float bot, float top, float ih) - { - return (bot - top) * ih; - } - - static inline Vector2 delta(Vector2::Arg bot, Vector2::Arg top, float ih) - { - return (bot - top) * ih; - } - - static inline Vector3 delta(Vector3::Arg bot, Vector3::Arg top, float ih) - { - return (bot - top) * ih; - } - - // @@ The implementation in nvmath.h should be equivalent. - static inline int iround(float f) - { - // @@ Optimize this. - return int(floorf(f+0.5f)); - //return int(round(f)); - //return int(f); - } - - /// A triangle vertex. - struct Vertex - { - Vector2 pos; // Position. - Vector3 tex; // Texcoord. (Barycentric coordinate) - }; - - - /// 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); - - bool computeDeltas(); - - bool draw(const Vector2 & extents, bool enableScissors, SamplingCallback cb, void * param); - bool drawAA(const Vector2 & extents, bool enableScissors, SamplingCallback cb, void * param); - bool drawC(const Vector2 & extents, bool enableScissors, SamplingCallback cb, void * param); - void flipBackface(); - void computeUnitInwardNormals(); - - // Vertices. - Vector2 v1, v2, v3; - Vector2 n1, n2, n3; // unit inward normals - Vector3 t1, t2, t3; - - // Deltas. - Vector3 dx, dy; - - float sign; - bool valid; - }; - - - /// Triangle ctor. - 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 Triangle::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 (!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; - } - - // compute unit inward normals for each edge. - void Triangle::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)); - } - - void Triangle::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 - } - } - - bool Triangle::draw(const Vector2 & extents, bool enableScissors, SamplingCallback cb, void * param) - { - // 28.4 fixed-point coordinates - const int Y1 = iround(16.0f * v1.y); - const int Y2 = iround(16.0f * v2.y); - const int Y3 = iround(16.0f * v3.y); - - const int X1 = iround(16.0f * v1.x); - const int X2 = iround(16.0f * v2.x); - const int X3 = iround(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 = (nv::max(min3(X1, X2, X3), frustumX0) + 0xF) >> 4; - miny = (nv::max(min3(Y1, Y2, Y3), frustumY0) + 0xF) >> 4; - maxx = (nv::min(max3(X1, X2, X3), frustumX1) + 0xF) >> 4; - maxy = (nv::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. - nvCheck(minx >= 0); - nvCheck(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; - } - - -#define PX_INSIDE 1.0f/sqrt(2.0f) -#define PX_OUTSIDE -1.0f/sqrt(2.0f) - -#define BK_SIZE 8 -#define BK_INSIDE sqrt(BK_SIZE*BK_SIZE/2.0f) -#define BK_OUTSIDE -sqrt(BK_SIZE*BK_SIZE/2.0f) - - // extents has to be multiple of BK_SIZE!! - bool Triangle::drawAA(const Vector2 & extents, bool enableScissors, SamplingCallback cb, void * param) - { - float minx, miny, maxx, maxy; - if (enableScissors) { - // Bounding rectangle - minx = floorf(max(min3(v1.x, v2.x, v3.x), 0.0f)); - miny = floorf(max(min3(v1.y, v2.y, v3.y), 0.0f)); - maxx = ceilf( min(max3(v1.x, v2.x, v3.x), extents.x-1.0f)); - maxy = ceilf( 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 tex = t1 + dx * (x - v1.x) + dy * (y - v1.y); - if (!cb(param, (int)x, (int)y, tex, 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; - //nvCheck(texCent.x >= -0.1f && texCent.x <= 1.1f); // @@ Centroid is not very exact... - //nvCheck(texCent.y >= -0.1f && texCent.y <= 1.1f); - //nvCheck(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; - } - -} // namespace - - -/// Process the given triangle. -bool nv::Raster::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; -} - -inline static float triangleArea(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); -} - -/// Process the given quad. -bool nv::Raster::drawQuad(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[4], SamplingCallback cb, void * param) -{ - bool sign0 = triangleArea(v[0], v[1], v[2]) > 0.0f; - bool sign1 = triangleArea(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; -} - - -static bool drawPoint(const Vector2 & p, const Vector2 v[2], LineSamplingCallback cb, void * param) { - - int x = ftoi_round(p.x); - int y = ftoi_round(p.y); - Vector2 ip = Vector2(float(x) + 0.5f, float(y) + 0.5f); - - float t; - - // Return minimum distance between line segment vw and point p - Vector2 dv = v[1] - v[0]; - const float l2 = nv::lengthSquared(dv); // i.e. |w-v|^2 - avoid a sqrt - if (l2 == 0.0) { - t = 0; // v0 == v1 case - } - else { - // Consider the line extending the segment, parameterized as v + t (w - v). - // We find projection of point p onto the line. - // It falls where t = [(p-v) . (w-v)] / |w-v|^2 - t = dot(ip - v[0], dv) / l2; - if (t < 0.0) { - t = 0; // Beyond the 'v0' end of the segment - } - else if (t > 1.0) { - t = 1; // Beyond the 'v1' end of the segment - } - } - - Vector2 projection = v[0] + t * dv; // Projection falls on the segment - - float d = distance(ip, projection); - - return cb(param, x, y, t, saturate(1-d)); -} - - -void nv::Raster::drawLine(bool antialias, Vector2::Arg extents, bool enableScissors, const Vector2 v[2], LineSamplingCallback cb, void * param) -{ - nvCheck(antialias == true); // @@ Not implemented. - //nvCheck(enableScissors == false); // @@ Not implemented. - - // Very crappy DDA implementation. - - Vector2 p = v[0]; - Vector2 dp, dpdy; - - float dx = v[1].x - v[0].x; - float dy = v[1].y - v[0].y; - int n; - - // Degenerate line. - if (dx == 0 && dy == 0) return; - - if (fabsf(dx) >= fabsf(dy)) { - n = iround(fabsf(dx)); - dp.x = dx / fabsf(dx); - dp.y = dy / fabsf(dx); - nvDebugCheck(fabsf(dp.y) <= 1.0f); - dpdy.x = 0; - dpdy.y = 1; - } - else { - n = iround(fabs(dy)); - dp.x = dx / fabsf(dy); - dp.y = dy / fabsf(dy); - nvDebugCheck(fabsf(dp.x) <= 1.0f); - dpdy.x = 1; - dpdy.y = 0; - } - - for (int i = 0; i <= n; i++) { - drawPoint(p, v, cb, param); - drawPoint(p + dpdy, v, cb, param); - drawPoint(p - dpdy, v, cb, param); - p += dp; - } -} - - -// Draw vertical or horizontal segments. For degenerate triangles. -/*bool nv::Raster::drawSegment(Vector2::Arg extents, bool enableScissors, const Vector2 v[2], LineSamplingCallback cb, void * param) -{ - nvCheck(enableScissors == false); - - - if (v[0].x == v[1].x) { // Vertical segment. - - } - else if (v[0].y == v[1].y) { // Horizontal segment. - int y = ftoi_round(v[0].y); - int x0 = ftoi_floor(v[0].x); - int x1 = ftoi_floor(v[0].x); - - for (int x = x0; x <= x1; x++) { - - cb(param, x, y, t, - } - } - - return false; // Not a valid segment. -} -*/ diff --git a/thirdparty/thekla_atlas/nvmesh/raster/Raster.h b/thirdparty/thekla_atlas/nvmesh/raster/Raster.h deleted file mode 100644 index 05af2ddb00..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/raster/Raster.h +++ /dev/null @@ -1,49 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_RASTER_H -#define NV_MESH_RASTER_H - -/** @file Raster.h - * @brief Rasterization library. - * - * This is just a standard scanline rasterizer that I took from one of my old - * projects. The perspective correction wasn't necessary so I just removed it. -**/ - -#include "nvmath/Vector.h" -#include "nvmesh/nvmesh.h" - -namespace nv -{ - - namespace Raster - { - enum Mode { - Mode_Nearest, - Mode_Antialiased, - //Mode_Conservative - }; - - - /// A callback to sample the environment. Return false to terminate rasterization. - typedef bool (NV_CDECL * SamplingCallback)(void * param, int x, int y, Vector3::Arg bar, Vector3::Arg dx, Vector3::Arg dy, float coverage); - - // Process the given triangle. Returns false if rasterization was interrupted by the callback. - NVMESH_API bool drawTriangle(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[3], SamplingCallback cb, void * param); - - // Process the given quad. Returns false if rasterization was interrupted by the callback. - NVMESH_API bool drawQuad(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[4], SamplingCallback cb, void * param); - - typedef bool (NV_CDECL * LineSamplingCallback)(void * param, int x, int y, float t, float d); // t is the position along the segment, d is the distance to the line. - - // Process the given line. - NVMESH_API void drawLine(bool antialias, Vector2::Arg extents, bool enableScissors, const Vector2 v[2], LineSamplingCallback cb, void * param); - - // Draw vertical or horizontal segments. For degenerate triangles. - //NVMESH_API void drawSegment(Vector2::Arg extents, bool enableScissors, const Vector2 v[2], SamplingCallback cb, void * param); - } -} - - -#endif // NV_MESH_RASTER_H diff --git a/thirdparty/thekla_atlas/nvmesh/weld/Snap.cpp b/thirdparty/thekla_atlas/nvmesh/weld/Snap.cpp deleted file mode 100644 index b6bff4d83d..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/weld/Snap.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include <nvcore/RadixSort.h> - -#include <nvmesh/weld/Snap.h> -#include <nvmesh/TriMesh.h> -#include <nvmesh/geometry/Bounds.h> - -using namespace nv; - -namespace { - - // Snap the given vertices. - void Snap(TriMesh::Vertex & a, TriMesh::Vertex & b, float texThreshold, float norThreshold) - { - a.pos = b.pos = (a.pos + b.pos) * 0.5f; - - if (equal(a.tex.x, b.tex.x, texThreshold) && equal(a.tex.y, b.tex.y, texThreshold)) { - b.tex = a.tex = (a.tex + b.tex) * 0.5f; - } - - if (equal(a.nor.x, b.nor.x, norThreshold) && equal(a.nor.y, b.nor.y, norThreshold) && equal(a.nor.z, b.nor.z, norThreshold)) { - b.nor = a.nor = (a.nor + b.nor) * 0.5f; - } - }; - -} // nv namespace - -uint nv::SnapVertices(TriMesh * mesh, float posThreshold, float texThreshold, float norThreshold) -{ - nvDebug("--- Snapping vertices.\n"); - - // Determine largest axis. - Box box = MeshBounds::box(mesh); - Vector3 extents = box.extents(); - - int axis = 2; - if( extents.x > extents.y ) { - if( extents.x > extents.z ) { - axis = 0; - } - } - else if(extents.y > extents.z) { - axis = 1; - } - - // @@ Use diagonal instead! - - - // Sort vertices according to the largest axis. - const uint vertexCount = mesh->vertexCount(); - nvCheck(vertexCount > 2); // Must have at least two vertices. - - // Get pos channel. - //PiMesh::Channel * pos_channel = mesh->GetChannel(mesh->FindChannel(VS_POS)); - //nvCheck( pos_channel != NULL ); - - //const PiArray<Vec4> & pos_array = pos_channel->data; - - Array<float> distArray; - distArray.resize(vertexCount); - - for(uint v = 0; v < vertexCount; v++) { - if (axis == 0) distArray[v] = mesh->vertexAt(v).pos.x; - else if (axis == 1) distArray[v] = mesh->vertexAt(v).pos.y; - else distArray[v] = mesh->vertexAt(v).pos.z; - } - - RadixSort radix; - const uint * xrefs = radix.sort(distArray.buffer(), distArray.count()).ranks(); - nvCheck(xrefs != NULL); - - uint snapCount = 0; - for(uint v = 0; v < vertexCount-1; v++) { - for(uint n = v+1; n < vertexCount; n++) { - nvDebugCheck( distArray[xrefs[v]] <= distArray[xrefs[n]] ); - - if (fabs(distArray[xrefs[n]] - distArray[xrefs[v]]) > posThreshold) { - break; - } - - TriMesh::Vertex & v0 = mesh->vertexAt(xrefs[v]); - TriMesh::Vertex & v1 = mesh->vertexAt(xrefs[n]); - - const float dist = length(v0.pos - v1.pos); - - if (dist <= posThreshold) { - Snap(v0, v1, texThreshold, norThreshold); - snapCount++; - } - } - } - - // @@ todo: debug, make sure that the distance between vertices is now >= threshold - - nvDebug("--- %u vertices snapped\n", snapCount); - - return snapCount; -}; - diff --git a/thirdparty/thekla_atlas/nvmesh/weld/Snap.h b/thirdparty/thekla_atlas/nvmesh/weld/Snap.h deleted file mode 100644 index 8e0566cda3..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/weld/Snap.h +++ /dev/null @@ -1,18 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#ifndef NV_MESH_SNAP_H -#define NV_MESH_SNAP_H - -#include <nvmesh/nvmesh.h> -#include <nvmath/nvmath.h> - -namespace nv -{ - class TriMesh; - - NVMESH_API uint SnapVertices(TriMesh * mesh, float posThreshold=NV_EPSILON, float texThreshold=1.0f/1024, float norThreshold=NV_NORMAL_EPSILON); - -} // nv namespace - - -#endif // NV_MESH_SNAP_H diff --git a/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.cpp b/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.cpp deleted file mode 100644 index 2ba4dcae18..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.cpp +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright NVIDIA Corporation 2006 -- Ignacio Castano <icastano@nvidia.com> - -#include <nvmesh/TriMesh.h> -#include <nvmesh/QuadTriMesh.h> - -#include <nvmesh/weld/VertexWeld.h> -#include <nvmesh/weld/Weld.h> - -using namespace nv; - -// Weld trimesh vertices -void nv::WeldVertices(TriMesh * mesh) -{ - nvDebug("--- Welding vertices.\n"); - - nvCheck(mesh != NULL); - - uint count = mesh->vertexCount(); - Array<uint> xrefs; - Weld<TriMesh::Vertex> weld; - uint newCount = weld(mesh->vertices(), xrefs); - - nvDebug("--- %d vertices welded\n", count - newCount); - - - // Remap faces. - const uint faceCount = mesh->faceCount(); - for(uint f = 0; f < faceCount; f++) - { - TriMesh::Face & face = mesh->faceAt(f); - face.v[0] = xrefs[face.v[0]]; - face.v[1] = xrefs[face.v[1]]; - face.v[2] = xrefs[face.v[2]]; - } -} - - -// Weld trimesh vertices -void nv::WeldVertices(QuadTriMesh * mesh) -{ - nvDebug("--- Welding vertices.\n"); - - nvCheck(mesh != NULL); - - uint count = mesh->vertexCount(); - Array<uint> xrefs; - Weld<TriMesh::Vertex> weld; - uint newCount = weld(mesh->vertices(), xrefs); - - nvDebug("--- %d vertices welded\n", count - newCount); - - // Remap faces. - const uint faceCount = mesh->faceCount(); - for(uint f = 0; f < faceCount; f++) - { - QuadTriMesh::Face & face = mesh->faceAt(f); - face.v[0] = xrefs[face.v[0]]; - face.v[1] = xrefs[face.v[1]]; - face.v[2] = xrefs[face.v[2]]; - - if (face.isQuadFace()) - { - face.v[3] = xrefs[face.v[3]]; - } - } -} - - - -// OLD code - -#if 0 - -namespace { - -struct VertexInfo { - uint id; ///< Original vertex id. - uint normal_face_group; - uint tangent_face_group; - uint material; - uint chart; -}; - - -/// VertexInfo hash functor. -struct VertexHash : public IHashFunctor<VertexInfo> { - VertexHash(PiMeshPtr m) : mesh(m) { - uint c = mesh->FindChannel(VS_POS); - piCheck(c != PI_NULL_INDEX); - channel = mesh->GetChannel(c); - piCheck(channel != NULL); - } - - uint32 operator () (const VertexInfo & v) const { - return channel->data[v.id].GetHash(); - } - -private: - PiMeshPtr mesh; - PiMesh::Channel * channel; -}; - - -/// VertexInfo comparator. -struct VertexEqual : public IBinaryPredicate<VertexInfo> { - VertexEqual(PiMeshPtr m) : mesh(m) {} - - bool operator () (const VertexInfo & a, const VertexInfo & b) const { - - bool equal = a.normal_face_group == b.normal_face_group && - a.tangent_face_group == b.tangent_face_group && - a.material == b.material && - a.chart == b.chart; - - // Split vertex shared by different face types. - if( !equal ) { - return false; - } - - // They were the same vertex. - if( a.id == b.id ) { - return true; - } - - // Vertex equal if all the channels are equal. - return mesh->IsVertexEqual(a.id, b.id); - } - -private: - PiMeshPtr mesh; -}; - -} // namespace - - -/// Weld the vertices. -void PiMeshVertexWeld::WeldVertices(const PiMeshSmoothGroup * mesh_smooth_group, - const PiMeshMaterial * mesh_material, const PiMeshAtlas * mesh_atlas ) -{ - piDebug( "--- Welding vertices:\n" ); - - piDebug( "--- Expand mesh vertices.\n" ); - PiArray<VertexInfo> vertex_array; - - const uint face_num = mesh->GetFaceNum(); - const uint vertex_max = face_num * 3; - vertex_array.Resize( vertex_max ); - - for(uint i = 0; i < vertex_max; i++) { - - uint f = i/3; - - const PiMesh::Face & face = mesh->GetFace(f); - vertex_array[i].id = face.v[i%3]; - - // Reset face attributes. - vertex_array[i].normal_face_group = PI_NULL_INDEX; - vertex_array[i].tangent_face_group = PI_NULL_INDEX; - vertex_array[i].material = PI_NULL_INDEX; - vertex_array[i].chart = PI_NULL_INDEX; - - // Set available attributes. - if( mesh_smooth_group != NULL ) { - if( mesh_smooth_group->HasNormalFaceGroups() ) { - vertex_array[i].normal_face_group = mesh_smooth_group->GetNormalFaceGroup( f ); - } - if( mesh_smooth_group->HasTangentFaceGroups() ) { - vertex_array[i].tangent_face_group = mesh_smooth_group->GetTangentFaceGroup( f ); - } - } - if( mesh_material != NULL ) { - vertex_array[i].material = mesh_material->GetFaceMaterial( f ); - } - if( mesh_atlas != NULL && mesh_atlas->HasCharts() ) { - vertex_array[i].chart = mesh_atlas->GetFaceChart( f ); - } - } - piDebug( "--- %d vertices.\n", vertex_max ); - - piDebug( "--- Collapse vertices.\n" ); - - uint * xrefs = new uint[vertex_max]; - VertexHash hash(mesh); - VertexEqual equal(mesh); - const uint vertex_num = Weld( vertex_array, xrefs, hash, equal ); - piCheck(vertex_num <= vertex_max); - piDebug( "--- %d vertices.\n", vertex_num ); - - // Remap face indices. - piDebug( "--- Remapping face indices.\n" ); - mesh->RemapFaceIndices(vertex_max, xrefs); - - - // Overwrite xrefs to map new vertices to old vertices. - for(uint v = 0; v < vertex_num; v++) { - xrefs[v] = vertex_array[v].id; - } - - // Update vertex order. - mesh->ReorderVertices(vertex_num, xrefs); - - delete [] xrefs; -} - -#endif // 0 diff --git a/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.h b/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.h deleted file mode 100644 index 1dc2e4ba4d..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright NVIDIA Corporation 2006 -- Ignacio Castano <icastano@nvidia.com> - -#ifndef NV_MESH_VERTEXWELD_H -#define NV_MESH_VERTEXWELD_H - -#include <nvmesh/nvmesh.h> - -namespace nv -{ - class TriMesh; - class QuadMesh; - - NVMESH_API void WeldVertices(TriMesh * mesh); - NVMESH_API void WeldVertices(QuadTriMesh * mesh); - -} // nv namespace - - -#endif // NV_MESH_VERTEXWELD_H diff --git a/thirdparty/thekla_atlas/nvmesh/weld/Weld.h b/thirdparty/thekla_atlas/nvmesh/weld/Weld.h deleted file mode 100644 index e615539461..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/weld/Weld.h +++ /dev/null @@ -1,171 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#ifndef NV_MESH_WELD_H -#define NV_MESH_WELD_H - -#include "nvcore/Array.h" -#include "nvcore/Hash.h" -#include "nvcore/Utils.h" // nextPowerOfTwo - -#include <string.h> // for memset, memcmp, memcpy - -// Weld function to remove array duplicates in linear time using hashing. - -namespace nv -{ - -/// Generic welding routine. This function welds the elements of the array p -/// and returns the cross references in the xrefs array. To compare the elements -/// it uses the given hash and equal functors. -/// -/// This code is based on the ideas of Ville Miettinen and Pierre Terdiman. -template <class T, class H=Hash<T>, class E=Equal<T> > -struct Weld -{ - // xrefs maps old elements to new elements - uint operator()(Array<T> & p, Array<uint> & xrefs) - { - const uint N = p.size(); // # of input vertices. - uint outputCount = 0; // # of output vertices - uint hashSize = nextPowerOfTwo(N); // size of the hash table - uint * hashTable = new uint[hashSize + N]; // hash table + linked list - uint * next = hashTable + hashSize; // use bottom part as linked list - - xrefs.resize(N); - memset( hashTable, NIL, hashSize*sizeof(uint) ); // init hash table (NIL = 0xFFFFFFFF so memset works) - - H hash; - E equal; - for (uint i = 0; i < N; i++) - { - const T & e = p[i]; - uint32 hashValue = hash(e) & (hashSize-1); - uint offset = hashTable[hashValue]; - - // traverse linked list - while( offset != NIL && !equal(p[offset], e) ) - { - offset = next[offset]; - } - - xrefs[i] = offset; - - // no match found - copy vertex & add to hash - if( offset == NIL ) - { - // save xref - xrefs[i] = outputCount; - - // copy element - p[outputCount] = e; - - // link to hash table - next[outputCount] = hashTable[hashValue]; - - // update hash heads and increase output counter - hashTable[hashValue] = outputCount++; - } - } - - // cleanup - delete [] hashTable; - - p.resize(outputCount); - - // number of output vertices - return outputCount; - } -}; - - -/// Reorder the given array accoding to the indices given in xrefs. -template <class T> -void reorderArray(Array<T> & array, const Array<uint> & xrefs) -{ - const uint count = xrefs.count(); - Array<T> new_array; - new_array.resize(count); - - for(uint i = 0; i < count; i++) { - new_array[i] = array[xrefs[i]]; - } - - swap(array, new_array); -} - -/// Reverse the given array so that new indices point to old indices. -inline void reverseXRefs(Array<uint> & xrefs, uint count) -{ - Array<uint> new_xrefs; - new_xrefs.resize(count); - - for(uint i = 0; i < xrefs.count(); i++) { - new_xrefs[xrefs[i]] = i; - } - - swap(xrefs, new_xrefs); -} - - - -// -struct WeldN -{ - uint vertexSize; - - WeldN(uint n) : vertexSize(n) {} - - // xrefs maps old elements to new elements - uint operator()(uint8 * ptr, uint N, Array<uint> & xrefs) - { - uint outputCount = 0; // # of output vertices - uint hashSize = nextPowerOfTwo(N); // size of the hash table - uint * hashTable = new uint[hashSize + N]; // hash table + linked list - uint * next = hashTable + hashSize; // use bottom part as linked list - - xrefs.resize(N); - memset( hashTable, NIL, hashSize*sizeof(uint) ); // init hash table (NIL = 0xFFFFFFFF so memset works) - - for (uint i = 0; i < N; i++) - { - const uint8 * vertex = ptr + i * vertexSize; - uint32 hashValue = sdbmHash(vertex, vertexSize) & (hashSize-1); - uint offset = hashTable[hashValue]; - - // traverse linked list - while (offset != NIL && memcmp(ptr + offset * vertexSize, vertex, vertexSize) != 0) - { - offset = next[offset]; - } - - xrefs[i] = offset; - - // no match found - copy vertex & add to hash - if (offset == NIL) - { - // save xref - xrefs[i] = outputCount; - - // copy element - memcpy(ptr + outputCount * vertexSize, vertex, vertexSize); - - // link to hash table - next[outputCount] = hashTable[hashValue]; - - // update hash heads and increase output counter - hashTable[hashValue] = outputCount++; - } - } - - // cleanup - delete [] hashTable; - - // number of output vertices - return outputCount; - } -}; - - -} // nv namespace - -#endif // NV_MESH_WELD_H diff --git a/thirdparty/thekla_atlas/poshlib/posh.c b/thirdparty/thekla_atlas/poshlib/posh.c deleted file mode 100644 index bd3fcc66ea..0000000000 --- a/thirdparty/thekla_atlas/poshlib/posh.c +++ /dev/null @@ -1,1006 +0,0 @@ -/* -LICENSE: - -Copyright (c) 2004, Brian Hook -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * The names of this package'ss contributors contributors may not - be used to endorse or promote products derived from this - software without specific prior written permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/** - @file posh.c - @author Brian Hook - @date 2002 - @brief Portable Open Source Harness primary source file -*/ -#include "posh.h" - -#if !defined FORCE_DOXYGEN - -#if !defined POSH_NO_FLOAT -# define POSH_FLOAT_STRING "enabled" -#else -# define POSH_FLOAT_STRING "disabled" -#endif - -#if defined POSH_64BIT_INTEGER -# define POSH_64BIT_INTEGER_STRING "yes" -#else -# define POSH_64BIT_INTEGER_STRING "no" -#endif - -#if defined POSH_64BIT_POINTER -# define POSH_POINTER_STRING "64-bits" -#else -# define POSH_POINTER_STRING "32-bits" -#endif - -#if defined POSH_LITTLE_ENDIAN -# define IS_BIG_ENDIAN 0 - -# define NATIVE16 POSH_LittleU16 -# define NATIVE32 POSH_LittleU32 -# define NATIVE64 POSH_LittleU64 -# define FOREIGN16 POSH_BigU16 -# define FOREIGN32 POSH_BigU32 -# define FOREIGN64 POSH_BigU64 -#else -# define IS_BIG_ENDIAN 1 - -# define NATIVE16 POSH_BigU16 -# define NATIVE32 POSH_BigU32 -# define NATIVE64 POSH_BigU64 -# define FOREIGN16 POSH_LittleU16 -# define FOREIGN32 POSH_LittleU32 -# define FOREIGN64 POSH_LittleU64 -#endif /* POSH_LITTLE_ENDIAN */ - -static -int -s_testBigEndian( void ) -{ - union - { - posh_byte_t c[ 4 ]; - posh_u32_t i; - } u; - - u.i= 1; - - if ( u.c[ 0 ] == 1 ) - { - return 0; - } - return 1; -} - -static -const char * -s_testSerialization( void ) -{ - posh_byte_t serbuf[ 8 ]; - posh_u16_t tmp16; - posh_u32_t tmp32; - - /* 16-bit serialization */ - POSH_WriteU16ToLittle( serbuf, 0xABCD ); - if ( ( tmp16 = POSH_ReadU16FromLittle( serbuf ) ) != 0xABCD ) - { - return "*ERROR: failed little-endian 16-bit serialization test"; - } - - POSH_WriteU16ToBig( serbuf, 0xABCD ); - if ( ( tmp16 = POSH_ReadU16FromBig( serbuf ) ) != 0xABCD ) - { - return "*ERROR: failed big-endian 16-bit serialization test"; - } - - /* 32-bit serialization */ - POSH_WriteU32ToLittle( serbuf, 0xABCD1234L ); - if ( ( tmp32 = POSH_ReadU32FromLittle( serbuf ) ) != 0xABCD1234 ) - { - return "*ERROR: failed little-endian 32-bit serialization test"; - } - - POSH_WriteU32ToBig( serbuf, 0xABCD1234L ); - if ( ( tmp32 = POSH_ReadU32FromBig( serbuf ) ) != 0xABCD1234 ) - { - return "*ERROR: failed big-endian 32-bit serialization test"; - } - -#if defined POSH_64BIT_INTEGER - { -#define REF64 POSH_U64(0xFEDCBA9876543210) - - posh_u64_t tmp64; - - POSH_WriteU64ToLittle( serbuf, REF64 ); - - if ( ( tmp64 = POSH_ReadU64FromLittle( serbuf ) ) != REF64 ) - { - return "*ERROR: failed little-endian 64-bit serialization test"; - } - - POSH_WriteU64ToBig( serbuf, REF64 ); - - if ( ( tmp64 = POSH_ReadU64FromBig( serbuf ) ) != REF64 ) - { - return "*ERROR: failed big-endian 64-bit serialization test"; - } - } -#endif - - return 0; -} - -#if !defined POSH_NO_FLOAT -static -const char * -s_testFloatingPoint( void ) -{ - float fRef = 10.0f/30.0f; - double dRef = 10.0/30.0; - posh_byte_t dbuf[ 8 ]; - float fTmp; - double dTmp; - - fTmp = POSH_FloatFromLittleBits( POSH_LittleFloatBits( fRef ) ); - - if ( fTmp != fRef ) - { - return "*ERROR: POSH little endian floating point conversion failed. Please report this to poshlib@poshlib.org!\n"; - } - - fTmp = POSH_FloatFromBigBits( POSH_BigFloatBits( fRef ) ); - if ( fTmp != fRef ) - { - return "*ERROR: POSH big endian floating point conversion failed. Please report this to poshlib@poshlib.org!\n"; - } - - POSH_DoubleBits( dRef, dbuf ); - - dTmp = POSH_DoubleFromBits( dbuf ); - - if ( dTmp != dRef ) - { - return "*ERROR: POSH double precision floating point serialization failed. Please report this to poshlib@poshlib.org!\n"; - } - - return 0; -} -#endif /* !defined POSH_NO_FLOAT */ - -static -const char * -s_testEndianess( void ) -{ - /* check endianess */ - if ( s_testBigEndian() != IS_BIG_ENDIAN ) - { - return "*ERROR: POSH compile time endianess does not match run-time endianess verification. Please report this to poshlib@poshlib.org!\n"; - } - - /* make sure our endian swap routines work */ - if ( ( NATIVE32( 0x11223344L ) != 0x11223344L ) || - ( FOREIGN32( 0x11223344L ) != 0x44332211L ) || - ( NATIVE16( 0x1234 ) != 0x1234 ) || - ( FOREIGN16( 0x1234 ) != 0x3412 ) ) - { - return "*ERROR: POSH endianess macro selection failed. Please report this to poshlib@poshlib.org!\n"; - } - - /* test serialization routines */ - - return 0; -} -#endif /* !defined FORCE_DOXYGEN */ - -/** - Returns a string describing this platform's basic attributes. - - POSH_GetArchString() reports on an architecture's statically determined - attributes. In addition, it will perform run-time verification checks - to make sure the various platform specific functions work. If an error - occurs, please contact me at poshlib@poshlib.org so we can try to resolve - what the specific failure case is. - @returns a string describing this platform on success, or a string in the - form "*ERROR: [text]" on failure. You can simply check to see if - the first character returned is '*' to verify an error condition. -*/ -const char * -POSH_GetArchString( void ) -{ - const char *err; - const char *s = "OS:.............."POSH_OS_STRING"\n" - "CPU:............."POSH_CPU_STRING"\n" - "endian:.........."POSH_ENDIAN_STRING"\n" - "ptr size:........"POSH_POINTER_STRING"\n" - "64-bit ints......"POSH_64BIT_INTEGER_STRING"\n" - "floating point..."POSH_FLOAT_STRING"\n" - "compiler........."POSH_COMPILER_STRING"\n"; - - /* test endianess */ - err = s_testEndianess(); - - if ( err != 0 ) - { - return err; - } - - /* test serialization */ - err = s_testSerialization(); - - if ( err != 0 ) - { - return err; - } - -#if !defined POSH_NO_FLOAT - /* check that our floating point support is correct */ - err = s_testFloatingPoint(); - - if ( err != 0 ) - { - return err; - } - -#endif - - return s; -} - -/* ---------------------------------------------------------------------------*/ -/* BYTE SWAPPING SUPPORT */ -/* ---------------------------------------------------------------------------*/ -/** - * Byte swaps a 16-bit unsigned value - * - @ingroup ByteSwapFunctions - @param v [in] unsigned 16-bit input value to swap - @returns a byte swapped version of v - */ -posh_u16_t -POSH_SwapU16( posh_u16_t v ) -{ - posh_u16_t swapped; - - swapped = v << 8; - swapped |= v >> 8; - - return swapped; -} - -/** - * Byte swaps a 16-bit signed value - * - @ingroup ByteSwapFunctions - @param v [in] signed 16-bit input value to swap - @returns a byte swapped version of v - @remarks This just calls back to the unsigned version, since byte swapping - is independent of sign. However, we still provide this function to - avoid signed/unsigned mismatch compiler warnings. - */ -posh_i16_t -POSH_SwapI16( posh_i16_t v ) -{ - return ( posh_i16_t ) POSH_SwapU16( v ); -} - -/** - * Byte swaps a 32-bit unsigned value - * - @ingroup ByteSwapFunctions - @param v [in] unsigned 32-bit input value to swap - @returns a byte swapped version of v - */ -posh_u32_t -POSH_SwapU32( posh_u32_t v ) -{ - posh_u32_t swapped; - - swapped = ( v & 0xFF ) << 24; - swapped |= ( v & 0xFF00 ) << 8; - swapped |= ( v >> 8 ) & 0xFF00; - swapped |= ( v >> 24 ); - - return swapped; -} - -/** - * Byte swaps a 32-bit signed value - * - @ingroup ByteSwapFunctions - @param v [in] signed 32-bit input value to swap - @returns a byte swapped version of v - @remarks This just calls back to the unsigned version, since byte swapping - is independent of sign. However, we still provide this function to - avoid signed/unsigned mismatch compiler warnings. - */ -posh_i32_t -POSH_SwapI32( posh_i32_t v ) -{ - return ( posh_i32_t ) POSH_SwapU32( ( posh_u32_t ) v ); -} - -#if defined POSH_64BIT_INTEGER -/** - * Byte swaps a 64-bit unsigned value - - @param v [in] a 64-bit input value to swap - @ingroup SixtyFourBit - @returns a byte swapped version of v -*/ -posh_u64_t -POSH_SwapU64( posh_u64_t v ) -{ - posh_byte_t tmp; - union { - posh_byte_t bytes[ 8 ]; - posh_u64_t u64; - } u; - - u.u64 = v; - - tmp = u.bytes[ 0 ]; u.bytes[ 0 ] = u.bytes[ 7 ]; u.bytes[ 7 ] = tmp; - tmp = u.bytes[ 1 ]; u.bytes[ 1 ] = u.bytes[ 6 ]; u.bytes[ 6 ] = tmp; - tmp = u.bytes[ 2 ]; u.bytes[ 2 ] = u.bytes[ 5 ]; u.bytes[ 5 ] = tmp; - tmp = u.bytes[ 3 ]; u.bytes[ 3 ] = u.bytes[ 4 ]; u.bytes[ 4 ] = tmp; - - return u.u64; -} - -/** - * Byte swaps a 64-bit signed value - - @param v [in] a 64-bit input value to swap - @ingroup SixtyFourBit - @returns a byte swapped version of v -*/ -posh_i64_t -POSH_SwapI64( posh_i64_t v ) -{ - return ( posh_i64_t ) POSH_SwapU64( ( posh_u64_t ) v ); -} - -#endif /* defined POSH_64BIT_INTEGER */ - -/* ---------------------------------------------------------------------------*/ -/* IN-MEMORY SERIALIZATION */ -/* ---------------------------------------------------------------------------*/ - -/** - * Writes an unsigned 16-bit value to a little endian buffer - - @ingroup MemoryBuffer - @param dst [out] pointer to the destination buffer, may not be NULL. Alignment doesn't matter. - @param value [in] host-endian unsigned 16-bit value - @returns a pointer to the location two bytes after dst - @remarks does no validation of the inputs -*/ -posh_u16_t * -POSH_WriteU16ToLittle( void *dst, posh_u16_t value ) -{ - posh_u16_t *p16 = ( posh_u16_t * ) dst; - posh_byte_t *p = ( posh_byte_t * ) dst; - - p[ 0 ] = value & 0xFF; - p[ 1 ] = ( value & 0xFF00) >> 8; - - return p16 + 1; -} - -/** - * Writes a signed 16-bit value to a little endian buffer - - @ingroup MemoryBuffer - @param dst [out] pointer to the destination buffer, may not be NULL - @param value [in] host-endian signed 16-bit value - @returns a pointer to the location two bytes after dst - @remarks does no validation of the inputs. This simply calls - POSH_WriteU16ToLittle() with appropriate casting. -*/ -posh_i16_t * -POSH_WriteI16ToLittle( void *dst, posh_i16_t value ) -{ - return ( posh_i16_t * ) POSH_WriteU16ToLittle( dst, ( posh_u16_t ) value ); -} - -/** - * Writes an unsigned 32-bit value to a little endian buffer - - @ingroup MemoryBuffer - @param dst [out] pointer to the destination buffer, may not be NULL - @param value [in] host-endian signed 32-bit value - @returns a pointer to the location four bytes after dst - @remarks does no validation of the inputs. -*/ -posh_u32_t * -POSH_WriteU32ToLittle( void *dst, posh_u32_t value ) -{ - posh_u32_t *p32 = ( posh_u32_t * ) dst; - posh_byte_t *p = ( posh_byte_t * ) dst; - - p[ 0 ] = ( value & 0xFF ); - p[ 1 ] = ( value & 0xFF00 ) >> 8; - p[ 2 ] = ( value & 0xFF0000 ) >> 16; - p[ 3 ] = ( value & 0xFF000000 ) >> 24; - - return p32 + 1; -} - -/** - * Writes a signed 32-bit value to a little endian buffer - - @ingroup MemoryBuffer - @param dst [out] pointer to the destination buffer, may not be NULL - @param value [in] host-endian signed 32-bit value - @returns a pointer to the location four bytes after dst - @remarks does no validation of the inputs. This simply calls - POSH_WriteU32ToLittle() with appropriate casting. -*/ -posh_i32_t * -POSH_WriteI32ToLittle( void *dst, posh_i32_t value ) -{ - return ( posh_i32_t * ) POSH_WriteU32ToLittle( dst, ( posh_u32_t ) value ); -} - -/** - * Writes an unsigned 16-bit value to a big endian buffer - - @ingroup MemoryBuffer - @param dst [out] pointer to the destination buffer, may not be NULL - @param value [in] host-endian unsigned 16-bit value - @returns a pointer to the location two bytes after dst - @remarks does no validation of the inputs -*/ -posh_u16_t * -POSH_WriteU16ToBig( void *dst, posh_u16_t value ) -{ - posh_u16_t *p16 = ( posh_u16_t * ) dst; - posh_byte_t *p = ( posh_byte_t * ) dst; - - p[ 1 ] = ( value & 0xFF ); - p[ 0 ] = ( value & 0xFF00 ) >> 8; - - return p16 + 1; -} - -/** - * Writes a signed 16-bit value to a big endian buffer - - @ingroup MemoryBuffer - @param dst [out] pointer to the destination buffer, may not be NULL - @param value [in] host-endian signed 16-bit value - @returns a pointer to the location two bytes after dst - @remarks does no validation of the inputs. This simply calls - POSH_WriteU16ToLittle() with appropriate casting. -*/ -posh_i16_t * -POSH_WriteI16ToBig( void *dst, posh_i16_t value ) -{ - return ( posh_i16_t * ) POSH_WriteU16ToBig( dst, ( posh_u16_t ) value ); -} - -/** - * Writes an unsigned 32-bit value to a big endian buffer - - @ingroup MemoryBuffer - @param dst [out] pointer to the destination buffer, may not be NULL - @param value [in] host-endian unsigned 32-bit value - @returns a pointer to the location four bytes after dst - @remarks does no validation of the inputs. -*/ -posh_u32_t * -POSH_WriteU32ToBig( void *dst, posh_u32_t value ) -{ - posh_u32_t *p32 = ( posh_u32_t * ) dst; - posh_byte_t *p = ( posh_byte_t * ) dst; - - p[ 3 ] = ( value & 0xFF ); - p[ 2 ] = ( value & 0xFF00 ) >> 8; - p[ 1 ] = ( value & 0xFF0000 ) >> 16; - p[ 0 ] = ( value & 0xFF000000 ) >> 24; - - return p32 + 1; -} - -/** - * Writes a signed 32-bit value to a big endian buffer - - @ingroup MemoryBuffer - @param dst [out] pointer to the destination buffer, may not be NULL - @param value [in] host-endian signed 32-bit value - @returns a pointer to the location four bytes after dst - @remarks does no validation of the inputs. This simply calls - POSH_WriteU32ToBig() with appropriate casting. -*/ -posh_i32_t * -POSH_WriteI32ToBig( void *dst, posh_i32_t value ) -{ - return ( posh_i32_t * ) POSH_WriteU32ToBig( dst, ( posh_u32_t ) value ); -} - -#if defined POSH_64BIT_INTEGER -/** - * Writes an unsigned 64-bit value to a little-endian buffer - - @ingroup SixtyFourBit - @param dst [out] pointer to the destination buffer, may not be NULL - @param value [in] host-endian unsigned 64-bit value - @returns a pointer to the location eight bytes after dst - @remarks does no validation of the inputs. -*/ -posh_u64_t * -POSH_WriteU64ToLittle( void *dst, posh_u64_t value ) -{ - posh_u64_t *p64 = ( posh_u64_t * ) dst; - posh_byte_t *p = ( posh_byte_t * ) dst; - int i; - - for ( i = 0; i < 8; i++, value >>= 8 ) - { - p[ i ] = ( posh_byte_t ) ( value & 0xFF ); - } - - return p64 + 1; -} - -/** - * Writes a signed 64-bit value to a little-endian buffer - - @ingroup SixtyFourBit - @param dst [out] pointer to the destination buffer, may not be NULL - @param value [in] host-endian unsigned 64-bit value - @returns a pointer to the location eight bytes after dst - @remarks does no validation of the inputs. -*/ -posh_i64_t * -POSH_WriteI64ToLittle( void *dst, posh_i64_t value ) -{ - return ( posh_i64_t * ) POSH_WriteU64ToLittle( dst, ( posh_u64_t ) value ); -} - -/** - * Writes an unsigned 64-bit value to a big-endian buffer - - @ingroup SixtyFourBit - @param dst [out] pointer to the destination buffer, may not be NULL - @param value [in] host-endian unsigned 64-bit value - @returns a pointer to the location eight bytes after dst - @remarks does no validation of the inputs. -*/ -posh_u64_t * -POSH_WriteU64ToBig( void *dst, posh_u64_t value ) -{ - posh_u64_t *p64 = ( posh_u64_t * ) dst; - posh_byte_t *p = ( posh_byte_t * ) dst; - int i; - - for ( i = 0; i < 8; i++, value >>= 8 ) - { - p[ 7-i ] = ( posh_byte_t ) ( value & 0xFF ); - } - - return p64 + 8; -} - -/** - * Writes a signed 64-bit value to a big-endian buffer - - @ingroup SixtyFourBit - @param dst [out] pointer to the destination buffer, may not be NULL - @param value [in] host-endian signed 64-bit value - @returns a pointer to the location eight bytes after dst - @remarks does no validation of the inputs. -*/ -posh_i64_t * -POSH_WriteI64ToBig( void *dst, posh_i64_t value ) -{ - return ( posh_i64_t * ) POSH_WriteU64ToBig( dst, ( posh_u64_t ) value ); -} - -#endif /* POSH_64BIT_INTEGER */ - -/* ---------------------------------------------------------------------------*/ -/* IN-MEMORY DESERIALIZATION */ -/* ---------------------------------------------------------------------------*/ - -/** - * Reads an unsigned 16-bit value from a little-endian buffer - @ingroup MemoryBuffer - @param src [in] source buffer - @returns host-endian unsigned 16-bit value -*/ -posh_u16_t -POSH_ReadU16FromLittle( const void *src ) -{ - posh_u16_t v = 0; - posh_byte_t *p = ( posh_byte_t * ) src; - - v |= p[ 0 ]; - v |= ( ( posh_u16_t ) p[ 1 ] ) << 8; - - return v; -} - -/** - * Reads a signed 16-bit value from a little-endian buffer - @ingroup MemoryBuffer - @param src [in] source buffer - @returns host-endian signed 16-bit value -*/ -posh_i16_t -POSH_ReadI16FromLittle( const void *src ) -{ - return ( posh_i16_t ) POSH_ReadU16FromLittle( src ); -} - -/** - * Reads an unsigned 32-bit value from a little-endian buffer - @ingroup MemoryBuffer - @param src [in] source buffer - @returns host-endian unsigned 32-bit value -*/ -posh_u32_t -POSH_ReadU32FromLittle( const void *src ) -{ - posh_u32_t v = 0; - posh_byte_t *p = ( posh_byte_t * ) src; - - v |= p[ 0 ]; - v |= ( ( posh_u32_t ) p[ 1 ] ) << 8; - v |= ( ( posh_u32_t ) p[ 2 ] ) << 16; - v |= ( ( posh_u32_t ) p[ 3 ] ) << 24; - - return v; -} - -/** - * Reads a signed 32-bit value from a little-endian buffer - @ingroup MemoryBuffer - @param src [in] source buffer - @returns host-endian signed 32-bit value -*/ -posh_i32_t -POSH_ReadI32FromLittle( const void *src ) -{ - return ( posh_i32_t ) POSH_ReadU32FromLittle( src ); -} - - -/** - * Reads an unsigned 16-bit value from a big-endian buffer - @ingroup MemoryBuffer - @param src [in] source buffer - @returns host-endian unsigned 16-bit value -*/ -posh_u16_t -POSH_ReadU16FromBig( const void *src ) -{ - posh_u16_t v = 0; - posh_byte_t *p = ( posh_byte_t * ) src; - - v |= p[ 1 ]; - v |= ( ( posh_u16_t ) p[ 0 ] ) << 8; - - return v; -} - -/** - * Reads a signed 16-bit value from a big-endian buffer - @ingroup MemoryBuffer - @param src [in] source buffer - @returns host-endian signed 16-bit value -*/ -posh_i16_t -POSH_ReadI16FromBig( const void *src ) -{ - return ( posh_i16_t ) POSH_ReadU16FromBig( src ); -} - -/** - * Reads an unsigned 32-bit value from a big-endian buffer - @ingroup MemoryBuffer - @param src [in] source buffer - @returns host-endian unsigned 32-bit value -*/ -posh_u32_t -POSH_ReadU32FromBig( const void *src ) -{ - posh_u32_t v = 0; - posh_byte_t *p = ( posh_byte_t * ) src; - - v |= p[ 3 ]; - v |= ( ( posh_u32_t ) p[ 2 ] ) << 8; - v |= ( ( posh_u32_t ) p[ 1 ] ) << 16; - v |= ( ( posh_u32_t ) p[ 0 ] ) << 24; - - return v; -} - -/** - * Reads a signed 32-bit value from a big-endian buffer - @ingroup MemoryBuffer - @param src [in] source buffer - @returns host-endian signed 32-bit value -*/ -posh_i32_t -POSH_ReadI32FromBig( const void *src ) -{ - return POSH_BigI32( (*(const posh_i32_t*)src ) ); -} - -#if defined POSH_64BIT_INTEGER - -/** - * Reads an unsigned 64-bit value from a little-endian buffer - @param src [in] source buffer - @returns host-endian unsigned 32-bit value -*/ -posh_u64_t -POSH_ReadU64FromLittle( const void *src ) -{ - posh_u64_t v = 0; - posh_byte_t *p = ( posh_byte_t * ) src; - int i; - - for ( i = 0; i < 8; i++ ) - { - v |= ( ( posh_u64_t ) p[ i ] ) << (i*8); - } - - return v; -} - -/** - * Reads a signed 64-bit value from a little-endian buffer - @param src [in] source buffer - @returns host-endian signed 32-bit value -*/ -posh_i64_t -POSH_ReadI64FromLittle( const void *src ) -{ - return ( posh_i64_t ) POSH_ReadU64FromLittle( src ); -} - -/** - * Reads an unsigned 64-bit value from a big-endian buffer - @param src [in] source buffer - @returns host-endian unsigned 32-bit value -*/ -posh_u64_t -POSH_ReadU64FromBig( const void *src ) -{ - posh_u64_t v = 0; - posh_byte_t *p = ( posh_byte_t * ) src; - int i; - - for ( i = 0; i < 8; i++ ) - { - v |= ( ( posh_u64_t ) p[ 7-i ] ) << (i*8); - } - - return v; -} - -/** - * Reads an signed 64-bit value from a big-endian buffer - @param src [in] source buffer - @returns host-endian signed 32-bit value -*/ -posh_i64_t -POSH_ReadI64FromBig( const void *src ) -{ - return ( posh_i64_t ) POSH_ReadU64FromBig( src ); -} - -#endif /* POSH_64BIT_INTEGER */ - -/* ---------------------------------------------------------------------------*/ -/* FLOATING POINT SUPPORT */ -/* ---------------------------------------------------------------------------*/ - -#if !defined POSH_NO_FLOAT - -/** @ingroup FloatingPoint - @param[in] f floating point value - @returns a little-endian bit representation of f - */ -posh_u32_t -POSH_LittleFloatBits( float f ) -{ - union - { - float f32; - posh_u32_t u32; - } u; - - u.f32 = f; - - return POSH_LittleU32( u.u32 ); -} - -/** - * Extracts raw big-endian bits from a 32-bit floating point value - * - @ingroup FloatingPoint - @param f [in] floating point value - @returns a big-endian bit representation of f - */ -posh_u32_t -POSH_BigFloatBits( float f ) -{ - union - { - float f32; - posh_u32_t u32; - } u; - - u.f32 = f; - - return POSH_BigU32( u.u32 ); -} - -/** - * Extracts raw, little-endian bit representation from a 64-bit double. - * - @param d [in] 64-bit double precision value - @param dst [out] 8-byte storage buffer - @ingroup FloatingPoint - @returns the raw bits used to represent the value 'd', in the form dst[0]=LSB - */ -void -POSH_DoubleBits( double d, posh_byte_t dst[ 8 ] ) -{ - union - { - double d64; - posh_byte_t bytes[ 8 ]; - } u; - - u.d64 = d; - -#if defined POSH_LITTLE_ENDIAN - dst[ 0 ] = u.bytes[ 0 ]; - dst[ 1 ] = u.bytes[ 1 ]; - dst[ 2 ] = u.bytes[ 2 ]; - dst[ 3 ] = u.bytes[ 3 ]; - dst[ 4 ] = u.bytes[ 4 ]; - dst[ 5 ] = u.bytes[ 5 ]; - dst[ 6 ] = u.bytes[ 6 ]; - dst[ 7 ] = u.bytes[ 7 ]; -#else - dst[ 0 ] = u.bytes[ 7 ]; - dst[ 1 ] = u.bytes[ 6 ]; - dst[ 2 ] = u.bytes[ 5 ]; - dst[ 3 ] = u.bytes[ 4 ]; - dst[ 4 ] = u.bytes[ 3 ]; - dst[ 5 ] = u.bytes[ 2 ]; - dst[ 6 ] = u.bytes[ 1 ]; - dst[ 7 ] = u.bytes[ 0 ]; -#endif -} - -/** - * Creates a double-precision, 64-bit floating point value from a set of raw, - * little-endian bits - - @ingroup FloatingPoint - @param src [in] little-endian byte representation of 64-bit double precision - floating point value - @returns double precision floating point representation of the raw bits - @remarks No error checking is performed, so there are no guarantees that the - result is a valid number, nor is there any check to ensure that src is - non-NULL. BE CAREFUL USING THIS. - */ -double -POSH_DoubleFromBits( const posh_byte_t src[ 8 ] ) -{ - union - { - double d64; - posh_byte_t bytes[ 8 ]; - } u; - -#if defined POSH_LITTLE_ENDIAN - u.bytes[ 0 ] = src[ 0 ]; - u.bytes[ 1 ] = src[ 1 ]; - u.bytes[ 2 ] = src[ 2 ]; - u.bytes[ 3 ] = src[ 3 ]; - u.bytes[ 4 ] = src[ 4 ]; - u.bytes[ 5 ] = src[ 5 ]; - u.bytes[ 6 ] = src[ 6 ]; - u.bytes[ 7 ] = src[ 7 ]; -#else - u.bytes[ 0 ] = src[ 7 ]; - u.bytes[ 1 ] = src[ 6 ]; - u.bytes[ 2 ] = src[ 5 ]; - u.bytes[ 3 ] = src[ 4 ]; - u.bytes[ 4 ] = src[ 3 ]; - u.bytes[ 5 ] = src[ 2 ]; - u.bytes[ 6 ] = src[ 1 ]; - u.bytes[ 7 ] = src[ 0 ]; -#endif - - return u.d64; -} - -/** - * Creates a floating point number from little endian bits - * - @ingroup FloatingPoint - @param bits [in] raw floating point bits in little-endian form - @returns a floating point number based on the given bit representation - @remarks No error checking is performed, so there are no guarantees that the - result is a valid number. BE CAREFUL USING THIS. - */ -float -POSH_FloatFromLittleBits( posh_u32_t bits ) -{ - union - { - float f32; - posh_u32_t u32; - } u; - - u.u32 = bits; -#if defined POSH_BIG_ENDIAN - u.u32 = POSH_SwapU32( u.u32 ); -#endif - - return u.f32; -} - -/** - * Creates a floating point number from big-endian bits - * - @ingroup FloatingPoint - @param bits [in] raw floating point bits in big-endian form - @returns a floating point number based on the given bit representation - @remarks No error checking is performed, so there are no guarantees that the - result is a valid number. BE CAREFUL USING THIS. - */ -float -POSH_FloatFromBigBits( posh_u32_t bits ) -{ - union - { - float f32; - posh_u32_t u32; - } u; - - u.u32 = bits; -#if defined POSH_LITTLE_ENDIAN - u.u32 = POSH_SwapU32( u.u32 ); -#endif - - return u.f32; -} - -#endif /* !defined POSH_NO_FLOAT */ diff --git a/thirdparty/thekla_atlas/poshlib/posh.h b/thirdparty/thekla_atlas/poshlib/posh.h deleted file mode 100644 index 72acd20ce0..0000000000 --- a/thirdparty/thekla_atlas/poshlib/posh.h +++ /dev/null @@ -1,1040 +0,0 @@ -/** -@file posh.h -@author Brian Hook -@version 1.3.001 - -Header file for POSH, the Portable Open Source Harness project. - -NOTE: Unlike most header files, this one is designed to be included -multiple times, which is why it does not have the @#ifndef/@#define -preamble. - -POSH relies on environment specified preprocessor symbols in order -to infer as much as possible about the target OS/architecture and -the host compiler capabilities. - -NOTE: POSH is simple and focused. It attempts to provide basic -functionality and information, but it does NOT attempt to emulate -missing functionality. I am also not willing to make POSH dirty -and hackish to support truly ancient and/or outmoded and/or bizarre -technologies such as non-ANSI compilers, systems with non-IEEE -floating point formats, segmented 16-bit operating systems, etc. - -Please refer to the accompanying HTML documentation or visit -http://www.poshlib.org for more information on how to use POSH. - -LICENSE: - -Copyright (c) 2004, Brian Hook -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * The names of this package'ss contributors contributors may not - be used to endorse or promote products derived from this - software without specific prior written permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -REVISION: - -I've been lax about revision histories, so this starts at, um, 1.3.001. -Sorry for any inconveniences. - -1.3.001 - 2/23/2006 - Incorporated fix for bug reported by Bill Cary, - where I was not detecting Visual Studio - compilation on x86-64 systems. Added check for - _M_X64 which should fix that. - -*/ -/* -I have yet to find an authoritative reference on preprocessor -symbols, but so far this is what I've gleaned: - -GNU GCC/G++: - - __GNUC__: GNU C version - - __GNUG__: GNU C++ compiler - - __sun__ : on Sun platforms - - __svr4__: on Solaris and other SysV R4 platforms - - __mips__: on MIPS processor platforms - - __sparc_v9__: on Sparc 64-bit CPUs - - __sparcv9: 64-bit Solaris - - __MIPSEL__: mips processor, compiled for little endian - - __MIPSEB__: mips processor, compiled for big endian - - _R5900: MIPS/Sony/Toshiba R5900 (PS2) - - mc68000: 68K - - m68000: 68K - - m68k: 68K - - __palmos__: PalmOS - -Intel C/C++ Compiler: - - __ECC : compiler version, IA64 only - - __EDG__ - - __ELF__ - - __GXX_ABI_VERSION - - __i386 : IA-32 only - - __i386__ : IA-32 only - - i386 : IA-32 only - - __ia64 : IA-64 only - - __ia64__ : IA-64 only - - ia64 : IA-64 only - - __ICC : IA-32 only - - __INTEL_COMPILER : IA-32 or IA-64, newer versions only - -Apple's C/C++ Compiler for OS X: - - __APPLE_CC__ - - __APPLE__ - - __BIG_ENDIAN__ - - __APPLE__ - - __ppc__ - - __MACH__ - -DJGPP: - - __MSDOS__ - - __unix__ - - __unix - - __GNUC__ - - __GO32 - - DJGPP - - __i386, __i386, i386 - -Cray's C compiler: - - _ADDR64: if 64-bit pointers - - _UNICOS: - - __unix: - -SGI's CC compiler predefines the following (and more) with -ansi: - - __sgi - - __unix - - __host_mips - - _SYSTYPE_SVR4 - - __mips - - _MIPSEB - - anyone know if there is a predefined symbol for the compiler?! - -MinGW: - - as GnuC but also defines _WIN32, __WIN32, WIN32, _X86_, __i386, __i386__, and several others - - __MINGW32__ - -Cygwin: - - as Gnu C, but also - - __unix__ - - __CYGWIN32__ - -Microsoft Visual Studio predefines the following: - - _MSC_VER - - _WIN32: on Win32 - - _M_IX6 (on x86 systems) - - _M_X64: on x86-64 systems - - _M_ALPHA (on DEC AXP systems) - - _SH3: WinCE, Hitachi SH-3 - - _MIPS: WinCE, MIPS - - _ARM: WinCE, ARM - -Sun's C Compiler: - - sun and _sun - - unix and _unix - - sparc and _sparc (SPARC systems only) - - i386 and _i386 (x86 systems only) - - __SVR4 (Solaris only) - - __sparcv9: 64-bit solaris - - __SUNPRO_C - - _LP64: defined in 64-bit LP64 mode, but only if <sys/types.h> is included - -Borland C/C++ predefines the following: - - __BORLANDC__: - -DEC/Compaq C/C++ on Alpha: - - __alpha - - __arch64__ - - __unix__ (on Tru64 Unix) - - __osf__ - - __DECC - - __DECCXX (C++ compilation) - - __DECC_VER - - __DECCXX_VER - -IBM's AIX compiler: - - __64BIT__ if 64-bit mode - - _AIX - - __IBMC__: C compiler version - - __IBMCPP__: C++ compiler version - - _LONG_LONG: compiler allows long long - -Watcom: - - __WATCOMC__ - - __DOS__ : if targeting DOS - - __386__ : if 32-bit support - - __WIN32__ : if targetin 32-bit Windows - -HP-UX C/C++ Compiler: - - __hpux - - __unix - - __hppa (on PA-RISC) - - __LP64__: if compiled in 64-bit mode - -Metrowerks: - - __MWERKS__ - - __powerpc__ - - _powerc - - __MC68K__ - - macintosh when compiling for MacOS - - __INTEL__ for x86 targets - - __POWERPC__ - -*/ - -/* -** ---------------------------------------------------------------------------- -** Include <limits.h> optionally -** ---------------------------------------------------------------------------- -*/ -#ifdef POSH_USE_LIMITS_H -# include <limits.h> -#endif - -/* -** ---------------------------------------------------------------------------- -** Determine compilation environment -** ---------------------------------------------------------------------------- -*/ -#if defined __ECC || defined __ICC || defined __INTEL_COMPILER -# define POSH_COMPILER_STRING "Intel C/C++" -# define POSH_COMPILER_INTEL 1 -#endif - -#if ( defined __host_mips || defined __sgi ) && !defined __GNUC__ -# define POSH_COMPILER_STRING "MIPSpro C/C++" -# define POSH_COMPILER_MIPSPRO 1 -#endif - -#if defined __hpux && !defined __GNUC__ -# define POSH_COMPILER_STRING "HP-UX CC" -# define POSH_COMPILER_HPCC 1 -#endif - -#if defined __GNUC__ && !defined __clang__ -# define POSH_COMPILER_STRING "Gnu GCC" -# define POSH_COMPILER_GCC 1 -#endif - -#if defined __clang__ -# define POSH_COMPILER_STRING "Clang" -# define POSH_COMPILER_CLANG 1 -#endif - -#if defined __APPLE_CC__ - /* we don't define the compiler string here, let it be GNU */ -# define POSH_COMPILER_APPLECC 1 -#endif - -#if defined __IBMC__ || defined __IBMCPP__ -# define POSH_COMPILER_STRING "IBM C/C++" -# define POSH_COMPILER_IBM 1 -#endif - -#if defined _MSC_VER -# define POSH_COMPILER_STRING "Microsoft Visual C++" -# define POSH_COMPILER_MSVC 1 -#endif - -#if defined __SUNPRO_C -# define POSH_COMPILER_STRING "Sun Pro" -# define POSH_COMPILER_SUN 1 -#endif - -#if defined __BORLANDC__ -# define POSH_COMPILER_STRING "Borland C/C++" -# define POSH_COMPILER_BORLAND 1 -#endif - -#if defined __MWERKS__ -# define POSH_COMPILER_STRING "MetroWerks CodeWarrior" -# define POSH_COMPILER_METROWERKS 1 -#endif - -#if defined __DECC || defined __DECCXX -# define POSH_COMPILER_STRING "Compaq/DEC C/C++" -# define POSH_COMPILER_DEC 1 -#endif - -#if defined __WATCOMC__ -# define POSH_COMPILER_STRING "Watcom C/C++" -# define POSH_COMPILER_WATCOM 1 -#endif - -#if !defined POSH_COMPILER_STRING -# define POSH_COMPILER_STRING "Unknown compiler" -#endif - -/* -** ---------------------------------------------------------------------------- -** Determine target operating system -** ---------------------------------------------------------------------------- -*/ -#if defined linux || defined __linux__ -# define POSH_OS_LINUX 1 -# define POSH_OS_STRING "Linux" -#endif - -#if defined __HAIKU__ -# define POSH_OS_HAIKU 1 -# define POSH_OS_STRING "Haiku" -#endif - -#if defined __FreeBSD__ -# define POSH_OS_FREEBSD 1 -# define POSH_OS_STRING "FreeBSD" -#endif - -#if defined __CYGWIN32__ -# define POSH_OS_CYGWIN32 1 -# define POSH_OS_STRING "Cygwin" -#endif - -#if defined GEKKO -# define POSH_OS_GAMECUBE -# define __powerpc__ -# define POSH_OS_STRING "GameCube" -#endif - -#if defined __MINGW32__ -# define POSH_OS_MINGW 1 -# define POSH_OS_STRING "MinGW" -#endif - -#if defined GO32 && defined DJGPP && defined __MSDOS__ -# define POSH_OS_GO32 1 -# define POSH_OS_STRING "GO32/MS-DOS" -#endif - -/* NOTE: make sure you use /bt=DOS if compiling for 32-bit DOS, - otherwise Watcom assumes host=target */ -#if defined __WATCOMC__ && defined __386__ && defined __DOS__ -# define POSH_OS_DOS32 1 -# define POSH_OS_STRING "DOS/32-bit" -#endif - -#if defined _UNICOS -# define POSH_OS_UNICOS 1 -# define POSH_OS_STRING "UNICOS" -#endif - -//ACS if we're in xcode, look at the target conditionals to figure out if this is ios or osx -#if defined __APPLE__ -# include "TargetConditionals.h" -#endif -#if TARGET_OS_IPHONE -# define POSH_OS_IOS 1 -# define POSH_OS_STRING "iOS" -#else -# if ( defined __MWERKS__ && defined __powerc && !defined macintosh ) || defined __APPLE_CC__ || defined macosx -# define POSH_OS_OSX 1 -# define POSH_OS_STRING "MacOS X" -# endif -#endif - -#if defined __sun__ || defined sun || defined __sun || defined __solaris__ -# if defined __SVR4 || defined __svr4__ || defined __solaris__ -# define POSH_OS_STRING "Solaris" -# define POSH_OS_SOLARIS 1 -# endif -# if !defined POSH_OS_STRING -# define POSH_OS_STRING "SunOS" -# define POSH_OS_SUNOS 1 -# endif -#endif - -#if defined __sgi__ || defined sgi || defined __sgi -# define POSH_OS_IRIX 1 -# define POSH_OS_STRING "Irix" -#endif - -#if defined __hpux__ || defined __hpux -# define POSH_OS_HPUX 1 -# define POSH_OS_STRING "HP-UX" -#endif - -#if defined _AIX -# define POSH_OS_AIX 1 -# define POSH_OS_STRING "AIX" -#endif - -#if ( defined __alpha && defined __osf__ ) -# define POSH_OS_TRU64 1 -# define POSH_OS_STRING "Tru64" -#endif - -#if defined __BEOS__ || defined __beos__ -# define POSH_OS_BEOS 1 -# define POSH_OS_STRING "BeOS" -#endif - -#if defined amiga || defined amigados || defined AMIGA || defined _AMIGA -# define POSH_OS_AMIGA 1 -# define POSH_OS_STRING "Amiga" -#endif - -#if defined __unix__ -# define POSH_OS_UNIX 1 -# if !defined POSH_OS_STRING -# define POSH_OS_STRING "Unix-like(generic)" -# endif -#endif - -#if defined _WIN32_WCE -# define POSH_OS_WINCE 1 -# define POSH_OS_STRING "Windows CE" -#endif - -#if defined _XBOX || defined _XBOX_VER -# define POSH_OS_XBOX 1 -# define POSH_OS_STRING "XBOX" -#endif - -#if defined __ORBIS__ -# define POSH_OS_ORBIS -#endif - -#if defined _WIN32 || defined WIN32 || defined __NT__ || defined __WIN32__ -# if !defined POSH_OS_XBOX -# define POSH_OS_WIN32 1 -# if defined _WIN64 -# define POSH_OS_WIN64 1 -# define POSH_OS_STRING "Win64" -# else -# if !defined POSH_OS_STRING -# define POSH_OS_STRING "Win32" -# endif -# endif -# endif -#endif - -#if defined __palmos__ -# define POSH_OS_PALM 1 -# define POSH_OS_STRING "PalmOS" -#endif - -#if defined THINK_C || defined macintosh -# define POSH_OS_MACOS 1 -# define POSH_OS_STRING "MacOS" -#endif - -/* -** ----------------------------------------------------------------------------- -** Determine target CPU -** ----------------------------------------------------------------------------- -*/ - -#if defined GEKKO -# define POSH_CPU_PPC750 1 -# define POSH_CPU_STRING "IBM PowerPC 750 (NGC)" -#endif - -#if defined mc68000 || defined m68k || defined __MC68K__ || defined m68000 -# define POSH_CPU_68K 1 -# define POSH_CPU_STRING "MC68000" -#endif - -#if defined __PPC__ || defined __POWERPC__ || defined powerpc || defined _POWER || defined __ppc__ || defined __powerpc__ || defined _M_PPC -# define POSH_CPU_PPC 1 -# if !defined POSH_CPU_STRING -# if defined __powerpc64__ -# define POSH_CPU_STRING "PowerPC64" -# else -# define POSH_CPU_STRING "PowerPC" -# endif -# endif -#endif - -#if defined _CRAYT3E || defined _CRAYMPP -# define POSH_CPU_CRAYT3E 1 /* target processor is a DEC Alpha 21164 used in a Cray T3E*/ -# define POSH_CPU_STRING "Cray T3E (Alpha 21164)" -#endif - -#if defined CRAY || defined _CRAY && !defined _CRAYT3E -# error Non-AXP Cray systems not supported -#endif - -#if defined _SH3 -# define POSH_CPU_SH3 1 -# define POSH_CPU_STRING "Hitachi SH-3" -#endif - -#if defined __sh4__ || defined __SH4__ -# define POSH_CPU_SH3 1 -# define POSH_CPU_SH4 1 -# define POSH_CPU_STRING "Hitachi SH-4" -#endif - -#if defined __sparc__ || defined __sparc -# if defined __arch64__ || defined __sparcv9 || defined __sparc_v9__ -# define POSH_CPU_SPARC64 1 -# define POSH_CPU_STRING "Sparc/64" -# else -# define POSH_CPU_STRING "Sparc/32" -# endif -# define POSH_CPU_SPARC 1 -#endif - -#if defined ARM || defined __arm__ || defined _ARM -# define POSH_CPU_STRONGARM 1 -# define POSH_CPU_STRING "ARM" -#endif - -#if defined AARCH64 || defined __aarch64__ || defined _AARCH64 -# define POSH_CPU_STRONGARM 1 -# define POSH_CPU_STRING "AARCH64" -#endif - -#if defined mips || defined __mips__ || defined __MIPS__ || defined _MIPS -# define POSH_CPU_MIPS 1 -# if defined _R5900 -# define POSH_CPU_STRING "MIPS R5900 (PS2)" -# else -# define POSH_CPU_STRING "MIPS" -# endif -#endif - -#if defined __ia64 || defined _M_IA64 || defined __ia64__ -# define POSH_CPU_IA64 1 -# define POSH_CPU_STRING "IA64" -#endif - -#if defined __X86__ || defined __i386__ || defined i386 || defined _M_IX86 || defined __386__ || defined __x86_64__ || defined _M_X64 -# define POSH_CPU_X86 1 -# if defined __x86_64__ || defined _M_X64 -# define POSH_CPU_X86_64 1 -# endif -# if defined POSH_CPU_X86_64 -# define POSH_CPU_STRING "AMD x86-64" -# else -# define POSH_CPU_STRING "Intel 386+" -# endif -#endif - -#if defined __alpha || defined alpha || defined _M_ALPHA || defined __alpha__ -# define POSH_CPU_AXP 1 -# define POSH_CPU_STRING "AXP" -#endif - -#if defined __hppa || defined hppa -# define POSH_CPU_HPPA 1 -# define POSH_CPU_STRING "PA-RISC" -#endif - -#if !defined POSH_CPU_STRING -# error POSH cannot determine target CPU -# define POSH_CPU_STRING "Unknown" /* this is here for Doxygen's benefit */ -#endif - -/* -** ----------------------------------------------------------------------------- -** Attempt to autodetect building for embedded on Sony PS2 -** ----------------------------------------------------------------------------- -*/ -#if !defined POSH_OS_STRING -# if !defined FORCE_DOXYGEN -# define POSH_OS_EMBEDDED 1 -# endif -# if defined _R5900 -# define POSH_OS_STRING "Sony PS2(embedded)" -# else -# define POSH_OS_STRING "Embedded/Unknown" -# endif -#endif - -/* -** --------------------------------------------------------------------------- -** Handle cdecl, stdcall, fastcall, etc. -** --------------------------------------------------------------------------- -*/ -#if defined POSH_CPU_X86 && !defined POSH_CPU_X86_64 -# if defined __GNUC__ -# define POSH_CDECL __attribute__((cdecl)) -# define POSH_STDCALL __attribute__((stdcall)) -# define POSH_FASTCALL __attribute__((fastcall)) -# elif ( defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ || defined __MWERKS__ ) -# define POSH_CDECL __cdecl -# define POSH_STDCALL __stdcall -# define POSH_FASTCALL __fastcall -# endif -#else -# define POSH_CDECL -# define POSH_STDCALL -# define POSH_FASTCALL -#endif - -/* -** --------------------------------------------------------------------------- -** Define POSH_IMPORTEXPORT signature based on POSH_DLL and POSH_BUILDING_LIB -** --------------------------------------------------------------------------- -*/ - -/* -** We undefine this so that multiple inclusions will work -*/ -#if defined POSH_IMPORTEXPORT -# undef POSH_IMPORTEXPORT -#endif - -#if defined POSH_DLL -# if defined POSH_OS_WIN32 -# if defined _MSC_VER -# if ( _MSC_VER >= 800 ) -# if defined POSH_BUILDING_LIB -# define POSH_IMPORTEXPORT __declspec( dllexport ) -# else -# define POSH_IMPORTEXPORT __declspec( dllimport ) -# endif -# else -# if defined POSH_BUILDING_LIB -# define POSH_IMPORTEXPORT __export -# else -# define POSH_IMPORTEXPORT -# endif -# endif -# endif /* defined _MSC_VER */ -# if defined __BORLANDC__ -# if ( __BORLANDC__ >= 0x500 ) -# if defined POSH_BUILDING_LIB -# define POSH_IMPORTEXPORT __declspec( dllexport ) -# else -# define POSH_IMPORTEXPORT __declspec( dllimport ) -# endif -# else -# if defined POSH_BUILDING_LIB -# define POSH_IMPORTEXPORT __export -# else -# define POSH_IMPORTEXPORT -# endif -# endif -# endif /* defined __BORLANDC__ */ - /* for all other compilers, we're just making a blanket assumption */ -# if defined __GNUC__ || defined __WATCOMC__ || defined __MWERKS__ -# if defined POSH_BUILDING_LIB -# define POSH_IMPORTEXPORT __declspec( dllexport ) -# else -# define POSH_IMPORTEXPORT __declspec( dllimport ) -# endif -# endif /* all other compilers */ -# if !defined POSH_IMPORTEXPORT -# error Building DLLs not supported on this compiler (poshlib@poshlib.org if you know how) -# endif -# endif /* defined POSH_OS_WIN32 */ -#endif - -/* On pretty much everything else, we can thankfully just ignore this */ -#if !defined POSH_IMPORTEXPORT -# define POSH_IMPORTEXPORT -#endif - -#if defined FORCE_DOXYGEN -# define POSH_DLL -# define POSH_BUILDING_LIB -# undef POSH_DLL -# undef POSH_BUILDING_LIB -#endif - -/* -** ---------------------------------------------------------------------------- -** (Re)define POSH_PUBLIC_API export signature -** ---------------------------------------------------------------------------- -*/ -#ifdef POSH_PUBLIC_API -# undef POSH_PUBLIC_API -#endif - -#if ( ( defined _MSC_VER ) && ( _MSC_VER < 800 ) ) || ( defined __BORLANDC__ && ( __BORLANDC__ < 0x500 ) ) -# define POSH_PUBLIC_API(rtype) extern rtype POSH_IMPORTEXPORT -#else -# define POSH_PUBLIC_API(rtype) extern POSH_IMPORTEXPORT rtype -#endif - -/* -** ---------------------------------------------------------------------------- -** Try to infer endianess. Basically we just go through the CPUs we know are -** little endian, and assume anything that isn't one of those is big endian. -** As a sanity check, we also do this with operating systems we know are -** little endian, such as Windows. Some processors are bi-endian, such as -** the MIPS series, so we have to be careful about those. -** ---------------------------------------------------------------------------- -*/ -#if defined POSH_CPU_X86 || defined POSH_CPU_AXP || defined POSH_CPU_STRONGARM || defined POSH_OS_WIN32 || defined POSH_OS_WINCE || defined __MIPSEL__ -# define POSH_ENDIAN_STRING "little" -# define POSH_LITTLE_ENDIAN 1 -#else -# define POSH_ENDIAN_STRING "big" -# define POSH_BIG_ENDIAN 1 -#endif - -#if defined FORCE_DOXYGEN -# define POSH_LITTLE_ENDIAN -#endif - -/* -** ---------------------------------------------------------------------------- -** Cross-platform compile time assertion macro -** ---------------------------------------------------------------------------- -*/ -#define POSH_COMPILE_TIME_ASSERT(name, x) typedef int _POSH_dummy_ ## name[(x) ? 1 : -1 ] - -/* -** ---------------------------------------------------------------------------- -** 64-bit Integer -** -** We don't require 64-bit support, nor do we emulate its functionality, we -** simply export it if it's available. Since we can't count on <limits.h> -** for 64-bit support, we ignore the POSH_USE_LIMITS_H directive. -** ---------------------------------------------------------------------------- -*/ -#if defined ( __LP64__ ) || defined ( __powerpc64__ ) || defined POSH_CPU_SPARC64 -# define POSH_64BIT_INTEGER 1 -typedef long posh_i64_t; -typedef unsigned long posh_u64_t; -# define POSH_I64( x ) ((posh_i64_t)x) -# define POSH_U64( x ) ((posh_u64_t)x) -# define POSH_I64_PRINTF_PREFIX "l" -#elif defined _MSC_VER || defined __BORLANDC__ || defined __WATCOMC__ || ( defined __alpha && defined __DECC ) -# define POSH_64BIT_INTEGER 1 -typedef __int64 posh_i64_t; -typedef unsigned __int64 posh_u64_t; -# define POSH_I64( x ) ((posh_i64_t)x) -# define POSH_U64( x ) ((posh_u64_t)x) -# define POSH_I64_PRINTF_PREFIX "I64" -#elif defined __GNUC__ || defined __MWERKS__ || defined __SUNPRO_C || defined __SUNPRO_CC || defined __APPLE_CC__ || defined POSH_OS_IRIX || defined _LONG_LONG || defined _CRAYC -# define POSH_64BIT_INTEGER 1 -typedef long long posh_i64_t; -typedef unsigned long long posh_u64_t; -# define POSH_U64( x ) ((posh_u64_t)(x##LL)) -# define POSH_I64( x ) ((posh_i64_t)(x##LL)) -# define POSH_I64_PRINTF_PREFIX "ll" -#endif - -/* hack */ -/*#ifdef __MINGW32__ -#undef POSH_I64 -#undef POSH_U64 -#undef POSH_I64_PRINTF_PREFIX -#define POSH_I64( x ) ((posh_i64_t)x) -#define POSH_U64( x ) ((posh_u64_t)x) -#define POSH_I64_PRINTF_PREFIX "I64" -#endif*/ - -#ifdef FORCE_DOXYGEN -typedef long long posh_i64_t; -typedef unsigned long posh_u64_t; -# define POSH_64BIT_INTEGER -# define POSH_I64_PRINTF_PREFIX -# define POSH_I64(x) -# define POSH_U64(x) -#endif - -/** Minimum value for a 64-bit signed integer */ -#define POSH_I64_MIN POSH_I64(0x8000000000000000) -/** Maximum value for a 64-bit signed integer */ -#define POSH_I64_MAX POSH_I64(0x7FFFFFFFFFFFFFFF) -/** Minimum value for a 64-bit unsigned integer */ -#define POSH_U64_MIN POSH_U64(0) -/** Maximum value for a 64-bit unsigned integer */ -#define POSH_U64_MAX POSH_U64(0xFFFFFFFFFFFFFFFF) - -/* ---------------------------------------------------------------------------- -** Basic Sized Types -** -** These types are expected to be EXACTLY sized so you can use them for -** serialization. -** ---------------------------------------------------------------------------- -*/ -#define POSH_FALSE 0 -#define POSH_TRUE 1 - -typedef int posh_bool_t; -typedef unsigned char posh_byte_t; - -/* NOTE: These assume that CHAR_BIT is 8!! */ -typedef unsigned char posh_u8_t; -typedef signed char posh_i8_t; - -#if defined POSH_USE_LIMITS_H -# if CHAR_BITS > 8 -# error This machine uses 9-bit characters. This is a warning, you can comment this out now. -# endif /* CHAR_BITS > 8 */ - -/* 16-bit */ -# if ( USHRT_MAX == 65535 ) - typedef unsigned short posh_u16_t; - typedef short posh_i16_t; -# else - /* Yes, in theory there could still be a 16-bit character type and shorts are - 32-bits in size...if you find such an architecture, let me know =P */ -# error No 16-bit type found -# endif - -/* 32-bit */ -# if ( INT_MAX == 2147483647 ) - typedef unsigned posh_u32_t; - typedef int posh_i32_t; -# elif ( LONG_MAX == 2147483647 ) - typedef unsigned long posh_u32_t; - typedef long posh_i32_t; -# else - error No 32-bit type found -# endif - -#else /* POSH_USE_LIMITS_H */ - - typedef unsigned short posh_u16_t; - typedef short posh_i16_t; - -# if !defined POSH_OS_PALM - typedef unsigned posh_u32_t; - typedef int posh_i32_t; -# else - typedef unsigned long posh_u32_t; - typedef long posh_i32_t; -# endif -#endif - -/** Minimum value for a byte */ -#define POSH_BYTE_MIN 0 -/** Maximum value for an 8-bit unsigned value */ -#define POSH_BYTE_MAX 255 -/** Minimum value for a byte */ -#define POSH_I16_MIN ( ( posh_i16_t ) 0x8000 ) -/** Maximum value for a 16-bit signed value */ -#define POSH_I16_MAX ( ( posh_i16_t ) 0x7FFF ) -/** Minimum value for a 16-bit unsigned value */ -#define POSH_U16_MIN 0 -/** Maximum value for a 16-bit unsigned value */ -#define POSH_U16_MAX ( ( posh_u16_t ) 0xFFFF ) -/** Minimum value for a 32-bit signed value */ -#define POSH_I32_MIN ( ( posh_i32_t ) 0x80000000 ) -/** Maximum value for a 32-bit signed value */ -#define POSH_I32_MAX ( ( posh_i32_t ) 0x7FFFFFFF ) -/** Minimum value for a 32-bit unsigned value */ -#define POSH_U32_MIN 0 -/** Maximum value for a 32-bit unsigned value */ -#define POSH_U32_MAX ( ( posh_u32_t ) 0xFFFFFFFF ) - -/* -** ---------------------------------------------------------------------------- -** Sanity checks on expected sizes -** ---------------------------------------------------------------------------- -*/ -#if !defined FORCE_DOXYGEN - -POSH_COMPILE_TIME_ASSERT(posh_byte_t, sizeof(posh_byte_t) == 1); -POSH_COMPILE_TIME_ASSERT(posh_u8_t, sizeof(posh_u8_t) == 1); -POSH_COMPILE_TIME_ASSERT(posh_i8_t, sizeof(posh_i8_t) == 1); -POSH_COMPILE_TIME_ASSERT(posh_u16_t, sizeof(posh_u16_t) == 2); -POSH_COMPILE_TIME_ASSERT(posh_i16_t, sizeof(posh_i16_t) == 2); -POSH_COMPILE_TIME_ASSERT(posh_u32_t, sizeof(posh_u32_t) == 4); -POSH_COMPILE_TIME_ASSERT(posh_i32_t, sizeof(posh_i32_t) == 4); - -#if !defined POSH_NO_FLOAT - POSH_COMPILE_TIME_ASSERT(posh_testfloat_t, sizeof(float)==4 ); - POSH_COMPILE_TIME_ASSERT(posh_testdouble_t, sizeof(double)==8); -#endif - -#if defined POSH_64BIT_INTEGER - POSH_COMPILE_TIME_ASSERT(posh_u64_t, sizeof(posh_u64_t) == 8); - POSH_COMPILE_TIME_ASSERT(posh_i64_t, sizeof(posh_i64_t) == 8); -#endif - -#endif - -/* -** ---------------------------------------------------------------------------- -** 64-bit pointer support -** ---------------------------------------------------------------------------- -*/ -#if defined POSH_CPU_AXP && ( defined POSH_OS_TRU64 || defined POSH_OS_LINUX ) -# define POSH_64BIT_POINTER 1 -#endif - -#if defined POSH_CPU_X86_64 && defined POSH_OS_LINUX -# define POSH_64BIT_POINTER 1 -#endif - -#if defined POSH_CPU_SPARC64 || defined POSH_OS_WIN64 || defined __64BIT__ || defined __LP64 || defined _LP64 || defined __LP64__ || defined _ADDR64 || defined _CRAYC -# define POSH_64BIT_POINTER 1 -#endif - -#if defined POSH_64BIT_POINTER - POSH_COMPILE_TIME_ASSERT( posh_64bit_pointer, sizeof( void * ) == 8 ); -#elif !defined FORCE_DOXYGEN -/* if this assertion is hit then you're on a system that either has 64-bit - addressing and we didn't catch it, or you're on a system with 16-bit - pointers. In the latter case, POSH doesn't actually care, we're just - triggering this assertion to make sure you're aware of the situation, - so feel free to delete it. - - If this assertion is triggered on a known 32 or 64-bit platform, - please let us know (poshlib@poshlib.org) */ - POSH_COMPILE_TIME_ASSERT( posh_32bit_pointer, sizeof( void * ) == 4 ); -#endif - -#if defined FORCE_DOXYGEN -# define POSH_64BIT_POINTER -#endif - -/* -** ---------------------------------------------------------------------------- -** POSH Utility Functions -** -** These are optional POSH utility functions that are not required if you don't -** need anything except static checking of your host and target environment. -** -** These functions are NOT wrapped with POSH_PUBLIC_API because I didn't want -** to enforce their export if your own library is only using them internally. -** ---------------------------------------------------------------------------- -*/ -#ifdef __cplusplus -extern "C" { -#endif - -const char *POSH_GetArchString( void ); - -#if !defined POSH_NO_FLOAT - -posh_u32_t POSH_LittleFloatBits( float f ); -posh_u32_t POSH_BigFloatBits( float f ); -float POSH_FloatFromLittleBits( posh_u32_t bits ); -float POSH_FloatFromBigBits( posh_u32_t bits ); - -void POSH_DoubleBits( double d, posh_byte_t dst[ 8 ] ); -double POSH_DoubleFromBits( const posh_byte_t src[ 8 ] ); - -/* unimplemented -float *POSH_WriteFloatToLittle( void *dst, float f ); -float *POSH_WriteFloatToBig( void *dst, float f ); -float POSH_ReadFloatFromLittle( const void *src ); -float POSH_ReadFloatFromBig( const void *src ); - -double *POSH_WriteDoubleToLittle( void *dst, double d ); -double *POSH_WriteDoubleToBig( void *dst, double d ); -double POSH_ReadDoubleFromLittle( const void *src ); -double POSH_ReadDoubleFromBig( const void *src ); -*/ -#endif /* !defined POSH_NO_FLOAT */ - -#if defined FORCE_DOXYGEN -# define POSH_NO_FLOAT -# undef POSH_NO_FLOAT -#endif - -extern posh_u16_t POSH_SwapU16( posh_u16_t u ); -extern posh_i16_t POSH_SwapI16( posh_i16_t u ); -extern posh_u32_t POSH_SwapU32( posh_u32_t u ); -extern posh_i32_t POSH_SwapI32( posh_i32_t u ); - -#if defined POSH_64BIT_INTEGER - -extern posh_u64_t POSH_SwapU64( posh_u64_t u ); -extern posh_i64_t POSH_SwapI64( posh_i64_t u ); - -#endif /*POSH_64BIT_INTEGER */ - -extern posh_u16_t *POSH_WriteU16ToLittle( void *dst, posh_u16_t value ); -extern posh_i16_t *POSH_WriteI16ToLittle( void *dst, posh_i16_t value ); -extern posh_u32_t *POSH_WriteU32ToLittle( void *dst, posh_u32_t value ); -extern posh_i32_t *POSH_WriteI32ToLittle( void *dst, posh_i32_t value ); - -extern posh_u16_t *POSH_WriteU16ToBig( void *dst, posh_u16_t value ); -extern posh_i16_t *POSH_WriteI16ToBig( void *dst, posh_i16_t value ); -extern posh_u32_t *POSH_WriteU32ToBig( void *dst, posh_u32_t value ); -extern posh_i32_t *POSH_WriteI32ToBig( void *dst, posh_i32_t value ); - -extern posh_u16_t POSH_ReadU16FromLittle( const void *src ); -extern posh_i16_t POSH_ReadI16FromLittle( const void *src ); -extern posh_u32_t POSH_ReadU32FromLittle( const void *src ); -extern posh_i32_t POSH_ReadI32FromLittle( const void *src ); - -extern posh_u16_t POSH_ReadU16FromBig( const void *src ); -extern posh_i16_t POSH_ReadI16FromBig( const void *src ); -extern posh_u32_t POSH_ReadU32FromBig( const void *src ); -extern posh_i32_t POSH_ReadI32FromBig( const void *src ); - -#if defined POSH_64BIT_INTEGER -extern posh_u64_t *POSH_WriteU64ToLittle( void *dst, posh_u64_t value ); -extern posh_i64_t *POSH_WriteI64ToLittle( void *dst, posh_i64_t value ); -extern posh_u64_t *POSH_WriteU64ToBig( void *dst, posh_u64_t value ); -extern posh_i64_t *POSH_WriteI64ToBig( void *dst, posh_i64_t value ); - -extern posh_u64_t POSH_ReadU64FromLittle( const void *src ); -extern posh_i64_t POSH_ReadI64FromLittle( const void *src ); -extern posh_u64_t POSH_ReadU64FromBig( const void *src ); -extern posh_i64_t POSH_ReadI64FromBig( const void *src ); -#endif /* POSH_64BIT_INTEGER */ - -#if defined POSH_LITTLE_ENDIAN - -# define POSH_LittleU16(x) (x) -# define POSH_LittleU32(x) (x) -# define POSH_LittleI16(x) (x) -# define POSH_LittleI32(x) (x) -# if defined POSH_64BIT_INTEGER -# define POSH_LittleU64(x) (x) -# define POSH_LittleI64(x) (x) -# endif /* defined POSH_64BIT_INTEGER */ - -# define POSH_BigU16(x) POSH_SwapU16(x) -# define POSH_BigU32(x) POSH_SwapU32(x) -# define POSH_BigI16(x) POSH_SwapI16(x) -# define POSH_BigI32(x) POSH_SwapI32(x) -# if defined POSH_64BIT_INTEGER -# define POSH_BigU64(x) POSH_SwapU64(x) -# define POSH_BigI64(x) POSH_SwapI64(x) -# endif /* defined POSH_64BIT_INTEGER */ - -#else - -# define POSH_BigU16(x) (x) -# define POSH_BigU32(x) (x) -# define POSH_BigI16(x) (x) -# define POSH_BigI32(x) (x) - -# if defined POSH_64BIT_INTEGER -# define POSH_BigU64(x) (x) -# define POSH_BigI64(x) (x) -# endif /* POSH_64BIT_INTEGER */ - -# define POSH_LittleU16(x) POSH_SwapU16(x) -# define POSH_LittleU32(x) POSH_SwapU32(x) -# define POSH_LittleI16(x) POSH_SwapI16(x) -# define POSH_LittleI32(x) POSH_SwapI32(x) - -# if defined POSH_64BIT_INTEGER -# define POSH_LittleU64(x) POSH_SwapU64(x) -# define POSH_LittleI64(x) POSH_SwapI64(x) -# endif /* POSH_64BIT_INTEGER */ - -#endif - -#ifdef __cplusplus -} -#endif - - diff --git a/thirdparty/thekla_atlas/thekla/thekla_atlas.cpp b/thirdparty/thekla_atlas/thekla/thekla_atlas.cpp deleted file mode 100644 index de1953db8a..0000000000 --- a/thirdparty/thekla_atlas/thekla/thekla_atlas.cpp +++ /dev/null @@ -1,288 +0,0 @@ - -#include "thekla_atlas.h" - -#include <cfloat> -// -- GODOT start -- -#include <stdio.h> -// -- GODOT end -- - -#include "nvmesh/halfedge/Edge.h" -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/halfedge/Vertex.h" -#include "nvmesh/param/Atlas.h" - -#include "nvmath/Vector.inl" -#include "nvmath/ftoi.h" - -#include "nvcore/Array.inl" - - -using namespace Thekla; -using namespace nv; - - -inline Atlas_Output_Mesh * set_error(Atlas_Error * error, Atlas_Error code) { - if (error) *error = code; - return NULL; -} - - - -static void input_to_mesh(const Atlas_Input_Mesh * input, HalfEdge::Mesh * mesh, Atlas_Error * error) { - - Array<uint> canonicalMap; - canonicalMap.reserve(input->vertex_count); - - for (int i = 0; i < input->vertex_count; i++) { - const Atlas_Input_Vertex & input_vertex = input->vertex_array[i]; - const float * pos = input_vertex.position; - const float * nor = input_vertex.normal; - const float * tex = input_vertex.uv; - - HalfEdge::Vertex * vertex = mesh->addVertex(Vector3(pos[0], pos[1], pos[2])); - vertex->nor.set(nor[0], nor[1], nor[2]); - vertex->tex.set(tex[0], tex[1]); - - canonicalMap.append(input_vertex.first_colocal); - } - - mesh->linkColocalsWithCanonicalMap(canonicalMap); - - - const int face_count = input->face_count; - - int non_manifold_faces = 0; - for (int i = 0; i < face_count; i++) { - const Atlas_Input_Face & input_face = input->face_array[i]; - - int v0 = input_face.vertex_index[0]; - int v1 = input_face.vertex_index[1]; - int v2 = input_face.vertex_index[2]; - - HalfEdge::Face * face = mesh->addFace(v0, v1, v2); - if (face != NULL) { - face->material = input_face.material_index; - } - else { - non_manifold_faces++; - } - } - - mesh->linkBoundary(); - - if (non_manifold_faces != 0 && error != NULL) { - *error = Atlas_Error_Invalid_Mesh_Non_Manifold; - } -} - -static Atlas_Output_Mesh * mesh_atlas_to_output(const HalfEdge::Mesh * mesh, const Atlas & atlas, Atlas_Error * error) { - - Atlas_Output_Mesh * output = new Atlas_Output_Mesh; - - const MeshCharts * charts = atlas.meshAt(0); - - // Allocate vertices. - const int vertex_count = charts->vertexCount(); - output->vertex_count = vertex_count; - output->vertex_array = new Atlas_Output_Vertex[vertex_count]; - - int w = 0; - int h = 0; - - // Output vertices. - const int chart_count = charts->chartCount(); - for (int i = 0; i < chart_count; i++) { - const Chart * chart = charts->chartAt(i); - uint vertexOffset = charts->vertexCountBeforeChartAt(i); - - const uint chart_vertex_count = chart->vertexCount(); - for (uint v = 0; v < chart_vertex_count; v++) { - Atlas_Output_Vertex & output_vertex = output->vertex_array[vertexOffset + v]; - - uint original_vertex = chart->mapChartVertexToOriginalVertex(v); - output_vertex.xref = original_vertex; - - Vector2 uv = chart->chartMesh()->vertexAt(v)->tex; - output_vertex.uv[0] = uv.x; - output_vertex.uv[1] = uv.y; - w = max(w, ftoi_ceil(uv.x)); - h = max(h, ftoi_ceil(uv.y)); - } - } - - const int face_count = mesh->faceCount(); - output->index_count = face_count * 3; - output->index_array = new int[face_count * 3]; - - // -- GODOT start -- - int face_ofs = 0; - // Set face indices. - for (int f = 0; f < face_count; f++) { - uint c = charts->faceChartAt(f); - uint i = charts->faceIndexWithinChartAt(f); - uint vertexOffset = charts->vertexCountBeforeChartAt(c); - - const Chart * chart = charts->chartAt(c); - nvDebugCheck(chart->faceAt(i) == f); - - if (i >= chart->chartMesh()->faceCount()) { - printf("WARNING: Faces may be missing in the final vertex, which could not be packed\n"); - continue; - } - - const HalfEdge::Face * face = chart->chartMesh()->faceAt(i); - const HalfEdge::Edge * edge = face->edge; - - //output->index_array[3*f+0] = vertexOffset + edge->vertex->id; - //output->index_array[3*f+1] = vertexOffset + edge->next->vertex->id; - //output->index_array[3*f+2] = vertexOffset + edge->next->next->vertex->id; - output->index_array[3 * face_ofs + 0] = vertexOffset + edge->vertex->id; - output->index_array[3 * face_ofs + 1] = vertexOffset + edge->next->vertex->id; - output->index_array[3 * face_ofs + 2] = vertexOffset + edge->next->next->vertex->id; - face_ofs++; - } - - output->index_count = face_ofs * 3; - // -- GODOT end -- - - *error = Atlas_Error_Success; - output->atlas_width = w; - output->atlas_height = h; - - return output; -} - - -void Thekla::atlas_set_default_options(Atlas_Options * options) { - if (options != NULL) { - // These are the default values we use on The Witness. - - options->charter = Atlas_Charter_Default; - options->charter_options.witness.proxy_fit_metric_weight = 2.0f; - options->charter_options.witness.roundness_metric_weight = 0.01f; - options->charter_options.witness.straightness_metric_weight = 6.0f; - options->charter_options.witness.normal_seam_metric_weight = 4.0f; - options->charter_options.witness.texture_seam_metric_weight = 0.5f; - options->charter_options.witness.max_chart_area = FLT_MAX; - options->charter_options.witness.max_boundary_length = FLT_MAX; - - options->mapper = Atlas_Mapper_Default; - - options->packer = Atlas_Packer_Default; - options->packer_options.witness.packing_quality = 0; - options->packer_options.witness.texel_area = 8; - options->packer_options.witness.block_align = true; - options->packer_options.witness.conservative = false; - } -} - - -Atlas_Output_Mesh * Thekla::atlas_generate(const Atlas_Input_Mesh * input, const Atlas_Options * options, Atlas_Error * error) { - // Validate args. - if (input == NULL || options == NULL || error == NULL) return set_error(error, Atlas_Error_Invalid_Args); - - // Validate options. - if (options->charter != Atlas_Charter_Witness) { - return set_error(error, Atlas_Error_Invalid_Options); - } - if (options->charter == Atlas_Charter_Witness) { - // @@ Validate input options! - } - - if (options->mapper != Atlas_Mapper_LSCM) { - return set_error(error, Atlas_Error_Invalid_Options); - } - if (options->mapper == Atlas_Mapper_LSCM) { - // No options. - } - - if (options->packer != Atlas_Packer_Witness) { - return set_error(error, Atlas_Error_Invalid_Options); - } - if (options->packer == Atlas_Packer_Witness) { - // @@ Validate input options! - } - - // Validate input mesh. - for (int i = 0; i < input->face_count; i++) { - int v0 = input->face_array[i].vertex_index[0]; - int v1 = input->face_array[i].vertex_index[1]; - int v2 = input->face_array[i].vertex_index[2]; - - if (v0 < 0 || v0 >= input->vertex_count || - v1 < 0 || v1 >= input->vertex_count || - v2 < 0 || v2 >= input->vertex_count) - { - return set_error(error, Atlas_Error_Invalid_Mesh); - } - } - - - // Build half edge mesh. - AutoPtr<HalfEdge::Mesh> mesh(new HalfEdge::Mesh); - - input_to_mesh(input, mesh.ptr(), error); - - if (*error == Atlas_Error_Invalid_Mesh) { - return NULL; - } - - Atlas atlas; - - // Charter. - if (options->charter == Atlas_Charter_Extract) { - return set_error(error, Atlas_Error_Not_Implemented); - } - else if (options->charter == Atlas_Charter_Witness) { - SegmentationSettings segmentation_settings; - segmentation_settings.proxyFitMetricWeight = options->charter_options.witness.proxy_fit_metric_weight; - segmentation_settings.roundnessMetricWeight = options->charter_options.witness.roundness_metric_weight; - segmentation_settings.straightnessMetricWeight = options->charter_options.witness.straightness_metric_weight; - segmentation_settings.normalSeamMetricWeight = options->charter_options.witness.normal_seam_metric_weight; - segmentation_settings.textureSeamMetricWeight = options->charter_options.witness.texture_seam_metric_weight; - segmentation_settings.maxChartArea = options->charter_options.witness.max_chart_area; - segmentation_settings.maxBoundaryLength = options->charter_options.witness.max_boundary_length; - - Array<uint> uncharted_materials; - atlas.computeCharts(mesh.ptr(), segmentation_settings, uncharted_materials); - } - - if (atlas.hasFailed()) - return NULL; - - // Mapper. - if (options->mapper == Atlas_Mapper_LSCM) { - atlas.parameterizeCharts(); - } - - if (atlas.hasFailed()) - return NULL; - - // Packer. - if (options->packer == Atlas_Packer_Witness) { - int packing_quality = options->packer_options.witness.packing_quality; - float texel_area = options->packer_options.witness.texel_area; - int block_align = options->packer_options.witness.block_align; - int conservative = options->packer_options.witness.conservative; - - /*float utilization =*/ atlas.packCharts(packing_quality, texel_area, block_align, conservative); - } - - if (atlas.hasFailed()) - return NULL; - - - // Build output mesh. - return mesh_atlas_to_output(mesh.ptr(), atlas, error); -} - - -void Thekla::atlas_free(Atlas_Output_Mesh * output) { - if (output != NULL) { - delete [] output->vertex_array; - delete [] output->index_array; - delete output; - } -} - diff --git a/thirdparty/thekla_atlas/thekla/thekla_atlas.h b/thirdparty/thekla_atlas/thekla/thekla_atlas.h deleted file mode 100644 index 1d0716e781..0000000000 --- a/thirdparty/thekla_atlas/thekla/thekla_atlas.h +++ /dev/null @@ -1,116 +0,0 @@ - -// Thekla Atlas Generator - -namespace Thekla { - -enum Atlas_Charter { - Atlas_Charter_Witness, // Options: threshold - Atlas_Charter_Extract, // Options: --- - Atlas_Charter_Default = Atlas_Charter_Witness -}; - -enum Atlas_Mapper { - Atlas_Mapper_LSCM, // Options: --- - Atlas_Mapper_Default = Atlas_Mapper_LSCM -}; - -enum Atlas_Packer { - Atlas_Packer_Witness, // Options: texel_area - Atlas_Packer_Default = Atlas_Packer_Witness -}; - -struct Atlas_Options { - Atlas_Charter charter; - union { - struct { - float proxy_fit_metric_weight; - float roundness_metric_weight; - float straightness_metric_weight; - float normal_seam_metric_weight; - float texture_seam_metric_weight; - float max_chart_area; - float max_boundary_length; - } witness; - struct { - } extract; - } charter_options; - - Atlas_Mapper mapper; - union { - } mapper_options; - - Atlas_Packer packer; - union { - struct { - int packing_quality; - float texel_area; // This is not really texel area, but 1 / texel width? - bool block_align; // Align charts to 4x4 blocks. - bool conservative; // Pack charts with extra padding. - } witness; - } packer_options; -}; - -struct Atlas_Input_Vertex { - float position[3]; - float normal[3]; - float uv[2]; - int first_colocal; -}; - -struct Atlas_Input_Face { - int vertex_index[3]; - int material_index; -}; - -struct Atlas_Input_Mesh { - int vertex_count; - int face_count; - Atlas_Input_Vertex * vertex_array; - Atlas_Input_Face * face_array; -}; - -struct Atlas_Output_Vertex { - float uv[2]; - int xref; // Index of input vertex from which this output vertex originated. -}; - -struct Atlas_Output_Mesh { - int atlas_width; - int atlas_height; - int vertex_count; - int index_count; - Atlas_Output_Vertex * vertex_array; - int * index_array; -}; - -enum Atlas_Error { - Atlas_Error_Success, - Atlas_Error_Invalid_Args, - Atlas_Error_Invalid_Options, - Atlas_Error_Invalid_Mesh, - Atlas_Error_Invalid_Mesh_Non_Manifold, - Atlas_Error_Not_Implemented, -}; - -void atlas_set_default_options(Atlas_Options * options); - -Atlas_Output_Mesh * atlas_generate(const Atlas_Input_Mesh * input, const Atlas_Options * options, Atlas_Error * error); - -void atlas_free(Atlas_Output_Mesh * output); - - -/* - -Should we represent the input mesh with an opaque structure that simply holds pointers to the user data? That would allow us to avoid having to copy attributes to an intermediate representation. - -struct Atlas_Input_Mesh; - -void mesh_set_vertex_position(Atlas_Input_Mesh * mesh, float * ptr, int stride); -void mesh_set_vertex_normal(Atlas_Input_Mesh * mesh, float * ptr, int stride); -void mesh_set_vertex_uv(Mesh * mesh, float * ptr, int stride); - -void mesh_set_index(Mesh * mesh, int * ptr); -*/ - -} // Thekla namespace - diff --git a/thirdparty/vhacd/0001-bullet-namespace.patch b/thirdparty/vhacd/0001-bullet-namespace.patch new file mode 100644 index 0000000000..cfb1ffeff0 --- /dev/null +++ b/thirdparty/vhacd/0001-bullet-namespace.patch @@ -0,0 +1,213 @@ +diff --git a/thirdparty/vhacd/inc/btAlignedAllocator.h b/thirdparty/vhacd/inc/btAlignedAllocator.h +index 11f6e12dc..94e71d512 100644 +--- a/thirdparty/vhacd/inc/btAlignedAllocator.h ++++ b/thirdparty/vhacd/inc/btAlignedAllocator.h +@@ -21,6 +21,11 @@ subject to the following restrictions: + ///that is better portable and more predictable + + #include "btScalar.h" ++ ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + //#define BT_DEBUG_MEMORY_ALLOCATIONS 1 + #ifdef BT_DEBUG_MEMORY_ALLOCATIONS + +@@ -101,4 +106,8 @@ public: + friend bool operator==(const self_type&, const self_type&) { return true; } + }; + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_ALIGNED_ALLOCATOR +diff --git a/thirdparty/vhacd/inc/btAlignedObjectArray.h b/thirdparty/vhacd/inc/btAlignedObjectArray.h +index e6620adf6..1ce03d21b 100644 +--- a/thirdparty/vhacd/inc/btAlignedObjectArray.h ++++ b/thirdparty/vhacd/inc/btAlignedObjectArray.h +@@ -38,6 +38,10 @@ subject to the following restrictions: + #include <new> //for placement new + #endif //BT_USE_PLACEMENT_NEW + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + ///The btAlignedObjectArray template class uses a subset of the stl::vector interface for its methods + ///It is developed to replace stl::vector to avoid portability issues, including STL alignment issues to add SIMD/SSE data + template <typename T> +@@ -445,4 +449,8 @@ public: + } + }; + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_OBJECT_ARRAY__ +diff --git a/thirdparty/vhacd/inc/btConvexHullComputer.h b/thirdparty/vhacd/inc/btConvexHullComputer.h +index 3c5075c2c..04bb96f64 100644 +--- a/thirdparty/vhacd/inc/btConvexHullComputer.h ++++ b/thirdparty/vhacd/inc/btConvexHullComputer.h +@@ -18,6 +18,10 @@ subject to the following restrictions: + #include "btAlignedObjectArray.h" + #include "btVector3.h" + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + /// Convex hull implementation based on Preparata and Hong + /// See http://code.google.com/p/bullet/issues/detail?id=275 + /// Ole Kniemeyer, MAXON Computer GmbH +@@ -94,4 +98,8 @@ public: + } + }; + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_CONVEX_HULL_COMPUTER_H +diff --git a/thirdparty/vhacd/inc/btMinMax.h b/thirdparty/vhacd/inc/btMinMax.h +index 40b0ceb6e..9bc1e1c77 100644 +--- a/thirdparty/vhacd/inc/btMinMax.h ++++ b/thirdparty/vhacd/inc/btMinMax.h +@@ -17,6 +17,10 @@ subject to the following restrictions: + + #include "btScalar.h" + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + template <class T> + SIMD_FORCE_INLINE const T& btMin(const T& a, const T& b) + { +@@ -62,4 +66,8 @@ SIMD_FORCE_INLINE void btClamp(T& a, const T& lb, const T& ub) + } + } + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_GEN_MINMAX_H +diff --git a/thirdparty/vhacd/inc/btScalar.h b/thirdparty/vhacd/inc/btScalar.h +index b814474bd..617fd7c44 100644 +--- a/thirdparty/vhacd/inc/btScalar.h ++++ b/thirdparty/vhacd/inc/btScalar.h +@@ -28,11 +28,19 @@ subject to the following restrictions: + /* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/ + #define BT_BULLET_VERSION 279 + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + inline int32_t btGetVersion() + { + return BT_BULLET_VERSION; + } + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #if defined(DEBUG) || defined(_DEBUG) + #define BT_DEBUG + #endif +@@ -199,6 +207,10 @@ inline int32_t btGetVersion() + #endif //__CELLOS_LV2__ + #endif + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + ///The btScalar type abstracts floating point numbers, to easily switch between double and single floating point precision. + #if defined(BT_USE_DOUBLE_PRECISION) + typedef double btScalar; +@@ -530,4 +542,9 @@ struct btTypedObject { + return m_objectType; + } + }; ++ ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_SCALAR_H +diff --git a/thirdparty/vhacd/inc/btVector3.h b/thirdparty/vhacd/inc/btVector3.h +index 0f2fefbbd..4ed971673 100644 +--- a/thirdparty/vhacd/inc/btVector3.h ++++ b/thirdparty/vhacd/inc/btVector3.h +@@ -26,6 +26,10 @@ subject to the following restrictions: + #define btVector3DataName "btVector3FloatData" + #endif //BT_USE_DOUBLE_PRECISION + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + /**@brief btVector3 can be used to represent 3D points and vectors. + * It has an un-used w component to suit 16-byte alignment when btVector3 is stored in containers. This extra component can be used by derived classes (Quaternion?) or by user + * Ideally, this class should be replaced by a platform optimized SIMD version that keeps the data in registers +@@ -712,4 +716,8 @@ SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3Data& dataIn + m_floats[i] = dataIn.m_floats[i]; + } + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_VECTOR3_H +diff --git a/thirdparty/vhacd/src/btAlignedAllocator.cpp b/thirdparty/vhacd/src/btAlignedAllocator.cpp +index 11d594f6c..ce0e7f26f 100644 +--- a/thirdparty/vhacd/src/btAlignedAllocator.cpp ++++ b/thirdparty/vhacd/src/btAlignedAllocator.cpp +@@ -15,6 +15,10 @@ subject to the following restrictions: + + #include "btAlignedAllocator.h" + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + #ifdef _MSC_VER + #pragma warning(disable:4311 4302) + #endif +@@ -177,4 +181,8 @@ void btAlignedFreeInternal(void* ptr) + sAlignedFreeFunc(ptr); + } + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_DEBUG_MEMORY_ALLOCATIONS +diff --git a/thirdparty/vhacd/src/btConvexHullComputer.cpp b/thirdparty/vhacd/src/btConvexHullComputer.cpp +index d3d749adb..8ab34af2a 100644 +--- a/thirdparty/vhacd/src/btConvexHullComputer.cpp ++++ b/thirdparty/vhacd/src/btConvexHullComputer.cpp +@@ -49,6 +49,10 @@ typedef unsigned long long int32_t uint64_t; + #include <stdio.h> + #endif + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + // Convex hull implementation based on Preparata and Hong + // Ole Kniemeyer, MAXON Computer GmbH + class btConvexHullInternal { +@@ -2477,3 +2481,7 @@ btScalar btConvexHullComputer::compute(const void* coords, bool doubleCoords, in + + return shift; + } ++ ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- diff --git a/thirdparty/vhacd/0002-fpermissive-fix.patch b/thirdparty/vhacd/0002-fpermissive-fix.patch new file mode 100644 index 0000000000..965df2944a --- /dev/null +++ b/thirdparty/vhacd/0002-fpermissive-fix.patch @@ -0,0 +1,53 @@ +diff --git a/thirdparty/vhacd/inc/btScalar.h b/thirdparty/vhacd/inc/btScalar.h +index 487205062..52297cd78 100644 +--- a/thirdparty/vhacd/inc/btScalar.h ++++ b/thirdparty/vhacd/inc/btScalar.h +@@ -535,6 +535,29 @@ struct btTypedObject { + } + }; + ++// -- GODOT start -- ++// Cherry-picked from Bullet 2.88 to fix GH-27926 ++///align a pointer to the provided alignment, upwards ++template <typename T> ++T *btAlignPointer(T *unalignedPtr, size_t alignment) ++{ ++ struct btConvertPointerSizeT ++ { ++ union { ++ T *ptr; ++ size_t integer; ++ }; ++ }; ++ btConvertPointerSizeT converter; ++ ++ const size_t bit_mask = ~(alignment - 1); ++ converter.ptr = unalignedPtr; ++ converter.integer += alignment - 1; ++ converter.integer &= bit_mask; ++ return converter.ptr; ++} ++// -- GODOT end -- ++ + // -- GODOT start -- + }; // namespace VHACD + // -- GODOT end -- +diff --git a/thirdparty/vhacd/src/btAlignedAllocator.cpp b/thirdparty/vhacd/src/btAlignedAllocator.cpp +index ce0e7f26f..8dee31e7e 100644 +--- a/thirdparty/vhacd/src/btAlignedAllocator.cpp ++++ b/thirdparty/vhacd/src/btAlignedAllocator.cpp +@@ -72,8 +72,12 @@ static inline void* btAlignedAllocDefault(size_t size, int32_t alignment) + + real = (char*)sAllocFunc(size + sizeof(void*) + (alignment - 1)); + if (real) { +- offset = (alignment - (unsigned long)(real + sizeof(void*))) & (alignment - 1); +- ret = (void*)((real + sizeof(void*)) + offset); ++ // -- GODOT start -- ++ // Synced with Bullet 2.88 to fix GH-27926 ++ //offset = (alignment - (unsigned long)(real + sizeof(void*))) & (alignment - 1); ++ //ret = (void*)((real + sizeof(void*)) + offset); ++ ret = btAlignPointer(real + sizeof(void *), alignment); ++ // -- GODOT end -- + *((void**)(ret)-1) = (void*)(real); + } + else { diff --git a/thirdparty/vhacd/LICENSE b/thirdparty/vhacd/LICENSE new file mode 100644 index 0000000000..efc7f64399 --- /dev/null +++ b/thirdparty/vhacd/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2011, Khaled Mamou (kmamou at gmail dot com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/thirdparty/vhacd/inc/FloatMath.h b/thirdparty/vhacd/inc/FloatMath.h new file mode 100644 index 0000000000..37b07cb69f --- /dev/null +++ b/thirdparty/vhacd/inc/FloatMath.h @@ -0,0 +1,525 @@ +#ifndef FLOAT_MATH_LIB_H + +#define FLOAT_MATH_LIB_H + + +#include <float.h> +#include <stdint.h> + +namespace FLOAT_MATH +{ + +enum FM_ClipState +{ + FMCS_XMIN = (1<<0), + FMCS_XMAX = (1<<1), + FMCS_YMIN = (1<<2), + FMCS_YMAX = (1<<3), + FMCS_ZMIN = (1<<4), + FMCS_ZMAX = (1<<5), +}; + +enum FM_Axis +{ + FM_XAXIS = (1<<0), + FM_YAXIS = (1<<1), + FM_ZAXIS = (1<<2) +}; + +enum LineSegmentType +{ + LS_START, + LS_MIDDLE, + LS_END +}; + + +const float FM_PI = 3.1415926535897932384626433832795028841971693993751f; +const float FM_DEG_TO_RAD = ((2.0f * FM_PI) / 360.0f); +const float FM_RAD_TO_DEG = (360.0f / (2.0f * FM_PI)); + +//***************** Float versions +//*** +//*** vectors are assumed to be 3 floats or 3 doubles representing X, Y, Z +//*** quaternions are assumed to be 4 floats or 4 doubles representing X,Y,Z,W +//*** matrices are assumed to be 16 floats or 16 doubles representing a standard D3D or OpenGL style 4x4 matrix +//*** bounding volumes are expressed as two sets of 3 floats/double representing bmin(x,y,z) and bmax(x,y,z) +//*** Plane equations are assumed to be 4 floats or 4 doubles representing Ax,By,Cz,D + +FM_Axis fm_getDominantAxis(const float normal[3]); +FM_Axis fm_getDominantAxis(const double normal[3]); + +void fm_decomposeTransform(const float local_transform[16],float trans[3],float rot[4],float scale[3]); +void fm_decomposeTransform(const double local_transform[16],double trans[3],double rot[4],double scale[3]); + +void fm_multiplyTransform(const float *pA,const float *pB,float *pM); +void fm_multiplyTransform(const double *pA,const double *pB,double *pM); + +void fm_inverseTransform(const float matrix[16],float inverse_matrix[16]); +void fm_inverseTransform(const double matrix[16],double inverse_matrix[16]); + +void fm_identity(float matrix[16]); // set 4x4 matrix to identity. +void fm_identity(double matrix[16]); // set 4x4 matrix to identity. + +void fm_inverseRT(const float matrix[16], const float pos[3], float t[3]); // inverse rotate translate the point. +void fm_inverseRT(const double matrix[16],const double pos[3],double t[3]); // inverse rotate translate the point. + +void fm_transform(const float matrix[16], const float pos[3], float t[3]); // rotate and translate this point. +void fm_transform(const double matrix[16],const double pos[3],double t[3]); // rotate and translate this point. + +float fm_getDeterminant(const float matrix[16]); +double fm_getDeterminant(const double matrix[16]); + +void fm_getSubMatrix(int32_t ki,int32_t kj,float pDst[16],const float matrix[16]); +void fm_getSubMatrix(int32_t ki,int32_t kj,double pDst[16],const float matrix[16]); + +void fm_rotate(const float matrix[16],const float pos[3],float t[3]); // only rotate the point by a 4x4 matrix, don't translate. +void fm_rotate(const double matri[16],const double pos[3],double t[3]); // only rotate the point by a 4x4 matrix, don't translate. + +void fm_eulerToMatrix(float ax,float ay,float az,float matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) +void fm_eulerToMatrix(double ax,double ay,double az,double matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) + +void fm_getAABB(uint32_t vcount,const float *points,uint32_t pstride,float bmin[3],float bmax[3]); +void fm_getAABB(uint32_t vcount,const double *points,uint32_t pstride,double bmin[3],double bmax[3]); + +void fm_getAABBCenter(const float bmin[3],const float bmax[3],float center[3]); +void fm_getAABBCenter(const double bmin[3],const double bmax[3],double center[3]); + +void fm_transformAABB(const float bmin[3],const float bmax[3],const float matrix[16],float tbmin[3],float tbmax[3]); +void fm_transformAABB(const double bmin[3],const double bmax[3],const double matrix[16],double tbmin[3],double tbmax[3]); + +void fm_eulerToQuat(float x,float y,float z,float quat[4]); // convert euler angles to quaternion. +void fm_eulerToQuat(double x,double y,double z,double quat[4]); // convert euler angles to quaternion. + +void fm_quatToEuler(const float quat[4],float &ax,float &ay,float &az); +void fm_quatToEuler(const double quat[4],double &ax,double &ay,double &az); + +void fm_eulerToQuat(const float euler[3],float quat[4]); // convert euler angles to quaternion. Angles must be radians not degrees! +void fm_eulerToQuat(const double euler[3],double quat[4]); // convert euler angles to quaternion. + +void fm_scale(float x,float y,float z,float matrix[16]); // apply scale to the matrix. +void fm_scale(double x,double y,double z,double matrix[16]); // apply scale to the matrix. + +void fm_eulerToQuatDX(float x,float y,float z,float quat[4]); // convert euler angles to quaternion using the fucked up DirectX method +void fm_eulerToQuatDX(double x,double y,double z,double quat[4]); // convert euler angles to quaternion using the fucked up DirectX method + +void fm_eulerToMatrixDX(float x,float y,float z,float matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method. +void fm_eulerToMatrixDX(double x,double y,double z,double matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method. + +void fm_quatToMatrix(const float quat[4],float matrix[16]); // convert quaterinion rotation to matrix, translation set to zero. +void fm_quatToMatrix(const double quat[4],double matrix[16]); // convert quaterinion rotation to matrix, translation set to zero. + +void fm_quatRotate(const float quat[4],const float v[3],float r[3]); // rotate a vector directly by a quaternion. +void fm_quatRotate(const double quat[4],const double v[3],double r[3]); // rotate a vector directly by a quaternion. + +void fm_getTranslation(const float matrix[16],float t[3]); +void fm_getTranslation(const double matrix[16],double t[3]); + +void fm_setTranslation(const float *translation,float matrix[16]); +void fm_setTranslation(const double *translation,double matrix[16]); + +void fm_multiplyQuat(const float *qa,const float *qb,float *quat); +void fm_multiplyQuat(const double *qa,const double *qb,double *quat); + +void fm_matrixToQuat(const float matrix[16],float quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w +void fm_matrixToQuat(const double matrix[16],double quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w + +float fm_sphereVolume(float radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed ) +double fm_sphereVolume(double radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed ) + +float fm_cylinderVolume(float radius,float h); +double fm_cylinderVolume(double radius,double h); + +float fm_capsuleVolume(float radius,float h); +double fm_capsuleVolume(double radius,double h); + +float fm_distance(const float p1[3],const float p2[3]); +double fm_distance(const double p1[3],const double p2[3]); + +float fm_distanceSquared(const float p1[3],const float p2[3]); +double fm_distanceSquared(const double p1[3],const double p2[3]); + +float fm_distanceSquaredXZ(const float p1[3],const float p2[3]); +double fm_distanceSquaredXZ(const double p1[3],const double p2[3]); + +float fm_computePlane(const float p1[3],const float p2[3],const float p3[3],float *n); // return D +double fm_computePlane(const double p1[3],const double p2[3],const double p3[3],double *n); // return D + +float fm_distToPlane(const float plane[4],const float pos[3]); // computes the distance of this point from the plane. +double fm_distToPlane(const double plane[4],const double pos[3]); // computes the distance of this point from the plane. + +float fm_dot(const float p1[3],const float p2[3]); +double fm_dot(const double p1[3],const double p2[3]); + +void fm_cross(float cross[3],const float a[3],const float b[3]); +void fm_cross(double cross[3],const double a[3],const double b[3]); + +float fm_computeNormalVector(float n[3],const float p1[3],const float p2[3]); // as P2-P1 normalized. +double fm_computeNormalVector(double n[3],const double p1[3],const double p2[3]); // as P2-P1 normalized. + +bool fm_computeWindingOrder(const float p1[3],const float p2[3],const float p3[3]); // returns true if the triangle is clockwise. +bool fm_computeWindingOrder(const double p1[3],const double p2[3],const double p3[3]); // returns true if the triangle is clockwise. + +float fm_normalize(float n[3]); // normalize this vector and return the distance +double fm_normalize(double n[3]); // normalize this vector and return the distance + +float fm_normalizeQuat(float n[4]); // normalize this quat +double fm_normalizeQuat(double n[4]); // normalize this quat + +void fm_matrixMultiply(const float A[16],const float B[16],float dest[16]); +void fm_matrixMultiply(const double A[16],const double B[16],double dest[16]); + +void fm_composeTransform(const float position[3],const float quat[4],const float scale[3],float matrix[16]); +void fm_composeTransform(const double position[3],const double quat[4],const double scale[3],double matrix[16]); + +float fm_computeArea(const float p1[3],const float p2[3],const float p3[3]); +double fm_computeArea(const double p1[3],const double p2[3],const double p3[3]); + +void fm_lerp(const float p1[3],const float p2[3],float dest[3],float lerpValue); +void fm_lerp(const double p1[3],const double p2[3],double dest[3],double lerpValue); + +bool fm_insideTriangleXZ(const float test[3],const float p1[3],const float p2[3],const float p3[3]); +bool fm_insideTriangleXZ(const double test[3],const double p1[3],const double p2[3],const double p3[3]); + +bool fm_insideAABB(const float pos[3],const float bmin[3],const float bmax[3]); +bool fm_insideAABB(const double pos[3],const double bmin[3],const double bmax[3]); + +bool fm_insideAABB(const float obmin[3],const float obmax[3],const float tbmin[3],const float tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax +bool fm_insideAABB(const double obmin[3],const double obmax[3],const double tbmin[3],const double tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax + +uint32_t fm_clipTestPoint(const float bmin[3],const float bmax[3],const float pos[3]); +uint32_t fm_clipTestPoint(const double bmin[3],const double bmax[3],const double pos[3]); + +uint32_t fm_clipTestPointXZ(const float bmin[3],const float bmax[3],const float pos[3]); // only tests X and Z, not Y +uint32_t fm_clipTestPointXZ(const double bmin[3],const double bmax[3],const double pos[3]); // only tests X and Z, not Y + + +uint32_t fm_clipTestAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],const float p3[3],uint32_t &andCode); +uint32_t fm_clipTestAABB(const double bmin[3],const double bmax[3],const double p1[3],const double p2[3],const double p3[3],uint32_t &andCode); + + +bool fm_lineTestAABBXZ(const float p1[3],const float p2[3],const float bmin[3],const float bmax[3],float &time); +bool fm_lineTestAABBXZ(const double p1[3],const double p2[3],const double bmin[3],const double bmax[3],double &time); + +bool fm_lineTestAABB(const float p1[3],const float p2[3],const float bmin[3],const float bmax[3],float &time); +bool fm_lineTestAABB(const double p1[3],const double p2[3],const double bmin[3],const double bmax[3],double &time); + + +void fm_initMinMax(const float p[3],float bmin[3],float bmax[3]); +void fm_initMinMax(const double p[3],double bmin[3],double bmax[3]); + +void fm_initMinMax(float bmin[3],float bmax[3]); +void fm_initMinMax(double bmin[3],double bmax[3]); + +void fm_minmax(const float p[3],float bmin[3],float bmax[3]); // accumulate to a min-max value +void fm_minmax(const double p[3],double bmin[3],double bmax[3]); // accumulate to a min-max value + +// Computes the diagonal length of the bounding box and then inflates the bounding box on all sides +// by the ratio provided. +void fm_inflateMinMax(float bmin[3], float bmax[3], float ratio); +void fm_inflateMinMax(double bmin[3], double bmax[3], double ratio); + +float fm_solveX(const float plane[4],float y,float z); // solve for X given this plane equation and the other two components. +double fm_solveX(const double plane[4],double y,double z); // solve for X given this plane equation and the other two components. + +float fm_solveY(const float plane[4],float x,float z); // solve for Y given this plane equation and the other two components. +double fm_solveY(const double plane[4],double x,double z); // solve for Y given this plane equation and the other two components. + +float fm_solveZ(const float plane[4],float x,float y); // solve for Z given this plane equation and the other two components. +double fm_solveZ(const double plane[4],double x,double y); // solve for Z given this plane equation and the other two components. + +bool fm_computeBestFitPlane(uint32_t vcount, // number of input data points + const float *points, // starting address of points array. + uint32_t vstride, // stride between input points. + const float *weights, // *optional point weighting values. + uint32_t wstride, // weight stride for each vertex. + float plane[4], // Best fit plane equation + float center[3]); // Best fit weighted center of input points + +bool fm_computeBestFitPlane(uint32_t vcount, // number of input data points + const double *points, // starting address of points array. + uint32_t vstride, // stride between input points. + const double *weights, // *optional point weighting values. + uint32_t wstride, // weight stride for each vertex. + double plane[4], + double center[3]); + +// Computes the average center of a set of data points +bool fm_computeCentroid(uint32_t vcount, // number of input data points + const float *points, // starting address of points array. + float *center); + +bool fm_computeCentroid(uint32_t vcount, // number of input data points + const double *points, // starting address of points array. + double *center); + +// Compute centroid of a triangle mesh; takes area of each triangle into account +// weighted average +bool fm_computeCentroid(uint32_t vcount, // number of input data points + const float *points, // starting address of points array. + uint32_t triangleCount, + const uint32_t *indices, + float *center); + +// Compute centroid of a triangle mesh; takes area of each triangle into account +// weighted average +bool fm_computeCentroid(uint32_t vcount, // number of input data points + const double *points, // starting address of points array. + uint32_t triangleCount, + const uint32_t *indices, + double *center); + + +float fm_computeBestFitAABB(uint32_t vcount,const float *points,uint32_t pstride,float bmin[3],float bmax[3]); // returns the diagonal distance +double fm_computeBestFitAABB(uint32_t vcount,const double *points,uint32_t pstride,double bmin[3],double bmax[3]); // returns the diagonal distance + +float fm_computeBestFitSphere(uint32_t vcount,const float *points,uint32_t pstride,float center[3]); +double fm_computeBestFitSphere(uint32_t vcount,const double *points,uint32_t pstride,double center[3]); + +bool fm_lineSphereIntersect(const float center[3],float radius,const float p1[3],const float p2[3],float intersect[3]); +bool fm_lineSphereIntersect(const double center[3],double radius,const double p1[3],const double p2[3],double intersect[3]); + +bool fm_intersectRayAABB(const float bmin[3],const float bmax[3],const float pos[3],const float dir[3],float intersect[3]); +bool fm_intersectLineSegmentAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],float intersect[3]); + +bool fm_lineIntersectsTriangle(const float rayStart[3],const float rayEnd[3],const float p1[3],const float p2[3],const float p3[3],float sect[3]); +bool fm_lineIntersectsTriangle(const double rayStart[3],const double rayEnd[3],const double p1[3],const double p2[3],const double p3[3],double sect[3]); + +bool fm_rayIntersectsTriangle(const float origin[3],const float dir[3],const float v0[3],const float v1[3],const float v2[3],float &t); +bool fm_rayIntersectsTriangle(const double origin[3],const double dir[3],const double v0[3],const double v1[3],const double v2[3],double &t); + +bool fm_raySphereIntersect(const float center[3],float radius,const float pos[3],const float dir[3],float distance,float intersect[3]); +bool fm_raySphereIntersect(const double center[3],double radius,const double pos[3],const double dir[3],double distance,double intersect[3]); + +void fm_catmullRom(float out_vector[3],const float p1[3],const float p2[3],const float p3[3],const float *p4, const float s); +void fm_catmullRom(double out_vector[3],const double p1[3],const double p2[3],const double p3[3],const double *p4, const double s); + +bool fm_intersectAABB(const float bmin1[3],const float bmax1[3],const float bmin2[3],const float bmax2[3]); +bool fm_intersectAABB(const double bmin1[3],const double bmax1[3],const double bmin2[3],const double bmax2[3]); + + +// computes the rotation quaternion to go from unit-vector v0 to unit-vector v1 +void fm_rotationArc(const float v0[3],const float v1[3],float quat[4]); +void fm_rotationArc(const double v0[3],const double v1[3],double quat[4]); + +float fm_distancePointLineSegment(const float Point[3],const float LineStart[3],const float LineEnd[3],float intersection[3],LineSegmentType &type,float epsilon); +double fm_distancePointLineSegment(const double Point[3],const double LineStart[3],const double LineEnd[3],double intersection[3],LineSegmentType &type,double epsilon); + + +bool fm_colinear(const double p1[3],const double p2[3],const double p3[3],double epsilon=0.999); // true if these three points in a row are co-linear +bool fm_colinear(const float p1[3],const float p2[3],const float p3[3],float epsilon=0.999f); + +bool fm_colinear(const float a1[3],const float a2[3],const float b1[3],const float b2[3],float epsilon=0.999f); // true if these two line segments are co-linear. +bool fm_colinear(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double epsilon=0.999); // true if these two line segments are co-linear. + +enum IntersectResult +{ + IR_DONT_INTERSECT, + IR_DO_INTERSECT, + IR_COINCIDENT, + IR_PARALLEL, +}; + +IntersectResult fm_intersectLineSegments2d(const float a1[3], const float a2[3], const float b1[3], const float b2[3], float intersectionPoint[3]); +IntersectResult fm_intersectLineSegments2d(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double intersectionPoint[3]); + +IntersectResult fm_intersectLineSegments2dTime(const float a1[3], const float a2[3], const float b1[3], const float b2[3],float &t1,float &t2); +IntersectResult fm_intersectLineSegments2dTime(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double &t1,double &t2); + +// Plane-Triangle splitting + +enum PlaneTriResult +{ + PTR_ON_PLANE, + PTR_FRONT, + PTR_BACK, + PTR_SPLIT, +}; + +PlaneTriResult fm_planeTriIntersection(const float plane[4], // the plane equation in Ax+By+Cz+D format + const float *triangle, // the source triangle. + uint32_t tstride, // stride in bytes of the input and output *vertices* + float epsilon, // the co-planer epsilon value. + float *front, // the triangle in front of the + uint32_t &fcount, // number of vertices in the 'front' triangle + float *back, // the triangle in back of the plane + uint32_t &bcount); // the number of vertices in the 'back' triangle. + + +PlaneTriResult fm_planeTriIntersection(const double plane[4], // the plane equation in Ax+By+Cz+D format + const double *triangle, // the source triangle. + uint32_t tstride, // stride in bytes of the input and output *vertices* + double epsilon, // the co-planer epsilon value. + double *front, // the triangle in front of the + uint32_t &fcount, // number of vertices in the 'front' triangle + double *back, // the triangle in back of the plane + uint32_t &bcount); // the number of vertices in the 'back' triangle. + + +bool fm_intersectPointPlane(const float p1[3],const float p2[3],float *split,const float plane[4]); +bool fm_intersectPointPlane(const double p1[3],const double p2[3],double *split,const double plane[4]); + +PlaneTriResult fm_getSidePlane(const float p[3],const float plane[4],float epsilon); +PlaneTriResult fm_getSidePlane(const double p[3],const double plane[4],double epsilon); + + +void fm_computeBestFitOBB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float matrix[16],bool bruteForce=true); +void fm_computeBestFitOBB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double matrix[16],bool bruteForce=true); + +void fm_computeBestFitOBB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float pos[3],float quat[4],bool bruteForce=true); +void fm_computeBestFitOBB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double pos[3],double quat[4],bool bruteForce=true); + +void fm_computeBestFitABB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float pos[3]); +void fm_computeBestFitABB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double pos[3]); + + +//** Note, if the returned capsule height is less than zero, then you must represent it is a sphere of size radius. +void fm_computeBestFitCapsule(uint32_t vcount,const float *points,uint32_t pstride,float &radius,float &height,float matrix[16],bool bruteForce=true); +void fm_computeBestFitCapsule(uint32_t vcount,const double *points,uint32_t pstride,float &radius,float &height,double matrix[16],bool bruteForce=true); + + +void fm_planeToMatrix(const float plane[4],float matrix[16]); // convert a plane equation to a 4x4 rotation matrix. Reference vector is 0,1,0 +void fm_planeToQuat(const float plane[4],float quat[4],float pos[3]); // convert a plane equation to a quaternion and translation + +void fm_planeToMatrix(const double plane[4],double matrix[16]); // convert a plane equation to a 4x4 rotation matrix +void fm_planeToQuat(const double plane[4],double quat[4],double pos[3]); // convert a plane equation to a quaternion and translation + +inline void fm_doubleToFloat3(const double p[3],float t[3]) { t[0] = (float) p[0]; t[1] = (float)p[1]; t[2] = (float)p[2]; }; +inline void fm_floatToDouble3(const float p[3],double t[3]) { t[0] = (double)p[0]; t[1] = (double)p[1]; t[2] = (double)p[2]; }; + + +void fm_eulerMatrix(float ax,float ay,float az,float matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) +void fm_eulerMatrix(double ax,double ay,double az,double matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) + + +float fm_computeMeshVolume(const float *vertices,uint32_t tcount,const uint32_t *indices); +double fm_computeMeshVolume(const double *vertices,uint32_t tcount,const uint32_t *indices); + + +#define FM_DEFAULT_GRANULARITY 0.001f // 1 millimeter is the default granularity + +class fm_VertexIndex +{ +public: + virtual uint32_t getIndex(const float pos[3],bool &newPos) = 0; // get welded index for this float vector[3] + virtual uint32_t getIndex(const double pos[3],bool &newPos) = 0; // get welded index for this double vector[3] + virtual const float * getVerticesFloat(void) const = 0; + virtual const double * getVerticesDouble(void) const = 0; + virtual const float * getVertexFloat(uint32_t index) const = 0; + virtual const double * getVertexDouble(uint32_t index) const = 0; + virtual uint32_t getVcount(void) const = 0; + virtual bool isDouble(void) const = 0; + virtual bool saveAsObj(const char *fname,uint32_t tcount,uint32_t *indices) = 0; +}; + +fm_VertexIndex * fm_createVertexIndex(double granularity,bool snapToGrid); // create an indexed vertex system for doubles +fm_VertexIndex * fm_createVertexIndex(float granularity,bool snapToGrid); // create an indexed vertext system for floats +void fm_releaseVertexIndex(fm_VertexIndex *vindex); + + +class fm_Triangulate +{ +public: + virtual const double * triangulate3d(uint32_t pcount, + const double *points, + uint32_t vstride, + uint32_t &tcount, + bool consolidate, + double epsilon) = 0; + + virtual const float * triangulate3d(uint32_t pcount, + const float *points, + uint32_t vstride, + uint32_t &tcount, + bool consolidate, + float epsilon) = 0; +}; + +fm_Triangulate * fm_createTriangulate(void); +void fm_releaseTriangulate(fm_Triangulate *t); + + +const float * fm_getPoint(const float *points,uint32_t pstride,uint32_t index); +const double * fm_getPoint(const double *points,uint32_t pstride,uint32_t index); + +bool fm_insideTriangle(float Ax, float Ay,float Bx, float By,float Cx, float Cy,float Px, float Py); +bool fm_insideTriangle(double Ax, double Ay,double Bx, double By,double Cx, double Cy,double Px, double Py); +float fm_areaPolygon2d(uint32_t pcount,const float *points,uint32_t pstride); +double fm_areaPolygon2d(uint32_t pcount,const double *points,uint32_t pstride); + +bool fm_pointInsidePolygon2d(uint32_t pcount,const float *points,uint32_t pstride,const float *point,uint32_t xindex=0,uint32_t yindex=1); +bool fm_pointInsidePolygon2d(uint32_t pcount,const double *points,uint32_t pstride,const double *point,uint32_t xindex=0,uint32_t yindex=1); + +uint32_t fm_consolidatePolygon(uint32_t pcount,const float *points,uint32_t pstride,float *dest,float epsilon=0.999999f); // collapses co-linear edges. +uint32_t fm_consolidatePolygon(uint32_t pcount,const double *points,uint32_t pstride,double *dest,double epsilon=0.999999); // collapses co-linear edges. + + +bool fm_computeSplitPlane(uint32_t vcount,const double *vertices,uint32_t tcount,const uint32_t *indices,double *plane); +bool fm_computeSplitPlane(uint32_t vcount,const float *vertices,uint32_t tcount,const uint32_t *indices,float *plane); + +void fm_nearestPointInTriangle(const float *pos,const float *p1,const float *p2,const float *p3,float *nearest); +void fm_nearestPointInTriangle(const double *pos,const double *p1,const double *p2,const double *p3,double *nearest); + +float fm_areaTriangle(const float *p1,const float *p2,const float *p3); +double fm_areaTriangle(const double *p1,const double *p2,const double *p3); + +void fm_subtract(const float *A,const float *B,float *diff); // compute A-B and store the result in 'diff' +void fm_subtract(const double *A,const double *B,double *diff); // compute A-B and store the result in 'diff' + +void fm_multiply(float *A,float scaler); +void fm_multiply(double *A,double scaler); + +void fm_add(const float *A,const float *B,float *sum); +void fm_add(const double *A,const double *B,double *sum); + +void fm_copy3(const float *source,float *dest); +void fm_copy3(const double *source,double *dest); + +// re-indexes an indexed triangle mesh but drops unused vertices. The output_indices can be the same pointer as the input indices. +// the output_vertices can point to the input vertices if you desire. The output_vertices buffer should be at least the same size +// is the input buffer. The routine returns the new vertex count after re-indexing. +uint32_t fm_copyUniqueVertices(uint32_t vcount,const float *input_vertices,float *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices); +uint32_t fm_copyUniqueVertices(uint32_t vcount,const double *input_vertices,double *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices); + +bool fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const float *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar! +bool fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const double *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar! + +bool fm_samePlane(const float p1[4],const float p2[4],float normalEpsilon=0.01f,float dEpsilon=0.001f,bool doubleSided=false); // returns true if these two plane equations are identical within an epsilon +bool fm_samePlane(const double p1[4],const double p2[4],double normalEpsilon=0.01,double dEpsilon=0.001,bool doubleSided=false); + +void fm_OBBtoAABB(const float obmin[3],const float obmax[3],const float matrix[16],float abmin[3],float abmax[3]); + +// a utility class that will tessellate a mesh. +class fm_Tesselate +{ +public: + virtual const uint32_t * tesselate(fm_VertexIndex *vindex,uint32_t tcount,const uint32_t *indices,float longEdge,uint32_t maxDepth,uint32_t &outcount) = 0; +}; + +fm_Tesselate * fm_createTesselate(void); +void fm_releaseTesselate(fm_Tesselate *t); + +void fm_computeMeanNormals(uint32_t vcount, // the number of vertices + const float *vertices, // the base address of the vertex position data. + uint32_t vstride, // the stride between position data. + float *normals, // the base address of the destination for mean vector normals + uint32_t nstride, // the stride between normals + uint32_t tcount, // the number of triangles + const uint32_t *indices); // the triangle indices + +void fm_computeMeanNormals(uint32_t vcount, // the number of vertices + const double *vertices, // the base address of the vertex position data. + uint32_t vstride, // the stride between position data. + double *normals, // the base address of the destination for mean vector normals + uint32_t nstride, // the stride between normals + uint32_t tcount, // the number of triangles + const uint32_t *indices); // the triangle indices + + +bool fm_isValidTriangle(const float *p1,const float *p2,const float *p3,float epsilon=0.00001f); +bool fm_isValidTriangle(const double *p1,const double *p2,const double *p3,double epsilon=0.00001f); + + +}; // end of namespace + +#endif diff --git a/thirdparty/vhacd/inc/btAlignedAllocator.h b/thirdparty/vhacd/inc/btAlignedAllocator.h new file mode 100644 index 0000000000..94e71d5125 --- /dev/null +++ b/thirdparty/vhacd/inc/btAlignedAllocator.h @@ -0,0 +1,113 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_ALIGNED_ALLOCATOR +#define BT_ALIGNED_ALLOCATOR + +///we probably replace this with our own aligned memory allocator +///so we replace _aligned_malloc and _aligned_free with our own +///that is better portable and more predictable + +#include "btScalar.h" + +// -- GODOT start -- +namespace VHACD { +// -- GODOT end -- + +//#define BT_DEBUG_MEMORY_ALLOCATIONS 1 +#ifdef BT_DEBUG_MEMORY_ALLOCATIONS + +#define btAlignedAlloc(a, b) \ + btAlignedAllocInternal(a, b, __LINE__, __FILE__) + +#define btAlignedFree(ptr) \ + btAlignedFreeInternal(ptr, __LINE__, __FILE__) + +void* btAlignedAllocInternal(size_t size, int32_t alignment, int32_t line, char* filename); + +void btAlignedFreeInternal(void* ptr, int32_t line, char* filename); + +#else +void* btAlignedAllocInternal(size_t size, int32_t alignment); +void btAlignedFreeInternal(void* ptr); + +#define btAlignedAlloc(size, alignment) btAlignedAllocInternal(size, alignment) +#define btAlignedFree(ptr) btAlignedFreeInternal(ptr) + +#endif +typedef int32_t size_type; + +typedef void*(btAlignedAllocFunc)(size_t size, int32_t alignment); +typedef void(btAlignedFreeFunc)(void* memblock); +typedef void*(btAllocFunc)(size_t size); +typedef void(btFreeFunc)(void* memblock); + +///The developer can let all Bullet memory allocations go through a custom memory allocator, using btAlignedAllocSetCustom +void btAlignedAllocSetCustom(btAllocFunc* allocFunc, btFreeFunc* freeFunc); +///If the developer has already an custom aligned allocator, then btAlignedAllocSetCustomAligned can be used. The default aligned allocator pre-allocates extra memory using the non-aligned allocator, and instruments it. +void btAlignedAllocSetCustomAligned(btAlignedAllocFunc* allocFunc, btAlignedFreeFunc* freeFunc); + +///The btAlignedAllocator is a portable class for aligned memory allocations. +///Default implementations for unaligned and aligned allocations can be overridden by a custom allocator using btAlignedAllocSetCustom and btAlignedAllocSetCustomAligned. +template <typename T, unsigned Alignment> +class btAlignedAllocator { + + typedef btAlignedAllocator<T, Alignment> self_type; + +public: + //just going down a list: + btAlignedAllocator() {} + /* + btAlignedAllocator( const self_type & ) {} + */ + + template <typename Other> + btAlignedAllocator(const btAlignedAllocator<Other, Alignment>&) {} + + typedef const T* const_pointer; + typedef const T& const_reference; + typedef T* pointer; + typedef T& reference; + typedef T value_type; + + pointer address(reference ref) const { return &ref; } + const_pointer address(const_reference ref) const { return &ref; } + pointer allocate(size_type n, const_pointer* hint = 0) + { + (void)hint; + return reinterpret_cast<pointer>(btAlignedAlloc(sizeof(value_type) * n, Alignment)); + } + void construct(pointer ptr, const value_type& value) { new (ptr) value_type(value); } + void deallocate(pointer ptr) + { + btAlignedFree(reinterpret_cast<void*>(ptr)); + } + void destroy(pointer ptr) { ptr->~value_type(); } + + template <typename O> + struct rebind { + typedef btAlignedAllocator<O, Alignment> other; + }; + template <typename O> + self_type& operator=(const btAlignedAllocator<O, Alignment>&) { return *this; } + + friend bool operator==(const self_type&, const self_type&) { return true; } +}; + +// -- GODOT start -- +}; // namespace VHACD +// -- GODOT end -- + +#endif //BT_ALIGNED_ALLOCATOR diff --git a/thirdparty/vhacd/inc/btAlignedObjectArray.h b/thirdparty/vhacd/inc/btAlignedObjectArray.h new file mode 100644 index 0000000000..1ce03d21bc --- /dev/null +++ b/thirdparty/vhacd/inc/btAlignedObjectArray.h @@ -0,0 +1,456 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_OBJECT_ARRAY__ +#define BT_OBJECT_ARRAY__ + +#include "btAlignedAllocator.h" +#include "btScalar.h" // has definitions like SIMD_FORCE_INLINE + +///If the platform doesn't support placement new, you can disable BT_USE_PLACEMENT_NEW +///then the btAlignedObjectArray doesn't support objects with virtual methods, and non-trivial constructors/destructors +///You can enable BT_USE_MEMCPY, then swapping elements in the array will use memcpy instead of operator= +///see discussion here: http://continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1231 and +///http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1240 + +#define BT_USE_PLACEMENT_NEW 1 +//#define BT_USE_MEMCPY 1 //disable, because it is cumbersome to find out for each platform where memcpy is defined. It can be in <memory.h> or <string.h> or otherwise... +#define BT_ALLOW_ARRAY_COPY_OPERATOR // enabling this can accidently perform deep copies of data if you are not careful + +#ifdef BT_USE_MEMCPY +#include <memory.h> +#include <string.h> +#endif //BT_USE_MEMCPY + +#ifdef BT_USE_PLACEMENT_NEW +#include <new> //for placement new +#endif //BT_USE_PLACEMENT_NEW + +// -- GODOT start -- +namespace VHACD { +// -- GODOT end -- + +///The btAlignedObjectArray template class uses a subset of the stl::vector interface for its methods +///It is developed to replace stl::vector to avoid portability issues, including STL alignment issues to add SIMD/SSE data +template <typename T> +//template <class T> +class btAlignedObjectArray { + btAlignedAllocator<T, 16> m_allocator; + + int32_t m_size; + int32_t m_capacity; + T* m_data; + //PCK: added this line + bool m_ownsMemory; + +#ifdef BT_ALLOW_ARRAY_COPY_OPERATOR +public: + SIMD_FORCE_INLINE btAlignedObjectArray<T>& operator=(const btAlignedObjectArray<T>& other) + { + copyFromArray(other); + return *this; + } +#else //BT_ALLOW_ARRAY_COPY_OPERATOR +private: + SIMD_FORCE_INLINE btAlignedObjectArray<T>& operator=(const btAlignedObjectArray<T>& other); +#endif //BT_ALLOW_ARRAY_COPY_OPERATOR + +protected: + SIMD_FORCE_INLINE int32_t allocSize(int32_t size) + { + return (size ? size * 2 : 1); + } + SIMD_FORCE_INLINE void copy(int32_t start, int32_t end, T* dest) const + { + int32_t i; + for (i = start; i < end; ++i) +#ifdef BT_USE_PLACEMENT_NEW + new (&dest[i]) T(m_data[i]); +#else + dest[i] = m_data[i]; +#endif //BT_USE_PLACEMENT_NEW + } + + SIMD_FORCE_INLINE void init() + { + //PCK: added this line + m_ownsMemory = true; + m_data = 0; + m_size = 0; + m_capacity = 0; + } + SIMD_FORCE_INLINE void destroy(int32_t first, int32_t last) + { + int32_t i; + for (i = first; i < last; i++) { + m_data[i].~T(); + } + } + + SIMD_FORCE_INLINE void* allocate(int32_t size) + { + if (size) + return m_allocator.allocate(size); + return 0; + } + + SIMD_FORCE_INLINE void deallocate() + { + if (m_data) { + //PCK: enclosed the deallocation in this block + if (m_ownsMemory) { + m_allocator.deallocate(m_data); + } + m_data = 0; + } + } + +public: + btAlignedObjectArray() + { + init(); + } + + ~btAlignedObjectArray() + { + clear(); + } + + ///Generally it is best to avoid using the copy constructor of an btAlignedObjectArray, and use a (const) reference to the array instead. + btAlignedObjectArray(const btAlignedObjectArray& otherArray) + { + init(); + + int32_t otherSize = otherArray.size(); + resize(otherSize); + otherArray.copy(0, otherSize, m_data); + } + + /// return the number of elements in the array + SIMD_FORCE_INLINE int32_t size() const + { + return m_size; + } + + SIMD_FORCE_INLINE const T& at(int32_t n) const + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE T& at(int32_t n) + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE const T& operator[](int32_t n) const + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE T& operator[](int32_t n) + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + ///clear the array, deallocated memory. Generally it is better to use array.resize(0), to reduce performance overhead of run-time memory (de)allocations. + SIMD_FORCE_INLINE void clear() + { + destroy(0, size()); + + deallocate(); + + init(); + } + + SIMD_FORCE_INLINE void pop_back() + { + btAssert(m_size > 0); + m_size--; + m_data[m_size].~T(); + } + + ///resize changes the number of elements in the array. If the new size is larger, the new elements will be constructed using the optional second argument. + ///when the new number of elements is smaller, the destructor will be called, but memory will not be freed, to reduce performance overhead of run-time memory (de)allocations. + SIMD_FORCE_INLINE void resize(int32_t newsize, const T& fillData = T()) + { + int32_t curSize = size(); + + if (newsize < curSize) { + for (int32_t i = newsize; i < curSize; i++) { + m_data[i].~T(); + } + } + else { + if (newsize > size()) { + reserve(newsize); + } +#ifdef BT_USE_PLACEMENT_NEW + for (int32_t i = curSize; i < newsize; i++) { + new (&m_data[i]) T(fillData); + } +#endif //BT_USE_PLACEMENT_NEW + } + + m_size = newsize; + } + + SIMD_FORCE_INLINE T& expandNonInitializing() + { + int32_t sz = size(); + if (sz == capacity()) { + reserve(allocSize(size())); + } + m_size++; + + return m_data[sz]; + } + + SIMD_FORCE_INLINE T& expand(const T& fillValue = T()) + { + int32_t sz = size(); + if (sz == capacity()) { + reserve(allocSize(size())); + } + m_size++; +#ifdef BT_USE_PLACEMENT_NEW + new (&m_data[sz]) T(fillValue); //use the in-place new (not really allocating heap memory) +#endif + + return m_data[sz]; + } + + SIMD_FORCE_INLINE void push_back(const T& _Val) + { + int32_t sz = size(); + if (sz == capacity()) { + reserve(allocSize(size())); + } + +#ifdef BT_USE_PLACEMENT_NEW + new (&m_data[m_size]) T(_Val); +#else + m_data[size()] = _Val; +#endif //BT_USE_PLACEMENT_NEW + + m_size++; + } + + /// return the pre-allocated (reserved) elements, this is at least as large as the total number of elements,see size() and reserve() + SIMD_FORCE_INLINE int32_t capacity() const + { + return m_capacity; + } + + SIMD_FORCE_INLINE void reserve(int32_t _Count) + { // determine new minimum length of allocated storage + if (capacity() < _Count) { // not enough room, reallocate + T* s = (T*)allocate(_Count); + + copy(0, size(), s); + + destroy(0, size()); + + deallocate(); + + //PCK: added this line + m_ownsMemory = true; + + m_data = s; + + m_capacity = _Count; + } + } + + class less { + public: + bool operator()(const T& a, const T& b) + { + return (a < b); + } + }; + + template <typename L> + void quickSortInternal(const L& CompareFunc, int32_t lo, int32_t hi) + { + // lo is the lower index, hi is the upper index + // of the region of array a that is to be sorted + int32_t i = lo, j = hi; + T x = m_data[(lo + hi) / 2]; + + // partition + do { + while (CompareFunc(m_data[i], x)) + i++; + while (CompareFunc(x, m_data[j])) + j--; + if (i <= j) { + swap(i, j); + i++; + j--; + } + } while (i <= j); + + // recursion + if (lo < j) + quickSortInternal(CompareFunc, lo, j); + if (i < hi) + quickSortInternal(CompareFunc, i, hi); + } + + template <typename L> + void quickSort(const L& CompareFunc) + { + //don't sort 0 or 1 elements + if (size() > 1) { + quickSortInternal(CompareFunc, 0, size() - 1); + } + } + + ///heap sort from http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Sort/Heap/ + template <typename L> + void downHeap(T* pArr, int32_t k, int32_t n, const L& CompareFunc) + { + /* PRE: a[k+1..N] is a heap */ + /* POST: a[k..N] is a heap */ + + T temp = pArr[k - 1]; + /* k has child(s) */ + while (k <= n / 2) { + int32_t child = 2 * k; + + if ((child < n) && CompareFunc(pArr[child - 1], pArr[child])) { + child++; + } + /* pick larger child */ + if (CompareFunc(temp, pArr[child - 1])) { + /* move child up */ + pArr[k - 1] = pArr[child - 1]; + k = child; + } + else { + break; + } + } + pArr[k - 1] = temp; + } /*downHeap*/ + + void swap(int32_t index0, int32_t index1) + { +#ifdef BT_USE_MEMCPY + char temp[sizeof(T)]; + memcpy(temp, &m_data[index0], sizeof(T)); + memcpy(&m_data[index0], &m_data[index1], sizeof(T)); + memcpy(&m_data[index1], temp, sizeof(T)); +#else + T temp = m_data[index0]; + m_data[index0] = m_data[index1]; + m_data[index1] = temp; +#endif //BT_USE_PLACEMENT_NEW + } + + template <typename L> + void heapSort(const L& CompareFunc) + { + /* sort a[0..N-1], N.B. 0 to N-1 */ + int32_t k; + int32_t n = m_size; + for (k = n / 2; k > 0; k--) { + downHeap(m_data, k, n, CompareFunc); + } + + /* a[1..N] is now a heap */ + while (n >= 1) { + swap(0, n - 1); /* largest of a[0..n-1] */ + + n = n - 1; + /* restore a[1..i-1] heap */ + downHeap(m_data, 1, n, CompareFunc); + } + } + + ///non-recursive binary search, assumes sorted array + int32_t findBinarySearch(const T& key) const + { + int32_t first = 0; + int32_t last = size() - 1; + + //assume sorted array + while (first <= last) { + int32_t mid = (first + last) / 2; // compute mid point. + if (key > m_data[mid]) + first = mid + 1; // repeat search in top half. + else if (key < m_data[mid]) + last = mid - 1; // repeat search in bottom half. + else + return mid; // found it. return position ///// + } + return size(); // failed to find key + } + + int32_t findLinearSearch(const T& key) const + { + int32_t index = size(); + int32_t i; + + for (i = 0; i < size(); i++) { + if (m_data[i] == key) { + index = i; + break; + } + } + return index; + } + + void remove(const T& key) + { + + int32_t findIndex = findLinearSearch(key); + if (findIndex < size()) { + swap(findIndex, size() - 1); + pop_back(); + } + } + + //PCK: whole function + void initializeFromBuffer(void* buffer, int32_t size, int32_t capacity) + { + clear(); + m_ownsMemory = false; + m_data = (T*)buffer; + m_size = size; + m_capacity = capacity; + } + + void copyFromArray(const btAlignedObjectArray& otherArray) + { + int32_t otherSize = otherArray.size(); + resize(otherSize); + otherArray.copy(0, otherSize, m_data); + } +}; + +// -- GODOT start -- +}; // namespace VHACD +// -- GODOT end -- + +#endif //BT_OBJECT_ARRAY__ diff --git a/thirdparty/vhacd/inc/btConvexHullComputer.h b/thirdparty/vhacd/inc/btConvexHullComputer.h new file mode 100644 index 0000000000..04bb96f64a --- /dev/null +++ b/thirdparty/vhacd/inc/btConvexHullComputer.h @@ -0,0 +1,105 @@ +/* +Copyright (c) 2011 Ole Kniemeyer, MAXON, www.maxon.net + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_CONVEX_HULL_COMPUTER_H +#define BT_CONVEX_HULL_COMPUTER_H + +#include "btAlignedObjectArray.h" +#include "btVector3.h" + +// -- GODOT start -- +namespace VHACD { +// -- GODOT end -- + +/// Convex hull implementation based on Preparata and Hong +/// See http://code.google.com/p/bullet/issues/detail?id=275 +/// Ole Kniemeyer, MAXON Computer GmbH +class btConvexHullComputer { +private: + btScalar compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp); + +public: + class Edge { + private: + int32_t next; + int32_t reverse; + int32_t targetVertex; + + friend class btConvexHullComputer; + + public: + int32_t getSourceVertex() const + { + return (this + reverse)->targetVertex; + } + + int32_t getTargetVertex() const + { + return targetVertex; + } + + const Edge* getNextEdgeOfVertex() const // clockwise list of all edges of a vertex + { + return this + next; + } + + const Edge* getNextEdgeOfFace() const // counter-clockwise list of all edges of a face + { + return (this + reverse)->getNextEdgeOfVertex(); + } + + const Edge* getReverseEdge() const + { + return this + reverse; + } + }; + + // Vertices of the output hull + btAlignedObjectArray<btVector3> vertices; + + // Edges of the output hull + btAlignedObjectArray<Edge> edges; + + // Faces of the convex hull. Each entry is an index into the "edges" array pointing to an edge of the face. Faces are planar n-gons + btAlignedObjectArray<int32_t> faces; + + /* + Compute convex hull of "count" vertices stored in "coords". "stride" is the difference in bytes + between the addresses of consecutive vertices. If "shrink" is positive, the convex hull is shrunken + by that amount (each face is moved by "shrink" length units towards the center along its normal). + If "shrinkClamp" is positive, "shrink" is clamped to not exceed "shrinkClamp * innerRadius", where "innerRadius" + is the minimum distance of a face to the center of the convex hull. + + The returned value is the amount by which the hull has been shrunken. If it is negative, the amount was so large + that the resulting convex hull is empty. + + The output convex hull can be found in the member variables "vertices", "edges", "faces". + */ + btScalar compute(const float* coords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) + { + return compute(coords, false, stride, count, shrink, shrinkClamp); + } + + // same as above, but double precision + btScalar compute(const double* coords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) + { + return compute(coords, true, stride, count, shrink, shrinkClamp); + } +}; + +// -- GODOT start -- +}; // namespace VHACD +// -- GODOT end -- + +#endif //BT_CONVEX_HULL_COMPUTER_H diff --git a/thirdparty/vhacd/inc/btMinMax.h b/thirdparty/vhacd/inc/btMinMax.h new file mode 100644 index 0000000000..9bc1e1c770 --- /dev/null +++ b/thirdparty/vhacd/inc/btMinMax.h @@ -0,0 +1,73 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_GEN_MINMAX_H +#define BT_GEN_MINMAX_H + +#include "btScalar.h" + +// -- GODOT start -- +namespace VHACD { +// -- GODOT end -- + +template <class T> +SIMD_FORCE_INLINE const T& btMin(const T& a, const T& b) +{ + return a < b ? a : b; +} + +template <class T> +SIMD_FORCE_INLINE const T& btMax(const T& a, const T& b) +{ + return a > b ? a : b; +} + +template <class T> +SIMD_FORCE_INLINE const T& btClamped(const T& a, const T& lb, const T& ub) +{ + return a < lb ? lb : (ub < a ? ub : a); +} + +template <class T> +SIMD_FORCE_INLINE void btSetMin(T& a, const T& b) +{ + if (b < a) { + a = b; + } +} + +template <class T> +SIMD_FORCE_INLINE void btSetMax(T& a, const T& b) +{ + if (a < b) { + a = b; + } +} + +template <class T> +SIMD_FORCE_INLINE void btClamp(T& a, const T& lb, const T& ub) +{ + if (a < lb) { + a = lb; + } + else if (ub < a) { + a = ub; + } +} + +// -- GODOT start -- +}; // namespace VHACD +// -- GODOT end -- + +#endif //BT_GEN_MINMAX_H diff --git a/thirdparty/vhacd/inc/btScalar.h b/thirdparty/vhacd/inc/btScalar.h new file mode 100644 index 0000000000..3999a71521 --- /dev/null +++ b/thirdparty/vhacd/inc/btScalar.h @@ -0,0 +1,573 @@ +/* +Copyright (c) 2003-2009 Erwin Coumans http://bullet.googlecode.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_SCALAR_H +#define BT_SCALAR_H + +#ifdef BT_MANAGED_CODE +//Aligned data types not supported in managed code +#pragma unmanaged +#endif + +#include <float.h> +#include <math.h> +#include <stdlib.h> //size_t for MSVC 6.0 +#include <stdint.h> + +/* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/ +#define BT_BULLET_VERSION 279 + +// -- GODOT start -- +namespace VHACD { +// -- GODOT end -- + +inline int32_t btGetVersion() +{ + return BT_BULLET_VERSION; +} + +// -- GODOT start -- +}; // namespace VHACD +// -- GODOT end -- + +#if defined(DEBUG) || defined(_DEBUG) +#define BT_DEBUG +#endif + +#ifdef _WIN32 + +#if defined(__MINGW32__) || defined(__CYGWIN__) || (defined(_MSC_VER) && _MSC_VER < 1300) + +#define SIMD_FORCE_INLINE inline +#define ATTRIBUTE_ALIGNED16(a) a +#define ATTRIBUTE_ALIGNED64(a) a +#define ATTRIBUTE_ALIGNED128(a) a +#else +//#define BT_HAS_ALIGNED_ALLOCATOR +#pragma warning(disable : 4324) // disable padding warning +// #pragma warning(disable:4530) // Disable the exception disable but used in MSCV Stl warning. +// #pragma warning(disable:4996) //Turn off warnings about deprecated C routines +// #pragma warning(disable:4786) // Disable the "debug name too long" warning + +#define SIMD_FORCE_INLINE __forceinline +#define ATTRIBUTE_ALIGNED16(a) __declspec(align(16)) a +#define ATTRIBUTE_ALIGNED64(a) __declspec(align(64)) a +#define ATTRIBUTE_ALIGNED128(a) __declspec(align(128)) a +#ifdef _XBOX +#define BT_USE_VMX128 + +#include <ppcintrinsics.h> +#define BT_HAVE_NATIVE_FSEL +#define btFsel(a, b, c) __fsel((a), (b), (c)) +#else + +#if (defined(_WIN32) && (_MSC_VER) && _MSC_VER >= 1400) && (!defined(BT_USE_DOUBLE_PRECISION)) +#define BT_USE_SSE +#include <emmintrin.h> +#endif + +#endif //_XBOX + +#endif //__MINGW32__ + +#include <assert.h> +#ifdef BT_DEBUG +#define btAssert assert +#else +#define btAssert(x) +#endif +//btFullAssert is optional, slows down a lot +#define btFullAssert(x) + +#define btLikely(_c) _c +#define btUnlikely(_c) _c + +#else + +#if defined(__CELLOS_LV2__) +#define SIMD_FORCE_INLINE inline __attribute__((always_inline)) +#define ATTRIBUTE_ALIGNED16(a) a __attribute__((aligned(16))) +#define ATTRIBUTE_ALIGNED64(a) a __attribute__((aligned(64))) +#define ATTRIBUTE_ALIGNED128(a) a __attribute__((aligned(128))) +#ifndef assert +#include <assert.h> +#endif +#ifdef BT_DEBUG +#ifdef __SPU__ +#include <spu_printf.h> +#define printf spu_printf +#define btAssert(x) \ + { \ + if (!(x)) { \ + printf("Assert " __FILE__ ":%u (" #x ")\n", __LINE__); \ + spu_hcmpeq(0, 0); \ + } \ + } +#else +#define btAssert assert +#endif + +#else +#define btAssert(x) +#endif +//btFullAssert is optional, slows down a lot +#define btFullAssert(x) + +#define btLikely(_c) _c +#define btUnlikely(_c) _c + +#else + +#ifdef USE_LIBSPE2 + +#define SIMD_FORCE_INLINE __inline +#define ATTRIBUTE_ALIGNED16(a) a __attribute__((aligned(16))) +#define ATTRIBUTE_ALIGNED64(a) a __attribute__((aligned(64))) +#define ATTRIBUTE_ALIGNED128(a) a __attribute__((aligned(128))) +#ifndef assert +#include <assert.h> +#endif +#ifdef BT_DEBUG +#define btAssert assert +#else +#define btAssert(x) +#endif +//btFullAssert is optional, slows down a lot +#define btFullAssert(x) + +#define btLikely(_c) __builtin_expect((_c), 1) +#define btUnlikely(_c) __builtin_expect((_c), 0) + +#else +//non-windows systems + +#if (defined(__APPLE__) && defined(__i386__) && (!defined(BT_USE_DOUBLE_PRECISION))) +#define BT_USE_SSE +#include <emmintrin.h> + +#define SIMD_FORCE_INLINE inline +///@todo: check out alignment methods for other platforms/compilers +#define ATTRIBUTE_ALIGNED16(a) a __attribute__((aligned(16))) +#define ATTRIBUTE_ALIGNED64(a) a __attribute__((aligned(64))) +#define ATTRIBUTE_ALIGNED128(a) a __attribute__((aligned(128))) +#ifndef assert +#include <assert.h> +#endif + +#if defined(DEBUG) || defined(_DEBUG) +#define btAssert assert +#else +#define btAssert(x) +#endif + +//btFullAssert is optional, slows down a lot +#define btFullAssert(x) +#define btLikely(_c) _c +#define btUnlikely(_c) _c + +#else + +#define SIMD_FORCE_INLINE inline +///@todo: check out alignment methods for other platforms/compilers +///#define ATTRIBUTE_ALIGNED16(a) a __attribute__ ((aligned (16))) +///#define ATTRIBUTE_ALIGNED64(a) a __attribute__ ((aligned (64))) +///#define ATTRIBUTE_ALIGNED128(a) a __attribute__ ((aligned (128))) +#define ATTRIBUTE_ALIGNED16(a) a +#define ATTRIBUTE_ALIGNED64(a) a +#define ATTRIBUTE_ALIGNED128(a) a +#ifndef assert +#include <assert.h> +#endif + +#if defined(DEBUG) || defined(_DEBUG) +#define btAssert assert +#else +#define btAssert(x) +#endif + +//btFullAssert is optional, slows down a lot +#define btFullAssert(x) +#define btLikely(_c) _c +#define btUnlikely(_c) _c +#endif //__APPLE__ + +#endif // LIBSPE2 + +#endif //__CELLOS_LV2__ +#endif + +// -- GODOT start -- +namespace VHACD { +// -- GODOT end -- + +///The btScalar type abstracts floating point numbers, to easily switch between double and single floating point precision. +#if defined(BT_USE_DOUBLE_PRECISION) +typedef double btScalar; +//this number could be bigger in double precision +#define BT_LARGE_FLOAT 1e30 +#else +typedef float btScalar; +//keep BT_LARGE_FLOAT*BT_LARGE_FLOAT < FLT_MAX +#define BT_LARGE_FLOAT 1e18f +#endif + +#define BT_DECLARE_ALIGNED_ALLOCATOR() \ + SIMD_FORCE_INLINE void* operator new(size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \ + SIMD_FORCE_INLINE void operator delete(void* ptr) { btAlignedFree(ptr); } \ + SIMD_FORCE_INLINE void* operator new(size_t, void* ptr) { return ptr; } \ + SIMD_FORCE_INLINE void operator delete(void*, void*) {} \ + SIMD_FORCE_INLINE void* operator new[](size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \ + SIMD_FORCE_INLINE void operator delete[](void* ptr) { btAlignedFree(ptr); } \ + SIMD_FORCE_INLINE void* operator new[](size_t, void* ptr) { return ptr; } \ + SIMD_FORCE_INLINE void operator delete[](void*, void*) {} + +#if defined(BT_USE_DOUBLE_PRECISION) || defined(BT_FORCE_DOUBLE_FUNCTIONS) + +SIMD_FORCE_INLINE btScalar btSqrt(btScalar x) +{ + return sqrt(x); +} +SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabs(x); } +SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cos(x); } +SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sin(x); } +SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tan(x); } +SIMD_FORCE_INLINE btScalar btAcos(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return acos(x); +} +SIMD_FORCE_INLINE btScalar btAsin(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return asin(x); +} +SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atan(x); } +SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2(x, y); } +SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return exp(x); } +SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return log(x); } +SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { return pow(x, y); } +SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { return fmod(x, y); } + +#else + +SIMD_FORCE_INLINE btScalar btSqrt(btScalar y) +{ +#ifdef USE_APPROXIMATION + double x, z, tempf; + unsigned long* tfptr = ((unsigned long*)&tempf) + 1; + + tempf = y; + *tfptr = (0xbfcdd90a - *tfptr) >> 1; /* estimate of 1/sqrt(y) */ + x = tempf; + z = y * btScalar(0.5); + x = (btScalar(1.5) * x) - (x * x) * (x * z); /* iteration formula */ + x = (btScalar(1.5) * x) - (x * x) * (x * z); + x = (btScalar(1.5) * x) - (x * x) * (x * z); + x = (btScalar(1.5) * x) - (x * x) * (x * z); + x = (btScalar(1.5) * x) - (x * x) * (x * z); + return x * y; +#else + return sqrtf(y); +#endif +} +SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabsf(x); } +SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cosf(x); } +SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sinf(x); } +SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tanf(x); } +SIMD_FORCE_INLINE btScalar btAcos(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return acosf(x); +} +SIMD_FORCE_INLINE btScalar btAsin(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return asinf(x); +} +SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atanf(x); } +SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2f(x, y); } +SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return expf(x); } +SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return logf(x); } +SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { return powf(x, y); } +SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { return fmodf(x, y); } + +#endif + +#define SIMD_2_PI btScalar(6.283185307179586232) +#define SIMD_PI (SIMD_2_PI * btScalar(0.5)) +#define SIMD_HALF_PI (SIMD_2_PI * btScalar(0.25)) +#define SIMD_RADS_PER_DEG (SIMD_2_PI / btScalar(360.0)) +#define SIMD_DEGS_PER_RAD (btScalar(360.0) / SIMD_2_PI) +#define SIMDSQRT12 btScalar(0.7071067811865475244008443621048490) + +#define btRecipSqrt(x) ((btScalar)(btScalar(1.0) / btSqrt(btScalar(x)))) /* reciprocal square root */ + +#ifdef BT_USE_DOUBLE_PRECISION +#define SIMD_EPSILON DBL_EPSILON +#define SIMD_INFINITY DBL_MAX +#else +#define SIMD_EPSILON FLT_EPSILON +#define SIMD_INFINITY FLT_MAX +#endif + +SIMD_FORCE_INLINE btScalar btAtan2Fast(btScalar y, btScalar x) +{ + btScalar coeff_1 = SIMD_PI / 4.0f; + btScalar coeff_2 = 3.0f * coeff_1; + btScalar abs_y = btFabs(y); + btScalar angle; + if (x >= 0.0f) { + btScalar r = (x - abs_y) / (x + abs_y); + angle = coeff_1 - coeff_1 * r; + } + else { + btScalar r = (x + abs_y) / (abs_y - x); + angle = coeff_2 - coeff_1 * r; + } + return (y < 0.0f) ? -angle : angle; +} + +SIMD_FORCE_INLINE bool btFuzzyZero(btScalar x) { return btFabs(x) < SIMD_EPSILON; } + +SIMD_FORCE_INLINE bool btEqual(btScalar a, btScalar eps) +{ + return (((a) <= eps) && !((a) < -eps)); +} +SIMD_FORCE_INLINE bool btGreaterEqual(btScalar a, btScalar eps) +{ + return (!((a) <= eps)); +} + +SIMD_FORCE_INLINE int32_t btIsNegative(btScalar x) +{ + return x < btScalar(0.0) ? 1 : 0; +} + +SIMD_FORCE_INLINE btScalar btRadians(btScalar x) { return x * SIMD_RADS_PER_DEG; } +SIMD_FORCE_INLINE btScalar btDegrees(btScalar x) { return x * SIMD_DEGS_PER_RAD; } + +#define BT_DECLARE_HANDLE(name) \ + typedef struct name##__ { \ + int32_t unused; \ + } * name + +#ifndef btFsel +SIMD_FORCE_INLINE btScalar btFsel(btScalar a, btScalar b, btScalar c) +{ + return a >= 0 ? b : c; +} +#endif +#define btFsels(a, b, c) (btScalar) btFsel(a, b, c) + +SIMD_FORCE_INLINE bool btMachineIsLittleEndian() +{ + long int i = 1; + const char* p = (const char*)&i; + if (p[0] == 1) // Lowest address contains the least significant byte + return true; + else + return false; +} + +///btSelect avoids branches, which makes performance much better for consoles like Playstation 3 and XBox 360 +///Thanks Phil Knight. See also http://www.cellperformance.com/articles/2006/04/more_techniques_for_eliminatin_1.html +SIMD_FORCE_INLINE unsigned btSelect(unsigned condition, unsigned valueIfConditionNonZero, unsigned valueIfConditionZero) +{ + // Set testNz to 0xFFFFFFFF if condition is nonzero, 0x00000000 if condition is zero + // Rely on positive value or'ed with its negative having sign bit on + // and zero value or'ed with its negative (which is still zero) having sign bit off + // Use arithmetic shift right, shifting the sign bit through all 32 bits + unsigned testNz = (unsigned)(((int32_t)condition | -(int32_t)condition) >> 31); + unsigned testEqz = ~testNz; + return ((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); +} +SIMD_FORCE_INLINE int32_t btSelect(unsigned condition, int32_t valueIfConditionNonZero, int32_t valueIfConditionZero) +{ + unsigned testNz = (unsigned)(((int32_t)condition | -(int32_t)condition) >> 31); + unsigned testEqz = ~testNz; + return static_cast<int32_t>((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); +} +SIMD_FORCE_INLINE float btSelect(unsigned condition, float valueIfConditionNonZero, float valueIfConditionZero) +{ +#ifdef BT_HAVE_NATIVE_FSEL + return (float)btFsel((btScalar)condition - btScalar(1.0f), valueIfConditionNonZero, valueIfConditionZero); +#else + return (condition != 0) ? valueIfConditionNonZero : valueIfConditionZero; +#endif +} + +template <typename T> +SIMD_FORCE_INLINE void btSwap(T& a, T& b) +{ + T tmp = a; + a = b; + b = tmp; +} + +//PCK: endian swapping functions +SIMD_FORCE_INLINE unsigned btSwapEndian(unsigned val) +{ + return (((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24)); +} + +SIMD_FORCE_INLINE unsigned short btSwapEndian(unsigned short val) +{ + return static_cast<unsigned short>(((val & 0xff00) >> 8) | ((val & 0x00ff) << 8)); +} + +SIMD_FORCE_INLINE unsigned btSwapEndian(int32_t val) +{ + return btSwapEndian((unsigned)val); +} + +SIMD_FORCE_INLINE unsigned short btSwapEndian(short val) +{ + return btSwapEndian((unsigned short)val); +} + +///btSwapFloat uses using char pointers to swap the endianness +////btSwapFloat/btSwapDouble will NOT return a float, because the machine might 'correct' invalid floating point values +///Not all values of sign/exponent/mantissa are valid floating point numbers according to IEEE 754. +///When a floating point unit is faced with an invalid value, it may actually change the value, or worse, throw an exception. +///In most systems, running user mode code, you wouldn't get an exception, but instead the hardware/os/runtime will 'fix' the number for you. +///so instead of returning a float/double, we return integer/long long integer +SIMD_FORCE_INLINE uint32_t btSwapEndianFloat(float d) +{ + uint32_t a = 0; + unsigned char* dst = (unsigned char*)&a; + unsigned char* src = (unsigned char*)&d; + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; + return a; +} + +// unswap using char pointers +SIMD_FORCE_INLINE float btUnswapEndianFloat(uint32_t a) +{ + float d = 0.0f; + unsigned char* src = (unsigned char*)&a; + unsigned char* dst = (unsigned char*)&d; + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; + + return d; +} + +// swap using char pointers +SIMD_FORCE_INLINE void btSwapEndianDouble(double d, unsigned char* dst) +{ + unsigned char* src = (unsigned char*)&d; + + dst[0] = src[7]; + dst[1] = src[6]; + dst[2] = src[5]; + dst[3] = src[4]; + dst[4] = src[3]; + dst[5] = src[2]; + dst[6] = src[1]; + dst[7] = src[0]; +} + +// unswap using char pointers +SIMD_FORCE_INLINE double btUnswapEndianDouble(const unsigned char* src) +{ + double d = 0.0; + unsigned char* dst = (unsigned char*)&d; + + dst[0] = src[7]; + dst[1] = src[6]; + dst[2] = src[5]; + dst[3] = src[4]; + dst[4] = src[3]; + dst[5] = src[2]; + dst[6] = src[1]; + dst[7] = src[0]; + + return d; +} + +// returns normalized value in range [-SIMD_PI, SIMD_PI] +SIMD_FORCE_INLINE btScalar btNormalizeAngle(btScalar angleInRadians) +{ + angleInRadians = btFmod(angleInRadians, SIMD_2_PI); + if (angleInRadians < -SIMD_PI) { + return angleInRadians + SIMD_2_PI; + } + else if (angleInRadians > SIMD_PI) { + return angleInRadians - SIMD_2_PI; + } + else { + return angleInRadians; + } +} + +///rudimentary class to provide type info +struct btTypedObject { + btTypedObject(int32_t objectType) + : m_objectType(objectType) + { + } + int32_t m_objectType; + inline int32_t getObjectType() const + { + return m_objectType; + } +}; + +// -- GODOT start -- +// Cherry-picked from Bullet 2.88 to fix GH-27926 +///align a pointer to the provided alignment, upwards +template <typename T> +T *btAlignPointer(T *unalignedPtr, size_t alignment) +{ + struct btConvertPointerSizeT + { + union { + T *ptr; + size_t integer; + }; + }; + btConvertPointerSizeT converter; + + const size_t bit_mask = ~(alignment - 1); + converter.ptr = unalignedPtr; + converter.integer += alignment - 1; + converter.integer &= bit_mask; + return converter.ptr; +} +// -- GODOT end -- + +// -- GODOT start -- +}; // namespace VHACD +// -- GODOT end -- + +#endif //BT_SCALAR_H diff --git a/thirdparty/vhacd/inc/btVector3.h b/thirdparty/vhacd/inc/btVector3.h new file mode 100644 index 0000000000..4ed9716734 --- /dev/null +++ b/thirdparty/vhacd/inc/btVector3.h @@ -0,0 +1,723 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_VECTOR3_H +#define BT_VECTOR3_H + +#include "btMinMax.h" +#include "btScalar.h" + +#ifdef BT_USE_DOUBLE_PRECISION +#define btVector3Data btVector3DoubleData +#define btVector3DataName "btVector3DoubleData" +#else +#define btVector3Data btVector3FloatData +#define btVector3DataName "btVector3FloatData" +#endif //BT_USE_DOUBLE_PRECISION + +// -- GODOT start -- +namespace VHACD { +// -- GODOT end -- + +/**@brief btVector3 can be used to represent 3D points and vectors. + * It has an un-used w component to suit 16-byte alignment when btVector3 is stored in containers. This extra component can be used by derived classes (Quaternion?) or by user + * Ideally, this class should be replaced by a platform optimized SIMD version that keeps the data in registers + */ +ATTRIBUTE_ALIGNED16(class) +btVector3 +{ +public: +#if defined(__SPU__) && defined(__CELLOS_LV2__) + btScalar m_floats[4]; + +public: + SIMD_FORCE_INLINE const vec_float4& get128() const + { + return *((const vec_float4*)&m_floats[0]); + } + +public: +#else //__CELLOS_LV2__ __SPU__ +#ifdef BT_USE_SSE // _WIN32 + union { + __m128 mVec128; + btScalar m_floats[4]; + }; + SIMD_FORCE_INLINE __m128 get128() const + { + return mVec128; + } + SIMD_FORCE_INLINE void set128(__m128 v128) + { + mVec128 = v128; + } +#else + btScalar m_floats[4]; +#endif +#endif //__CELLOS_LV2__ __SPU__ + +public: + /**@brief No initialization constructor */ + SIMD_FORCE_INLINE btVector3() {} + + /**@brief Constructor from scalars + * @param x X value + * @param y Y value + * @param z Z value + */ + SIMD_FORCE_INLINE btVector3(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_floats[0] = x; + m_floats[1] = y; + m_floats[2] = z; + m_floats[3] = btScalar(0.); + } + + /**@brief Add a vector to this one + * @param The vector to add to this one */ + SIMD_FORCE_INLINE btVector3& operator+=(const btVector3& v) + { + + m_floats[0] += v.m_floats[0]; + m_floats[1] += v.m_floats[1]; + m_floats[2] += v.m_floats[2]; + return *this; + } + + /**@brief Subtract a vector from this one + * @param The vector to subtract */ + SIMD_FORCE_INLINE btVector3& operator-=(const btVector3& v) + { + m_floats[0] -= v.m_floats[0]; + m_floats[1] -= v.m_floats[1]; + m_floats[2] -= v.m_floats[2]; + return *this; + } + /**@brief Scale the vector + * @param s Scale factor */ + SIMD_FORCE_INLINE btVector3& operator*=(const btScalar& s) + { + m_floats[0] *= s; + m_floats[1] *= s; + m_floats[2] *= s; + return *this; + } + + /**@brief Inversely scale the vector + * @param s Scale factor to divide by */ + SIMD_FORCE_INLINE btVector3& operator/=(const btScalar& s) + { + btFullAssert(s != btScalar(0.0)); + return * this *= btScalar(1.0) / s; + } + + /**@brief Return the dot product + * @param v The other vector in the dot product */ + SIMD_FORCE_INLINE btScalar dot(const btVector3& v) const + { + return m_floats[0] * v.m_floats[0] + m_floats[1] * v.m_floats[1] + m_floats[2] * v.m_floats[2]; + } + + /**@brief Return the length of the vector squared */ + SIMD_FORCE_INLINE btScalar length2() const + { + return dot(*this); + } + + /**@brief Return the length of the vector */ + SIMD_FORCE_INLINE btScalar length() const + { + return btSqrt(length2()); + } + + /**@brief Return the distance squared between the ends of this and another vector + * This is symantically treating the vector like a point */ + SIMD_FORCE_INLINE btScalar distance2(const btVector3& v) const; + + /**@brief Return the distance between the ends of this and another vector + * This is symantically treating the vector like a point */ + SIMD_FORCE_INLINE btScalar distance(const btVector3& v) const; + + SIMD_FORCE_INLINE btVector3& safeNormalize() + { + btVector3 absVec = this->absolute(); + int32_t maxIndex = absVec.maxAxis(); + if (absVec[maxIndex] > 0) { + *this /= absVec[maxIndex]; + return * this /= length(); + } + setValue(1, 0, 0); + return *this; + } + + /**@brief Normalize this vector + * x^2 + y^2 + z^2 = 1 */ + SIMD_FORCE_INLINE btVector3& normalize() + { + return * this /= length(); + } + + /**@brief Return a normalized version of this vector */ + SIMD_FORCE_INLINE btVector3 normalized() const; + + /**@brief Return a rotated version of this vector + * @param wAxis The axis to rotate about + * @param angle The angle to rotate by */ + SIMD_FORCE_INLINE btVector3 rotate(const btVector3& wAxis, const btScalar angle) const; + + /**@brief Return the angle between this and another vector + * @param v The other vector */ + SIMD_FORCE_INLINE btScalar angle(const btVector3& v) const + { + btScalar s = btSqrt(length2() * v.length2()); + btFullAssert(s != btScalar(0.0)); + return btAcos(dot(v) / s); + } + /**@brief Return a vector will the absolute values of each element */ + SIMD_FORCE_INLINE btVector3 absolute() const + { + return btVector3( + btFabs(m_floats[0]), + btFabs(m_floats[1]), + btFabs(m_floats[2])); + } + /**@brief Return the cross product between this and another vector + * @param v The other vector */ + SIMD_FORCE_INLINE btVector3 cross(const btVector3& v) const + { + return btVector3( + m_floats[1] * v.m_floats[2] - m_floats[2] * v.m_floats[1], + m_floats[2] * v.m_floats[0] - m_floats[0] * v.m_floats[2], + m_floats[0] * v.m_floats[1] - m_floats[1] * v.m_floats[0]); + } + + SIMD_FORCE_INLINE btScalar triple(const btVector3& v1, const btVector3& v2) const + { + return m_floats[0] * (v1.m_floats[1] * v2.m_floats[2] - v1.m_floats[2] * v2.m_floats[1]) + m_floats[1] * (v1.m_floats[2] * v2.m_floats[0] - v1.m_floats[0] * v2.m_floats[2]) + m_floats[2] * (v1.m_floats[0] * v2.m_floats[1] - v1.m_floats[1] * v2.m_floats[0]); + } + + /**@brief Return the axis with the smallest value + * Note return values are 0,1,2 for x, y, or z */ + SIMD_FORCE_INLINE int32_t minAxis() const + { + return m_floats[0] < m_floats[1] ? (m_floats[0] < m_floats[2] ? 0 : 2) : (m_floats[1] < m_floats[2] ? 1 : 2); + } + + /**@brief Return the axis with the largest value + * Note return values are 0,1,2 for x, y, or z */ + SIMD_FORCE_INLINE int32_t maxAxis() const + { + return m_floats[0] < m_floats[1] ? (m_floats[1] < m_floats[2] ? 2 : 1) : (m_floats[0] < m_floats[2] ? 2 : 0); + } + + SIMD_FORCE_INLINE int32_t furthestAxis() const + { + return absolute().minAxis(); + } + + SIMD_FORCE_INLINE int32_t closestAxis() const + { + return absolute().maxAxis(); + } + + SIMD_FORCE_INLINE void setInterpolate3(const btVector3& v0, const btVector3& v1, btScalar rt) + { + btScalar s = btScalar(1.0) - rt; + m_floats[0] = s * v0.m_floats[0] + rt * v1.m_floats[0]; + m_floats[1] = s * v0.m_floats[1] + rt * v1.m_floats[1]; + m_floats[2] = s * v0.m_floats[2] + rt * v1.m_floats[2]; + //don't do the unused w component + // m_co[3] = s * v0[3] + rt * v1[3]; + } + + /**@brief Return the linear interpolation between this and another vector + * @param v The other vector + * @param t The ration of this to v (t = 0 => return this, t=1 => return other) */ + SIMD_FORCE_INLINE btVector3 lerp(const btVector3& v, const btScalar& t) const + { + return btVector3(m_floats[0] + (v.m_floats[0] - m_floats[0]) * t, + m_floats[1] + (v.m_floats[1] - m_floats[1]) * t, + m_floats[2] + (v.m_floats[2] - m_floats[2]) * t); + } + + /**@brief Elementwise multiply this vector by the other + * @param v The other vector */ + SIMD_FORCE_INLINE btVector3& operator*=(const btVector3& v) + { + m_floats[0] *= v.m_floats[0]; + m_floats[1] *= v.m_floats[1]; + m_floats[2] *= v.m_floats[2]; + return *this; + } + + /**@brief Return the x value */ + SIMD_FORCE_INLINE const btScalar& getX() const { return m_floats[0]; } + /**@brief Return the y value */ + SIMD_FORCE_INLINE const btScalar& getY() const { return m_floats[1]; } + /**@brief Return the z value */ + SIMD_FORCE_INLINE const btScalar& getZ() const { return m_floats[2]; } + /**@brief Set the x value */ + SIMD_FORCE_INLINE void setX(btScalar x) { m_floats[0] = x; }; + /**@brief Set the y value */ + SIMD_FORCE_INLINE void setY(btScalar y) { m_floats[1] = y; }; + /**@brief Set the z value */ + SIMD_FORCE_INLINE void setZ(btScalar z) { m_floats[2] = z; }; + /**@brief Set the w value */ + SIMD_FORCE_INLINE void setW(btScalar w) { m_floats[3] = w; }; + /**@brief Return the x value */ + SIMD_FORCE_INLINE const btScalar& x() const { return m_floats[0]; } + /**@brief Return the y value */ + SIMD_FORCE_INLINE const btScalar& y() const { return m_floats[1]; } + /**@brief Return the z value */ + SIMD_FORCE_INLINE const btScalar& z() const { return m_floats[2]; } + /**@brief Return the w value */ + SIMD_FORCE_INLINE const btScalar& w() const { return m_floats[3]; } + + //SIMD_FORCE_INLINE btScalar& operator[](int32_t i) { return (&m_floats[0])[i]; } + //SIMD_FORCE_INLINE const btScalar& operator[](int32_t i) const { return (&m_floats[0])[i]; } + ///operator btScalar*() replaces operator[], using implicit conversion. We added operator != and operator == to avoid pointer comparisons. + SIMD_FORCE_INLINE operator btScalar*() { return &m_floats[0]; } + SIMD_FORCE_INLINE operator const btScalar*() const { return &m_floats[0]; } + + SIMD_FORCE_INLINE bool operator==(const btVector3& other) const + { + return ((m_floats[3] == other.m_floats[3]) && (m_floats[2] == other.m_floats[2]) && (m_floats[1] == other.m_floats[1]) && (m_floats[0] == other.m_floats[0])); + } + + SIMD_FORCE_INLINE bool operator!=(const btVector3& other) const + { + return !(*this == other); + } + + /**@brief Set each element to the max of the current values and the values of another btVector3 + * @param other The other btVector3 to compare with + */ + SIMD_FORCE_INLINE void setMax(const btVector3& other) + { + btSetMax(m_floats[0], other.m_floats[0]); + btSetMax(m_floats[1], other.m_floats[1]); + btSetMax(m_floats[2], other.m_floats[2]); + btSetMax(m_floats[3], other.w()); + } + /**@brief Set each element to the min of the current values and the values of another btVector3 + * @param other The other btVector3 to compare with + */ + SIMD_FORCE_INLINE void setMin(const btVector3& other) + { + btSetMin(m_floats[0], other.m_floats[0]); + btSetMin(m_floats[1], other.m_floats[1]); + btSetMin(m_floats[2], other.m_floats[2]); + btSetMin(m_floats[3], other.w()); + } + + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_floats[0] = x; + m_floats[1] = y; + m_floats[2] = z; + m_floats[3] = btScalar(0.); + } + + void getSkewSymmetricMatrix(btVector3 * v0, btVector3 * v1, btVector3 * v2) const + { + v0->setValue(0., -z(), y()); + v1->setValue(z(), 0., -x()); + v2->setValue(-y(), x(), 0.); + } + + void setZero() + { + setValue(btScalar(0.), btScalar(0.), btScalar(0.)); + } + + SIMD_FORCE_INLINE bool isZero() const + { + return m_floats[0] == btScalar(0) && m_floats[1] == btScalar(0) && m_floats[2] == btScalar(0); + } + + SIMD_FORCE_INLINE bool fuzzyZero() const + { + return length2() < SIMD_EPSILON; + } + + SIMD_FORCE_INLINE void serialize(struct btVector3Data & dataOut) const; + + SIMD_FORCE_INLINE void deSerialize(const struct btVector3Data& dataIn); + + SIMD_FORCE_INLINE void serializeFloat(struct btVector3FloatData & dataOut) const; + + SIMD_FORCE_INLINE void deSerializeFloat(const struct btVector3FloatData& dataIn); + + SIMD_FORCE_INLINE void serializeDouble(struct btVector3DoubleData & dataOut) const; + + SIMD_FORCE_INLINE void deSerializeDouble(const struct btVector3DoubleData& dataIn); +}; + +/**@brief Return the sum of two vectors (Point symantics)*/ +SIMD_FORCE_INLINE btVector3 +operator+(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] + v2.m_floats[0], v1.m_floats[1] + v2.m_floats[1], v1.m_floats[2] + v2.m_floats[2]); +} + +/**@brief Return the elementwise product of two vectors */ +SIMD_FORCE_INLINE btVector3 +operator*(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] * v2.m_floats[0], v1.m_floats[1] * v2.m_floats[1], v1.m_floats[2] * v2.m_floats[2]); +} + +/**@brief Return the difference between two vectors */ +SIMD_FORCE_INLINE btVector3 +operator-(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] - v2.m_floats[0], v1.m_floats[1] - v2.m_floats[1], v1.m_floats[2] - v2.m_floats[2]); +} +/**@brief Return the negative of the vector */ +SIMD_FORCE_INLINE btVector3 +operator-(const btVector3& v) +{ + return btVector3(-v.m_floats[0], -v.m_floats[1], -v.m_floats[2]); +} + +/**@brief Return the vector scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator*(const btVector3& v, const btScalar& s) +{ + return btVector3(v.m_floats[0] * s, v.m_floats[1] * s, v.m_floats[2] * s); +} + +/**@brief Return the vector scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator*(const btScalar& s, const btVector3& v) +{ + return v * s; +} + +/**@brief Return the vector inversely scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator/(const btVector3& v, const btScalar& s) +{ + btFullAssert(s != btScalar(0.0)); + return v * (btScalar(1.0) / s); +} + +/**@brief Return the vector inversely scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator/(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] / v2.m_floats[0], v1.m_floats[1] / v2.m_floats[1], v1.m_floats[2] / v2.m_floats[2]); +} + +/**@brief Return the dot product between two vectors */ +SIMD_FORCE_INLINE btScalar +btDot(const btVector3& v1, const btVector3& v2) +{ + return v1.dot(v2); +} + +/**@brief Return the distance squared between two vectors */ +SIMD_FORCE_INLINE btScalar +btDistance2(const btVector3& v1, const btVector3& v2) +{ + return v1.distance2(v2); +} + +/**@brief Return the distance between two vectors */ +SIMD_FORCE_INLINE btScalar +btDistance(const btVector3& v1, const btVector3& v2) +{ + return v1.distance(v2); +} + +/**@brief Return the angle between two vectors */ +SIMD_FORCE_INLINE btScalar +btAngle(const btVector3& v1, const btVector3& v2) +{ + return v1.angle(v2); +} + +/**@brief Return the cross product of two vectors */ +SIMD_FORCE_INLINE btVector3 +btCross(const btVector3& v1, const btVector3& v2) +{ + return v1.cross(v2); +} + +SIMD_FORCE_INLINE btScalar +btTriple(const btVector3& v1, const btVector3& v2, const btVector3& v3) +{ + return v1.triple(v2, v3); +} + +/**@brief Return the linear interpolation between two vectors + * @param v1 One vector + * @param v2 The other vector + * @param t The ration of this to v (t = 0 => return v1, t=1 => return v2) */ +SIMD_FORCE_INLINE btVector3 +lerp(const btVector3& v1, const btVector3& v2, const btScalar& t) +{ + return v1.lerp(v2, t); +} + +SIMD_FORCE_INLINE btScalar btVector3::distance2(const btVector3& v) const +{ + return (v - *this).length2(); +} + +SIMD_FORCE_INLINE btScalar btVector3::distance(const btVector3& v) const +{ + return (v - *this).length(); +} + +SIMD_FORCE_INLINE btVector3 btVector3::normalized() const +{ + return *this / length(); +} + +SIMD_FORCE_INLINE btVector3 btVector3::rotate(const btVector3& wAxis, const btScalar angle) const +{ + // wAxis must be a unit lenght vector + + btVector3 o = wAxis * wAxis.dot(*this); + btVector3 x = *this - o; + btVector3 y; + + y = wAxis.cross(*this); + + return (o + x * btCos(angle) + y * btSin(angle)); +} + +class btVector4 : public btVector3 { +public: + SIMD_FORCE_INLINE btVector4() {} + + SIMD_FORCE_INLINE btVector4(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w) + : btVector3(x, y, z) + { + m_floats[3] = w; + } + + SIMD_FORCE_INLINE btVector4 absolute4() const + { + return btVector4( + btFabs(m_floats[0]), + btFabs(m_floats[1]), + btFabs(m_floats[2]), + btFabs(m_floats[3])); + } + + btScalar getW() const { return m_floats[3]; } + + SIMD_FORCE_INLINE int32_t maxAxis4() const + { + int32_t maxIndex = -1; + btScalar maxVal = btScalar(-BT_LARGE_FLOAT); + if (m_floats[0] > maxVal) { + maxIndex = 0; + maxVal = m_floats[0]; + } + if (m_floats[1] > maxVal) { + maxIndex = 1; + maxVal = m_floats[1]; + } + if (m_floats[2] > maxVal) { + maxIndex = 2; + maxVal = m_floats[2]; + } + if (m_floats[3] > maxVal) { + maxIndex = 3; + } + return maxIndex; + } + + SIMD_FORCE_INLINE int32_t minAxis4() const + { + int32_t minIndex = -1; + btScalar minVal = btScalar(BT_LARGE_FLOAT); + if (m_floats[0] < minVal) { + minIndex = 0; + minVal = m_floats[0]; + } + if (m_floats[1] < minVal) { + minIndex = 1; + minVal = m_floats[1]; + } + if (m_floats[2] < minVal) { + minIndex = 2; + minVal = m_floats[2]; + } + if (m_floats[3] < minVal) { + minIndex = 3; + } + + return minIndex; + } + + SIMD_FORCE_INLINE int32_t closestAxis4() const + { + return absolute4().maxAxis4(); + } + + /**@brief Set x,y,z and zero w + * @param x Value of x + * @param y Value of y + * @param z Value of z + */ + + /* void getValue(btScalar *m) const + { + m[0] = m_floats[0]; + m[1] = m_floats[1]; + m[2] =m_floats[2]; + } +*/ + /**@brief Set the values + * @param x Value of x + * @param y Value of y + * @param z Value of z + * @param w Value of w + */ + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w) + { + m_floats[0] = x; + m_floats[1] = y; + m_floats[2] = z; + m_floats[3] = w; + } +}; + +///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization +SIMD_FORCE_INLINE void btSwapScalarEndian(const btScalar& sourceVal, btScalar& destVal) +{ +#ifdef BT_USE_DOUBLE_PRECISION + unsigned char* dest = (unsigned char*)&destVal; + unsigned char* src = (unsigned char*)&sourceVal; + dest[0] = src[7]; + dest[1] = src[6]; + dest[2] = src[5]; + dest[3] = src[4]; + dest[4] = src[3]; + dest[5] = src[2]; + dest[6] = src[1]; + dest[7] = src[0]; +#else + unsigned char* dest = (unsigned char*)&destVal; + unsigned char* src = (unsigned char*)&sourceVal; + dest[0] = src[3]; + dest[1] = src[2]; + dest[2] = src[1]; + dest[3] = src[0]; +#endif //BT_USE_DOUBLE_PRECISION +} +///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization +SIMD_FORCE_INLINE void btSwapVector3Endian(const btVector3& sourceVec, btVector3& destVec) +{ + for (int32_t i = 0; i < 4; i++) { + btSwapScalarEndian(sourceVec[i], destVec[i]); + } +} + +///btUnSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization +SIMD_FORCE_INLINE void btUnSwapVector3Endian(btVector3& vector) +{ + + btVector3 swappedVec; + for (int32_t i = 0; i < 4; i++) { + btSwapScalarEndian(vector[i], swappedVec[i]); + } + vector = swappedVec; +} + +template <class T> +SIMD_FORCE_INLINE void btPlaneSpace1(const T& n, T& p, T& q) +{ + if (btFabs(n[2]) > SIMDSQRT12) { + // choose p in y-z plane + btScalar a = n[1] * n[1] + n[2] * n[2]; + btScalar k = btRecipSqrt(a); + p[0] = 0; + p[1] = -n[2] * k; + p[2] = n[1] * k; + // set q = n x p + q[0] = a * k; + q[1] = -n[0] * p[2]; + q[2] = n[0] * p[1]; + } + else { + // choose p in x-y plane + btScalar a = n[0] * n[0] + n[1] * n[1]; + btScalar k = btRecipSqrt(a); + p[0] = -n[1] * k; + p[1] = n[0] * k; + p[2] = 0; + // set q = n x p + q[0] = -n[2] * p[1]; + q[1] = n[2] * p[0]; + q[2] = a * k; + } +} + +struct btVector3FloatData { + float m_floats[4]; +}; + +struct btVector3DoubleData { + double m_floats[4]; +}; + +SIMD_FORCE_INLINE void btVector3::serializeFloat(struct btVector3FloatData& dataOut) const +{ + ///could also do a memcpy, check if it is worth it + for (int32_t i = 0; i < 4; i++) + dataOut.m_floats[i] = float(m_floats[i]); +} + +SIMD_FORCE_INLINE void btVector3::deSerializeFloat(const struct btVector3FloatData& dataIn) +{ + for (int32_t i = 0; i < 4; i++) + m_floats[i] = btScalar(dataIn.m_floats[i]); +} + +SIMD_FORCE_INLINE void btVector3::serializeDouble(struct btVector3DoubleData& dataOut) const +{ + ///could also do a memcpy, check if it is worth it + for (int32_t i = 0; i < 4; i++) + dataOut.m_floats[i] = double(m_floats[i]); +} + +SIMD_FORCE_INLINE void btVector3::deSerializeDouble(const struct btVector3DoubleData& dataIn) +{ + for (int32_t i = 0; i < 4; i++) + m_floats[i] = btScalar(dataIn.m_floats[i]); +} + +SIMD_FORCE_INLINE void btVector3::serialize(struct btVector3Data& dataOut) const +{ + ///could also do a memcpy, check if it is worth it + for (int32_t i = 0; i < 4; i++) + dataOut.m_floats[i] = m_floats[i]; +} + +SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3Data& dataIn) +{ + for (int32_t i = 0; i < 4; i++) + m_floats[i] = dataIn.m_floats[i]; +} + +// -- GODOT start -- +}; // namespace VHACD +// -- GODOT end -- + +#endif //BT_VECTOR3_H diff --git a/thirdparty/vhacd/inc/vhacdCircularList.h b/thirdparty/vhacd/inc/vhacdCircularList.h new file mode 100644 index 0000000000..0f5ddf9ecf --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdCircularList.h @@ -0,0 +1,79 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_CIRCULAR_LIST_H +#define VHACD_CIRCULAR_LIST_H +#include <stdlib.h> +namespace VHACD { +//! CircularListElement class. +template <typename T> +class CircularListElement { +public: + T& GetData() { return m_data; } + const T& GetData() const { return m_data; } + CircularListElement<T>*& GetNext() { return m_next; } + CircularListElement<T>*& GetPrev() { return m_prev; } + const CircularListElement<T>*& GetNext() const { return m_next; } + const CircularListElement<T>*& GetPrev() const { return m_prev; } + //! Constructor + CircularListElement(const T& data) { m_data = data; } + CircularListElement(void) {} + //! Destructor + ~CircularListElement(void) {} +private: + T m_data; + CircularListElement<T>* m_next; + CircularListElement<T>* m_prev; + + CircularListElement(const CircularListElement& rhs); +}; +//! CircularList class. +template <typename T> +class CircularList { +public: + CircularListElement<T>*& GetHead() { return m_head; } + const CircularListElement<T>* GetHead() const { return m_head; } + bool IsEmpty() const { return (m_size == 0); } + size_t GetSize() const { return m_size; } + const T& GetData() const { return m_head->GetData(); } + T& GetData() { return m_head->GetData(); } + bool Delete(); + bool Delete(CircularListElement<T>* element); + CircularListElement<T>* Add(const T* data = 0); + CircularListElement<T>* Add(const T& data); + bool Next(); + bool Prev(); + void Clear() + { + while (Delete()) + ; + }; + const CircularList& operator=(const CircularList& rhs); + //! Constructor + CircularList() + { + m_head = 0; + m_size = 0; + } + CircularList(const CircularList& rhs); + //! Destructor + ~CircularList(void) { Clear(); }; +private: + CircularListElement<T>* m_head; //!< a pointer to the head of the circular list + size_t m_size; //!< number of element in the circular list +}; +} +#include "vhacdCircularList.inl" +#endif // VHACD_CIRCULAR_LIST_H
\ No newline at end of file diff --git a/thirdparty/vhacd/inc/vhacdCircularList.inl b/thirdparty/vhacd/inc/vhacdCircularList.inl new file mode 100644 index 0000000000..2be5180524 --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdCircularList.inl @@ -0,0 +1,161 @@ +#pragma once +#ifndef HACD_CIRCULAR_LIST_INL +#define HACD_CIRCULAR_LIST_INL +namespace VHACD +{ + template < typename T > + inline bool CircularList<T>::Delete(CircularListElement<T> * element) + { + if (!element) + { + return false; + } + if (m_size > 1) + { + CircularListElement<T> * next = element->GetNext(); + CircularListElement<T> * prev = element->GetPrev(); + delete element; + m_size--; + if (element == m_head) + { + m_head = next; + } + next->GetPrev() = prev; + prev->GetNext() = next; + return true; + } + else if (m_size == 1) + { + delete m_head; + m_size--; + m_head = 0; + return true; + } + else + { + return false; + } + } + + template < typename T > + inline bool CircularList<T>::Delete() + { + if (m_size > 1) + { + CircularListElement<T> * next = m_head->GetNext(); + CircularListElement<T> * prev = m_head->GetPrev(); + delete m_head; + m_size--; + m_head = next; + next->GetPrev() = prev; + prev->GetNext() = next; + return true; + } + else if (m_size == 1) + { + delete m_head; + m_size--; + m_head = 0; + return true; + } + else + { + return false; + } + } + template < typename T > + inline CircularListElement<T> * CircularList<T>::Add(const T * data) + { + if (m_size == 0) + { + if (data) + { + m_head = new CircularListElement<T>(*data); + } + else + { + m_head = new CircularListElement<T>(); + } + m_head->GetNext() = m_head->GetPrev() = m_head; + } + else + { + CircularListElement<T> * next = m_head->GetNext(); + CircularListElement<T> * element = m_head; + if (data) + { + m_head = new CircularListElement<T>(*data); + } + else + { + m_head = new CircularListElement<T>(); + } + m_head->GetNext() = next; + m_head->GetPrev() = element; + element->GetNext() = m_head; + next->GetPrev() = m_head; + } + m_size++; + return m_head; + } + template < typename T > + inline CircularListElement<T> * CircularList<T>::Add(const T & data) + { + const T * pData = &data; + return Add(pData); + } + template < typename T > + inline bool CircularList<T>::Next() + { + if (m_size == 0) + { + return false; + } + m_head = m_head->GetNext(); + return true; + } + template < typename T > + inline bool CircularList<T>::Prev() + { + if (m_size == 0) + { + return false; + } + m_head = m_head->GetPrev(); + return true; + } + template < typename T > + inline CircularList<T>::CircularList(const CircularList& rhs) + { + if (rhs.m_size > 0) + { + CircularListElement<T> * current = rhs.m_head; + do + { + current = current->GetNext(); + Add(current->GetData()); + } + while ( current != rhs.m_head ); + } + } + template < typename T > + inline const CircularList<T>& CircularList<T>::operator=(const CircularList& rhs) + { + if (&rhs != this) + { + Clear(); + if (rhs.m_size > 0) + { + CircularListElement<T> * current = rhs.m_head; + do + { + current = current->GetNext(); + Add(current->GetData()); + } + while ( current != rhs.m_head ); + } + } + return (*this); + } +} +#endif
\ No newline at end of file diff --git a/thirdparty/vhacd/inc/vhacdICHull.h b/thirdparty/vhacd/inc/vhacdICHull.h new file mode 100644 index 0000000000..132bdcfb3e --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdICHull.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_ICHULL_H +#define VHACD_ICHULL_H +#include "vhacdManifoldMesh.h" +#include "vhacdVector.h" + +namespace VHACD { +//! Incremental Convex Hull algorithm (cf. http://cs.smith.edu/~orourke/books/ftp.html ). +enum ICHullError { + ICHullErrorOK = 0, + ICHullErrorCoplanarPoints, + ICHullErrorNoVolume, + ICHullErrorInconsistent, + ICHullErrorNotEnoughPoints +}; +class ICHull { +public: + static const double sc_eps; + //! + bool IsFlat() { return m_isFlat; } + //! Returns the computed mesh + TMMesh& GetMesh() { return m_mesh; } + //! Add one point to the convex-hull + bool AddPoint(const Vec3<double>& point) { return AddPoints(&point, 1); } + //! Add one point to the convex-hull + bool AddPoint(const Vec3<double>& point, int32_t id); + //! Add points to the convex-hull + bool AddPoints(const Vec3<double>* points, size_t nPoints); + //! + ICHullError Process(); + //! + ICHullError Process(const uint32_t nPointsCH, const double minVolume = 0.0); + //! + bool IsInside(const Vec3<double>& pt0, const double eps = 0.0); + //! + const ICHull& operator=(ICHull& rhs); + + //! Constructor + ICHull(); + //! Destructor + ~ICHull(void){}; + +private: + //! DoubleTriangle builds the initial double triangle. It first finds 3 noncollinear points and makes two faces out of them, in opposite order. It then finds a fourth point that is not coplanar with that face. The vertices are stored in the face structure in counterclockwise order so that the volume between the face and the point is negative. Lastly, the 3 newfaces to the fourth point are constructed and the data structures are cleaned up. + ICHullError DoubleTriangle(); + //! MakeFace creates a new face structure from three vertices (in ccw order). It returns a pointer to the face. + CircularListElement<TMMTriangle>* MakeFace(CircularListElement<TMMVertex>* v0, + CircularListElement<TMMVertex>* v1, + CircularListElement<TMMVertex>* v2, + CircularListElement<TMMTriangle>* fold); + //! + CircularListElement<TMMTriangle>* MakeConeFace(CircularListElement<TMMEdge>* e, CircularListElement<TMMVertex>* v); + //! + bool ProcessPoint(); + //! + bool ComputePointVolume(double& totalVolume, bool markVisibleFaces); + //! + bool FindMaxVolumePoint(const double minVolume = 0.0); + //! + bool CleanEdges(); + //! + bool CleanVertices(uint32_t& addedPoints); + //! + bool CleanTriangles(); + //! + bool CleanUp(uint32_t& addedPoints); + //! + bool MakeCCW(CircularListElement<TMMTriangle>* f, + CircularListElement<TMMEdge>* e, + CircularListElement<TMMVertex>* v); + void Clear(); + +private: + static const int32_t sc_dummyIndex; + TMMesh m_mesh; + SArray<CircularListElement<TMMEdge>*> m_edgesToDelete; + SArray<CircularListElement<TMMEdge>*> m_edgesToUpdate; + SArray<CircularListElement<TMMTriangle>*> m_trianglesToDelete; + Vec3<double> m_normal; + bool m_isFlat; + ICHull(const ICHull& rhs); +}; +} +#endif // VHACD_ICHULL_H diff --git a/thirdparty/vhacd/inc/vhacdManifoldMesh.h b/thirdparty/vhacd/inc/vhacdManifoldMesh.h new file mode 100644 index 0000000000..a48f53c5c5 --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdManifoldMesh.h @@ -0,0 +1,142 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) +All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once +#ifndef VHACD_MANIFOLD_MESH_H +#define VHACD_MANIFOLD_MESH_H +#include "vhacdCircularList.h" +#include "vhacdSArray.h" +#include "vhacdVector.h" +namespace VHACD { +class TMMTriangle; +class TMMEdge; +class TMMesh; +class ICHull; + +//! Vertex data structure used in a triangular manifold mesh (TMM). +class TMMVertex { +public: + void Initialize(); + TMMVertex(void); + ~TMMVertex(void); + +private: + Vec3<double> m_pos; + int32_t m_name; + size_t m_id; + CircularListElement<TMMEdge>* m_duplicate; // pointer to incident cone edge (or NULL) + bool m_onHull; + bool m_tag; + TMMVertex(const TMMVertex& rhs); + friend class ICHull; + friend class TMMesh; + friend class TMMTriangle; + friend class TMMEdge; +}; + +//! Edge data structure used in a triangular manifold mesh (TMM). +class TMMEdge { +public: + void Initialize(); + TMMEdge(void); + ~TMMEdge(void); + +private: + size_t m_id; + CircularListElement<TMMTriangle>* m_triangles[2]; + CircularListElement<TMMVertex>* m_vertices[2]; + CircularListElement<TMMTriangle>* m_newFace; + TMMEdge(const TMMEdge& rhs); + friend class ICHull; + friend class TMMTriangle; + friend class TMMVertex; + friend class TMMesh; +}; + +//! Triangle data structure used in a triangular manifold mesh (TMM). +class TMMTriangle { +public: + void Initialize(); + TMMTriangle(void); + ~TMMTriangle(void); + +private: + size_t m_id; + CircularListElement<TMMEdge>* m_edges[3]; + CircularListElement<TMMVertex>* m_vertices[3]; + bool m_visible; + + TMMTriangle(const TMMTriangle& rhs); + friend class ICHull; + friend class TMMesh; + friend class TMMVertex; + friend class TMMEdge; +}; +//! triangular manifold mesh data structure. +class TMMesh { +public: + //! Returns the number of vertices> + inline size_t GetNVertices() const { return m_vertices.GetSize(); } + //! Returns the number of edges + inline size_t GetNEdges() const { return m_edges.GetSize(); } + //! Returns the number of triangles + inline size_t GetNTriangles() const { return m_triangles.GetSize(); } + //! Returns the vertices circular list + inline const CircularList<TMMVertex>& GetVertices() const { return m_vertices; } + //! Returns the edges circular list + inline const CircularList<TMMEdge>& GetEdges() const { return m_edges; } + //! Returns the triangles circular list + inline const CircularList<TMMTriangle>& GetTriangles() const { return m_triangles; } + //! Returns the vertices circular list + inline CircularList<TMMVertex>& GetVertices() { return m_vertices; } + //! Returns the edges circular list + inline CircularList<TMMEdge>& GetEdges() { return m_edges; } + //! Returns the triangles circular list + inline CircularList<TMMTriangle>& GetTriangles() { return m_triangles; } + //! Add vertex to the mesh + CircularListElement<TMMVertex>* AddVertex() { return m_vertices.Add(); } + //! Add vertex to the mesh + CircularListElement<TMMEdge>* AddEdge() { return m_edges.Add(); } + //! Add vertex to the mesh + CircularListElement<TMMTriangle>* AddTriangle() { return m_triangles.Add(); } + //! Print mesh information + void Print(); + //! + void GetIFS(Vec3<double>* const points, Vec3<int32_t>* const triangles); + //! + void Clear(); + //! + void Copy(TMMesh& mesh); + //! + bool CheckConsistancy(); + //! + bool Normalize(); + //! + bool Denormalize(); + //! Constructor + TMMesh(); + //! Destructor + virtual ~TMMesh(void); + +private: + CircularList<TMMVertex> m_vertices; + CircularList<TMMEdge> m_edges; + CircularList<TMMTriangle> m_triangles; + + // not defined + TMMesh(const TMMesh& rhs); + friend class ICHull; +}; +} +#endif // VHACD_MANIFOLD_MESH_H
\ No newline at end of file diff --git a/thirdparty/vhacd/inc/vhacdMesh.h b/thirdparty/vhacd/inc/vhacdMesh.h new file mode 100644 index 0000000000..a282e2b4fb --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdMesh.h @@ -0,0 +1,130 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_MESH_H +#define VHACD_MESH_H +#include "vhacdSArray.h" +#include "vhacdVector.h" + +#define VHACD_DEBUG_MESH + +namespace VHACD { +enum AXIS { + AXIS_X = 0, + AXIS_Y = 1, + AXIS_Z = 2 +}; +struct Plane { + double m_a; + double m_b; + double m_c; + double m_d; + AXIS m_axis; + short m_index; +}; +#ifdef VHACD_DEBUG_MESH +struct Material { + + Vec3<double> m_diffuseColor; + double m_ambientIntensity; + Vec3<double> m_specularColor; + Vec3<double> m_emissiveColor; + double m_shininess; + double m_transparency; + Material(void) + { + m_diffuseColor.X() = 0.5; + m_diffuseColor.Y() = 0.5; + m_diffuseColor.Z() = 0.5; + m_specularColor.X() = 0.5; + m_specularColor.Y() = 0.5; + m_specularColor.Z() = 0.5; + m_ambientIntensity = 0.4; + m_emissiveColor.X() = 0.0; + m_emissiveColor.Y() = 0.0; + m_emissiveColor.Z() = 0.0; + m_shininess = 0.4; + m_transparency = 0.0; + }; +}; +#endif // VHACD_DEBUG_MESH + +//! Triangular mesh data structure +class Mesh { +public: + void AddPoint(const Vec3<double>& pt) { m_points.PushBack(pt); }; + void SetPoint(size_t index, const Vec3<double>& pt) { m_points[index] = pt; }; + const Vec3<double>& GetPoint(size_t index) const { return m_points[index]; }; + Vec3<double>& GetPoint(size_t index) { return m_points[index]; }; + size_t GetNPoints() const { return m_points.Size(); }; + double* GetPoints() { return (double*)m_points.Data(); } // ugly + const double* const GetPoints() const { return (double*)m_points.Data(); } // ugly + const Vec3<double>* const GetPointsBuffer() const { return m_points.Data(); } // + Vec3<double>* const GetPointsBuffer() { return m_points.Data(); } // + void AddTriangle(const Vec3<int32_t>& tri) { m_triangles.PushBack(tri); }; + void SetTriangle(size_t index, const Vec3<int32_t>& tri) { m_triangles[index] = tri; }; + const Vec3<int32_t>& GetTriangle(size_t index) const { return m_triangles[index]; }; + Vec3<int32_t>& GetTriangle(size_t index) { return m_triangles[index]; }; + size_t GetNTriangles() const { return m_triangles.Size(); }; + int32_t* GetTriangles() { return (int32_t*)m_triangles.Data(); } // ugly + const int32_t* const GetTriangles() const { return (int32_t*)m_triangles.Data(); } // ugly + const Vec3<int32_t>* const GetTrianglesBuffer() const { return m_triangles.Data(); } + Vec3<int32_t>* const GetTrianglesBuffer() { return m_triangles.Data(); } + const Vec3<double>& GetCenter() const { return m_center; } + const Vec3<double>& GetMinBB() const { return m_minBB; } + const Vec3<double>& GetMaxBB() const { return m_maxBB; } + void ClearPoints() { m_points.Clear(); } + void ClearTriangles() { m_triangles.Clear(); } + void Clear() + { + ClearPoints(); + ClearTriangles(); + } + void ResizePoints(size_t nPts) { m_points.Resize(nPts); } + void ResizeTriangles(size_t nTri) { m_triangles.Resize(nTri); } + void CopyPoints(SArray<Vec3<double> >& points) const { points = m_points; } + double GetDiagBB() const { return m_diag; } + double ComputeVolume() const; + void ComputeConvexHull(const double* const pts, + const size_t nPts); + void Clip(const Plane& plane, + SArray<Vec3<double> >& positivePart, + SArray<Vec3<double> >& negativePart) const; + bool IsInside(const Vec3<double>& pt) const; + double ComputeDiagBB(); + Vec3<double> &ComputeCenter(void); + +#ifdef VHACD_DEBUG_MESH + bool LoadOFF(const std::string& fileName, bool invert); + bool SaveVRML2(const std::string& fileName) const; + bool SaveVRML2(std::ofstream& fout, const Material& material) const; + bool SaveOFF(const std::string& fileName) const; +#endif // VHACD_DEBUG_MESH + + //! Constructor. + Mesh(); + //! Destructor. + ~Mesh(void); + +private: + SArray<Vec3<double> > m_points; + SArray<Vec3<int32_t> > m_triangles; + Vec3<double> m_minBB; + Vec3<double> m_maxBB; + Vec3<double> m_center; + double m_diag; +}; +} +#endif
\ No newline at end of file diff --git a/thirdparty/vhacd/inc/vhacdMutex.h b/thirdparty/vhacd/inc/vhacdMutex.h new file mode 100644 index 0000000000..6b09259200 --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdMutex.h @@ -0,0 +1,148 @@ +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** 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. + +*/ + +#pragma once +#ifndef VHACD_MUTEX_H +#define VHACD_MUTEX_H + +#if defined(WIN32) + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x400 +#endif +#include <windows.h> +#pragma comment(lib, "winmm.lib") +#endif + +#if defined(__linux__) +//#include <sys/time.h> +#include <errno.h> +#include <time.h> +#include <unistd.h> +#define __stdcall +#endif + +#if defined(__APPLE__) || defined(__linux__) +#include <pthread.h> +#endif + +#if defined(__APPLE__) +#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE +#endif + +#define VHACD_DEBUG + +//#define VHACD_NDEBUG +#ifdef VHACD_NDEBUG +#define VHACD_VERIFY(x) (x) +#else +#define VHACD_VERIFY(x) assert((x)) +#endif + +namespace VHACD { +class Mutex { +public: + Mutex(void) + { +#if defined(WIN32) || defined(_XBOX) + InitializeCriticalSection(&m_mutex); +#elif defined(__APPLE__) || defined(__linux__) + pthread_mutexattr_t mutexAttr; // Mutex Attribute + VHACD_VERIFY(pthread_mutexattr_init(&mutexAttr) == 0); + VHACD_VERIFY(pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP) == 0); + VHACD_VERIFY(pthread_mutex_init(&m_mutex, &mutexAttr) == 0); + VHACD_VERIFY(pthread_mutexattr_destroy(&mutexAttr) == 0); +#endif + } + ~Mutex(void) + { +#if defined(WIN32) || defined(_XBOX) + DeleteCriticalSection(&m_mutex); +#elif defined(__APPLE__) || defined(__linux__) + VHACD_VERIFY(pthread_mutex_destroy(&m_mutex) == 0); +#endif + } + void Lock(void) + { +#if defined(WIN32) || defined(_XBOX) + EnterCriticalSection(&m_mutex); +#elif defined(__APPLE__) || defined(__linux__) + VHACD_VERIFY(pthread_mutex_lock(&m_mutex) == 0); +#endif + } + bool TryLock(void) + { +#if defined(WIN32) || defined(_XBOX) + bool bRet = false; + //assert(("TryEnterCriticalSection seems to not work on XP???", 0)); + bRet = TryEnterCriticalSection(&m_mutex) ? true : false; + return bRet; +#elif defined(__APPLE__) || defined(__linux__) + int32_t result = pthread_mutex_trylock(&m_mutex); + return (result == 0); +#endif + } + + void Unlock(void) + { +#if defined(WIN32) || defined(_XBOX) + LeaveCriticalSection(&m_mutex); +#elif defined(__APPLE__) || defined(__linux__) + VHACD_VERIFY(pthread_mutex_unlock(&m_mutex) == 0); +#endif + } + +private: +#if defined(WIN32) || defined(_XBOX) + CRITICAL_SECTION m_mutex; +#elif defined(__APPLE__) || defined(__linux__) + pthread_mutex_t m_mutex; +#endif +}; +} +#endif // VHACD_MUTEX_H diff --git a/thirdparty/vhacd/inc/vhacdRaycastMesh.h b/thirdparty/vhacd/inc/vhacdRaycastMesh.h new file mode 100644 index 0000000000..93bf299c07 --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdRaycastMesh.h @@ -0,0 +1,39 @@ +#ifndef RAYCAST_MESH_H + +#define RAYCAST_MESH_H + +#include <stdint.h> + +namespace VHACD +{ + + // Very simple brute force raycast against a triangle mesh. Tests every triangle; no hierachy. + // Does a deep copy, always does calculations with full double float precision + class RaycastMesh + { + public: + static RaycastMesh * createRaycastMesh(uint32_t vcount, // The number of vertices in the source triangle mesh + const double *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc. + uint32_t tcount, // The number of triangles in the source triangle mesh + const uint32_t *indices); // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ... + + static RaycastMesh * createRaycastMesh(uint32_t vcount, // The number of vertices in the source triangle mesh + const float *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc. + uint32_t tcount, // The number of triangles in the source triangle mesh + const uint32_t *indices); // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ... + + + virtual bool raycast(const double *from, // The starting point of the raycast + const double *to, // The ending point of the raycast + const double *closestToPoint, // The point to match the nearest hit location (can just be the 'from' location of no specific point) + double *hitLocation, // The point where the ray hit nearest to the 'closestToPoint' location + double *hitDistance) = 0; // The distance the ray traveled to the hit location + + virtual void release(void) = 0; + protected: + virtual ~RaycastMesh(void) { }; + }; + +} // end of VHACD namespace + +#endif diff --git a/thirdparty/vhacd/inc/vhacdSArray.h b/thirdparty/vhacd/inc/vhacdSArray.h new file mode 100644 index 0000000000..4d84d1ae26 --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdSArray.h @@ -0,0 +1,158 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_SARRAY_H +#define VHACD_SARRAY_H +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define SARRAY_DEFAULT_MIN_SIZE 16 + +namespace VHACD { +//! SArray. +template <typename T, size_t N = 64> +class SArray { +public: + T& operator[](size_t i) + { + T* const data = Data(); + return data[i]; + } + const T& operator[](size_t i) const + { + const T* const data = Data(); + return data[i]; + } + size_t Size() const + { + return m_size; + } + T* const Data() + { + return (m_maxSize == N) ? m_data0 : m_data; + } + const T* const Data() const + { + return (m_maxSize == N) ? m_data0 : m_data; + } + void Clear() + { + m_size = 0; + delete[] m_data; + m_data = 0; + m_maxSize = N; + } + void PopBack() + { + --m_size; + } + void Allocate(size_t size) + { + if (size > m_maxSize) { + T* temp = new T[size]; + memcpy(temp, Data(), m_size * sizeof(T)); + delete[] m_data; + m_data = temp; + m_maxSize = size; + } + } + void Resize(size_t size) + { + Allocate(size); + m_size = size; + } + + void PushBack(const T& value) + { + if (m_size == m_maxSize) { + size_t maxSize = (m_maxSize << 1); + T* temp = new T[maxSize]; + memcpy(temp, Data(), m_maxSize * sizeof(T)); + delete[] m_data; + m_data = temp; + m_maxSize = maxSize; + } + T* const data = Data(); + data[m_size++] = value; + } + bool Find(const T& value, size_t& pos) + { + T* const data = Data(); + for (pos = 0; pos < m_size; ++pos) + if (value == data[pos]) + return true; + return false; + } + bool Insert(const T& value) + { + size_t pos; + if (Find(value, pos)) + return false; + PushBack(value); + return true; + } + bool Erase(const T& value) + { + size_t pos; + T* const data = Data(); + if (Find(value, pos)) { + for (size_t j = pos + 1; j < m_size; ++j) + data[j - 1] = data[j]; + --m_size; + return true; + } + return false; + } + void operator=(const SArray& rhs) + { + if (m_maxSize < rhs.m_size) { + delete[] m_data; + m_maxSize = rhs.m_maxSize; + m_data = new T[m_maxSize]; + } + m_size = rhs.m_size; + memcpy(Data(), rhs.Data(), m_size * sizeof(T)); + } + void Initialize() + { + m_data = 0; + m_size = 0; + m_maxSize = N; + } + SArray(const SArray& rhs) + { + m_data = 0; + m_size = 0; + m_maxSize = N; + *this = rhs; + } + SArray() + { + Initialize(); + } + ~SArray() + { + delete[] m_data; + } + +private: + T m_data0[N]; + T* m_data; + size_t m_size; + size_t m_maxSize; +}; +} +#endif
\ No newline at end of file diff --git a/thirdparty/vhacd/inc/vhacdTimer.h b/thirdparty/vhacd/inc/vhacdTimer.h new file mode 100644 index 0000000000..ba0e2c3364 --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdTimer.h @@ -0,0 +1,121 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_TIMER_H +#define VHACD_TIMER_H + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#endif +#include <windows.h> +#elif __MACH__ +#include <mach/clock.h> +#include <mach/mach.h> +#else +#include <sys/time.h> +#include <time.h> +#endif + +namespace VHACD { +#ifdef _WIN32 +class Timer { +public: + Timer(void) + { + m_start.QuadPart = 0; + m_stop.QuadPart = 0; + QueryPerformanceFrequency(&m_freq); + }; + ~Timer(void){}; + void Tic() + { + QueryPerformanceCounter(&m_start); + } + void Toc() + { + QueryPerformanceCounter(&m_stop); + } + double GetElapsedTime() // in ms + { + LARGE_INTEGER delta; + delta.QuadPart = m_stop.QuadPart - m_start.QuadPart; + return (1000.0 * delta.QuadPart) / (double)m_freq.QuadPart; + } + +private: + LARGE_INTEGER m_start; + LARGE_INTEGER m_stop; + LARGE_INTEGER m_freq; +}; + +#elif __MACH__ +class Timer { +public: + Timer(void) + { + memset(this, 0, sizeof(Timer)); + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &m_cclock); + }; + ~Timer(void) + { + mach_port_deallocate(mach_task_self(), m_cclock); + }; + void Tic() + { + clock_get_time(m_cclock, &m_start); + } + void Toc() + { + clock_get_time(m_cclock, &m_stop); + } + double GetElapsedTime() // in ms + { + return 1000.0 * (m_stop.tv_sec - m_start.tv_sec + (1.0E-9) * (m_stop.tv_nsec - m_start.tv_nsec)); + } + +private: + clock_serv_t m_cclock; + mach_timespec_t m_start; + mach_timespec_t m_stop; +}; +#else +class Timer { +public: + Timer(void) + { + memset(this, 0, sizeof(Timer)); + }; + ~Timer(void){}; + void Tic() + { + clock_gettime(CLOCK_REALTIME, &m_start); + } + void Toc() + { + clock_gettime(CLOCK_REALTIME, &m_stop); + } + double GetElapsedTime() // in ms + { + return 1000.0 * (m_stop.tv_sec - m_start.tv_sec + (1.0E-9) * (m_stop.tv_nsec - m_start.tv_nsec)); + } + +private: + struct timespec m_start; + struct timespec m_stop; +}; +#endif +} +#endif // VHACD_TIMER_H diff --git a/thirdparty/vhacd/inc/vhacdVHACD.h b/thirdparty/vhacd/inc/vhacdVHACD.h new file mode 100644 index 0000000000..d67b107642 --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdVHACD.h @@ -0,0 +1,371 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) +All rights reserved. + + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once +#ifndef VHACD_VHACD_H +#define VHACD_VHACD_H + +#ifdef OPENCL_FOUND +#ifdef __MACH__ +#include <OpenCL/cl.h> +#else +#include <CL/cl.h> +#endif +#endif //OPENCL_FOUND + +#include "vhacdMutex.h" +#include "vhacdVolume.h" +#include "vhacdRaycastMesh.h" +#include <vector> + +#define USE_THREAD 1 +#define OCL_MIN_NUM_PRIMITIVES 4096 +#define CH_APP_MIN_NUM_PRIMITIVES 64000 +namespace VHACD { +class VHACD : public IVHACD { +public: + //! Constructor. + VHACD() + { +#if USE_THREAD == 1 && _OPENMP + m_ompNumProcessors = 2 * omp_get_num_procs(); + omp_set_num_threads(m_ompNumProcessors); +#else //USE_THREAD == 1 && _OPENMP + m_ompNumProcessors = 1; +#endif //USE_THREAD == 1 && _OPENMP +#ifdef CL_VERSION_1_1 + m_oclWorkGroupSize = 0; + m_oclDevice = 0; + m_oclQueue = 0; + m_oclKernelComputePartialVolumes = 0; + m_oclKernelComputeSum = 0; +#endif //CL_VERSION_1_1 + Init(); + } + //! Destructor. + ~VHACD(void) + { + } + uint32_t GetNConvexHulls() const + { + return (uint32_t)m_convexHulls.Size(); + } + void Cancel() + { + SetCancel(true); + } + void GetConvexHull(const uint32_t index, ConvexHull& ch) const + { + Mesh* mesh = m_convexHulls[index]; + ch.m_nPoints = (uint32_t)mesh->GetNPoints(); + ch.m_nTriangles = (uint32_t)mesh->GetNTriangles(); + ch.m_points = mesh->GetPoints(); + ch.m_triangles = (uint32_t *)mesh->GetTriangles(); + ch.m_volume = mesh->ComputeVolume(); + Vec3<double> ¢er = mesh->ComputeCenter(); + ch.m_center[0] = center.X(); + ch.m_center[1] = center.Y(); + ch.m_center[2] = center.Z(); + } + void Clean(void) + { + if (mRaycastMesh) + { + mRaycastMesh->release(); + mRaycastMesh = nullptr; + } + delete m_volume; + delete m_pset; + size_t nCH = m_convexHulls.Size(); + for (size_t p = 0; p < nCH; ++p) { + delete m_convexHulls[p]; + } + m_convexHulls.Clear(); + Init(); + } + void Release(void) + { + delete this; + } + bool Compute(const float* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params); + bool Compute(const double* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params); + bool OCLInit(void* const oclDevice, + IUserLogger* const logger = 0); + bool OCLRelease(IUserLogger* const logger = 0); + + virtual bool ComputeCenterOfMass(double centerOfMass[3]) const; + +private: + void SetCancel(bool cancel) + { + m_cancelMutex.Lock(); + m_cancel = cancel; + m_cancelMutex.Unlock(); + } + bool GetCancel() + { + + m_cancelMutex.Lock(); + bool cancel = m_cancel; + m_cancelMutex.Unlock(); + return cancel; + } + void Update(const double stageProgress, + const double operationProgress, + const Parameters& params) + { + m_stageProgress = stageProgress; + m_operationProgress = operationProgress; + if (params.m_callback) { + params.m_callback->Update(m_overallProgress, + m_stageProgress, + m_operationProgress, + m_stage.c_str(), + m_operation.c_str()); + } + } + void Init() + { + if (mRaycastMesh) + { + mRaycastMesh->release(); + mRaycastMesh = nullptr; + } + memset(m_rot, 0, sizeof(double) * 9); + m_dim = 64; + m_volume = 0; + m_volumeCH0 = 0.0; + m_pset = 0; + m_overallProgress = 0.0; + m_stageProgress = 0.0; + m_operationProgress = 0.0; + m_stage = ""; + m_operation = ""; + m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0; + m_rot[0][0] = m_rot[1][1] = m_rot[2][2] = 1.0; + SetCancel(false); + } + void ComputePrimitiveSet(const Parameters& params); + void ComputeACD(const Parameters& params); + void MergeConvexHulls(const Parameters& params); + void SimplifyConvexHull(Mesh* const ch, const size_t nvertices, const double minVolume); + void SimplifyConvexHulls(const Parameters& params); + void ComputeBestClippingPlane(const PrimitiveSet* inputPSet, + const double volume, + const SArray<Plane>& planes, + const Vec3<double>& preferredCuttingDirection, + const double w, + const double alpha, + const double beta, + const int32_t convexhullDownsampling, + const double progress0, + const double progress1, + Plane& bestPlane, + double& minConcavity, + const Parameters& params); + template <class T> + void AlignMesh(const T* const points, + const uint32_t stridePoints, + const uint32_t nPoints, + const int32_t* const triangles, + const uint32_t strideTriangles, + const uint32_t nTriangles, + const Parameters& params) + { + if (GetCancel() || !params.m_pca) { + return; + } + m_timer.Tic(); + + m_stage = "Align mesh"; + m_operation = "Voxelization"; + + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(0.0, 0.0, params); + if (GetCancel()) { + return; + } + m_dim = (size_t)(pow((double)params.m_resolution, 1.0 / 3.0) + 0.5); + Volume volume; + volume.Voxelize(points, stridePoints, nPoints, + triangles, strideTriangles, nTriangles, + m_dim, m_barycenter, m_rot); + size_t n = volume.GetNPrimitivesOnSurf() + volume.GetNPrimitivesInsideSurf(); + Update(50.0, 100.0, params); + + if (params.m_logger) { + msg.str(""); + msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + if (GetCancel()) { + return; + } + m_operation = "PCA"; + Update(50.0, 0.0, params); + volume.AlignToPrincipalAxes(m_rot); + m_overallProgress = 1.0; + Update(100.0, 100.0, params); + + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + } + template <class T> + void VoxelizeMesh(const T* const points, + const uint32_t stridePoints, + const uint32_t nPoints, + const int32_t* const triangles, + const uint32_t strideTriangles, + const uint32_t nTriangles, + const Parameters& params) + { + if (GetCancel()) { + return; + } + + m_timer.Tic(); + m_stage = "Voxelization"; + + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + delete m_volume; + m_volume = 0; + int32_t iteration = 0; + const int32_t maxIteration = 5; + double progress = 0.0; + while (iteration++ < maxIteration && !m_cancel) { + msg.str(""); + msg << "Iteration " << iteration; + m_operation = msg.str(); + + progress = iteration * 100.0 / maxIteration; + Update(progress, 0.0, params); + + m_volume = new Volume; + m_volume->Voxelize(points, stridePoints, nPoints, + triangles, strideTriangles, nTriangles, + m_dim, m_barycenter, m_rot); + + Update(progress, 100.0, params); + + size_t n = m_volume->GetNPrimitivesOnSurf() + m_volume->GetNPrimitivesInsideSurf(); + if (params.m_logger) { + msg.str(""); + msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + double a = pow((double)(params.m_resolution) / n, 0.33); + size_t dim_next = (size_t)(m_dim * a + 0.5); + if (n < params.m_resolution && iteration < maxIteration && m_volume->GetNPrimitivesOnSurf() < params.m_resolution / 8 && m_dim != dim_next) { + delete m_volume; + m_volume = 0; + m_dim = dim_next; + } + else { + break; + } + } + m_overallProgress = 10.0; + Update(100.0, 100.0, params); + + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + } + template <class T> + bool ComputeACD(const T* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params) + { + Init(); + if (params.m_projectHullVertices) + { + mRaycastMesh = RaycastMesh::createRaycastMesh(nPoints, points, nTriangles, (const uint32_t *)triangles); + } + if (params.m_oclAcceleration) { + // build kernels + } + AlignMesh(points, 3, nPoints, (int32_t *)triangles, 3, nTriangles, params); + VoxelizeMesh(points, 3, nPoints, (int32_t *)triangles, 3, nTriangles, params); + ComputePrimitiveSet(params); + ComputeACD(params); + MergeConvexHulls(params); + SimplifyConvexHulls(params); + if (params.m_oclAcceleration) { + // Release kernels + } + if (GetCancel()) { + Clean(); + return false; + } + return true; + } + +private: + RaycastMesh *mRaycastMesh{ nullptr }; + SArray<Mesh*> m_convexHulls; + std::string m_stage; + std::string m_operation; + double m_overallProgress; + double m_stageProgress; + double m_operationProgress; + double m_rot[3][3]; + double m_volumeCH0; + Vec3<double> m_barycenter; + Timer m_timer; + size_t m_dim; + Volume* m_volume; + PrimitiveSet* m_pset; + Mutex m_cancelMutex; + bool m_cancel; + int32_t m_ompNumProcessors; +#ifdef CL_VERSION_1_1 + cl_device_id* m_oclDevice; + cl_context m_oclContext; + cl_program m_oclProgram; + cl_command_queue* m_oclQueue; + cl_kernel* m_oclKernelComputePartialVolumes; + cl_kernel* m_oclKernelComputeSum; + size_t m_oclWorkGroupSize; +#endif //CL_VERSION_1_1 +}; +} +#endif // VHACD_VHACD_H diff --git a/thirdparty/vhacd/inc/vhacdVector.h b/thirdparty/vhacd/inc/vhacdVector.h new file mode 100644 index 0000000000..efcfcf6ad7 --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdVector.h @@ -0,0 +1,168 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_VECTOR_H +#define VHACD_VECTOR_H +#include <iostream> +#include <math.h> + +namespace VHACD { +//! Vector dim 3. +template <typename T> +class Vec3 { +public: + T& operator[](size_t i) { return m_data[i]; } + const T& operator[](size_t i) const { return m_data[i]; } + T& X(); + T& Y(); + T& Z(); + const T& X() const; + const T& Y() const; + const T& Z() const; + void Normalize(); + T GetNorm() const; + void operator=(const Vec3& rhs); + void operator+=(const Vec3& rhs); + void operator-=(const Vec3& rhs); + void operator-=(T a); + void operator+=(T a); + void operator/=(T a); + void operator*=(T a); + Vec3 operator^(const Vec3& rhs) const; + T operator*(const Vec3& rhs) const; + Vec3 operator+(const Vec3& rhs) const; + Vec3 operator-(const Vec3& rhs) const; + Vec3 operator-() const; + Vec3 operator*(T rhs) const; + Vec3 operator/(T rhs) const; + bool operator<(const Vec3& rhs) const; + bool operator>(const Vec3& rhs) const; + Vec3(); + Vec3(T a); + Vec3(T x, T y, T z); + Vec3(const Vec3& rhs); + /*virtual*/ ~Vec3(void); + + // Compute the center of this bounding box and return the diagonal length + T GetCenter(const Vec3 &bmin, const Vec3 &bmax) + { + X() = (bmin.X() + bmax.X())*0.5; + Y() = (bmin.Y() + bmax.Y())*0.5; + Z() = (bmin.Z() + bmax.Z())*0.5; + T dx = bmax.X() - bmin.X(); + T dy = bmax.Y() - bmin.Y(); + T dz = bmax.Z() - bmin.Z(); + T diagonal = T(sqrt(dx*dx + dy*dy + dz*dz)); + return diagonal; + } + + // Update the min/max values relative to this point + void UpdateMinMax(Vec3 &bmin,Vec3 &bmax) const + { + if (X() < bmin.X()) + { + bmin.X() = X(); + } + if (Y() < bmin.Y()) + { + bmin.Y() = Y(); + } + if (Z() < bmin.Z()) + { + bmin.Z() = Z(); + } + if (X() > bmax.X()) + { + bmax.X() = X(); + } + if (X() > bmax.X()) + { + bmax.X() = X(); + } + if (Y() > bmax.Y()) + { + bmax.Y() = Y(); + } + if (Z() > bmax.Z()) + { + bmax.Z() = Z(); + } + } + + // Returns the squared distance between these two points + T GetDistanceSquared(const Vec3 &p) const + { + T dx = X() - p.X(); + T dy = Y() - p.Y(); + T dz = Z() - p.Z(); + return dx*dx + dy*dy + dz*dz; + } + + T GetDistance(const Vec3 &p) const + { + return sqrt(GetDistanceSquared(p)); + } + + // Returns the raw vector data as a pointer + T* GetData(void) + { + return m_data; + } +private: + T m_data[3]; +}; +//! Vector dim 2. +template <typename T> +class Vec2 { +public: + T& operator[](size_t i) { return m_data[i]; } + const T& operator[](size_t i) const { return m_data[i]; } + T& X(); + T& Y(); + const T& X() const; + const T& Y() const; + void Normalize(); + T GetNorm() const; + void operator=(const Vec2& rhs); + void operator+=(const Vec2& rhs); + void operator-=(const Vec2& rhs); + void operator-=(T a); + void operator+=(T a); + void operator/=(T a); + void operator*=(T a); + T operator^(const Vec2& rhs) const; + T operator*(const Vec2& rhs) const; + Vec2 operator+(const Vec2& rhs) const; + Vec2 operator-(const Vec2& rhs) const; + Vec2 operator-() const; + Vec2 operator*(T rhs) const; + Vec2 operator/(T rhs) const; + Vec2(); + Vec2(T a); + Vec2(T x, T y); + Vec2(const Vec2& rhs); + /*virtual*/ ~Vec2(void); + +private: + T m_data[2]; +}; + +template <typename T> +const bool Colinear(const Vec3<T>& a, const Vec3<T>& b, const Vec3<T>& c); +template <typename T> +const T ComputeVolume4(const Vec3<T>& a, const Vec3<T>& b, const Vec3<T>& c, const Vec3<T>& d); +} +#include "vhacdVector.inl" // template implementation +#endif
\ No newline at end of file diff --git a/thirdparty/vhacd/inc/vhacdVector.inl b/thirdparty/vhacd/inc/vhacdVector.inl new file mode 100644 index 0000000000..223c2ef173 --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdVector.inl @@ -0,0 +1,362 @@ +#pragma once +#ifndef VHACD_VECTOR_INL +#define VHACD_VECTOR_INL +namespace VHACD +{ + template <typename T> + inline Vec3<T> operator*(T lhs, const Vec3<T> & rhs) + { + return Vec3<T>(lhs * rhs.X(), lhs * rhs.Y(), lhs * rhs.Z()); + } + template <typename T> + inline T & Vec3<T>::X() + { + return m_data[0]; + } + template <typename T> + inline T & Vec3<T>::Y() + { + return m_data[1]; + } + template <typename T> + inline T & Vec3<T>::Z() + { + return m_data[2]; + } + template <typename T> + inline const T & Vec3<T>::X() const + { + return m_data[0]; + } + template <typename T> + inline const T & Vec3<T>::Y() const + { + return m_data[1]; + } + template <typename T> + inline const T & Vec3<T>::Z() const + { + return m_data[2]; + } + template <typename T> + inline void Vec3<T>::Normalize() + { + T n = sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]+m_data[2]*m_data[2]); + if (n != 0.0) (*this) /= n; + } + template <typename T> + inline T Vec3<T>::GetNorm() const + { + return sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]+m_data[2]*m_data[2]); + } + template <typename T> + inline void Vec3<T>::operator= (const Vec3 & rhs) + { + this->m_data[0] = rhs.m_data[0]; + this->m_data[1] = rhs.m_data[1]; + this->m_data[2] = rhs.m_data[2]; + } + template <typename T> + inline void Vec3<T>::operator+=(const Vec3 & rhs) + { + this->m_data[0] += rhs.m_data[0]; + this->m_data[1] += rhs.m_data[1]; + this->m_data[2] += rhs.m_data[2]; + } + template <typename T> + inline void Vec3<T>::operator-=(const Vec3 & rhs) + { + this->m_data[0] -= rhs.m_data[0]; + this->m_data[1] -= rhs.m_data[1]; + this->m_data[2] -= rhs.m_data[2]; + } + template <typename T> + inline void Vec3<T>::operator-=(T a) + { + this->m_data[0] -= a; + this->m_data[1] -= a; + this->m_data[2] -= a; + } + template <typename T> + inline void Vec3<T>::operator+=(T a) + { + this->m_data[0] += a; + this->m_data[1] += a; + this->m_data[2] += a; + } + template <typename T> + inline void Vec3<T>::operator/=(T a) + { + this->m_data[0] /= a; + this->m_data[1] /= a; + this->m_data[2] /= a; + } + template <typename T> + inline void Vec3<T>::operator*=(T a) + { + this->m_data[0] *= a; + this->m_data[1] *= a; + this->m_data[2] *= a; + } + template <typename T> + inline Vec3<T> Vec3<T>::operator^ (const Vec3<T> & rhs) const + { + return Vec3<T>(m_data[1] * rhs.m_data[2] - m_data[2] * rhs.m_data[1], + m_data[2] * rhs.m_data[0] - m_data[0] * rhs.m_data[2], + m_data[0] * rhs.m_data[1] - m_data[1] * rhs.m_data[0]); + } + template <typename T> + inline T Vec3<T>::operator*(const Vec3<T> & rhs) const + { + return (m_data[0] * rhs.m_data[0] + m_data[1] * rhs.m_data[1] + m_data[2] * rhs.m_data[2]); + } + template <typename T> + inline Vec3<T> Vec3<T>::operator+(const Vec3<T> & rhs) const + { + return Vec3<T>(m_data[0] + rhs.m_data[0],m_data[1] + rhs.m_data[1],m_data[2] + rhs.m_data[2]); + } + template <typename T> + inline Vec3<T> Vec3<T>::operator-(const Vec3<T> & rhs) const + { + return Vec3<T>(m_data[0] - rhs.m_data[0],m_data[1] - rhs.m_data[1],m_data[2] - rhs.m_data[2]) ; + } + template <typename T> + inline Vec3<T> Vec3<T>::operator-() const + { + return Vec3<T>(-m_data[0],-m_data[1],-m_data[2]) ; + } + + template <typename T> + inline Vec3<T> Vec3<T>::operator*(T rhs) const + { + return Vec3<T>(rhs * this->m_data[0], rhs * this->m_data[1], rhs * this->m_data[2]); + } + template <typename T> + inline Vec3<T> Vec3<T>::operator/ (T rhs) const + { + return Vec3<T>(m_data[0] / rhs, m_data[1] / rhs, m_data[2] / rhs); + } + template <typename T> + inline Vec3<T>::Vec3(T a) + { + m_data[0] = m_data[1] = m_data[2] = a; + } + template <typename T> + inline Vec3<T>::Vec3(T x, T y, T z) + { + m_data[0] = x; + m_data[1] = y; + m_data[2] = z; + } + template <typename T> + inline Vec3<T>::Vec3(const Vec3 & rhs) + { + m_data[0] = rhs.m_data[0]; + m_data[1] = rhs.m_data[1]; + m_data[2] = rhs.m_data[2]; + } + template <typename T> + inline Vec3<T>::~Vec3(void){}; + + template <typename T> + inline Vec3<T>::Vec3() {} + + template<typename T> + inline const bool Colinear(const Vec3<T> & a, const Vec3<T> & b, const Vec3<T> & c) + { + return ((c.Z() - a.Z()) * (b.Y() - a.Y()) - (b.Z() - a.Z()) * (c.Y() - a.Y()) == 0.0 /*EPS*/) && + ((b.Z() - a.Z()) * (c.X() - a.X()) - (b.X() - a.X()) * (c.Z() - a.Z()) == 0.0 /*EPS*/) && + ((b.X() - a.X()) * (c.Y() - a.Y()) - (b.Y() - a.Y()) * (c.X() - a.X()) == 0.0 /*EPS*/); + } + + template<typename T> + inline const T ComputeVolume4(const Vec3<T> & a, const Vec3<T> & b, const Vec3<T> & c, const Vec3<T> & d) + { + return (a-d) * ((b-d) ^ (c-d)); + } + + template <typename T> + inline bool Vec3<T>::operator<(const Vec3 & rhs) const + { + if (X() == rhs[0]) + { + if (Y() == rhs[1]) + { + return (Z()<rhs[2]); + } + return (Y()<rhs[1]); + } + return (X()<rhs[0]); + } + template <typename T> + inline bool Vec3<T>::operator>(const Vec3 & rhs) const + { + if (X() == rhs[0]) + { + if (Y() == rhs[1]) + { + return (Z()>rhs[2]); + } + return (Y()>rhs[1]); + } + return (X()>rhs[0]); + } + template <typename T> + inline Vec2<T> operator*(T lhs, const Vec2<T> & rhs) + { + return Vec2<T>(lhs * rhs.X(), lhs * rhs.Y()); + } + template <typename T> + inline T & Vec2<T>::X() + { + return m_data[0]; + } + template <typename T> + inline T & Vec2<T>::Y() + { + return m_data[1]; + } + template <typename T> + inline const T & Vec2<T>::X() const + { + return m_data[0]; + } + template <typename T> + inline const T & Vec2<T>::Y() const + { + return m_data[1]; + } + template <typename T> + inline void Vec2<T>::Normalize() + { + T n = sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]); + if (n != 0.0) (*this) /= n; + } + template <typename T> + inline T Vec2<T>::GetNorm() const + { + return sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]); + } + template <typename T> + inline void Vec2<T>::operator= (const Vec2 & rhs) + { + this->m_data[0] = rhs.m_data[0]; + this->m_data[1] = rhs.m_data[1]; + } + template <typename T> + inline void Vec2<T>::operator+=(const Vec2 & rhs) + { + this->m_data[0] += rhs.m_data[0]; + this->m_data[1] += rhs.m_data[1]; + } + template <typename T> + inline void Vec2<T>::operator-=(const Vec2 & rhs) + { + this->m_data[0] -= rhs.m_data[0]; + this->m_data[1] -= rhs.m_data[1]; + } + template <typename T> + inline void Vec2<T>::operator-=(T a) + { + this->m_data[0] -= a; + this->m_data[1] -= a; + } + template <typename T> + inline void Vec2<T>::operator+=(T a) + { + this->m_data[0] += a; + this->m_data[1] += a; + } + template <typename T> + inline void Vec2<T>::operator/=(T a) + { + this->m_data[0] /= a; + this->m_data[1] /= a; + } + template <typename T> + inline void Vec2<T>::operator*=(T a) + { + this->m_data[0] *= a; + this->m_data[1] *= a; + } + template <typename T> + inline T Vec2<T>::operator^ (const Vec2<T> & rhs) const + { + return m_data[0] * rhs.m_data[1] - m_data[1] * rhs.m_data[0]; + } + template <typename T> + inline T Vec2<T>::operator*(const Vec2<T> & rhs) const + { + return (m_data[0] * rhs.m_data[0] + m_data[1] * rhs.m_data[1]); + } + template <typename T> + inline Vec2<T> Vec2<T>::operator+(const Vec2<T> & rhs) const + { + return Vec2<T>(m_data[0] + rhs.m_data[0],m_data[1] + rhs.m_data[1]); + } + template <typename T> + inline Vec2<T> Vec2<T>::operator-(const Vec2<T> & rhs) const + { + return Vec2<T>(m_data[0] - rhs.m_data[0],m_data[1] - rhs.m_data[1]); + } + template <typename T> + inline Vec2<T> Vec2<T>::operator-() const + { + return Vec2<T>(-m_data[0],-m_data[1]) ; + } + + template <typename T> + inline Vec2<T> Vec2<T>::operator*(T rhs) const + { + return Vec2<T>(rhs * this->m_data[0], rhs * this->m_data[1]); + } + template <typename T> + inline Vec2<T> Vec2<T>::operator/ (T rhs) const + { + return Vec2<T>(m_data[0] / rhs, m_data[1] / rhs); + } + template <typename T> + inline Vec2<T>::Vec2(T a) + { + m_data[0] = m_data[1] = a; + } + template <typename T> + inline Vec2<T>::Vec2(T x, T y) + { + m_data[0] = x; + m_data[1] = y; + } + template <typename T> + inline Vec2<T>::Vec2(const Vec2 & rhs) + { + m_data[0] = rhs.m_data[0]; + m_data[1] = rhs.m_data[1]; + } + template <typename T> + inline Vec2<T>::~Vec2(void){}; + + template <typename T> + inline Vec2<T>::Vec2() {} + + /* + InsideTriangle decides if a point P is Inside of the triangle + defined by A, B, C. + */ + template<typename T> + inline const bool InsideTriangle(const Vec2<T> & a, const Vec2<T> & b, const Vec2<T> & c, const Vec2<T> & p) + { + T ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; + T cCROSSap, bCROSScp, aCROSSbp; + ax = c.X() - b.X(); ay = c.Y() - b.Y(); + bx = a.X() - c.X(); by = a.Y() - c.Y(); + cx = b.X() - a.X(); cy = b.Y() - a.Y(); + apx= p.X() - a.X(); apy= p.Y() - a.Y(); + bpx= p.X() - b.X(); bpy= p.Y() - b.Y(); + cpx= p.X() - c.X(); cpy= p.Y() - c.Y(); + aCROSSbp = ax*bpy - ay*bpx; + cCROSSap = cx*apy - cy*apx; + bCROSScp = bx*cpy - by*cpx; + return ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0)); + } +} +#endif //VHACD_VECTOR_INL
\ No newline at end of file diff --git a/thirdparty/vhacd/inc/vhacdVolume.h b/thirdparty/vhacd/inc/vhacdVolume.h new file mode 100644 index 0000000000..8c47fa1e2c --- /dev/null +++ b/thirdparty/vhacd/inc/vhacdVolume.h @@ -0,0 +1,430 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_VOLUME_H +#define VHACD_VOLUME_H +#include "vhacdMesh.h" +#include "vhacdVector.h" +#include <assert.h> + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4456 4701) +#endif + +namespace VHACD { + +enum VOXEL_VALUE { + PRIMITIVE_UNDEFINED = 0, + PRIMITIVE_OUTSIDE_SURFACE = 1, + PRIMITIVE_INSIDE_SURFACE = 2, + PRIMITIVE_ON_SURFACE = 3 +}; + +struct Voxel { +public: + short m_coord[3]; + short m_data; +}; + +class PrimitiveSet { +public: + virtual ~PrimitiveSet(){}; + virtual PrimitiveSet* Create() const = 0; + virtual const size_t GetNPrimitives() const = 0; + virtual const size_t GetNPrimitivesOnSurf() const = 0; + virtual const size_t GetNPrimitivesInsideSurf() const = 0; + virtual const double GetEigenValue(AXIS axis) const = 0; + virtual const double ComputeMaxVolumeError() const = 0; + virtual const double ComputeVolume() const = 0; + virtual void Clip(const Plane& plane, PrimitiveSet* const positivePart, + PrimitiveSet* const negativePart) const = 0; + virtual void Intersect(const Plane& plane, SArray<Vec3<double> >* const positivePts, + SArray<Vec3<double> >* const negativePts, const size_t sampling) const = 0; + virtual void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, + SArray<Vec3<double> >* const exteriorPts) const = 0; + virtual void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, + double& negativeVolume) const = 0; + virtual void SelectOnSurface(PrimitiveSet* const onSurfP) const = 0; + virtual void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const = 0; + virtual void ComputeBB() = 0; + virtual void ComputePrincipalAxes() = 0; + virtual void AlignToPrincipalAxes() = 0; + virtual void RevertAlignToPrincipalAxes() = 0; + virtual void Convert(Mesh& mesh, const VOXEL_VALUE value) const = 0; + const Mesh& GetConvexHull() const { return m_convexHull; }; + Mesh& GetConvexHull() { return m_convexHull; }; +private: + Mesh m_convexHull; +}; + +//! +class VoxelSet : public PrimitiveSet { + friend class Volume; + +public: + //! Destructor. + ~VoxelSet(void); + //! Constructor. + VoxelSet(); + + const size_t GetNPrimitives() const { return m_voxels.Size(); } + const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { return m_numVoxelsInsideSurface; } + const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } + const double ComputeVolume() const { return m_unitVolume * m_voxels.Size(); } + const double ComputeMaxVolumeError() const { return m_unitVolume * m_numVoxelsOnSurface; } + const Vec3<short>& GetMinBBVoxels() const { return m_minBBVoxels; } + const Vec3<short>& GetMaxBBVoxels() const { return m_maxBBVoxels; } + const Vec3<double>& GetMinBB() const { return m_minBB; } + const double& GetScale() const { return m_scale; } + const double& GetUnitVolume() const { return m_unitVolume; } + Vec3<double> GetPoint(Vec3<short> voxel) const + { + return Vec3<double>(voxel[0] * m_scale + m_minBB[0], + voxel[1] * m_scale + m_minBB[1], + voxel[2] * m_scale + m_minBB[2]); + } + Vec3<double> GetPoint(const Voxel& voxel) const + { + return Vec3<double>(voxel.m_coord[0] * m_scale + m_minBB[0], + voxel.m_coord[1] * m_scale + m_minBB[1], + voxel.m_coord[2] * m_scale + m_minBB[2]); + } + Vec3<double> GetPoint(Vec3<double> voxel) const + { + return Vec3<double>(voxel[0] * m_scale + m_minBB[0], + voxel[1] * m_scale + m_minBB[1], + voxel[2] * m_scale + m_minBB[2]); + } + void GetPoints(const Voxel& voxel, Vec3<double>* const pts) const; + void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; + void Clip(const Plane& plane, PrimitiveSet* const positivePart, PrimitiveSet* const negativePart) const; + void Intersect(const Plane& plane, SArray<Vec3<double> >* const positivePts, + SArray<Vec3<double> >* const negativePts, const size_t sampling) const; + void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, + SArray<Vec3<double> >* const exteriorPts) const; + void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, double& negativeVolume) const; + void SelectOnSurface(PrimitiveSet* const onSurfP) const; + void ComputeBB(); + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + void ComputePrincipalAxes(); + PrimitiveSet* Create() const + { + return new VoxelSet(); + } + void AlignToPrincipalAxes(){}; + void RevertAlignToPrincipalAxes(){}; + Voxel* const GetVoxels() { return m_voxels.Data(); } + const Voxel* const GetVoxels() const { return m_voxels.Data(); } + +private: + size_t m_numVoxelsOnSurface; + size_t m_numVoxelsInsideSurface; + Vec3<double> m_minBB; + double m_scale; + SArray<Voxel, 8> m_voxels; + double m_unitVolume; + Vec3<double> m_minBBPts; + Vec3<double> m_maxBBPts; + Vec3<short> m_minBBVoxels; + Vec3<short> m_maxBBVoxels; + Vec3<short> m_barycenter; + double m_Q[3][3]; + double m_D[3][3]; + Vec3<double> m_barycenterPCA; +}; + +struct Tetrahedron { +public: + Vec3<double> m_pts[4]; + unsigned char m_data; +}; + +//! +class TetrahedronSet : public PrimitiveSet { + friend class Volume; + +public: + //! Destructor. + ~TetrahedronSet(void); + //! Constructor. + TetrahedronSet(); + + const size_t GetNPrimitives() const { return m_tetrahedra.Size(); } + const size_t GetNPrimitivesOnSurf() const { return m_numTetrahedraOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { return m_numTetrahedraInsideSurface; } + const Vec3<double>& GetMinBB() const { return m_minBB; } + const Vec3<double>& GetMaxBB() const { return m_maxBB; } + const Vec3<double>& GetBarycenter() const { return m_barycenter; } + const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } + const double GetSacle() const { return m_scale; } + const double ComputeVolume() const; + const double ComputeMaxVolumeError() const; + void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; + void ComputePrincipalAxes(); + void AlignToPrincipalAxes(); + void RevertAlignToPrincipalAxes(); + void Clip(const Plane& plane, PrimitiveSet* const positivePart, PrimitiveSet* const negativePart) const; + void Intersect(const Plane& plane, SArray<Vec3<double> >* const positivePts, + SArray<Vec3<double> >* const negativePts, const size_t sampling) const; + void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, + SArray<Vec3<double> >* const exteriorPts) const; + void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, double& negativeVolume) const; + void SelectOnSurface(PrimitiveSet* const onSurfP) const; + void ComputeBB(); + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + inline bool Add(Tetrahedron& tetrahedron); + PrimitiveSet* Create() const + { + return new TetrahedronSet(); + } + static const double EPS; + +private: + void AddClippedTetrahedra(const Vec3<double> (&pts)[10], const int32_t nPts); + + size_t m_numTetrahedraOnSurface; + size_t m_numTetrahedraInsideSurface; + double m_scale; + Vec3<double> m_minBB; + Vec3<double> m_maxBB; + Vec3<double> m_barycenter; + SArray<Tetrahedron, 8> m_tetrahedra; + double m_Q[3][3]; + double m_D[3][3]; +}; + +//! +class Volume { +public: + //! Destructor. + ~Volume(void); + + //! Constructor. + Volume(); + + //! Voxelize + template <class T> + void Voxelize(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, + const int32_t* const triangles, const uint32_t strideTriangles, const uint32_t nTriangles, + const size_t dim, const Vec3<double>& barycenter, const double (&rot)[3][3]); + unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k) + { + assert(i < m_dim[0] || i >= 0); + assert(j < m_dim[0] || j >= 0); + assert(k < m_dim[0] || k >= 0); + return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; + } + const unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k) const + { + assert(i < m_dim[0] || i >= 0); + assert(j < m_dim[0] || j >= 0); + assert(k < m_dim[0] || k >= 0); + return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; + } + const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { return m_numVoxelsInsideSurface; } + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + void Convert(VoxelSet& vset) const; + void Convert(TetrahedronSet& tset) const; + void AlignToPrincipalAxes(double (&rot)[3][3]) const; + +private: + void FillOutsideSurface(const size_t i0, const size_t j0, const size_t k0, const size_t i1, + const size_t j1, const size_t k1); + void FillInsideSurface(); + template <class T> + void ComputeBB(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, + const Vec3<double>& barycenter, const double (&rot)[3][3]); + void Allocate(); + void Free(); + + Vec3<double> m_minBB; + Vec3<double> m_maxBB; + double m_scale; + size_t m_dim[3]; //>! dim + size_t m_numVoxelsOnSurface; + size_t m_numVoxelsInsideSurface; + size_t m_numVoxelsOutsideSurface; + unsigned char* m_data; +}; +int32_t TriBoxOverlap(const Vec3<double>& boxcenter, const Vec3<double>& boxhalfsize, const Vec3<double>& triver0, + const Vec3<double>& triver1, const Vec3<double>& triver2); +template <class T> +inline void ComputeAlignedPoint(const T* const points, const uint32_t idx, const Vec3<double>& barycenter, + const double (&rot)[3][3], Vec3<double>& pt){}; +template <> +inline void ComputeAlignedPoint<float>(const float* const points, const uint32_t idx, const Vec3<double>& barycenter, const double (&rot)[3][3], Vec3<double>& pt) +{ + double x = points[idx + 0] - barycenter[0]; + double y = points[idx + 1] - barycenter[1]; + double z = points[idx + 2] - barycenter[2]; + pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; + pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; + pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; +} +template <> +inline void ComputeAlignedPoint<double>(const double* const points, const uint32_t idx, const Vec3<double>& barycenter, const double (&rot)[3][3], Vec3<double>& pt) +{ + double x = points[idx + 0] - barycenter[0]; + double y = points[idx + 1] - barycenter[1]; + double z = points[idx + 2] - barycenter[2]; + pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; + pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; + pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; +} +template <class T> +void Volume::ComputeBB(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, + const Vec3<double>& barycenter, const double (&rot)[3][3]) +{ + Vec3<double> pt; + ComputeAlignedPoint(points, 0, barycenter, rot, pt); + m_maxBB = pt; + m_minBB = pt; + for (uint32_t v = 1; v < nPoints; ++v) { + ComputeAlignedPoint(points, v * stridePoints, barycenter, rot, pt); + for (int32_t i = 0; i < 3; ++i) { + if (pt[i] < m_minBB[i]) + m_minBB[i] = pt[i]; + else if (pt[i] > m_maxBB[i]) + m_maxBB[i] = pt[i]; + } + } +} +template <class T> +void Volume::Voxelize(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, + const int32_t* const triangles, const uint32_t strideTriangles, const uint32_t nTriangles, + const size_t dim, const Vec3<double>& barycenter, const double (&rot)[3][3]) +{ + if (nPoints == 0) { + return; + } + ComputeBB(points, stridePoints, nPoints, barycenter, rot); + + double d[3] = { m_maxBB[0] - m_minBB[0], m_maxBB[1] - m_minBB[1], m_maxBB[2] - m_minBB[2] }; + double r; + if (d[0] > d[1] && d[0] > d[2]) { + r = d[0]; + m_dim[0] = dim; + m_dim[1] = 2 + static_cast<size_t>(dim * d[1] / d[0]); + m_dim[2] = 2 + static_cast<size_t>(dim * d[2] / d[0]); + } + else if (d[1] > d[0] && d[1] > d[2]) { + r = d[1]; + m_dim[1] = dim; + m_dim[0] = 2 + static_cast<size_t>(dim * d[0] / d[1]); + m_dim[2] = 2 + static_cast<size_t>(dim * d[2] / d[1]); + } + else { + r = d[2]; + m_dim[2] = dim; + m_dim[0] = 2 + static_cast<size_t>(dim * d[0] / d[2]); + m_dim[1] = 2 + static_cast<size_t>(dim * d[1] / d[2]); + } + + m_scale = r / (dim - 1); + double invScale = (dim - 1) / r; + + Allocate(); + m_numVoxelsOnSurface = 0; + m_numVoxelsInsideSurface = 0; + m_numVoxelsOutsideSurface = 0; + + Vec3<double> p[3]; + size_t i, j, k; + size_t i0, j0, k0; + size_t i1, j1, k1; + Vec3<double> boxcenter; + Vec3<double> pt; + const Vec3<double> boxhalfsize(0.5, 0.5, 0.5); + for (size_t t = 0, ti = 0; t < nTriangles; ++t, ti += strideTriangles) { + Vec3<int32_t> tri(triangles[ti + 0], + triangles[ti + 1], + triangles[ti + 2]); + for (int32_t c = 0; c < 3; ++c) { + ComputeAlignedPoint(points, tri[c] * stridePoints, barycenter, rot, pt); + p[c][0] = (pt[0] - m_minBB[0]) * invScale; + p[c][1] = (pt[1] - m_minBB[1]) * invScale; + p[c][2] = (pt[2] - m_minBB[2]) * invScale; + i = static_cast<size_t>(p[c][0] + 0.5); + j = static_cast<size_t>(p[c][1] + 0.5); + k = static_cast<size_t>(p[c][2] + 0.5); + assert(i < m_dim[0] && i >= 0 && j < m_dim[1] && j >= 0 && k < m_dim[2] && k >= 0); + + if (c == 0) { + i0 = i1 = i; + j0 = j1 = j; + k0 = k1 = k; + } + else { + if (i < i0) + i0 = i; + if (j < j0) + j0 = j; + if (k < k0) + k0 = k; + if (i > i1) + i1 = i; + if (j > j1) + j1 = j; + if (k > k1) + k1 = k; + } + } + if (i0 > 0) + --i0; + if (j0 > 0) + --j0; + if (k0 > 0) + --k0; + if (i1 < m_dim[0]) + ++i1; + if (j1 < m_dim[1]) + ++j1; + if (k1 < m_dim[2]) + ++k1; + for (size_t i = i0; i < i1; ++i) { + boxcenter[0] = (double)i; + for (size_t j = j0; j < j1; ++j) { + boxcenter[1] = (double)j; + for (size_t k = k0; k < k1; ++k) { + boxcenter[2] = (double)k; + int32_t res = TriBoxOverlap(boxcenter, boxhalfsize, p[0], p[1], p[2]); + unsigned char& value = GetVoxel(i, j, k); + if (res == 1 && value == PRIMITIVE_UNDEFINED) { + value = PRIMITIVE_ON_SURFACE; + ++m_numVoxelsOnSurface; + } + } + } + } + } + FillOutsideSurface(0, 0, 0, m_dim[0], m_dim[1], 1); + FillOutsideSurface(0, 0, m_dim[2] - 1, m_dim[0], m_dim[1], m_dim[2]); + FillOutsideSurface(0, 0, 0, m_dim[0], 1, m_dim[2]); + FillOutsideSurface(0, m_dim[1] - 1, 0, m_dim[0], m_dim[1], m_dim[2]); + FillOutsideSurface(0, 0, 0, 1, m_dim[1], m_dim[2]); + FillOutsideSurface(m_dim[0] - 1, 0, 0, m_dim[0], m_dim[1], m_dim[2]); + FillInsideSurface(); +} +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +#endif // VHACD_VOLUME_H diff --git a/thirdparty/vhacd/public/VHACD.h b/thirdparty/vhacd/public/VHACD.h new file mode 100644 index 0000000000..863aeda9de --- /dev/null +++ b/thirdparty/vhacd/public/VHACD.h @@ -0,0 +1,153 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_H +#define VHACD_H + +#define VHACD_VERSION_MAJOR 2 +#define VHACD_VERSION_MINOR 3 + +// Changes for version 2.3 +// +// m_gamma : Has been removed. This used to control the error metric to merge convex hulls. Now it uses the 'm_maxConvexHulls' value instead. +// m_maxConvexHulls : This is the maximum number of convex hulls to produce from the merge operation; replaces 'm_gamma'. +// +// Note that decomposition depth is no longer a user provided value. It is now derived from the +// maximum number of hulls requested. +// +// As a convenience to the user, each convex hull produced now includes the volume of the hull as well as it's center. +// +// This version supports a convenience method to automatically make V-HACD run asynchronously in a background thread. +// To get a fully asynchronous version, call 'CreateVHACD_ASYNC' instead of 'CreateVHACD'. You get the same interface however, +// now when computing convex hulls, it is no longer a blocking operation. All callback messages are still returned +// in the application's thread so you don't need to worry about mutex locks or anything in that case. +// To tell if the operation is complete, the application should call 'IsReady'. This will return true if +// the last approximation operation is complete and will dispatch any pending messages. +// If you call 'Compute' while a previous operation was still running, it will automatically cancel the last request +// and begin a new one. To cancel a currently running approximation just call 'Cancel'. +#include <stdint.h> + +namespace VHACD { +class IVHACD { +public: + class IUserCallback { + public: + virtual ~IUserCallback(){}; + virtual void Update(const double overallProgress, + const double stageProgress, + const double operationProgress, + const char* const stage, + const char* const operation) + = 0; + }; + + class IUserLogger { + public: + virtual ~IUserLogger(){}; + virtual void Log(const char* const msg) = 0; + }; + + class ConvexHull { + public: + double* m_points; + uint32_t* m_triangles; + uint32_t m_nPoints; + uint32_t m_nTriangles; + double m_volume; + double m_center[3]; + }; + + class Parameters { + public: + Parameters(void) { Init(); } + void Init(void) + { + m_resolution = 100000; + m_concavity = 0.001; + m_planeDownsampling = 4; + m_convexhullDownsampling = 4; + m_alpha = 0.05; + m_beta = 0.05; + m_pca = 0; + m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based + m_maxNumVerticesPerCH = 64; + m_minVolumePerCH = 0.0001; + m_callback = 0; + m_logger = 0; + m_convexhullApproximation = true; + m_oclAcceleration = true; + m_maxConvexHulls = 1024; + m_projectHullVertices = true; // This will project the output convex hull vertices onto the original source mesh to increase the floating point accuracy of the results + } + double m_concavity; + double m_alpha; + double m_beta; + double m_minVolumePerCH; + IUserCallback* m_callback; + IUserLogger* m_logger; + uint32_t m_resolution; + uint32_t m_maxNumVerticesPerCH; + uint32_t m_planeDownsampling; + uint32_t m_convexhullDownsampling; + uint32_t m_pca; + uint32_t m_mode; + uint32_t m_convexhullApproximation; + uint32_t m_oclAcceleration; + uint32_t m_maxConvexHulls; + bool m_projectHullVertices; + }; + + virtual void Cancel() = 0; + virtual bool Compute(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) + = 0; + virtual bool Compute(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) + = 0; + virtual uint32_t GetNConvexHulls() const = 0; + virtual void GetConvexHull(const uint32_t index, ConvexHull& ch) const = 0; + virtual void Clean(void) = 0; // release internally allocated memory + virtual void Release(void) = 0; // release IVHACD + virtual bool OCLInit(void* const oclDevice, + IUserLogger* const logger = 0) + = 0; + virtual bool OCLRelease(IUserLogger* const logger = 0) = 0; + + // Will compute the center of mass of the convex hull decomposition results and return it + // in 'centerOfMass'. Returns false if the center of mass could not be computed. + virtual bool ComputeCenterOfMass(double centerOfMass[3]) const = 0; + + // In synchronous mode (non-multi-threaded) the state is always 'ready' + // In asynchronous mode, this returns true if the background thread is not still actively computing + // a new solution. In an asynchronous config the 'IsReady' call will report any update or log + // messages in the caller's current thread. + virtual bool IsReady(void) const + { + return true; + } + +protected: + virtual ~IVHACD(void) {} +}; +IVHACD* CreateVHACD(void); +IVHACD* CreateVHACD_ASYNC(void); +} +#endif // VHACD_H diff --git a/thirdparty/vhacd/src/FloatMath.cpp b/thirdparty/vhacd/src/FloatMath.cpp new file mode 100644 index 0000000000..481e875104 --- /dev/null +++ b/thirdparty/vhacd/src/FloatMath.cpp @@ -0,0 +1,17 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <math.h> +#include <float.h> +#include "FloatMath.h" +#include <vector> + +#define REAL float + +#include "FloatMath.inl" + +#undef REAL +#define REAL double + +#include "FloatMath.inl" diff --git a/thirdparty/vhacd/src/FloatMath.inl b/thirdparty/vhacd/src/FloatMath.inl new file mode 100644 index 0000000000..ce529e6f71 --- /dev/null +++ b/thirdparty/vhacd/src/FloatMath.inl @@ -0,0 +1,5276 @@ +// a set of routines that let you do common 3d math +// operations without any vector, matrix, or quaternion +// classes or templates. +// +// a vector (or point) is a 'float *' to 3 floating point numbers. +// a matrix is a 'float *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL +// a quaternion is a 'float *' to 4 floats representing a quaternion x,y,z,w +// + +namespace FLOAT_MATH +{ + +void fm_inverseRT(const REAL matrix[16],const REAL pos[3],REAL t[3]) // inverse rotate translate the point. +{ + + REAL _x = pos[0] - matrix[3*4+0]; + REAL _y = pos[1] - matrix[3*4+1]; + REAL _z = pos[2] - matrix[3*4+2]; + + // Multiply inverse-translated source vector by inverted rotation transform + + t[0] = (matrix[0*4+0] * _x) + (matrix[0*4+1] * _y) + (matrix[0*4+2] * _z); + t[1] = (matrix[1*4+0] * _x) + (matrix[1*4+1] * _y) + (matrix[1*4+2] * _z); + t[2] = (matrix[2*4+0] * _x) + (matrix[2*4+1] * _y) + (matrix[2*4+2] * _z); + +} + +REAL fm_getDeterminant(const REAL matrix[16]) +{ + REAL tempv[3]; + REAL p0[3]; + REAL p1[3]; + REAL p2[3]; + + + p0[0] = matrix[0*4+0]; + p0[1] = matrix[0*4+1]; + p0[2] = matrix[0*4+2]; + + p1[0] = matrix[1*4+0]; + p1[1] = matrix[1*4+1]; + p1[2] = matrix[1*4+2]; + + p2[0] = matrix[2*4+0]; + p2[1] = matrix[2*4+1]; + p2[2] = matrix[2*4+2]; + + fm_cross(tempv,p1,p2); + + return fm_dot(p0,tempv); + +} + +REAL fm_squared(REAL x) { return x*x; }; + +void fm_decomposeTransform(const REAL local_transform[16],REAL trans[3],REAL rot[4],REAL scale[3]) +{ + + trans[0] = local_transform[12]; + trans[1] = local_transform[13]; + trans[2] = local_transform[14]; + + scale[0] = (REAL)sqrt(fm_squared(local_transform[0*4+0]) + fm_squared(local_transform[0*4+1]) + fm_squared(local_transform[0*4+2])); + scale[1] = (REAL)sqrt(fm_squared(local_transform[1*4+0]) + fm_squared(local_transform[1*4+1]) + fm_squared(local_transform[1*4+2])); + scale[2] = (REAL)sqrt(fm_squared(local_transform[2*4+0]) + fm_squared(local_transform[2*4+1]) + fm_squared(local_transform[2*4+2])); + + REAL m[16]; + memcpy(m,local_transform,sizeof(REAL)*16); + + REAL sx = 1.0f / scale[0]; + REAL sy = 1.0f / scale[1]; + REAL sz = 1.0f / scale[2]; + + m[0*4+0]*=sx; + m[0*4+1]*=sx; + m[0*4+2]*=sx; + + m[1*4+0]*=sy; + m[1*4+1]*=sy; + m[1*4+2]*=sy; + + m[2*4+0]*=sz; + m[2*4+1]*=sz; + m[2*4+2]*=sz; + + fm_matrixToQuat(m,rot); + +} + +void fm_getSubMatrix(int32_t ki,int32_t kj,REAL pDst[16],const REAL matrix[16]) +{ + int32_t row, col; + int32_t dstCol = 0, dstRow = 0; + + for ( col = 0; col < 4; col++ ) + { + if ( col == kj ) + { + continue; + } + for ( dstRow = 0, row = 0; row < 4; row++ ) + { + if ( row == ki ) + { + continue; + } + pDst[dstCol*4+dstRow] = matrix[col*4+row]; + dstRow++; + } + dstCol++; + } +} + +void fm_inverseTransform(const REAL matrix[16],REAL inverse_matrix[16]) +{ + REAL determinant = fm_getDeterminant(matrix); + determinant = 1.0f / determinant; + for (int32_t i = 0; i < 4; i++ ) + { + for (int32_t j = 0; j < 4; j++ ) + { + int32_t sign = 1 - ( ( i + j ) % 2 ) * 2; + REAL subMat[16]; + fm_identity(subMat); + fm_getSubMatrix( i, j, subMat, matrix ); + REAL subDeterminant = fm_getDeterminant(subMat); + inverse_matrix[i*4+j] = ( subDeterminant * sign ) * determinant; + } + } +} + +void fm_identity(REAL matrix[16]) // set 4x4 matrix to identity. +{ + matrix[0*4+0] = 1; + matrix[1*4+1] = 1; + matrix[2*4+2] = 1; + matrix[3*4+3] = 1; + + matrix[1*4+0] = 0; + matrix[2*4+0] = 0; + matrix[3*4+0] = 0; + + matrix[0*4+1] = 0; + matrix[2*4+1] = 0; + matrix[3*4+1] = 0; + + matrix[0*4+2] = 0; + matrix[1*4+2] = 0; + matrix[3*4+2] = 0; + + matrix[0*4+3] = 0; + matrix[1*4+3] = 0; + matrix[2*4+3] = 0; + +} + +void fm_quatToEuler(const REAL quat[4],REAL &ax,REAL &ay,REAL &az) +{ + REAL x = quat[0]; + REAL y = quat[1]; + REAL z = quat[2]; + REAL w = quat[3]; + + REAL sint = (2.0f * w * y) - (2.0f * x * z); + REAL cost_temp = 1.0f - (sint * sint); + REAL cost = 0; + + if ( (REAL)fabs(cost_temp) > 0.001f ) + { + cost = (REAL)sqrt( cost_temp ); + } + + REAL sinv, cosv, sinf, cosf; + if ( (REAL)fabs(cost) > 0.001f ) + { + cost = 1.0f / cost; + sinv = ((2.0f * y * z) + (2.0f * w * x)) * cost; + cosv = (1.0f - (2.0f * x * x) - (2.0f * y * y)) * cost; + sinf = ((2.0f * x * y) + (2.0f * w * z)) * cost; + cosf = (1.0f - (2.0f * y * y) - (2.0f * z * z)) * cost; + } + else + { + sinv = (2.0f * w * x) - (2.0f * y * z); + cosv = 1.0f - (2.0f * x * x) - (2.0f * z * z); + sinf = 0; + cosf = 1.0f; + } + + // compute output rotations + ax = (REAL)atan2( sinv, cosv ); + ay = (REAL)atan2( sint, cost ); + az = (REAL)atan2( sinf, cosf ); + +} + +void fm_eulerToMatrix(REAL ax,REAL ay,REAL az,REAL *matrix) // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) +{ + REAL quat[4]; + fm_eulerToQuat(ax,ay,az,quat); + fm_quatToMatrix(quat,matrix); +} + +void fm_getAABB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *bmin,REAL *bmax) +{ + + const uint8_t *source = (const uint8_t *) points; + + bmin[0] = points[0]; + bmin[1] = points[1]; + bmin[2] = points[2]; + + bmax[0] = points[0]; + bmax[1] = points[1]; + bmax[2] = points[2]; + + + for (uint32_t i=1; i<vcount; i++) + { + source+=pstride; + const REAL *p = (const REAL *) source; + + if ( p[0] < bmin[0] ) bmin[0] = p[0]; + if ( p[1] < bmin[1] ) bmin[1] = p[1]; + if ( p[2] < bmin[2] ) bmin[2] = p[2]; + + if ( p[0] > bmax[0] ) bmax[0] = p[0]; + if ( p[1] > bmax[1] ) bmax[1] = p[1]; + if ( p[2] > bmax[2] ) bmax[2] = p[2]; + + } +} + +void fm_eulerToQuat(const REAL *euler,REAL *quat) // convert euler angles to quaternion. +{ + fm_eulerToQuat(euler[0],euler[1],euler[2],quat); +} + +void fm_eulerToQuat(REAL roll,REAL pitch,REAL yaw,REAL *quat) // convert euler angles to quaternion. +{ + roll *= 0.5f; + pitch *= 0.5f; + yaw *= 0.5f; + + REAL cr = (REAL)cos(roll); + REAL cp = (REAL)cos(pitch); + REAL cy = (REAL)cos(yaw); + + REAL sr = (REAL)sin(roll); + REAL sp = (REAL)sin(pitch); + REAL sy = (REAL)sin(yaw); + + REAL cpcy = cp * cy; + REAL spsy = sp * sy; + REAL spcy = sp * cy; + REAL cpsy = cp * sy; + + quat[0] = ( sr * cpcy - cr * spsy); + quat[1] = ( cr * spcy + sr * cpsy); + quat[2] = ( cr * cpsy - sr * spcy); + quat[3] = cr * cpcy + sr * spsy; +} + +void fm_quatToMatrix(const REAL *quat,REAL *matrix) // convert quaterinion rotation to matrix, zeros out the translation component. +{ + + REAL xx = quat[0]*quat[0]; + REAL yy = quat[1]*quat[1]; + REAL zz = quat[2]*quat[2]; + REAL xy = quat[0]*quat[1]; + REAL xz = quat[0]*quat[2]; + REAL yz = quat[1]*quat[2]; + REAL wx = quat[3]*quat[0]; + REAL wy = quat[3]*quat[1]; + REAL wz = quat[3]*quat[2]; + + matrix[0*4+0] = 1 - 2 * ( yy + zz ); + matrix[1*4+0] = 2 * ( xy - wz ); + matrix[2*4+0] = 2 * ( xz + wy ); + + matrix[0*4+1] = 2 * ( xy + wz ); + matrix[1*4+1] = 1 - 2 * ( xx + zz ); + matrix[2*4+1] = 2 * ( yz - wx ); + + matrix[0*4+2] = 2 * ( xz - wy ); + matrix[1*4+2] = 2 * ( yz + wx ); + matrix[2*4+2] = 1 - 2 * ( xx + yy ); + + matrix[3*4+0] = matrix[3*4+1] = matrix[3*4+2] = (REAL) 0.0f; + matrix[0*4+3] = matrix[1*4+3] = matrix[2*4+3] = (REAL) 0.0f; + matrix[3*4+3] =(REAL) 1.0f; + +} + + +void fm_quatRotate(const REAL *quat,const REAL *v,REAL *r) // rotate a vector directly by a quaternion. +{ + REAL left[4]; + + left[0] = quat[3]*v[0] + quat[1]*v[2] - v[1]*quat[2]; + left[1] = quat[3]*v[1] + quat[2]*v[0] - v[2]*quat[0]; + left[2] = quat[3]*v[2] + quat[0]*v[1] - v[0]*quat[1]; + left[3] = - quat[0]*v[0] - quat[1]*v[1] - quat[2]*v[2]; + + r[0] = (left[3]*-quat[0]) + (quat[3]*left[0]) + (left[1]*-quat[2]) - (-quat[1]*left[2]); + r[1] = (left[3]*-quat[1]) + (quat[3]*left[1]) + (left[2]*-quat[0]) - (-quat[2]*left[0]); + r[2] = (left[3]*-quat[2]) + (quat[3]*left[2]) + (left[0]*-quat[1]) - (-quat[0]*left[1]); + +} + + +void fm_getTranslation(const REAL *matrix,REAL *t) +{ + t[0] = matrix[3*4+0]; + t[1] = matrix[3*4+1]; + t[2] = matrix[3*4+2]; +} + +void fm_matrixToQuat(const REAL *matrix,REAL *quat) // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w +{ + + REAL tr = matrix[0*4+0] + matrix[1*4+1] + matrix[2*4+2]; + + // check the diagonal + + if (tr > 0.0f ) + { + REAL s = (REAL) sqrt ( (double) (tr + 1.0f) ); + quat[3] = s * 0.5f; + s = 0.5f / s; + quat[0] = (matrix[1*4+2] - matrix[2*4+1]) * s; + quat[1] = (matrix[2*4+0] - matrix[0*4+2]) * s; + quat[2] = (matrix[0*4+1] - matrix[1*4+0]) * s; + + } + else + { + // diagonal is negative + int32_t nxt[3] = {1, 2, 0}; + REAL qa[4]; + + int32_t i = 0; + + if (matrix[1*4+1] > matrix[0*4+0]) i = 1; + if (matrix[2*4+2] > matrix[i*4+i]) i = 2; + + int32_t j = nxt[i]; + int32_t k = nxt[j]; + + REAL s = (REAL)sqrt ( ((matrix[i*4+i] - (matrix[j*4+j] + matrix[k*4+k])) + 1.0f) ); + + qa[i] = s * 0.5f; + + if (s != 0.0f ) s = 0.5f / s; + + qa[3] = (matrix[j*4+k] - matrix[k*4+j]) * s; + qa[j] = (matrix[i*4+j] + matrix[j*4+i]) * s; + qa[k] = (matrix[i*4+k] + matrix[k*4+i]) * s; + + quat[0] = qa[0]; + quat[1] = qa[1]; + quat[2] = qa[2]; + quat[3] = qa[3]; + } +// fm_normalizeQuat(quat); +} + + +REAL fm_sphereVolume(REAL radius) // return's the volume of a sphere of this radius (4/3 PI * R cubed ) +{ + return (4.0f / 3.0f ) * FM_PI * radius * radius * radius; +} + + +REAL fm_cylinderVolume(REAL radius,REAL h) +{ + return FM_PI * radius * radius *h; +} + +REAL fm_capsuleVolume(REAL radius,REAL h) +{ + REAL volume = fm_sphereVolume(radius); // volume of the sphere portion. + REAL ch = h-radius*2; // this is the cylinder length + if ( ch > 0 ) + { + volume+=fm_cylinderVolume(radius,ch); + } + return volume; +} + +void fm_transform(const REAL matrix[16],const REAL v[3],REAL t[3]) // rotate and translate this point +{ + if ( matrix ) + { + REAL tx = (matrix[0*4+0] * v[0]) + (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]) + matrix[3*4+0]; + REAL ty = (matrix[0*4+1] * v[0]) + (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]) + matrix[3*4+1]; + REAL tz = (matrix[0*4+2] * v[0]) + (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]) + matrix[3*4+2]; + t[0] = tx; + t[1] = ty; + t[2] = tz; + } + else + { + t[0] = v[0]; + t[1] = v[1]; + t[2] = v[2]; + } +} + +void fm_rotate(const REAL matrix[16],const REAL v[3],REAL t[3]) // rotate and translate this point +{ + if ( matrix ) + { + REAL tx = (matrix[0*4+0] * v[0]) + (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]); + REAL ty = (matrix[0*4+1] * v[0]) + (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]); + REAL tz = (matrix[0*4+2] * v[0]) + (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]); + t[0] = tx; + t[1] = ty; + t[2] = tz; + } + else + { + t[0] = v[0]; + t[1] = v[1]; + t[2] = v[2]; + } +} + + +REAL fm_distance(const REAL *p1,const REAL *p2) +{ + REAL dx = p1[0] - p2[0]; + REAL dy = p1[1] - p2[1]; + REAL dz = p1[2] - p2[2]; + + return (REAL)sqrt( dx*dx + dy*dy + dz *dz ); +} + +REAL fm_distanceSquared(const REAL *p1,const REAL *p2) +{ + REAL dx = p1[0] - p2[0]; + REAL dy = p1[1] - p2[1]; + REAL dz = p1[2] - p2[2]; + + return dx*dx + dy*dy + dz *dz; +} + + +REAL fm_distanceSquaredXZ(const REAL *p1,const REAL *p2) +{ + REAL dx = p1[0] - p2[0]; + REAL dz = p1[2] - p2[2]; + + return dx*dx + dz *dz; +} + + +REAL fm_computePlane(const REAL *A,const REAL *B,const REAL *C,REAL *n) // returns D +{ + REAL vx = (B[0] - C[0]); + REAL vy = (B[1] - C[1]); + REAL vz = (B[2] - C[2]); + + REAL wx = (A[0] - B[0]); + REAL wy = (A[1] - B[1]); + REAL wz = (A[2] - B[2]); + + REAL vw_x = vy * wz - vz * wy; + REAL vw_y = vz * wx - vx * wz; + REAL vw_z = vx * wy - vy * wx; + + REAL mag = (REAL)sqrt((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z)); + + if ( mag < 0.000001f ) + { + mag = 0; + } + else + { + mag = 1.0f/mag; + } + + REAL x = vw_x * mag; + REAL y = vw_y * mag; + REAL z = vw_z * mag; + + + REAL D = 0.0f - ((x*A[0])+(y*A[1])+(z*A[2])); + + n[0] = x; + n[1] = y; + n[2] = z; + + return D; +} + +REAL fm_distToPlane(const REAL *plane,const REAL *p) // computes the distance of this point from the plane. +{ + return p[0]*plane[0]+p[1]*plane[1]+p[2]*plane[2]+plane[3]; +} + +REAL fm_dot(const REAL *p1,const REAL *p2) +{ + return p1[0]*p2[0]+p1[1]*p2[1]+p1[2]*p2[2]; +} + +void fm_cross(REAL *cross,const REAL *a,const REAL *b) +{ + cross[0] = a[1]*b[2] - a[2]*b[1]; + cross[1] = a[2]*b[0] - a[0]*b[2]; + cross[2] = a[0]*b[1] - a[1]*b[0]; +} + +REAL fm_computeNormalVector(REAL *n,const REAL *p1,const REAL *p2) +{ + n[0] = p2[0] - p1[0]; + n[1] = p2[1] - p1[1]; + n[2] = p2[2] - p1[2]; + return fm_normalize(n); +} + +bool fm_computeWindingOrder(const REAL *p1,const REAL *p2,const REAL *p3) // returns true if the triangle is clockwise. +{ + bool ret = false; + + REAL v1[3]; + REAL v2[3]; + + fm_computeNormalVector(v1,p1,p2); // p2-p1 (as vector) and then normalized + fm_computeNormalVector(v2,p1,p3); // p3-p1 (as vector) and then normalized + + REAL cross[3]; + + fm_cross(cross, v1, v2 ); + REAL ref[3] = { 1, 0, 0 }; + + REAL d = fm_dot( cross, ref ); + + + if ( d <= 0 ) + ret = false; + else + ret = true; + + return ret; +} + +REAL fm_normalize(REAL *n) // normalize this vector +{ + REAL dist = (REAL)sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]); + if ( dist > 0.0000001f ) + { + REAL mag = 1.0f / dist; + n[0]*=mag; + n[1]*=mag; + n[2]*=mag; + } + else + { + n[0] = 1; + n[1] = 0; + n[2] = 0; + } + + return dist; +} + + +void fm_matrixMultiply(const REAL *pA,const REAL *pB,REAL *pM) +{ +#if 1 + + REAL a = pA[0*4+0] * pB[0*4+0] + pA[0*4+1] * pB[1*4+0] + pA[0*4+2] * pB[2*4+0] + pA[0*4+3] * pB[3*4+0]; + REAL b = pA[0*4+0] * pB[0*4+1] + pA[0*4+1] * pB[1*4+1] + pA[0*4+2] * pB[2*4+1] + pA[0*4+3] * pB[3*4+1]; + REAL c = pA[0*4+0] * pB[0*4+2] + pA[0*4+1] * pB[1*4+2] + pA[0*4+2] * pB[2*4+2] + pA[0*4+3] * pB[3*4+2]; + REAL d = pA[0*4+0] * pB[0*4+3] + pA[0*4+1] * pB[1*4+3] + pA[0*4+2] * pB[2*4+3] + pA[0*4+3] * pB[3*4+3]; + + REAL e = pA[1*4+0] * pB[0*4+0] + pA[1*4+1] * pB[1*4+0] + pA[1*4+2] * pB[2*4+0] + pA[1*4+3] * pB[3*4+0]; + REAL f = pA[1*4+0] * pB[0*4+1] + pA[1*4+1] * pB[1*4+1] + pA[1*4+2] * pB[2*4+1] + pA[1*4+3] * pB[3*4+1]; + REAL g = pA[1*4+0] * pB[0*4+2] + pA[1*4+1] * pB[1*4+2] + pA[1*4+2] * pB[2*4+2] + pA[1*4+3] * pB[3*4+2]; + REAL h = pA[1*4+0] * pB[0*4+3] + pA[1*4+1] * pB[1*4+3] + pA[1*4+2] * pB[2*4+3] + pA[1*4+3] * pB[3*4+3]; + + REAL i = pA[2*4+0] * pB[0*4+0] + pA[2*4+1] * pB[1*4+0] + pA[2*4+2] * pB[2*4+0] + pA[2*4+3] * pB[3*4+0]; + REAL j = pA[2*4+0] * pB[0*4+1] + pA[2*4+1] * pB[1*4+1] + pA[2*4+2] * pB[2*4+1] + pA[2*4+3] * pB[3*4+1]; + REAL k = pA[2*4+0] * pB[0*4+2] + pA[2*4+1] * pB[1*4+2] + pA[2*4+2] * pB[2*4+2] + pA[2*4+3] * pB[3*4+2]; + REAL l = pA[2*4+0] * pB[0*4+3] + pA[2*4+1] * pB[1*4+3] + pA[2*4+2] * pB[2*4+3] + pA[2*4+3] * pB[3*4+3]; + + REAL m = pA[3*4+0] * pB[0*4+0] + pA[3*4+1] * pB[1*4+0] + pA[3*4+2] * pB[2*4+0] + pA[3*4+3] * pB[3*4+0]; + REAL n = pA[3*4+0] * pB[0*4+1] + pA[3*4+1] * pB[1*4+1] + pA[3*4+2] * pB[2*4+1] + pA[3*4+3] * pB[3*4+1]; + REAL o = pA[3*4+0] * pB[0*4+2] + pA[3*4+1] * pB[1*4+2] + pA[3*4+2] * pB[2*4+2] + pA[3*4+3] * pB[3*4+2]; + REAL p = pA[3*4+0] * pB[0*4+3] + pA[3*4+1] * pB[1*4+3] + pA[3*4+2] * pB[2*4+3] + pA[3*4+3] * pB[3*4+3]; + + pM[0] = a; + pM[1] = b; + pM[2] = c; + pM[3] = d; + + pM[4] = e; + pM[5] = f; + pM[6] = g; + pM[7] = h; + + pM[8] = i; + pM[9] = j; + pM[10] = k; + pM[11] = l; + + pM[12] = m; + pM[13] = n; + pM[14] = o; + pM[15] = p; + + +#else + memset(pM, 0, sizeof(REAL)*16); + for(int32_t i=0; i<4; i++ ) + for(int32_t j=0; j<4; j++ ) + for(int32_t k=0; k<4; k++ ) + pM[4*i+j] += pA[4*i+k] * pB[4*k+j]; +#endif +} + + +void fm_eulerToQuatDX(REAL x,REAL y,REAL z,REAL *quat) // convert euler angles to quaternion using the fucked up DirectX method +{ + REAL matrix[16]; + fm_eulerToMatrix(x,y,z,matrix); + fm_matrixToQuat(matrix,quat); +} + +// implementation copied from: http://blogs.msdn.com/mikepelton/archive/2004/10/29/249501.aspx +void fm_eulerToMatrixDX(REAL x,REAL y,REAL z,REAL *matrix) // convert euler angles to quaternion using the fucked up DirectX method. +{ + fm_identity(matrix); + matrix[0*4+0] = (REAL)(cos(z)*cos(y) + sin(z)*sin(x)*sin(y)); + matrix[0*4+1] = (REAL)(sin(z)*cos(x)); + matrix[0*4+2] = (REAL)(cos(z)*-sin(y) + sin(z)*sin(x)*cos(y)); + + matrix[1*4+0] = (REAL)(-sin(z)*cos(y)+cos(z)*sin(x)*sin(y)); + matrix[1*4+1] = (REAL)(cos(z)*cos(x)); + matrix[1*4+2] = (REAL)(sin(z)*sin(y) +cos(z)*sin(x)*cos(y)); + + matrix[2*4+0] = (REAL)(cos(x)*sin(y)); + matrix[2*4+1] = (REAL)(-sin(x)); + matrix[2*4+2] = (REAL)(cos(x)*cos(y)); +} + + +void fm_scale(REAL x,REAL y,REAL z,REAL *fscale) // apply scale to the matrix. +{ + fscale[0*4+0] = x; + fscale[1*4+1] = y; + fscale[2*4+2] = z; +} + + +void fm_composeTransform(const REAL *position,const REAL *quat,const REAL *scale,REAL *matrix) +{ + fm_identity(matrix); + fm_quatToMatrix(quat,matrix); + + if ( scale && ( scale[0] != 1 || scale[1] != 1 || scale[2] != 1 ) ) + { + REAL work[16]; + memcpy(work,matrix,sizeof(REAL)*16); + REAL mscale[16]; + fm_identity(mscale); + fm_scale(scale[0],scale[1],scale[2],mscale); + fm_matrixMultiply(work,mscale,matrix); + } + + matrix[12] = position[0]; + matrix[13] = position[1]; + matrix[14] = position[2]; +} + + +void fm_setTranslation(const REAL *translation,REAL *matrix) +{ + matrix[12] = translation[0]; + matrix[13] = translation[1]; + matrix[14] = translation[2]; +} + +static REAL enorm0_3d ( REAL x0, REAL y0, REAL z0, REAL x1, REAL y1, REAL z1 ) + +/**********************************************************************/ + +/* +Purpose: + +ENORM0_3D computes the Euclidean norm of (P1-P0) in 3D. + +Modified: + +18 April 1999 + +Author: + +John Burkardt + +Parameters: + +Input, REAL X0, Y0, Z0, X1, Y1, Z1, the coordinates of the points +P0 and P1. + +Output, REAL ENORM0_3D, the Euclidean norm of (P1-P0). +*/ +{ + REAL value; + + value = (REAL)sqrt ( + ( x1 - x0 ) * ( x1 - x0 ) + + ( y1 - y0 ) * ( y1 - y0 ) + + ( z1 - z0 ) * ( z1 - z0 ) ); + + return value; +} + + +static REAL triangle_area_3d ( REAL x1, REAL y1, REAL z1, REAL x2,REAL y2, REAL z2, REAL x3, REAL y3, REAL z3 ) + + /**********************************************************************/ + + /* + Purpose: + + TRIANGLE_AREA_3D computes the area of a triangle in 3D. + + Modified: + + 22 April 1999 + + Author: + + John Burkardt + + Parameters: + + Input, REAL X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, the (X,Y,Z) + coordinates of the corners of the triangle. + + Output, REAL TRIANGLE_AREA_3D, the area of the triangle. + */ +{ + REAL a; + REAL alpha; + REAL area; + REAL b; + REAL base; + REAL c; + REAL dot; + REAL height; + /* + Find the projection of (P3-P1) onto (P2-P1). + */ + dot = + ( x2 - x1 ) * ( x3 - x1 ) + + ( y2 - y1 ) * ( y3 - y1 ) + + ( z2 - z1 ) * ( z3 - z1 ); + + base = enorm0_3d ( x1, y1, z1, x2, y2, z2 ); + /* + The height of the triangle is the length of (P3-P1) after its + projection onto (P2-P1) has been subtracted. + */ + if ( base == 0.0 ) { + + height = 0.0; + + } + else { + + alpha = dot / ( base * base ); + + a = x3 - x1 - alpha * ( x2 - x1 ); + b = y3 - y1 - alpha * ( y2 - y1 ); + c = z3 - z1 - alpha * ( z2 - z1 ); + + height = (REAL)sqrt ( a * a + b * b + c * c ); + + } + + area = 0.5f * base * height; + + return area; +} + + +REAL fm_computeArea(const REAL *p1,const REAL *p2,const REAL *p3) +{ + REAL ret = 0; + + ret = triangle_area_3d(p1[0],p1[1],p1[2],p2[0],p2[1],p2[2],p3[0],p3[1],p3[2]); + + return ret; +} + + +void fm_lerp(const REAL *p1,const REAL *p2,REAL *dest,REAL lerpValue) +{ + dest[0] = ((p2[0] - p1[0])*lerpValue) + p1[0]; + dest[1] = ((p2[1] - p1[1])*lerpValue) + p1[1]; + dest[2] = ((p2[2] - p1[2])*lerpValue) + p1[2]; +} + +bool fm_pointTestXZ(const REAL *p,const REAL *i,const REAL *j) +{ + bool ret = false; + + if (((( i[2] <= p[2] ) && ( p[2] < j[2] )) || (( j[2] <= p[2] ) && ( p[2] < i[2] ))) && ( p[0] < (j[0] - i[0]) * (p[2] - i[2]) / (j[2] - i[2]) + i[0])) + ret = true; + + return ret; +}; + + +bool fm_insideTriangleXZ(const REAL *p,const REAL *p1,const REAL *p2,const REAL *p3) +{ + bool ret = false; + + int32_t c = 0; + if ( fm_pointTestXZ(p,p1,p2) ) c = !c; + if ( fm_pointTestXZ(p,p2,p3) ) c = !c; + if ( fm_pointTestXZ(p,p3,p1) ) c = !c; + if ( c ) ret = true; + + return ret; +} + +bool fm_insideAABB(const REAL *pos,const REAL *bmin,const REAL *bmax) +{ + bool ret = false; + + if ( pos[0] >= bmin[0] && pos[0] <= bmax[0] && + pos[1] >= bmin[1] && pos[1] <= bmax[1] && + pos[2] >= bmin[2] && pos[2] <= bmax[2] ) + ret = true; + + return ret; +} + + +uint32_t fm_clipTestPoint(const REAL *bmin,const REAL *bmax,const REAL *pos) +{ + uint32_t ret = 0; + + if ( pos[0] < bmin[0] ) + ret|=FMCS_XMIN; + else if ( pos[0] > bmax[0] ) + ret|=FMCS_XMAX; + + if ( pos[1] < bmin[1] ) + ret|=FMCS_YMIN; + else if ( pos[1] > bmax[1] ) + ret|=FMCS_YMAX; + + if ( pos[2] < bmin[2] ) + ret|=FMCS_ZMIN; + else if ( pos[2] > bmax[2] ) + ret|=FMCS_ZMAX; + + return ret; +} + +uint32_t fm_clipTestPointXZ(const REAL *bmin,const REAL *bmax,const REAL *pos) // only tests X and Z, not Y +{ + uint32_t ret = 0; + + if ( pos[0] < bmin[0] ) + ret|=FMCS_XMIN; + else if ( pos[0] > bmax[0] ) + ret|=FMCS_XMAX; + + if ( pos[2] < bmin[2] ) + ret|=FMCS_ZMIN; + else if ( pos[2] > bmax[2] ) + ret|=FMCS_ZMAX; + + return ret; +} + +uint32_t fm_clipTestAABB(const REAL *bmin,const REAL *bmax,const REAL *p1,const REAL *p2,const REAL *p3,uint32_t &andCode) +{ + uint32_t orCode = 0; + + andCode = FMCS_XMIN | FMCS_XMAX | FMCS_YMIN | FMCS_YMAX | FMCS_ZMIN | FMCS_ZMAX; + + uint32_t c = fm_clipTestPoint(bmin,bmax,p1); + orCode|=c; + andCode&=c; + + c = fm_clipTestPoint(bmin,bmax,p2); + orCode|=c; + andCode&=c; + + c = fm_clipTestPoint(bmin,bmax,p3); + orCode|=c; + andCode&=c; + + return orCode; +} + +bool intersect(const REAL *si,const REAL *ei,const REAL *bmin,const REAL *bmax,REAL *time) +{ + REAL st,et,fst = 0,fet = 1; + + for (int32_t i = 0; i < 3; i++) + { + if (*si < *ei) + { + if (*si > *bmax || *ei < *bmin) + return false; + REAL di = *ei - *si; + st = (*si < *bmin)? (*bmin - *si) / di: 0; + et = (*ei > *bmax)? (*bmax - *si) / di: 1; + } + else + { + if (*ei > *bmax || *si < *bmin) + return false; + REAL di = *ei - *si; + st = (*si > *bmax)? (*bmax - *si) / di: 0; + et = (*ei < *bmin)? (*bmin - *si) / di: 1; + } + + if (st > fst) fst = st; + if (et < fet) fet = et; + if (fet < fst) + return false; + bmin++; bmax++; + si++; ei++; + } + + *time = fst; + return true; +} + + + +bool fm_lineTestAABB(const REAL *p1,const REAL *p2,const REAL *bmin,const REAL *bmax,REAL &time) +{ + bool sect = intersect(p1,p2,bmin,bmax,&time); + return sect; +} + + +bool fm_lineTestAABBXZ(const REAL *p1,const REAL *p2,const REAL *bmin,const REAL *bmax,REAL &time) +{ + REAL _bmin[3]; + REAL _bmax[3]; + + _bmin[0] = bmin[0]; + _bmin[1] = -1e9; + _bmin[2] = bmin[2]; + + _bmax[0] = bmax[0]; + _bmax[1] = 1e9; + _bmax[2] = bmax[2]; + + bool sect = intersect(p1,p2,_bmin,_bmax,&time); + + return sect; +} + +void fm_minmax(const REAL *p,REAL *bmin,REAL *bmax) // accmulate to a min-max value +{ + + if ( p[0] < bmin[0] ) bmin[0] = p[0]; + if ( p[1] < bmin[1] ) bmin[1] = p[1]; + if ( p[2] < bmin[2] ) bmin[2] = p[2]; + + if ( p[0] > bmax[0] ) bmax[0] = p[0]; + if ( p[1] > bmax[1] ) bmax[1] = p[1]; + if ( p[2] > bmax[2] ) bmax[2] = p[2]; + +} + +REAL fm_solveX(const REAL *plane,REAL y,REAL z) // solve for X given this plane equation and the other two components. +{ + REAL x = (y*plane[1]+z*plane[2]+plane[3]) / -plane[0]; + return x; +} + +REAL fm_solveY(const REAL *plane,REAL x,REAL z) // solve for Y given this plane equation and the other two components. +{ + REAL y = (x*plane[0]+z*plane[2]+plane[3]) / -plane[1]; + return y; +} + + +REAL fm_solveZ(const REAL *plane,REAL x,REAL y) // solve for Y given this plane equation and the other two components. +{ + REAL z = (x*plane[0]+y*plane[1]+plane[3]) / -plane[2]; + return z; +} + + +void fm_getAABBCenter(const REAL *bmin,const REAL *bmax,REAL *center) +{ + center[0] = (bmax[0]-bmin[0])*0.5f+bmin[0]; + center[1] = (bmax[1]-bmin[1])*0.5f+bmin[1]; + center[2] = (bmax[2]-bmin[2])*0.5f+bmin[2]; +} + +FM_Axis fm_getDominantAxis(const REAL normal[3]) +{ + FM_Axis ret = FM_XAXIS; + + REAL x = (REAL)fabs(normal[0]); + REAL y = (REAL)fabs(normal[1]); + REAL z = (REAL)fabs(normal[2]); + + if ( y > x && y > z ) + ret = FM_YAXIS; + else if ( z > x && z > y ) + ret = FM_ZAXIS; + + return ret; +} + + +bool fm_lineSphereIntersect(const REAL *center,REAL radius,const REAL *p1,const REAL *p2,REAL *intersect) +{ + bool ret = false; + + REAL dir[3]; + + dir[0] = p2[0]-p1[0]; + dir[1] = p2[1]-p1[1]; + dir[2] = p2[2]-p1[2]; + + REAL distance = (REAL)sqrt( dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2]); + + if ( distance > 0 ) + { + REAL recip = 1.0f / distance; + dir[0]*=recip; + dir[1]*=recip; + dir[2]*=recip; + ret = fm_raySphereIntersect(center,radius,p1,dir,distance,intersect); + } + else + { + dir[0] = center[0]-p1[0]; + dir[1] = center[1]-p1[1]; + dir[2] = center[2]-p1[2]; + REAL d2 = dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2]; + REAL r2 = radius*radius; + if ( d2 < r2 ) + { + ret = true; + if ( intersect ) + { + intersect[0] = p1[0]; + intersect[1] = p1[1]; + intersect[2] = p1[2]; + } + } + } + return ret; +} + +#define DOT(p1,p2) (p1[0]*p2[0]+p1[1]*p2[1]+p1[2]*p2[2]) + +bool fm_raySphereIntersect(const REAL *center,REAL radius,const REAL *pos,const REAL *dir,REAL distance,REAL *intersect) +{ + bool ret = false; + + REAL E0[3]; + + E0[0] = center[0] - pos[0]; + E0[1] = center[1] - pos[1]; + E0[2] = center[2] - pos[2]; + + REAL V[3]; + + V[0] = dir[0]; + V[1] = dir[1]; + V[2] = dir[2]; + + + REAL dist2 = E0[0]*E0[0] + E0[1]*E0[1] + E0[2] * E0[2]; + REAL radius2 = radius*radius; // radius squared.. + + // Bug Fix For Gem, if origin is *inside* the sphere, invert the + // direction vector so that we get a valid intersection location. + if ( dist2 < radius2 ) + { + V[0]*=-1; + V[1]*=-1; + V[2]*=-1; + } + + + REAL v = DOT(E0,V); + + REAL disc = radius2 - (dist2 - v*v); + + if (disc > 0.0f) + { + if ( intersect ) + { + REAL d = (REAL)sqrt(disc); + REAL diff = v-d; + if ( diff < distance ) + { + intersect[0] = pos[0]+V[0]*diff; + intersect[1] = pos[1]+V[1]*diff; + intersect[2] = pos[2]+V[2]*diff; + ret = true; + } + } + } + + return ret; +} + + +void fm_catmullRom(REAL *out_vector,const REAL *p1,const REAL *p2,const REAL *p3,const REAL *p4, const REAL s) +{ + REAL s_squared = s * s; + REAL s_cubed = s_squared * s; + + REAL coefficient_p1 = -s_cubed + 2*s_squared - s; + REAL coefficient_p2 = 3 * s_cubed - 5 * s_squared + 2; + REAL coefficient_p3 = -3 * s_cubed +4 * s_squared + s; + REAL coefficient_p4 = s_cubed - s_squared; + + out_vector[0] = (coefficient_p1 * p1[0] + coefficient_p2 * p2[0] + coefficient_p3 * p3[0] + coefficient_p4 * p4[0])*0.5f; + out_vector[1] = (coefficient_p1 * p1[1] + coefficient_p2 * p2[1] + coefficient_p3 * p3[1] + coefficient_p4 * p4[1])*0.5f; + out_vector[2] = (coefficient_p1 * p1[2] + coefficient_p2 * p2[2] + coefficient_p3 * p3[2] + coefficient_p4 * p4[2])*0.5f; +} + +bool fm_intersectAABB(const REAL *bmin1,const REAL *bmax1,const REAL *bmin2,const REAL *bmax2) +{ + if ((bmin1[0] > bmax2[0]) || (bmin2[0] > bmax1[0])) return false; + if ((bmin1[1] > bmax2[1]) || (bmin2[1] > bmax1[1])) return false; + if ((bmin1[2] > bmax2[2]) || (bmin2[2] > bmax1[2])) return false; + return true; + +} + +bool fm_insideAABB(const REAL *obmin,const REAL *obmax,const REAL *tbmin,const REAL *tbmax) // test if bounding box tbmin/tmbax is fully inside obmin/obmax +{ + bool ret = false; + + if ( tbmax[0] <= obmax[0] && + tbmax[1] <= obmax[1] && + tbmax[2] <= obmax[2] && + tbmin[0] >= obmin[0] && + tbmin[1] >= obmin[1] && + tbmin[2] >= obmin[2] ) ret = true; + + return ret; +} + + +// Reference, from Stan Melax in Game Gems I +// Quaternion q; +// vector3 c = CrossProduct(v0,v1); +// REAL d = DotProduct(v0,v1); +// REAL s = (REAL)sqrt((1+d)*2); +// q.x = c.x / s; +// q.y = c.y / s; +// q.z = c.z / s; +// q.w = s /2.0f; +// return q; +void fm_rotationArc(const REAL *v0,const REAL *v1,REAL *quat) +{ + REAL cross[3]; + + fm_cross(cross,v0,v1); + REAL d = fm_dot(v0,v1); + + if( d<= -0.99999f ) // 180 about x axis + { + if ( fabsf((float)v0[0]) < 0.1f ) + { + quat[0] = 0; + quat[1] = v0[2]; + quat[2] = -v0[1]; + quat[3] = 0; + } + else + { + quat[0] = v0[1]; + quat[1] = -v0[0]; + quat[2] = 0; + quat[3] = 0; + } + REAL magnitudeSquared = quat[0]*quat[0] + quat[1]*quat[1] + quat[2]*quat[2] + quat[3]*quat[3]; + REAL magnitude = sqrtf((float)magnitudeSquared); + REAL recip = 1.0f / magnitude; + quat[0]*=recip; + quat[1]*=recip; + quat[2]*=recip; + quat[3]*=recip; + } + else + { + REAL s = (REAL)sqrt((1+d)*2); + REAL recip = 1.0f / s; + + quat[0] = cross[0] * recip; + quat[1] = cross[1] * recip; + quat[2] = cross[2] * recip; + quat[3] = s * 0.5f; + } +} + + +REAL fm_distancePointLineSegment(const REAL *Point,const REAL *LineStart,const REAL *LineEnd,REAL *intersection,LineSegmentType &type,REAL epsilon) +{ + REAL ret; + + REAL LineMag = fm_distance( LineEnd, LineStart ); + + if ( LineMag > 0 ) + { + REAL U = ( ( ( Point[0] - LineStart[0] ) * ( LineEnd[0] - LineStart[0] ) ) + ( ( Point[1] - LineStart[1] ) * ( LineEnd[1] - LineStart[1] ) ) + ( ( Point[2] - LineStart[2] ) * ( LineEnd[2] - LineStart[2] ) ) ) / ( LineMag * LineMag ); + if( U < 0.0f || U > 1.0f ) + { + REAL d1 = fm_distanceSquared(Point,LineStart); + REAL d2 = fm_distanceSquared(Point,LineEnd); + if ( d1 <= d2 ) + { + ret = (REAL)sqrt(d1); + intersection[0] = LineStart[0]; + intersection[1] = LineStart[1]; + intersection[2] = LineStart[2]; + type = LS_START; + } + else + { + ret = (REAL)sqrt(d2); + intersection[0] = LineEnd[0]; + intersection[1] = LineEnd[1]; + intersection[2] = LineEnd[2]; + type = LS_END; + } + } + else + { + intersection[0] = LineStart[0] + U * ( LineEnd[0] - LineStart[0] ); + intersection[1] = LineStart[1] + U * ( LineEnd[1] - LineStart[1] ); + intersection[2] = LineStart[2] + U * ( LineEnd[2] - LineStart[2] ); + + ret = fm_distance(Point,intersection); + + REAL d1 = fm_distanceSquared(intersection,LineStart); + REAL d2 = fm_distanceSquared(intersection,LineEnd); + REAL mag = (epsilon*2)*(epsilon*2); + + if ( d1 < mag ) // if less than 1/100th the total distance, treat is as the 'start' + { + type = LS_START; + } + else if ( d2 < mag ) + { + type = LS_END; + } + else + { + type = LS_MIDDLE; + } + + } + } + else + { + ret = LineMag; + intersection[0] = LineEnd[0]; + intersection[1] = LineEnd[1]; + intersection[2] = LineEnd[2]; + type = LS_END; + } + + return ret; +} + + +#ifndef BEST_FIT_PLANE_H + +#define BEST_FIT_PLANE_H + +template <class Type> class Eigen +{ +public: + + + void DecrSortEigenStuff(void) + { + Tridiagonal(); //diagonalize the matrix. + QLAlgorithm(); // + DecreasingSort(); + GuaranteeRotation(); + } + + void Tridiagonal(void) + { + Type fM00 = mElement[0][0]; + Type fM01 = mElement[0][1]; + Type fM02 = mElement[0][2]; + Type fM11 = mElement[1][1]; + Type fM12 = mElement[1][2]; + Type fM22 = mElement[2][2]; + + m_afDiag[0] = fM00; + m_afSubd[2] = 0; + if (fM02 != (Type)0.0) + { + Type fLength = (REAL)sqrt(fM01*fM01+fM02*fM02); + Type fInvLength = ((Type)1.0)/fLength; + fM01 *= fInvLength; + fM02 *= fInvLength; + Type fQ = ((Type)2.0)*fM01*fM12+fM02*(fM22-fM11); + m_afDiag[1] = fM11+fM02*fQ; + m_afDiag[2] = fM22-fM02*fQ; + m_afSubd[0] = fLength; + m_afSubd[1] = fM12-fM01*fQ; + mElement[0][0] = (Type)1.0; + mElement[0][1] = (Type)0.0; + mElement[0][2] = (Type)0.0; + mElement[1][0] = (Type)0.0; + mElement[1][1] = fM01; + mElement[1][2] = fM02; + mElement[2][0] = (Type)0.0; + mElement[2][1] = fM02; + mElement[2][2] = -fM01; + m_bIsRotation = false; + } + else + { + m_afDiag[1] = fM11; + m_afDiag[2] = fM22; + m_afSubd[0] = fM01; + m_afSubd[1] = fM12; + mElement[0][0] = (Type)1.0; + mElement[0][1] = (Type)0.0; + mElement[0][2] = (Type)0.0; + mElement[1][0] = (Type)0.0; + mElement[1][1] = (Type)1.0; + mElement[1][2] = (Type)0.0; + mElement[2][0] = (Type)0.0; + mElement[2][1] = (Type)0.0; + mElement[2][2] = (Type)1.0; + m_bIsRotation = true; + } + } + + bool QLAlgorithm(void) + { + const int32_t iMaxIter = 32; + + for (int32_t i0 = 0; i0 <3; i0++) + { + int32_t i1; + for (i1 = 0; i1 < iMaxIter; i1++) + { + int32_t i2; + for (i2 = i0; i2 <= (3-2); i2++) + { + Type fTmp = fabs(m_afDiag[i2]) + fabs(m_afDiag[i2+1]); + if ( fabs(m_afSubd[i2]) + fTmp == fTmp ) + break; + } + if (i2 == i0) + { + break; + } + + Type fG = (m_afDiag[i0+1] - m_afDiag[i0])/(((Type)2.0) * m_afSubd[i0]); + Type fR = (REAL)sqrt(fG*fG+(Type)1.0); + if (fG < (Type)0.0) + { + fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG-fR); + } + else + { + fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG+fR); + } + Type fSin = (Type)1.0, fCos = (Type)1.0, fP = (Type)0.0; + for (int32_t i3 = i2-1; i3 >= i0; i3--) + { + Type fF = fSin*m_afSubd[i3]; + Type fB = fCos*m_afSubd[i3]; + if (fabs(fF) >= fabs(fG)) + { + fCos = fG/fF; + fR = (REAL)sqrt(fCos*fCos+(Type)1.0); + m_afSubd[i3+1] = fF*fR; + fSin = ((Type)1.0)/fR; + fCos *= fSin; + } + else + { + fSin = fF/fG; + fR = (REAL)sqrt(fSin*fSin+(Type)1.0); + m_afSubd[i3+1] = fG*fR; + fCos = ((Type)1.0)/fR; + fSin *= fCos; + } + fG = m_afDiag[i3+1]-fP; + fR = (m_afDiag[i3]-fG)*fSin+((Type)2.0)*fB*fCos; + fP = fSin*fR; + m_afDiag[i3+1] = fG+fP; + fG = fCos*fR-fB; + for (int32_t i4 = 0; i4 < 3; i4++) + { + fF = mElement[i4][i3+1]; + mElement[i4][i3+1] = fSin*mElement[i4][i3]+fCos*fF; + mElement[i4][i3] = fCos*mElement[i4][i3]-fSin*fF; + } + } + m_afDiag[i0] -= fP; + m_afSubd[i0] = fG; + m_afSubd[i2] = (Type)0.0; + } + if (i1 == iMaxIter) + { + return false; + } + } + return true; + } + + void DecreasingSort(void) + { + //sort eigenvalues in decreasing order, e[0] >= ... >= e[iSize-1] + for (int32_t i0 = 0, i1; i0 <= 3-2; i0++) + { + // locate maximum eigenvalue + i1 = i0; + Type fMax = m_afDiag[i1]; + int32_t i2; + for (i2 = i0+1; i2 < 3; i2++) + { + if (m_afDiag[i2] > fMax) + { + i1 = i2; + fMax = m_afDiag[i1]; + } + } + + if (i1 != i0) + { + // swap eigenvalues + m_afDiag[i1] = m_afDiag[i0]; + m_afDiag[i0] = fMax; + // swap eigenvectors + for (i2 = 0; i2 < 3; i2++) + { + Type fTmp = mElement[i2][i0]; + mElement[i2][i0] = mElement[i2][i1]; + mElement[i2][i1] = fTmp; + m_bIsRotation = !m_bIsRotation; + } + } + } + } + + + void GuaranteeRotation(void) + { + if (!m_bIsRotation) + { + // change sign on the first column + for (int32_t iRow = 0; iRow <3; iRow++) + { + mElement[iRow][0] = -mElement[iRow][0]; + } + } + } + + Type mElement[3][3]; + Type m_afDiag[3]; + Type m_afSubd[3]; + bool m_bIsRotation; +}; + +#endif + +bool fm_computeBestFitPlane(uint32_t vcount, + const REAL *points, + uint32_t vstride, + const REAL *weights, + uint32_t wstride, + REAL *plane, + REAL *center) +{ + bool ret = false; + + REAL kOrigin[3] = { 0, 0, 0 }; + + REAL wtotal = 0; + + { + const char *source = (const char *) points; + const char *wsource = (const char *) weights; + + for (uint32_t i=0; i<vcount; i++) + { + + const REAL *p = (const REAL *) source; + + REAL w = 1; + + if ( wsource ) + { + const REAL *ws = (const REAL *) wsource; + w = *ws; // + wsource+=wstride; + } + + kOrigin[0]+=p[0]*w; + kOrigin[1]+=p[1]*w; + kOrigin[2]+=p[2]*w; + + wtotal+=w; + + source+=vstride; + } + } + + REAL recip = 1.0f / wtotal; // reciprocol of total weighting + + kOrigin[0]*=recip; + kOrigin[1]*=recip; + kOrigin[2]*=recip; + + center[0] = kOrigin[0]; + center[1] = kOrigin[1]; + center[2] = kOrigin[2]; + + + REAL fSumXX=0; + REAL fSumXY=0; + REAL fSumXZ=0; + + REAL fSumYY=0; + REAL fSumYZ=0; + REAL fSumZZ=0; + + + { + const char *source = (const char *) points; + const char *wsource = (const char *) weights; + + for (uint32_t i=0; i<vcount; i++) + { + + const REAL *p = (const REAL *) source; + + REAL w = 1; + + if ( wsource ) + { + const REAL *ws = (const REAL *) wsource; + w = *ws; // + wsource+=wstride; + } + + REAL kDiff[3]; + + kDiff[0] = w*(p[0] - kOrigin[0]); // apply vertex weighting! + kDiff[1] = w*(p[1] - kOrigin[1]); + kDiff[2] = w*(p[2] - kOrigin[2]); + + fSumXX+= kDiff[0] * kDiff[0]; // sume of the squares of the differences. + fSumXY+= kDiff[0] * kDiff[1]; // sume of the squares of the differences. + fSumXZ+= kDiff[0] * kDiff[2]; // sume of the squares of the differences. + + fSumYY+= kDiff[1] * kDiff[1]; + fSumYZ+= kDiff[1] * kDiff[2]; + fSumZZ+= kDiff[2] * kDiff[2]; + + + source+=vstride; + } + } + + fSumXX *= recip; + fSumXY *= recip; + fSumXZ *= recip; + fSumYY *= recip; + fSumYZ *= recip; + fSumZZ *= recip; + + // setup the eigensolver + Eigen<REAL> kES; + + kES.mElement[0][0] = fSumXX; + kES.mElement[0][1] = fSumXY; + kES.mElement[0][2] = fSumXZ; + + kES.mElement[1][0] = fSumXY; + kES.mElement[1][1] = fSumYY; + kES.mElement[1][2] = fSumYZ; + + kES.mElement[2][0] = fSumXZ; + kES.mElement[2][1] = fSumYZ; + kES.mElement[2][2] = fSumZZ; + + // compute eigenstuff, smallest eigenvalue is in last position + kES.DecrSortEigenStuff(); + + REAL kNormal[3]; + + kNormal[0] = kES.mElement[0][2]; + kNormal[1] = kES.mElement[1][2]; + kNormal[2] = kES.mElement[2][2]; + + // the minimum energy + plane[0] = kNormal[0]; + plane[1] = kNormal[1]; + plane[2] = kNormal[2]; + + plane[3] = 0 - fm_dot(kNormal,kOrigin); + + ret = true; + + return ret; +} + + +bool fm_colinear(const REAL a1[3],const REAL a2[3],const REAL b1[3],const REAL b2[3],REAL epsilon) // true if these two line segments are co-linear. +{ + bool ret = false; + + REAL dir1[3]; + REAL dir2[3]; + + dir1[0] = (a2[0] - a1[0]); + dir1[1] = (a2[1] - a1[1]); + dir1[2] = (a2[2] - a1[2]); + + dir2[0] = (b2[0]-a1[0]) - (b1[0]-a1[0]); + dir2[1] = (b2[1]-a1[1]) - (b1[1]-a1[1]); + dir2[2] = (b2[2]-a2[2]) - (b1[2]-a2[2]); + + fm_normalize(dir1); + fm_normalize(dir2); + + REAL dot = fm_dot(dir1,dir2); + + if ( dot >= epsilon ) + { + ret = true; + } + + + return ret; +} + +bool fm_colinear(const REAL *p1,const REAL *p2,const REAL *p3,REAL epsilon) +{ + bool ret = false; + + REAL dir1[3]; + REAL dir2[3]; + + dir1[0] = p2[0] - p1[0]; + dir1[1] = p2[1] - p1[1]; + dir1[2] = p2[2] - p1[2]; + + dir2[0] = p3[0] - p2[0]; + dir2[1] = p3[1] - p2[1]; + dir2[2] = p3[2] - p2[2]; + + fm_normalize(dir1); + fm_normalize(dir2); + + REAL dot = fm_dot(dir1,dir2); + + if ( dot >= epsilon ) + { + ret = true; + } + + + return ret; +} + +void fm_initMinMax(const REAL *p,REAL *bmin,REAL *bmax) +{ + bmax[0] = bmin[0] = p[0]; + bmax[1] = bmin[1] = p[1]; + bmax[2] = bmin[2] = p[2]; +} + +IntersectResult fm_intersectLineSegments2d(const REAL *a1,const REAL *a2,const REAL *b1,const REAL *b2,REAL *intersection) +{ + IntersectResult ret; + + REAL denom = ((b2[1] - b1[1])*(a2[0] - a1[0])) - ((b2[0] - b1[0])*(a2[1] - a1[1])); + REAL nume_a = ((b2[0] - b1[0])*(a1[1] - b1[1])) - ((b2[1] - b1[1])*(a1[0] - b1[0])); + REAL nume_b = ((a2[0] - a1[0])*(a1[1] - b1[1])) - ((a2[1] - a1[1])*(a1[0] - b1[0])); + if (denom == 0 ) + { + if(nume_a == 0 && nume_b == 0) + { + ret = IR_COINCIDENT; + } + else + { + ret = IR_PARALLEL; + } + } + else + { + + REAL recip = 1 / denom; + REAL ua = nume_a * recip; + REAL ub = nume_b * recip; + + if(ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1 ) + { + // Get the intersection point. + intersection[0] = a1[0] + ua*(a2[0] - a1[0]); + intersection[1] = a1[1] + ua*(a2[1] - a1[1]); + ret = IR_DO_INTERSECT; + } + else + { + ret = IR_DONT_INTERSECT; + } + } + return ret; +} + +IntersectResult fm_intersectLineSegments2dTime(const REAL *a1,const REAL *a2,const REAL *b1,const REAL *b2,REAL &t1,REAL &t2) +{ + IntersectResult ret; + + REAL denom = ((b2[1] - b1[1])*(a2[0] - a1[0])) - ((b2[0] - b1[0])*(a2[1] - a1[1])); + REAL nume_a = ((b2[0] - b1[0])*(a1[1] - b1[1])) - ((b2[1] - b1[1])*(a1[0] - b1[0])); + REAL nume_b = ((a2[0] - a1[0])*(a1[1] - b1[1])) - ((a2[1] - a1[1])*(a1[0] - b1[0])); + if (denom == 0 ) + { + if(nume_a == 0 && nume_b == 0) + { + ret = IR_COINCIDENT; + } + else + { + ret = IR_PARALLEL; + } + } + else + { + + REAL recip = 1 / denom; + REAL ua = nume_a * recip; + REAL ub = nume_b * recip; + + if(ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1 ) + { + t1 = ua; + t2 = ub; + ret = IR_DO_INTERSECT; + } + else + { + ret = IR_DONT_INTERSECT; + } + } + return ret; +} + +//**** Plane Triangle Intersection + + + + + +// assumes that the points are on opposite sides of the plane! +bool fm_intersectPointPlane(const REAL *p1,const REAL *p2,REAL *split,const REAL *plane) +{ + + REAL dp1 = fm_distToPlane(plane,p1); + REAL dp2 = fm_distToPlane(plane, p2); + if (dp1 <= 0 && dp2 <= 0) + { + return false; + } + if (dp1 >= 0 && dp2 >= 0) + { + return false; + } + + REAL dir[3]; + + dir[0] = p2[0] - p1[0]; + dir[1] = p2[1] - p1[1]; + dir[2] = p2[2] - p1[2]; + + REAL dot1 = dir[0]*plane[0] + dir[1]*plane[1] + dir[2]*plane[2]; + REAL dot2 = dp1 - plane[3]; + + REAL t = -(plane[3] + dot2 ) / dot1; + + split[0] = (dir[0]*t)+p1[0]; + split[1] = (dir[1]*t)+p1[1]; + split[2] = (dir[2]*t)+p1[2]; + + return true; +} + +PlaneTriResult fm_getSidePlane(const REAL *p,const REAL *plane,REAL epsilon) +{ + PlaneTriResult ret = PTR_ON_PLANE; + + REAL d = fm_distToPlane(plane,p); + + if ( d < -epsilon || d > epsilon ) + { + if ( d > 0 ) + ret = PTR_FRONT; // it is 'in front' within the provided epsilon value. + else + ret = PTR_BACK; + } + + return ret; +} + + + +#ifndef PLANE_TRIANGLE_INTERSECTION_H + +#define PLANE_TRIANGLE_INTERSECTION_H + +#define MAXPTS 256 + +template <class Type> class point +{ +public: + + void set(const Type *p) + { + x = p[0]; + y = p[1]; + z = p[2]; + } + + Type x; + Type y; + Type z; +}; + +template <class Type> class plane +{ +public: + plane(const Type *p) + { + normal.x = p[0]; + normal.y = p[1]; + normal.z = p[2]; + D = p[3]; + } + + Type Classify_Point(const point<Type> &p) + { + return p.x*normal.x + p.y*normal.y + p.z*normal.z + D; + } + + point<Type> normal; + Type D; +}; + +template <class Type> class polygon +{ +public: + polygon(void) + { + mVcount = 0; + } + + polygon(const Type *p1,const Type *p2,const Type *p3) + { + mVcount = 3; + mVertices[0].set(p1); + mVertices[1].set(p2); + mVertices[2].set(p3); + } + + + int32_t NumVertices(void) const { return mVcount; }; + + const point<Type>& Vertex(int32_t index) + { + if ( index < 0 ) index+=mVcount; + return mVertices[index]; + }; + + + void set(const point<Type> *pts,int32_t count) + { + for (int32_t i=0; i<count; i++) + { + mVertices[i] = pts[i]; + } + mVcount = count; + } + + + void Split_Polygon(polygon<Type> *poly,plane<Type> *part, polygon<Type> &front, polygon<Type> &back) + { + int32_t count = poly->NumVertices (); + int32_t out_c = 0, in_c = 0; + point<Type> ptA, ptB,outpts[MAXPTS],inpts[MAXPTS]; + Type sideA, sideB; + ptA = poly->Vertex (count - 1); + sideA = part->Classify_Point (ptA); + for (int32_t i = -1; ++i < count;) + { + ptB = poly->Vertex(i); + sideB = part->Classify_Point(ptB); + if (sideB > 0) + { + if (sideA < 0) + { + point<Type> v; + fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x ); + outpts[out_c++] = inpts[in_c++] = v; + } + outpts[out_c++] = ptB; + } + else if (sideB < 0) + { + if (sideA > 0) + { + point<Type> v; + fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x ); + outpts[out_c++] = inpts[in_c++] = v; + } + inpts[in_c++] = ptB; + } + else + outpts[out_c++] = inpts[in_c++] = ptB; + ptA = ptB; + sideA = sideB; + } + + front.set(&outpts[0], out_c); + back.set(&inpts[0], in_c); + } + + int32_t mVcount; + point<Type> mVertices[MAXPTS]; +}; + + + +#endif + +static inline void add(const REAL *p,REAL *dest,uint32_t tstride,uint32_t &pcount) +{ + char *d = (char *) dest; + d = d + pcount*tstride; + dest = (REAL *) d; + dest[0] = p[0]; + dest[1] = p[1]; + dest[2] = p[2]; + pcount++; + assert( pcount <= 4 ); +} + + +PlaneTriResult fm_planeTriIntersection(const REAL *_plane, // the plane equation in Ax+By+Cz+D format + const REAL *triangle, // the source triangle. + uint32_t tstride, // stride in bytes of the input and output *vertices* + REAL epsilon, // the co-planar epsilon value. + REAL *front, // the triangle in front of the + uint32_t &fcount, // number of vertices in the 'front' triangle + REAL *back, // the triangle in back of the plane + uint32_t &bcount) // the number of vertices in the 'back' triangle. +{ + + fcount = 0; + bcount = 0; + + const char *tsource = (const char *) triangle; + + // get the three vertices of the triangle. + const REAL *p1 = (const REAL *) (tsource); + const REAL *p2 = (const REAL *) (tsource+tstride); + const REAL *p3 = (const REAL *) (tsource+tstride*2); + + + PlaneTriResult r1 = fm_getSidePlane(p1,_plane,epsilon); // compute the side of the plane each vertex is on + PlaneTriResult r2 = fm_getSidePlane(p2,_plane,epsilon); + PlaneTriResult r3 = fm_getSidePlane(p3,_plane,epsilon); + + // If any of the points lay right *on* the plane.... + if ( r1 == PTR_ON_PLANE || r2 == PTR_ON_PLANE || r3 == PTR_ON_PLANE ) + { + // If the triangle is completely co-planar, then just treat it as 'front' and return! + if ( r1 == PTR_ON_PLANE && r2 == PTR_ON_PLANE && r3 == PTR_ON_PLANE ) + { + add(p1,front,tstride,fcount); + add(p2,front,tstride,fcount); + add(p3,front,tstride,fcount); + return PTR_FRONT; + } + // Decide to place the co-planar points on the same side as the co-planar point. + PlaneTriResult r= PTR_ON_PLANE; + if ( r1 != PTR_ON_PLANE ) + r = r1; + else if ( r2 != PTR_ON_PLANE ) + r = r2; + else if ( r3 != PTR_ON_PLANE ) + r = r3; + + if ( r1 == PTR_ON_PLANE ) r1 = r; + if ( r2 == PTR_ON_PLANE ) r2 = r; + if ( r3 == PTR_ON_PLANE ) r3 = r; + + } + + if ( r1 == r2 && r1 == r3 ) // if all three vertices are on the same side of the plane. + { + if ( r1 == PTR_FRONT ) // if all three are in front of the plane, then copy to the 'front' output triangle. + { + add(p1,front,tstride,fcount); + add(p2,front,tstride,fcount); + add(p3,front,tstride,fcount); + } + else + { + add(p1,back,tstride,bcount); // if all three are in 'back' then copy to the 'back' output triangle. + add(p2,back,tstride,bcount); + add(p3,back,tstride,bcount); + } + return r1; // if all three points are on the same side of the plane return result + } + + + polygon<REAL> pi(p1,p2,p3); + polygon<REAL> pfront,pback; + + plane<REAL> part(_plane); + + pi.Split_Polygon(&pi,&part,pfront,pback); + + for (int32_t i=0; i<pfront.mVcount; i++) + { + add( &pfront.mVertices[i].x, front, tstride, fcount ); + } + + for (int32_t i=0; i<pback.mVcount; i++) + { + add( &pback.mVertices[i].x, back, tstride, bcount ); + } + + PlaneTriResult ret = PTR_SPLIT; + + if ( fcount < 3 ) fcount = 0; + if ( bcount < 3 ) bcount = 0; + + if ( fcount == 0 && bcount ) + ret = PTR_BACK; + + if ( bcount == 0 && fcount ) + ret = PTR_FRONT; + + + return ret; +} + +// computes the OBB for this set of points relative to this transform matrix. +void computeOBB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *sides,REAL *matrix) +{ + const char *src = (const char *) points; + + REAL bmin[3] = { 1e9, 1e9, 1e9 }; + REAL bmax[3] = { -1e9, -1e9, -1e9 }; + + for (uint32_t i=0; i<vcount; i++) + { + const REAL *p = (const REAL *) src; + REAL t[3]; + + fm_inverseRT(matrix, p, t ); // inverse rotate translate + + if ( t[0] < bmin[0] ) bmin[0] = t[0]; + if ( t[1] < bmin[1] ) bmin[1] = t[1]; + if ( t[2] < bmin[2] ) bmin[2] = t[2]; + + if ( t[0] > bmax[0] ) bmax[0] = t[0]; + if ( t[1] > bmax[1] ) bmax[1] = t[1]; + if ( t[2] > bmax[2] ) bmax[2] = t[2]; + + src+=pstride; + } + + REAL center[3]; + + sides[0] = bmax[0]-bmin[0]; + sides[1] = bmax[1]-bmin[1]; + sides[2] = bmax[2]-bmin[2]; + + center[0] = sides[0]*0.5f+bmin[0]; + center[1] = sides[1]*0.5f+bmin[1]; + center[2] = sides[2]*0.5f+bmin[2]; + + REAL ocenter[3]; + + fm_rotate(matrix,center,ocenter); + + matrix[12]+=ocenter[0]; + matrix[13]+=ocenter[1]; + matrix[14]+=ocenter[2]; + +} + +void fm_computeBestFitOBB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *sides,REAL *matrix,bool bruteForce) +{ + REAL plane[4]; + REAL center[3]; + fm_computeBestFitPlane(vcount,points,pstride,0,0,plane,center); + fm_planeToMatrix(plane,matrix); + computeOBB( vcount, points, pstride, sides, matrix ); + + REAL refmatrix[16]; + memcpy(refmatrix,matrix,16*sizeof(REAL)); + + REAL volume = sides[0]*sides[1]*sides[2]; + if ( bruteForce ) + { + for (REAL a=10; a<180; a+=10) + { + REAL quat[4]; + fm_eulerToQuat(0,a*FM_DEG_TO_RAD,0,quat); + REAL temp[16]; + REAL pmatrix[16]; + fm_quatToMatrix(quat,temp); + fm_matrixMultiply(temp,refmatrix,pmatrix); + REAL psides[3]; + computeOBB( vcount, points, pstride, psides, pmatrix ); + REAL v = psides[0]*psides[1]*psides[2]; + if ( v < volume ) + { + volume = v; + memcpy(matrix,pmatrix,sizeof(REAL)*16); + sides[0] = psides[0]; + sides[1] = psides[1]; + sides[2] = psides[2]; + } + } + } +} + +void fm_computeBestFitOBB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *sides,REAL *pos,REAL *quat,bool bruteForce) +{ + REAL matrix[16]; + fm_computeBestFitOBB(vcount,points,pstride,sides,matrix,bruteForce); + fm_getTranslation(matrix,pos); + fm_matrixToQuat(matrix,quat); +} + +void fm_computeBestFitABB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *sides,REAL *pos) +{ + REAL bmin[3]; + REAL bmax[3]; + + bmin[0] = points[0]; + bmin[1] = points[1]; + bmin[2] = points[2]; + + bmax[0] = points[0]; + bmax[1] = points[1]; + bmax[2] = points[2]; + + const char *cp = (const char *) points; + for (uint32_t i=0; i<vcount; i++) + { + const REAL *p = (const REAL *) cp; + + if ( p[0] < bmin[0] ) bmin[0] = p[0]; + if ( p[1] < bmin[1] ) bmin[1] = p[1]; + if ( p[2] < bmin[2] ) bmin[2] = p[2]; + + if ( p[0] > bmax[0] ) bmax[0] = p[0]; + if ( p[1] > bmax[1] ) bmax[1] = p[1]; + if ( p[2] > bmax[2] ) bmax[2] = p[2]; + + cp+=pstride; + } + + + sides[0] = bmax[0] - bmin[0]; + sides[1] = bmax[1] - bmin[1]; + sides[2] = bmax[2] - bmin[2]; + + pos[0] = bmin[0]+sides[0]*0.5f; + pos[1] = bmin[1]+sides[1]*0.5f; + pos[2] = bmin[2]+sides[2]*0.5f; + +} + + +void fm_planeToMatrix(const REAL *plane,REAL *matrix) // convert a plane equation to a 4x4 rotation matrix +{ + REAL ref[3] = { 0, 1, 0 }; + REAL quat[4]; + fm_rotationArc(ref,plane,quat); + fm_quatToMatrix(quat,matrix); + REAL origin[3] = { 0, -plane[3], 0 }; + REAL center[3]; + fm_transform(matrix,origin,center); + fm_setTranslation(center,matrix); +} + +void fm_planeToQuat(const REAL *plane,REAL *quat,REAL *pos) // convert a plane equation to a quaternion and translation +{ + REAL ref[3] = { 0, 1, 0 }; + REAL matrix[16]; + fm_rotationArc(ref,plane,quat); + fm_quatToMatrix(quat,matrix); + REAL origin[3] = { 0, plane[3], 0 }; + fm_transform(matrix,origin,pos); +} + +void fm_eulerMatrix(REAL ax,REAL ay,REAL az,REAL *matrix) // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) +{ + REAL quat[4]; + fm_eulerToQuat(ax,ay,az,quat); + fm_quatToMatrix(quat,matrix); +} + + +//********************************************************** +//********************************************************** +//**** Vertex Welding +//********************************************************** +//********************************************************** + +#ifndef VERTEX_INDEX_H + +#define VERTEX_INDEX_H + +namespace VERTEX_INDEX +{ + +class KdTreeNode; + +typedef std::vector< KdTreeNode * > KdTreeNodeVector; + +enum Axes +{ + X_AXIS = 0, + Y_AXIS = 1, + Z_AXIS = 2 +}; + +class KdTreeFindNode +{ +public: + KdTreeFindNode(void) + { + mNode = 0; + mDistance = 0; + } + KdTreeNode *mNode; + double mDistance; +}; + +class KdTreeInterface +{ +public: + virtual const double * getPositionDouble(uint32_t index) const = 0; + virtual const float * getPositionFloat(uint32_t index) const = 0; +}; + +class KdTreeNode +{ +public: + KdTreeNode(void) + { + mIndex = 0; + mLeft = 0; + mRight = 0; + } + + KdTreeNode(uint32_t index) + { + mIndex = index; + mLeft = 0; + mRight = 0; + }; + + ~KdTreeNode(void) + { + } + + + void addDouble(KdTreeNode *node,Axes dim,const KdTreeInterface *iface) + { + const double *nodePosition = iface->getPositionDouble( node->mIndex ); + const double *position = iface->getPositionDouble( mIndex ); + switch ( dim ) + { + case X_AXIS: + if ( nodePosition[0] <= position[0] ) + { + if ( mLeft ) + mLeft->addDouble(node,Y_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addDouble(node,Y_AXIS,iface); + else + mRight = node; + } + break; + case Y_AXIS: + if ( nodePosition[1] <= position[1] ) + { + if ( mLeft ) + mLeft->addDouble(node,Z_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addDouble(node,Z_AXIS,iface); + else + mRight = node; + } + break; + case Z_AXIS: + if ( nodePosition[2] <= position[2] ) + { + if ( mLeft ) + mLeft->addDouble(node,X_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addDouble(node,X_AXIS,iface); + else + mRight = node; + } + break; + } + + } + + + void addFloat(KdTreeNode *node,Axes dim,const KdTreeInterface *iface) + { + const float *nodePosition = iface->getPositionFloat( node->mIndex ); + const float *position = iface->getPositionFloat( mIndex ); + switch ( dim ) + { + case X_AXIS: + if ( nodePosition[0] <= position[0] ) + { + if ( mLeft ) + mLeft->addFloat(node,Y_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addFloat(node,Y_AXIS,iface); + else + mRight = node; + } + break; + case Y_AXIS: + if ( nodePosition[1] <= position[1] ) + { + if ( mLeft ) + mLeft->addFloat(node,Z_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addFloat(node,Z_AXIS,iface); + else + mRight = node; + } + break; + case Z_AXIS: + if ( nodePosition[2] <= position[2] ) + { + if ( mLeft ) + mLeft->addFloat(node,X_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addFloat(node,X_AXIS,iface); + else + mRight = node; + } + break; + } + + } + + + uint32_t getIndex(void) const { return mIndex; }; + + void search(Axes axis,const double *pos,double radius,uint32_t &count,uint32_t maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface) + { + + const double *position = iface->getPositionDouble(mIndex); + + double dx = pos[0] - position[0]; + double dy = pos[1] - position[1]; + double dz = pos[2] - position[2]; + + KdTreeNode *search1 = 0; + KdTreeNode *search2 = 0; + + switch ( axis ) + { + case X_AXIS: + if ( dx <= 0 ) // JWR if we are to the left + { + search1 = mLeft; // JWR then search to the left + if ( -dx < radius ) // JWR if distance to the right is less than our search radius, continue on the right as well. + search2 = mRight; + } + else + { + search1 = mRight; // JWR ok, we go down the left tree + if ( dx < radius ) // JWR if the distance from the right is less than our search radius + search2 = mLeft; + } + axis = Y_AXIS; + break; + case Y_AXIS: + if ( dy <= 0 ) + { + search1 = mLeft; + if ( -dy < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dy < radius ) + search2 = mLeft; + } + axis = Z_AXIS; + break; + case Z_AXIS: + if ( dz <= 0 ) + { + search1 = mLeft; + if ( -dz < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dz < radius ) + search2 = mLeft; + } + axis = X_AXIS; + break; + } + + double r2 = radius*radius; + double m = dx*dx+dy*dy+dz*dz; + + if ( m < r2 ) + { + switch ( count ) + { + case 0: + found[count].mNode = this; + found[count].mDistance = m; + break; + case 1: + if ( m < found[0].mDistance ) + { + if ( maxObjects == 1 ) + { + found[0].mNode = this; + found[0].mDistance = m; + } + else + { + found[1] = found[0]; + found[0].mNode = this; + found[0].mDistance = m; + } + } + else if ( maxObjects > 1) + { + found[1].mNode = this; + found[1].mDistance = m; + } + break; + default: + { + bool inserted = false; + + for (uint32_t i=0; i<count; i++) + { + if ( m < found[i].mDistance ) // if this one is closer than a pre-existing one... + { + // insertion sort... + uint32_t scan = count; + if ( scan >= maxObjects ) scan=maxObjects-1; + for (uint32_t j=scan; j>i; j--) + { + found[j] = found[j-1]; + } + found[i].mNode = this; + found[i].mDistance = m; + inserted = true; + break; + } + } + + if ( !inserted && count < maxObjects ) + { + found[count].mNode = this; + found[count].mDistance = m; + } + } + break; + } + count++; + if ( count > maxObjects ) + { + count = maxObjects; + } + } + + + if ( search1 ) + search1->search( axis, pos,radius, count, maxObjects, found, iface); + + if ( search2 ) + search2->search( axis, pos,radius, count, maxObjects, found, iface); + + } + + void search(Axes axis,const float *pos,float radius,uint32_t &count,uint32_t maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface) + { + + const float *position = iface->getPositionFloat(mIndex); + + float dx = pos[0] - position[0]; + float dy = pos[1] - position[1]; + float dz = pos[2] - position[2]; + + KdTreeNode *search1 = 0; + KdTreeNode *search2 = 0; + + switch ( axis ) + { + case X_AXIS: + if ( dx <= 0 ) // JWR if we are to the left + { + search1 = mLeft; // JWR then search to the left + if ( -dx < radius ) // JWR if distance to the right is less than our search radius, continue on the right as well. + search2 = mRight; + } + else + { + search1 = mRight; // JWR ok, we go down the left tree + if ( dx < radius ) // JWR if the distance from the right is less than our search radius + search2 = mLeft; + } + axis = Y_AXIS; + break; + case Y_AXIS: + if ( dy <= 0 ) + { + search1 = mLeft; + if ( -dy < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dy < radius ) + search2 = mLeft; + } + axis = Z_AXIS; + break; + case Z_AXIS: + if ( dz <= 0 ) + { + search1 = mLeft; + if ( -dz < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dz < radius ) + search2 = mLeft; + } + axis = X_AXIS; + break; + } + + float r2 = radius*radius; + float m = dx*dx+dy*dy+dz*dz; + + if ( m < r2 ) + { + switch ( count ) + { + case 0: + found[count].mNode = this; + found[count].mDistance = m; + break; + case 1: + if ( m < found[0].mDistance ) + { + if ( maxObjects == 1 ) + { + found[0].mNode = this; + found[0].mDistance = m; + } + else + { + found[1] = found[0]; + found[0].mNode = this; + found[0].mDistance = m; + } + } + else if ( maxObjects > 1) + { + found[1].mNode = this; + found[1].mDistance = m; + } + break; + default: + { + bool inserted = false; + + for (uint32_t i=0; i<count; i++) + { + if ( m < found[i].mDistance ) // if this one is closer than a pre-existing one... + { + // insertion sort... + uint32_t scan = count; + if ( scan >= maxObjects ) scan=maxObjects-1; + for (uint32_t j=scan; j>i; j--) + { + found[j] = found[j-1]; + } + found[i].mNode = this; + found[i].mDistance = m; + inserted = true; + break; + } + } + + if ( !inserted && count < maxObjects ) + { + found[count].mNode = this; + found[count].mDistance = m; + } + } + break; + } + count++; + if ( count > maxObjects ) + { + count = maxObjects; + } + } + + + if ( search1 ) + search1->search( axis, pos,radius, count, maxObjects, found, iface); + + if ( search2 ) + search2->search( axis, pos,radius, count, maxObjects, found, iface); + + } + +private: + + void setLeft(KdTreeNode *left) { mLeft = left; }; + void setRight(KdTreeNode *right) { mRight = right; }; + + KdTreeNode *getLeft(void) { return mLeft; } + KdTreeNode *getRight(void) { return mRight; } + + uint32_t mIndex; + KdTreeNode *mLeft; + KdTreeNode *mRight; +}; + + +#define MAX_BUNDLE_SIZE 1024 // 1024 nodes at a time, to minimize memory allocation and guarantee that pointers are persistent. + +class KdTreeNodeBundle +{ +public: + + KdTreeNodeBundle(void) + { + mNext = 0; + mIndex = 0; + } + + bool isFull(void) const + { + return (bool)( mIndex == MAX_BUNDLE_SIZE ); + } + + KdTreeNode * getNextNode(void) + { + assert(mIndex<MAX_BUNDLE_SIZE); + KdTreeNode *ret = &mNodes[mIndex]; + mIndex++; + return ret; + } + + KdTreeNodeBundle *mNext; + uint32_t mIndex; + KdTreeNode mNodes[MAX_BUNDLE_SIZE]; +}; + + +typedef std::vector< double > DoubleVector; +typedef std::vector< float > FloatVector; + +class KdTree : public KdTreeInterface +{ +public: + KdTree(void) + { + mRoot = 0; + mBundle = 0; + mVcount = 0; + mUseDouble = false; + } + + virtual ~KdTree(void) + { + reset(); + } + + const double * getPositionDouble(uint32_t index) const + { + assert( mUseDouble ); + assert ( index < mVcount ); + return &mVerticesDouble[index*3]; + } + + const float * getPositionFloat(uint32_t index) const + { + assert( !mUseDouble ); + assert ( index < mVcount ); + return &mVerticesFloat[index*3]; + } + + uint32_t search(const double *pos,double radius,uint32_t maxObjects,KdTreeFindNode *found) const + { + assert( mUseDouble ); + if ( !mRoot ) return 0; + uint32_t count = 0; + mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this); + return count; + } + + uint32_t search(const float *pos,float radius,uint32_t maxObjects,KdTreeFindNode *found) const + { + assert( !mUseDouble ); + if ( !mRoot ) return 0; + uint32_t count = 0; + mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this); + return count; + } + + void reset(void) + { + mRoot = 0; + mVerticesDouble.clear(); + mVerticesFloat.clear(); + KdTreeNodeBundle *bundle = mBundle; + while ( bundle ) + { + KdTreeNodeBundle *next = bundle->mNext; + delete bundle; + bundle = next; + } + mBundle = 0; + mVcount = 0; + } + + uint32_t add(double x,double y,double z) + { + assert(mUseDouble); + uint32_t ret = mVcount; + mVerticesDouble.push_back(x); + mVerticesDouble.push_back(y); + mVerticesDouble.push_back(z); + mVcount++; + KdTreeNode *node = getNewNode(ret); + if ( mRoot ) + { + mRoot->addDouble(node,X_AXIS,this); + } + else + { + mRoot = node; + } + return ret; + } + + uint32_t add(float x,float y,float z) + { + assert(!mUseDouble); + uint32_t ret = mVcount; + mVerticesFloat.push_back(x); + mVerticesFloat.push_back(y); + mVerticesFloat.push_back(z); + mVcount++; + KdTreeNode *node = getNewNode(ret); + if ( mRoot ) + { + mRoot->addFloat(node,X_AXIS,this); + } + else + { + mRoot = node; + } + return ret; + } + + KdTreeNode * getNewNode(uint32_t index) + { + if ( mBundle == 0 ) + { + mBundle = new KdTreeNodeBundle; + } + if ( mBundle->isFull() ) + { + KdTreeNodeBundle *bundle = new KdTreeNodeBundle; + mBundle->mNext = bundle; + mBundle = bundle; + } + KdTreeNode *node = mBundle->getNextNode(); + new ( node ) KdTreeNode(index); + return node; + } + + uint32_t getNearest(const double *pos,double radius,bool &_found) const // returns the nearest possible neighbor's index. + { + assert( mUseDouble ); + uint32_t ret = 0; + + _found = false; + KdTreeFindNode found[1]; + uint32_t count = search(pos,radius,1,found); + if ( count ) + { + KdTreeNode *node = found[0].mNode; + ret = node->getIndex(); + _found = true; + } + return ret; + } + + uint32_t getNearest(const float *pos,float radius,bool &_found) const // returns the nearest possible neighbor's index. + { + assert( !mUseDouble ); + uint32_t ret = 0; + + _found = false; + KdTreeFindNode found[1]; + uint32_t count = search(pos,radius,1,found); + if ( count ) + { + KdTreeNode *node = found[0].mNode; + ret = node->getIndex(); + _found = true; + } + return ret; + } + + const double * getVerticesDouble(void) const + { + assert( mUseDouble ); + const double *ret = 0; + if ( !mVerticesDouble.empty() ) + { + ret = &mVerticesDouble[0]; + } + return ret; + } + + const float * getVerticesFloat(void) const + { + assert( !mUseDouble ); + const float * ret = 0; + if ( !mVerticesFloat.empty() ) + { + ret = &mVerticesFloat[0]; + } + return ret; + } + + uint32_t getVcount(void) const { return mVcount; }; + + void setUseDouble(bool useDouble) + { + mUseDouble = useDouble; + } + +private: + bool mUseDouble; + KdTreeNode *mRoot; + KdTreeNodeBundle *mBundle; + uint32_t mVcount; + DoubleVector mVerticesDouble; + FloatVector mVerticesFloat; +}; + +}; // end of namespace VERTEX_INDEX + +class MyVertexIndex : public fm_VertexIndex +{ +public: + MyVertexIndex(double granularity,bool snapToGrid) + { + mDoubleGranularity = granularity; + mFloatGranularity = (float)granularity; + mSnapToGrid = snapToGrid; + mUseDouble = true; + mKdTree.setUseDouble(true); + } + + MyVertexIndex(float granularity,bool snapToGrid) + { + mDoubleGranularity = granularity; + mFloatGranularity = (float)granularity; + mSnapToGrid = snapToGrid; + mUseDouble = false; + mKdTree.setUseDouble(false); + } + + virtual ~MyVertexIndex(void) + { + + } + + + double snapToGrid(double p) + { + double m = fmod(p,mDoubleGranularity); + p-=m; + return p; + } + + float snapToGrid(float p) + { + float m = fmodf(p,mFloatGranularity); + p-=m; + return p; + } + + uint32_t getIndex(const float *_p,bool &newPos) // get index for a vector float + { + uint32_t ret; + + if ( mUseDouble ) + { + double p[3]; + p[0] = _p[0]; + p[1] = _p[1]; + p[2] = _p[2]; + return getIndex(p,newPos); + } + + newPos = false; + + float p[3]; + + if ( mSnapToGrid ) + { + p[0] = snapToGrid(_p[0]); + p[1] = snapToGrid(_p[1]); + p[2] = snapToGrid(_p[2]); + } + else + { + p[0] = _p[0]; + p[1] = _p[1]; + p[2] = _p[2]; + } + + bool found; + ret = mKdTree.getNearest(p,mFloatGranularity,found); + if ( !found ) + { + newPos = true; + ret = mKdTree.add(p[0],p[1],p[2]); + } + + + return ret; + } + + uint32_t getIndex(const double *_p,bool &newPos) // get index for a vector double + { + uint32_t ret; + + if ( !mUseDouble ) + { + float p[3]; + p[0] = (float)_p[0]; + p[1] = (float)_p[1]; + p[2] = (float)_p[2]; + return getIndex(p,newPos); + } + + newPos = false; + + double p[3]; + + if ( mSnapToGrid ) + { + p[0] = snapToGrid(_p[0]); + p[1] = snapToGrid(_p[1]); + p[2] = snapToGrid(_p[2]); + } + else + { + p[0] = _p[0]; + p[1] = _p[1]; + p[2] = _p[2]; + } + + bool found; + ret = mKdTree.getNearest(p,mDoubleGranularity,found); + if ( !found ) + { + newPos = true; + ret = mKdTree.add(p[0],p[1],p[2]); + } + + + return ret; + } + + const float * getVerticesFloat(void) const + { + const float * ret = 0; + + assert( !mUseDouble ); + + ret = mKdTree.getVerticesFloat(); + + return ret; + } + + const double * getVerticesDouble(void) const + { + const double * ret = 0; + + assert( mUseDouble ); + + ret = mKdTree.getVerticesDouble(); + + return ret; + } + + const float * getVertexFloat(uint32_t index) const + { + const float * ret = 0; + assert( !mUseDouble ); +#ifdef _DEBUG + uint32_t vcount = mKdTree.getVcount(); + assert( index < vcount ); +#endif + ret = mKdTree.getVerticesFloat(); + ret = &ret[index*3]; + return ret; + } + + const double * getVertexDouble(uint32_t index) const + { + const double * ret = 0; + assert( mUseDouble ); +#ifdef _DEBUG + uint32_t vcount = mKdTree.getVcount(); + assert( index < vcount ); +#endif + ret = mKdTree.getVerticesDouble(); + ret = &ret[index*3]; + + return ret; + } + + uint32_t getVcount(void) const + { + return mKdTree.getVcount(); + } + + bool isDouble(void) const + { + return mUseDouble; + } + + + bool saveAsObj(const char *fname,uint32_t tcount,uint32_t *indices) + { + bool ret = false; + + + FILE *fph = fopen(fname,"wb"); + if ( fph ) + { + ret = true; + + uint32_t vcount = getVcount(); + if ( mUseDouble ) + { + const double *v = getVerticesDouble(); + for (uint32_t i=0; i<vcount; i++) + { + fprintf(fph,"v %0.9f %0.9f %0.9f\r\n", (float)v[0], (float)v[1], (float)v[2] ); + v+=3; + } + } + else + { + const float *v = getVerticesFloat(); + for (uint32_t i=0; i<vcount; i++) + { + fprintf(fph,"v %0.9f %0.9f %0.9f\r\n", v[0], v[1], v[2] ); + v+=3; + } + } + + for (uint32_t i=0; i<tcount; i++) + { + uint32_t i1 = *indices++; + uint32_t i2 = *indices++; + uint32_t i3 = *indices++; + fprintf(fph,"f %d %d %d\r\n", i1+1, i2+1, i3+1 ); + } + fclose(fph); + } + + return ret; + } + +private: + bool mUseDouble:1; + bool mSnapToGrid:1; + double mDoubleGranularity; + float mFloatGranularity; + VERTEX_INDEX::KdTree mKdTree; +}; + +fm_VertexIndex * fm_createVertexIndex(double granularity,bool snapToGrid) // create an indexed vertex system for doubles +{ + MyVertexIndex *ret = new MyVertexIndex(granularity,snapToGrid); + return static_cast< fm_VertexIndex *>(ret); +} + +fm_VertexIndex * fm_createVertexIndex(float granularity,bool snapToGrid) // create an indexed vertext system for floats +{ + MyVertexIndex *ret = new MyVertexIndex(granularity,snapToGrid); + return static_cast< fm_VertexIndex *>(ret); +} + +void fm_releaseVertexIndex(fm_VertexIndex *vindex) +{ + MyVertexIndex *m = static_cast< MyVertexIndex *>(vindex); + delete m; +} + +#endif // END OF VERTEX WELDING CODE + + +REAL fm_computeBestFitAABB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *bmin,REAL *bmax) // returns the diagonal distance +{ + + const uint8_t *source = (const uint8_t *) points; + + bmin[0] = points[0]; + bmin[1] = points[1]; + bmin[2] = points[2]; + + bmax[0] = points[0]; + bmax[1] = points[1]; + bmax[2] = points[2]; + + + for (uint32_t i=1; i<vcount; i++) + { + source+=pstride; + const REAL *p = (const REAL *) source; + + if ( p[0] < bmin[0] ) bmin[0] = p[0]; + if ( p[1] < bmin[1] ) bmin[1] = p[1]; + if ( p[2] < bmin[2] ) bmin[2] = p[2]; + + if ( p[0] > bmax[0] ) bmax[0] = p[0]; + if ( p[1] > bmax[1] ) bmax[1] = p[1]; + if ( p[2] > bmax[2] ) bmax[2] = p[2]; + + } + + REAL dx = bmax[0] - bmin[0]; + REAL dy = bmax[1] - bmin[1]; + REAL dz = bmax[2] - bmin[2]; + + return (REAL) sqrt( dx*dx + dy*dy + dz*dz ); + +} + + + +/* a = b - c */ +#define vector(a,b,c) \ + (a)[0] = (b)[0] - (c)[0]; \ + (a)[1] = (b)[1] - (c)[1]; \ + (a)[2] = (b)[2] - (c)[2]; + + + +#define innerProduct(v,q) \ + ((v)[0] * (q)[0] + \ + (v)[1] * (q)[1] + \ + (v)[2] * (q)[2]) + +#define crossProduct(a,b,c) \ + (a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \ + (a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \ + (a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1]; + + +bool fm_lineIntersectsTriangle(const REAL *rayStart,const REAL *rayEnd,const REAL *p1,const REAL *p2,const REAL *p3,REAL *sect) +{ + REAL dir[3]; + + dir[0] = rayEnd[0] - rayStart[0]; + dir[1] = rayEnd[1] - rayStart[1]; + dir[2] = rayEnd[2] - rayStart[2]; + + REAL d = (REAL)sqrt(dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]); + REAL r = 1.0f / d; + + dir[0]*=r; + dir[1]*=r; + dir[2]*=r; + + + REAL t; + + bool ret = fm_rayIntersectsTriangle(rayStart, dir, p1, p2, p3, t ); + + if ( ret ) + { + if ( t > d ) + { + sect[0] = rayStart[0] + dir[0]*t; + sect[1] = rayStart[1] + dir[1]*t; + sect[2] = rayStart[2] + dir[2]*t; + } + else + { + ret = false; + } + } + + return ret; +} + + + +bool fm_rayIntersectsTriangle(const REAL *p,const REAL *d,const REAL *v0,const REAL *v1,const REAL *v2,REAL &t) +{ + REAL e1[3],e2[3],h[3],s[3],q[3]; + REAL a,f,u,v; + + vector(e1,v1,v0); + vector(e2,v2,v0); + crossProduct(h,d,e2); + a = innerProduct(e1,h); + + if (a > -0.00001 && a < 0.00001) + return(false); + + f = 1/a; + vector(s,p,v0); + u = f * (innerProduct(s,h)); + + if (u < 0.0 || u > 1.0) + return(false); + + crossProduct(q,s,e1); + v = f * innerProduct(d,q); + if (v < 0.0 || u + v > 1.0) + return(false); + // at this stage we can compute t to find out where + // the intersection point is on the line + t = f * innerProduct(e2,q); + if (t > 0) // ray intersection + return(true); + else // this means that there is a line intersection + // but not a ray intersection + return (false); +} + + +inline REAL det(const REAL *p1,const REAL *p2,const REAL *p3) +{ + return p1[0]*p2[1]*p3[2] + p2[0]*p3[1]*p1[2] + p3[0]*p1[1]*p2[2] -p1[0]*p3[1]*p2[2] - p2[0]*p1[1]*p3[2] - p3[0]*p2[1]*p1[2]; +} + + +REAL fm_computeMeshVolume(const REAL *vertices,uint32_t tcount,const uint32_t *indices) +{ + REAL volume = 0; + + for (uint32_t i=0; i<tcount; i++,indices+=3) + { + const REAL *p1 = &vertices[ indices[0]*3 ]; + const REAL *p2 = &vertices[ indices[1]*3 ]; + const REAL *p3 = &vertices[ indices[2]*3 ]; + volume+=det(p1,p2,p3); // compute the volume of the tetrahedran relative to the origin. + } + + volume*=(1.0f/6.0f); + if ( volume < 0 ) + volume*=-1; + return volume; +} + + +const REAL * fm_getPoint(const REAL *points,uint32_t pstride,uint32_t index) +{ + const uint8_t *scan = (const uint8_t *)points; + scan+=(index*pstride); + return (REAL *)scan; +} + + +bool fm_insideTriangle(REAL Ax, REAL Ay, + REAL Bx, REAL By, + REAL Cx, REAL Cy, + REAL Px, REAL Py) + +{ + REAL ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; + REAL cCROSSap, bCROSScp, aCROSSbp; + + ax = Cx - Bx; ay = Cy - By; + bx = Ax - Cx; by = Ay - Cy; + cx = Bx - Ax; cy = By - Ay; + apx= Px - Ax; apy= Py - Ay; + bpx= Px - Bx; bpy= Py - By; + cpx= Px - Cx; cpy= Py - Cy; + + aCROSSbp = ax*bpy - ay*bpx; + cCROSSap = cx*apy - cy*apx; + bCROSScp = bx*cpy - by*cpx; + + return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); +} + + +REAL fm_areaPolygon2d(uint32_t pcount,const REAL *points,uint32_t pstride) +{ + int32_t n = (int32_t)pcount; + + REAL A=0.0f; + for(int32_t p=n-1,q=0; q<n; p=q++) + { + const REAL *p1 = fm_getPoint(points,pstride,p); + const REAL *p2 = fm_getPoint(points,pstride,q); + A+= p1[0]*p2[1] - p2[0]*p1[1]; + } + return A*0.5f; +} + + +bool fm_pointInsidePolygon2d(uint32_t pcount,const REAL *points,uint32_t pstride,const REAL *point,uint32_t xindex,uint32_t yindex) +{ + uint32_t j = pcount-1; + int32_t oddNodes = 0; + + REAL x = point[xindex]; + REAL y = point[yindex]; + + for (uint32_t i=0; i<pcount; i++) + { + const REAL *p1 = fm_getPoint(points,pstride,i); + const REAL *p2 = fm_getPoint(points,pstride,j); + + REAL x1 = p1[xindex]; + REAL y1 = p1[yindex]; + + REAL x2 = p2[xindex]; + REAL y2 = p2[yindex]; + + if ( (y1 < y && y2 >= y) || (y2 < y && y1 >= y) ) + { + if (x1+(y-y1)/(y2-y1)*(x2-x1)<x) + { + oddNodes = 1-oddNodes; + } + } + j = i; + } + + return oddNodes ? true : false; +} + + +uint32_t fm_consolidatePolygon(uint32_t pcount,const REAL *points,uint32_t pstride,REAL *_dest,REAL epsilon) // collapses co-linear edges. +{ + uint32_t ret = 0; + + + if ( pcount >= 3 ) + { + const REAL *prev = fm_getPoint(points,pstride,pcount-1); + const REAL *current = points; + const REAL *next = fm_getPoint(points,pstride,1); + REAL *dest = _dest; + + for (uint32_t i=0; i<pcount; i++) + { + + next = (i+1)==pcount ? points : next; + + if ( !fm_colinear(prev,current,next,epsilon) ) + { + dest[0] = current[0]; + dest[1] = current[1]; + dest[2] = current[2]; + + dest+=3; + ret++; + } + + prev = current; + current+=3; + next+=3; + + } + } + + return ret; +} + + +#ifndef RECT3D_TEMPLATE + +#define RECT3D_TEMPLATE + +template <class T> class Rect3d +{ +public: + Rect3d(void) { }; + + Rect3d(const T *bmin,const T *bmax) + { + + mMin[0] = bmin[0]; + mMin[1] = bmin[1]; + mMin[2] = bmin[2]; + + mMax[0] = bmax[0]; + mMax[1] = bmax[1]; + mMax[2] = bmax[2]; + + } + + void SetMin(const T *bmin) + { + mMin[0] = bmin[0]; + mMin[1] = bmin[1]; + mMin[2] = bmin[2]; + } + + void SetMax(const T *bmax) + { + mMax[0] = bmax[0]; + mMax[1] = bmax[1]; + mMax[2] = bmax[2]; + } + + void SetMin(T x,T y,T z) + { + mMin[0] = x; + mMin[1] = y; + mMin[2] = z; + } + + void SetMax(T x,T y,T z) + { + mMax[0] = x; + mMax[1] = y; + mMax[2] = z; + } + + T mMin[3]; + T mMax[3]; +}; + +#endif + +void splitRect(uint32_t axis, + const Rect3d<REAL> &source, + Rect3d<REAL> &b1, + Rect3d<REAL> &b2, + const REAL *midpoint) +{ + switch ( axis ) + { + case 0: + b1.SetMin(source.mMin); + b1.SetMax( midpoint[0], source.mMax[1], source.mMax[2] ); + + b2.SetMin( midpoint[0], source.mMin[1], source.mMin[2] ); + b2.SetMax(source.mMax); + + break; + case 1: + b1.SetMin(source.mMin); + b1.SetMax( source.mMax[0], midpoint[1], source.mMax[2] ); + + b2.SetMin( source.mMin[0], midpoint[1], source.mMin[2] ); + b2.SetMax(source.mMax); + + break; + case 2: + b1.SetMin(source.mMin); + b1.SetMax( source.mMax[0], source.mMax[1], midpoint[2] ); + + b2.SetMin( source.mMin[0], source.mMin[1], midpoint[2] ); + b2.SetMax(source.mMax); + + break; + } +} + +bool fm_computeSplitPlane(uint32_t vcount, + const REAL *vertices, + uint32_t /* tcount */, + const uint32_t * /* indices */, + REAL *plane) +{ + + REAL sides[3]; + REAL matrix[16]; + + fm_computeBestFitOBB( vcount, vertices, sizeof(REAL)*3, sides, matrix ); + + REAL bmax[3]; + REAL bmin[3]; + + bmax[0] = sides[0]*0.5f; + bmax[1] = sides[1]*0.5f; + bmax[2] = sides[2]*0.5f; + + bmin[0] = -bmax[0]; + bmin[1] = -bmax[1]; + bmin[2] = -bmax[2]; + + + REAL dx = sides[0]; + REAL dy = sides[1]; + REAL dz = sides[2]; + + + uint32_t axis = 0; + + if ( dy > dx ) + { + axis = 1; + } + + if ( dz > dx && dz > dy ) + { + axis = 2; + } + + REAL p1[3]; + REAL p2[3]; + REAL p3[3]; + + p3[0] = p2[0] = p1[0] = bmin[0] + dx*0.5f; + p3[1] = p2[1] = p1[1] = bmin[1] + dy*0.5f; + p3[2] = p2[2] = p1[2] = bmin[2] + dz*0.5f; + + Rect3d<REAL> b(bmin,bmax); + + Rect3d<REAL> b1,b2; + + splitRect(axis,b,b1,b2,p1); + + + switch ( axis ) + { + case 0: + p2[1] = bmin[1]; + p2[2] = bmin[2]; + + if ( dz > dy ) + { + p3[1] = bmax[1]; + p3[2] = bmin[2]; + } + else + { + p3[1] = bmin[1]; + p3[2] = bmax[2]; + } + + break; + case 1: + p2[0] = bmin[0]; + p2[2] = bmin[2]; + + if ( dx > dz ) + { + p3[0] = bmax[0]; + p3[2] = bmin[2]; + } + else + { + p3[0] = bmin[0]; + p3[2] = bmax[2]; + } + + break; + case 2: + p2[0] = bmin[0]; + p2[1] = bmin[1]; + + if ( dx > dy ) + { + p3[0] = bmax[0]; + p3[1] = bmin[1]; + } + else + { + p3[0] = bmin[0]; + p3[1] = bmax[1]; + } + + break; + } + + REAL tp1[3]; + REAL tp2[3]; + REAL tp3[3]; + + fm_transform(matrix,p1,tp1); + fm_transform(matrix,p2,tp2); + fm_transform(matrix,p3,tp3); + + plane[3] = fm_computePlane(tp1,tp2,tp3,plane); + + return true; + +} + +#pragma warning(disable:4100) + +void fm_nearestPointInTriangle(const REAL * /*nearestPoint*/,const REAL * /*p1*/,const REAL * /*p2*/,const REAL * /*p3*/,REAL * /*nearest*/) +{ + +} + +static REAL Partial(const REAL *a,const REAL *p) +{ + return (a[0]*p[1]) - (p[0]*a[1]); +} + +REAL fm_areaTriangle(const REAL *p0,const REAL *p1,const REAL *p2) +{ + REAL A = Partial(p0,p1); + A+= Partial(p1,p2); + A+= Partial(p2,p0); + return A*0.5f; +} + +void fm_subtract(const REAL *A,const REAL *B,REAL *diff) // compute A-B and store the result in 'diff' +{ + diff[0] = A[0]-B[0]; + diff[1] = A[1]-B[1]; + diff[2] = A[2]-B[2]; +} + + +void fm_multiplyTransform(const REAL *pA,const REAL *pB,REAL *pM) +{ + + REAL a = pA[0*4+0] * pB[0*4+0] + pA[0*4+1] * pB[1*4+0] + pA[0*4+2] * pB[2*4+0] + pA[0*4+3] * pB[3*4+0]; + REAL b = pA[0*4+0] * pB[0*4+1] + pA[0*4+1] * pB[1*4+1] + pA[0*4+2] * pB[2*4+1] + pA[0*4+3] * pB[3*4+1]; + REAL c = pA[0*4+0] * pB[0*4+2] + pA[0*4+1] * pB[1*4+2] + pA[0*4+2] * pB[2*4+2] + pA[0*4+3] * pB[3*4+2]; + REAL d = pA[0*4+0] * pB[0*4+3] + pA[0*4+1] * pB[1*4+3] + pA[0*4+2] * pB[2*4+3] + pA[0*4+3] * pB[3*4+3]; + + REAL e = pA[1*4+0] * pB[0*4+0] + pA[1*4+1] * pB[1*4+0] + pA[1*4+2] * pB[2*4+0] + pA[1*4+3] * pB[3*4+0]; + REAL f = pA[1*4+0] * pB[0*4+1] + pA[1*4+1] * pB[1*4+1] + pA[1*4+2] * pB[2*4+1] + pA[1*4+3] * pB[3*4+1]; + REAL g = pA[1*4+0] * pB[0*4+2] + pA[1*4+1] * pB[1*4+2] + pA[1*4+2] * pB[2*4+2] + pA[1*4+3] * pB[3*4+2]; + REAL h = pA[1*4+0] * pB[0*4+3] + pA[1*4+1] * pB[1*4+3] + pA[1*4+2] * pB[2*4+3] + pA[1*4+3] * pB[3*4+3]; + + REAL i = pA[2*4+0] * pB[0*4+0] + pA[2*4+1] * pB[1*4+0] + pA[2*4+2] * pB[2*4+0] + pA[2*4+3] * pB[3*4+0]; + REAL j = pA[2*4+0] * pB[0*4+1] + pA[2*4+1] * pB[1*4+1] + pA[2*4+2] * pB[2*4+1] + pA[2*4+3] * pB[3*4+1]; + REAL k = pA[2*4+0] * pB[0*4+2] + pA[2*4+1] * pB[1*4+2] + pA[2*4+2] * pB[2*4+2] + pA[2*4+3] * pB[3*4+2]; + REAL l = pA[2*4+0] * pB[0*4+3] + pA[2*4+1] * pB[1*4+3] + pA[2*4+2] * pB[2*4+3] + pA[2*4+3] * pB[3*4+3]; + + REAL m = pA[3*4+0] * pB[0*4+0] + pA[3*4+1] * pB[1*4+0] + pA[3*4+2] * pB[2*4+0] + pA[3*4+3] * pB[3*4+0]; + REAL n = pA[3*4+0] * pB[0*4+1] + pA[3*4+1] * pB[1*4+1] + pA[3*4+2] * pB[2*4+1] + pA[3*4+3] * pB[3*4+1]; + REAL o = pA[3*4+0] * pB[0*4+2] + pA[3*4+1] * pB[1*4+2] + pA[3*4+2] * pB[2*4+2] + pA[3*4+3] * pB[3*4+2]; + REAL p = pA[3*4+0] * pB[0*4+3] + pA[3*4+1] * pB[1*4+3] + pA[3*4+2] * pB[2*4+3] + pA[3*4+3] * pB[3*4+3]; + + pM[0] = a; pM[1] = b; pM[2] = c; pM[3] = d; + + pM[4] = e; pM[5] = f; pM[6] = g; pM[7] = h; + + pM[8] = i; pM[9] = j; pM[10] = k; pM[11] = l; + + pM[12] = m; pM[13] = n; pM[14] = o; pM[15] = p; +} + +void fm_multiply(REAL *A,REAL scaler) +{ + A[0]*=scaler; + A[1]*=scaler; + A[2]*=scaler; +} + +void fm_add(const REAL *A,const REAL *B,REAL *sum) +{ + sum[0] = A[0]+B[0]; + sum[1] = A[1]+B[1]; + sum[2] = A[2]+B[2]; +} + +void fm_copy3(const REAL *source,REAL *dest) +{ + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; +} + + +uint32_t fm_copyUniqueVertices(uint32_t vcount,const REAL *input_vertices,REAL *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices) +{ + uint32_t ret = 0; + + REAL *vertices = (REAL *)malloc(sizeof(REAL)*vcount*3); + memcpy(vertices,input_vertices,sizeof(REAL)*vcount*3); + REAL *dest = output_vertices; + + uint32_t *reindex = (uint32_t *)malloc(sizeof(uint32_t)*vcount); + memset(reindex,0xFF,sizeof(uint32_t)*vcount); + + uint32_t icount = tcount*3; + + for (uint32_t i=0; i<icount; i++) + { + uint32_t index = *input_indices++; + + assert( index < vcount ); + + if ( reindex[index] == 0xFFFFFFFF ) + { + *output_indices++ = ret; + reindex[index] = ret; + const REAL *pos = &vertices[index*3]; + dest[0] = pos[0]; + dest[1] = pos[1]; + dest[2] = pos[2]; + dest+=3; + ret++; + } + else + { + *output_indices++ = reindex[index]; + } + } + free(vertices); + free(reindex); + return ret; +} + +bool fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const REAL *vertices,bool doubleSided) // returns true if this collection of indexed triangles are co-planar! +{ + bool ret = true; + + if ( tcount > 0 ) + { + uint32_t i1 = indices[0]; + uint32_t i2 = indices[1]; + uint32_t i3 = indices[2]; + const REAL *p1 = &vertices[i1*3]; + const REAL *p2 = &vertices[i2*3]; + const REAL *p3 = &vertices[i3*3]; + REAL plane[4]; + plane[3] = fm_computePlane(p1,p2,p3,plane); + const uint32_t *scan = &indices[3]; + for (uint32_t i=1; i<tcount; i++) + { + i1 = *scan++; + i2 = *scan++; + i3 = *scan++; + p1 = &vertices[i1*3]; + p2 = &vertices[i2*3]; + p3 = &vertices[i3*3]; + REAL _plane[4]; + _plane[3] = fm_computePlane(p1,p2,p3,_plane); + if ( !fm_samePlane(plane,_plane,0.01f,0.001f,doubleSided) ) + { + ret = false; + break; + } + } + } + return ret; +} + + +bool fm_samePlane(const REAL p1[4],const REAL p2[4],REAL normalEpsilon,REAL dEpsilon,bool doubleSided) +{ + bool ret = false; + +#if 0 + if (p1[0] == p2[0] && + p1[1] == p2[1] && + p1[2] == p2[2] && + p1[3] == p2[3]) + { + ret = true; + } +#else + REAL diff = (REAL) fabs(p1[3]-p2[3]); + if ( diff < dEpsilon ) // if the plane -d co-efficient is within our epsilon + { + REAL dot = fm_dot(p1,p2); // compute the dot-product of the vector normals. + if ( doubleSided ) dot = (REAL)fabs(dot); + REAL dmin = 1 - normalEpsilon; + REAL dmax = 1 + normalEpsilon; + if ( dot >= dmin && dot <= dmax ) + { + ret = true; // then the plane equation is for practical purposes identical. + } + } +#endif + return ret; +} + + +void fm_initMinMax(REAL bmin[3],REAL bmax[3]) +{ + bmin[0] = FLT_MAX; + bmin[1] = FLT_MAX; + bmin[2] = FLT_MAX; + + bmax[0] = -FLT_MAX; + bmax[1] = -FLT_MAX; + bmax[2] = -FLT_MAX; +} + +void fm_inflateMinMax(REAL bmin[3], REAL bmax[3], REAL ratio) +{ + REAL inflate = fm_distance(bmin, bmax)*0.5f*ratio; + + bmin[0] -= inflate; + bmin[1] -= inflate; + bmin[2] -= inflate; + + bmax[0] += inflate; + bmax[1] += inflate; + bmax[2] += inflate; +} + +#ifndef TESSELATE_H + +#define TESSELATE_H + +typedef std::vector< uint32_t > UintVector; + +class Myfm_Tesselate : public fm_Tesselate +{ +public: + virtual ~Myfm_Tesselate(void) + { + + } + + const uint32_t * tesselate(fm_VertexIndex *vindex,uint32_t tcount,const uint32_t *indices,float longEdge,uint32_t maxDepth,uint32_t &outcount) + { + const uint32_t *ret = 0; + + mMaxDepth = maxDepth; + mLongEdge = longEdge*longEdge; + mLongEdgeD = mLongEdge; + mVertices = vindex; + + if ( mVertices->isDouble() ) + { + uint32_t vcount = mVertices->getVcount(); + double *vertices = (double *)malloc(sizeof(double)*vcount*3); + memcpy(vertices,mVertices->getVerticesDouble(),sizeof(double)*vcount*3); + + for (uint32_t i=0; i<tcount; i++) + { + uint32_t i1 = *indices++; + uint32_t i2 = *indices++; + uint32_t i3 = *indices++; + + const double *p1 = &vertices[i1*3]; + const double *p2 = &vertices[i2*3]; + const double *p3 = &vertices[i3*3]; + + tesselate(p1,p2,p3,0); + + } + free(vertices); + } + else + { + uint32_t vcount = mVertices->getVcount(); + float *vertices = (float *)malloc(sizeof(float)*vcount*3); + memcpy(vertices,mVertices->getVerticesFloat(),sizeof(float)*vcount*3); + + + for (uint32_t i=0; i<tcount; i++) + { + uint32_t i1 = *indices++; + uint32_t i2 = *indices++; + uint32_t i3 = *indices++; + + const float *p1 = &vertices[i1*3]; + const float *p2 = &vertices[i2*3]; + const float *p3 = &vertices[i3*3]; + + tesselate(p1,p2,p3,0); + + } + free(vertices); + } + + outcount = (uint32_t)(mIndices.size()/3); + ret = &mIndices[0]; + + + return ret; + } + + void tesselate(const float *p1,const float *p2,const float *p3,uint32_t recurse) + { + bool split = false; + float l1,l2,l3; + + l1 = l2 = l3 = 0; + + if ( recurse < mMaxDepth ) + { + l1 = fm_distanceSquared(p1,p2); + l2 = fm_distanceSquared(p2,p3); + l3 = fm_distanceSquared(p3,p1); + + if ( l1 > mLongEdge || l2 > mLongEdge || l3 > mLongEdge ) + split = true; + + } + + if ( split ) + { + uint32_t edge; + + if ( l1 >= l2 && l1 >= l3 ) + edge = 0; + else if ( l2 >= l1 && l2 >= l3 ) + edge = 1; + else + edge = 2; + + float splits[3]; + + switch ( edge ) + { + case 0: + { + fm_lerp(p1,p2,splits,0.5f); + tesselate(p1,splits,p3, recurse+1 ); + tesselate(splits,p2,p3, recurse+1 ); + } + break; + case 1: + { + fm_lerp(p2,p3,splits,0.5f); + tesselate(p1,p2,splits, recurse+1 ); + tesselate(p1,splits,p3, recurse+1 ); + } + break; + case 2: + { + fm_lerp(p3,p1,splits,0.5f); + tesselate(p1,p2,splits, recurse+1 ); + tesselate(splits,p2,p3, recurse+1 ); + } + break; + } + } + else + { + bool newp; + + uint32_t i1 = mVertices->getIndex(p1,newp); + uint32_t i2 = mVertices->getIndex(p2,newp); + uint32_t i3 = mVertices->getIndex(p3,newp); + + mIndices.push_back(i1); + mIndices.push_back(i2); + mIndices.push_back(i3); + } + + } + + void tesselate(const double *p1,const double *p2,const double *p3,uint32_t recurse) + { + bool split = false; + double l1,l2,l3; + + l1 = l2 = l3 = 0; + + if ( recurse < mMaxDepth ) + { + l1 = fm_distanceSquared(p1,p2); + l2 = fm_distanceSquared(p2,p3); + l3 = fm_distanceSquared(p3,p1); + + if ( l1 > mLongEdgeD || l2 > mLongEdgeD || l3 > mLongEdgeD ) + split = true; + + } + + if ( split ) + { + uint32_t edge; + + if ( l1 >= l2 && l1 >= l3 ) + edge = 0; + else if ( l2 >= l1 && l2 >= l3 ) + edge = 1; + else + edge = 2; + + double splits[3]; + + switch ( edge ) + { + case 0: + { + fm_lerp(p1,p2,splits,0.5); + tesselate(p1,splits,p3, recurse+1 ); + tesselate(splits,p2,p3, recurse+1 ); + } + break; + case 1: + { + fm_lerp(p2,p3,splits,0.5); + tesselate(p1,p2,splits, recurse+1 ); + tesselate(p1,splits,p3, recurse+1 ); + } + break; + case 2: + { + fm_lerp(p3,p1,splits,0.5); + tesselate(p1,p2,splits, recurse+1 ); + tesselate(splits,p2,p3, recurse+1 ); + } + break; + } + } + else + { + bool newp; + + uint32_t i1 = mVertices->getIndex(p1,newp); + uint32_t i2 = mVertices->getIndex(p2,newp); + uint32_t i3 = mVertices->getIndex(p3,newp); + + mIndices.push_back(i1); + mIndices.push_back(i2); + mIndices.push_back(i3); + } + + } + +private: + float mLongEdge; + double mLongEdgeD; + fm_VertexIndex *mVertices; + UintVector mIndices; + uint32_t mMaxDepth; +}; + +fm_Tesselate * fm_createTesselate(void) +{ + Myfm_Tesselate *m = new Myfm_Tesselate; + return static_cast< fm_Tesselate * >(m); +} + +void fm_releaseTesselate(fm_Tesselate *t) +{ + Myfm_Tesselate *m = static_cast< Myfm_Tesselate *>(t); + delete m; +} + +#endif + + +#ifndef RAY_ABB_INTERSECT + +#define RAY_ABB_INTERSECT + +//! Integer representation of a floating-point value. +#define IR(x) ((uint32_t&)x) + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* A method to compute a ray-AABB intersection. +* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 +* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) +* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) +* +* Hence this version is faster as well as more robust than the original one. +* +* Should work provided: +* 1) the integer representation of 0.0f is 0x00000000 +* 2) the sign bit of the float is the most significant one +* +* Report bugs: p.terdiman@codercorner.com +* +* \param aabb [in] the axis-aligned bounding box +* \param origin [in] ray origin +* \param dir [in] ray direction +* \param coord [out] impact coordinates +* \return true if ray intersects AABB +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define RAYAABB_EPSILON 0.00001f +bool fm_intersectRayAABB(const float MinB[3],const float MaxB[3],const float origin[3],const float dir[3],float coord[3]) +{ + bool Inside = true; + float MaxT[3]; + MaxT[0]=MaxT[1]=MaxT[2]=-1.0f; + + // Find candidate planes. + for(uint32_t i=0;i<3;i++) + { + if(origin[i] < MinB[i]) + { + coord[i] = MinB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if(IR(dir[i])) MaxT[i] = (MinB[i] - origin[i]) / dir[i]; + } + else if(origin[i] > MaxB[i]) + { + coord[i] = MaxB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if(IR(dir[i])) MaxT[i] = (MaxB[i] - origin[i]) / dir[i]; + } + } + + // Ray origin inside bounding box + if(Inside) + { + coord[0] = origin[0]; + coord[1] = origin[1]; + coord[2] = origin[2]; + return true; + } + + // Get largest of the maxT's for final choice of intersection + uint32_t WhichPlane = 0; + if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1; + if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2; + + // Check final candidate actually inside box + if(IR(MaxT[WhichPlane])&0x80000000) return false; + + for(uint32_t i=0;i<3;i++) + { + if(i!=WhichPlane) + { + coord[i] = origin[i] + MaxT[WhichPlane] * dir[i]; +#ifdef RAYAABB_EPSILON + if(coord[i] < MinB[i] - RAYAABB_EPSILON || coord[i] > MaxB[i] + RAYAABB_EPSILON) return false; +#else + if(coord[i] < MinB[i] || coord[i] > MaxB[i]) return false; +#endif + } + } + return true; // ray hits box +} + +bool fm_intersectLineSegmentAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],float intersect[3]) +{ + bool ret = false; + + float dir[3]; + dir[0] = p2[0] - p1[0]; + dir[1] = p2[1] - p1[1]; + dir[2] = p2[2] - p1[2]; + float dist = fm_normalize(dir); + if ( dist > RAYAABB_EPSILON ) + { + ret = fm_intersectRayAABB(bmin,bmax,p1,dir,intersect); + if ( ret ) + { + float d = fm_distanceSquared(p1,intersect); + if ( d > (dist*dist) ) + { + ret = false; + } + } + } + return ret; +} + +#endif + +#ifndef OBB_TO_AABB + +#define OBB_TO_AABB + +#pragma warning(disable:4100) + +void fm_OBBtoAABB(const float /*obmin*/[3],const float /*obmax*/[3],const float /*matrix*/[16],float /*abmin*/[3],float /*abmax*/[3]) +{ + assert(0); // not yet implemented. +} + + +const REAL * computePos(uint32_t index,const REAL *vertices,uint32_t vstride) +{ + const char *tmp = (const char *)vertices; + tmp+=(index*vstride); + return (const REAL*)tmp; +} + +void computeNormal(uint32_t index,REAL *normals,uint32_t nstride,const REAL *normal) +{ + char *tmp = (char *)normals; + tmp+=(index*nstride); + REAL *dest = (REAL *)tmp; + dest[0]+=normal[0]; + dest[1]+=normal[1]; + dest[2]+=normal[2]; +} + +void fm_computeMeanNormals(uint32_t vcount, // the number of vertices + const REAL *vertices, // the base address of the vertex position data. + uint32_t vstride, // the stride between position data. + REAL *normals, // the base address of the destination for mean vector normals + uint32_t nstride, // the stride between normals + uint32_t tcount, // the number of triangles + const uint32_t *indices) // the triangle indices +{ + + // Step #1 : Zero out the vertex normals + char *dest = (char *)normals; + for (uint32_t i=0; i<vcount; i++) + { + REAL *n = (REAL *)dest; + n[0] = 0; + n[1] = 0; + n[2] = 0; + dest+=nstride; + } + + // Step #2 : Compute the face normals and accumulate them + const uint32_t *scan = indices; + for (uint32_t i=0; i<tcount; i++) + { + + uint32_t i1 = *scan++; + uint32_t i2 = *scan++; + uint32_t i3 = *scan++; + + const REAL *p1 = computePos(i1,vertices,vstride); + const REAL *p2 = computePos(i2,vertices,vstride); + const REAL *p3 = computePos(i3,vertices,vstride); + + REAL normal[3]; + fm_computePlane(p3,p2,p1,normal); + + computeNormal(i1,normals,nstride,normal); + computeNormal(i2,normals,nstride,normal); + computeNormal(i3,normals,nstride,normal); + } + + + // Normalize the accumulated normals + dest = (char *)normals; + for (uint32_t i=0; i<vcount; i++) + { + REAL *n = (REAL *)dest; + fm_normalize(n); + dest+=nstride; + } + +} + +#endif + + +#define BIGNUMBER 100000000.0 /* hundred million */ + +static inline void Set(REAL *n,REAL x,REAL y,REAL z) +{ + n[0] = x; + n[1] = y; + n[2] = z; +}; + +static inline void Copy(REAL *dest,const REAL *source) +{ + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; +} + + +REAL fm_computeBestFitSphere(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *center) +{ + REAL radius; + REAL radius2; + + REAL xmin[3]; + REAL xmax[3]; + REAL ymin[3]; + REAL ymax[3]; + REAL zmin[3]; + REAL zmax[3]; + REAL dia1[3]; + REAL dia2[3]; + + /* FIRST PASS: find 6 minima/maxima points */ + Set(xmin,BIGNUMBER,BIGNUMBER,BIGNUMBER); + Set(xmax,-BIGNUMBER,-BIGNUMBER,-BIGNUMBER); + Set(ymin,BIGNUMBER,BIGNUMBER,BIGNUMBER); + Set(ymax,-BIGNUMBER,-BIGNUMBER,-BIGNUMBER); + Set(zmin,BIGNUMBER,BIGNUMBER,BIGNUMBER); + Set(zmax,-BIGNUMBER,-BIGNUMBER,-BIGNUMBER); + + { + const char *scan = (const char *)points; + for (uint32_t i=0; i<vcount; i++) + { + const REAL *caller_p = (const REAL *)scan; + if (caller_p[0]<xmin[0]) + Copy(xmin,caller_p); /* New xminimum point */ + if (caller_p[0]>xmax[0]) + Copy(xmax,caller_p); + if (caller_p[1]<ymin[1]) + Copy(ymin,caller_p); + if (caller_p[1]>ymax[1]) + Copy(ymax,caller_p); + if (caller_p[2]<zmin[2]) + Copy(zmin,caller_p); + if (caller_p[2]>zmax[2]) + Copy(zmax,caller_p); + scan+=pstride; + } + } + + /* Set xspan = distance between the 2 points xmin & xmax (squared) */ + REAL dx = xmax[0] - xmin[0]; + REAL dy = xmax[1] - xmin[1]; + REAL dz = xmax[2] - xmin[2]; + REAL xspan = dx*dx + dy*dy + dz*dz; + +/* Same for y & z spans */ + dx = ymax[0] - ymin[0]; + dy = ymax[1] - ymin[1]; + dz = ymax[2] - ymin[2]; + REAL yspan = dx*dx + dy*dy + dz*dz; + + dx = zmax[0] - zmin[0]; + dy = zmax[1] - zmin[1]; + dz = zmax[2] - zmin[2]; + REAL zspan = dx*dx + dy*dy + dz*dz; + + /* Set points dia1 & dia2 to the maximally separated pair */ + Copy(dia1,xmin); + Copy(dia2,xmax); /* assume xspan biggest */ + REAL maxspan = xspan; + + if (yspan>maxspan) + { + maxspan = yspan; + Copy(dia1,ymin); + Copy(dia2,ymax); + } + + if (zspan>maxspan) + { + maxspan = zspan; + Copy(dia1,zmin); + Copy(dia2,zmax); + } + + + /* dia1,dia2 is a diameter of initial sphere */ + /* calc initial center */ + center[0] = (dia1[0]+dia2[0])*0.5f; + center[1] = (dia1[1]+dia2[1])*0.5f; + center[2] = (dia1[2]+dia2[2])*0.5f; + + /* calculate initial radius**2 and radius */ + + dx = dia2[0]-center[0]; /* x component of radius vector */ + dy = dia2[1]-center[1]; /* y component of radius vector */ + dz = dia2[2]-center[2]; /* z component of radius vector */ + + radius2 = dx*dx + dy*dy + dz*dz; + radius = REAL(sqrt(radius2)); + + /* SECOND PASS: increment current sphere */ + { + const char *scan = (const char *)points; + for (uint32_t i=0; i<vcount; i++) + { + const REAL *caller_p = (const REAL *)scan; + dx = caller_p[0]-center[0]; + dy = caller_p[1]-center[1]; + dz = caller_p[2]-center[2]; + REAL old_to_p_sq = dx*dx + dy*dy + dz*dz; + if (old_to_p_sq > radius2) /* do r**2 test first */ + { /* this point is outside of current sphere */ + REAL old_to_p = REAL(sqrt(old_to_p_sq)); + /* calc radius of new sphere */ + radius = (radius + old_to_p) * 0.5f; + radius2 = radius*radius; /* for next r**2 compare */ + REAL old_to_new = old_to_p - radius; + /* calc center of new sphere */ + REAL recip = 1.0f /old_to_p; + REAL cx = (radius*center[0] + old_to_new*caller_p[0]) * recip; + REAL cy = (radius*center[1] + old_to_new*caller_p[1]) * recip; + REAL cz = (radius*center[2] + old_to_new*caller_p[2]) * recip; + Set(center,cx,cy,cz); + scan+=pstride; + } + } + } + return radius; +} + + +void fm_computeBestFitCapsule(uint32_t vcount,const REAL *points,uint32_t pstride,REAL &radius,REAL &height,REAL matrix[16],bool bruteForce) +{ + REAL sides[3]; + REAL omatrix[16]; + fm_computeBestFitOBB(vcount,points,pstride,sides,omatrix,bruteForce); + + int32_t axis = 0; + if ( sides[0] > sides[1] && sides[0] > sides[2] ) + axis = 0; + else if ( sides[1] > sides[0] && sides[1] > sides[2] ) + axis = 1; + else + axis = 2; + + REAL localTransform[16]; + + REAL maxDist = 0; + REAL maxLen = 0; + + switch ( axis ) + { + case 0: + { + fm_eulerMatrix(0,0,FM_PI/2,localTransform); + fm_matrixMultiply(localTransform,omatrix,matrix); + + const uint8_t *scan = (const uint8_t *)points; + for (uint32_t i=0; i<vcount; i++) + { + const REAL *p = (const REAL *)scan; + REAL t[3]; + fm_inverseRT(omatrix,p,t); + REAL dist = t[1]*t[1]+t[2]*t[2]; + if ( dist > maxDist ) + { + maxDist = dist; + } + REAL l = (REAL) fabs(t[0]); + if ( l > maxLen ) + { + maxLen = l; + } + scan+=pstride; + } + } + height = sides[0]; + break; + case 1: + { + fm_eulerMatrix(0,FM_PI/2,0,localTransform); + fm_matrixMultiply(localTransform,omatrix,matrix); + + const uint8_t *scan = (const uint8_t *)points; + for (uint32_t i=0; i<vcount; i++) + { + const REAL *p = (const REAL *)scan; + REAL t[3]; + fm_inverseRT(omatrix,p,t); + REAL dist = t[0]*t[0]+t[2]*t[2]; + if ( dist > maxDist ) + { + maxDist = dist; + } + REAL l = (REAL) fabs(t[1]); + if ( l > maxLen ) + { + maxLen = l; + } + scan+=pstride; + } + } + height = sides[1]; + break; + case 2: + { + fm_eulerMatrix(FM_PI/2,0,0,localTransform); + fm_matrixMultiply(localTransform,omatrix,matrix); + + const uint8_t *scan = (const uint8_t *)points; + for (uint32_t i=0; i<vcount; i++) + { + const REAL *p = (const REAL *)scan; + REAL t[3]; + fm_inverseRT(omatrix,p,t); + REAL dist = t[0]*t[0]+t[1]*t[1]; + if ( dist > maxDist ) + { + maxDist = dist; + } + REAL l = (REAL) fabs(t[2]); + if ( l > maxLen ) + { + maxLen = l; + } + scan+=pstride; + } + } + height = sides[2]; + break; + } + radius = (REAL)sqrt(maxDist); + height = (maxLen*2)-(radius*2); +} + + +//************* Triangulation + +#ifndef TRIANGULATE_H + +#define TRIANGULATE_H + +typedef uint32_t TU32; + +class TVec +{ +public: + TVec(double _x,double _y,double _z) { x = _x; y = _y; z = _z; }; + TVec(void) { }; + + double x; + double y; + double z; +}; + +typedef std::vector< TVec > TVecVector; +typedef std::vector< TU32 > TU32Vector; + +class CTriangulator +{ +public: + /// Default constructor + CTriangulator(); + + /// Default destructor + virtual ~CTriangulator(); + + /// Triangulates the contour + void triangulate(TU32Vector &indices); + + /// Returns the given point in the triangulator array + inline TVec get(const TU32 id) { return mPoints[id]; } + + virtual void reset(void) + { + mInputPoints.clear(); + mPoints.clear(); + mIndices.clear(); + } + + virtual void addPoint(double x,double y,double z) + { + TVec v(x,y,z); + // update bounding box... + if ( mInputPoints.empty() ) + { + mMin = v; + mMax = v; + } + else + { + if ( x < mMin.x ) mMin.x = x; + if ( y < mMin.y ) mMin.y = y; + if ( z < mMin.z ) mMin.z = z; + + if ( x > mMax.x ) mMax.x = x; + if ( y > mMax.y ) mMax.y = y; + if ( z > mMax.z ) mMax.z = z; + } + mInputPoints.push_back(v); + } + + // Triangulation happens in 2d. We could inverse transform the polygon around the normal direction, or we just use the two most signficant axes + // Here we find the two longest axes and use them to triangulate. Inverse transforming them would introduce more doubleing point error and isn't worth it. + virtual uint32_t * triangulate(uint32_t &tcount,double epsilon) + { + uint32_t *ret = 0; + tcount = 0; + mEpsilon = epsilon; + + if ( !mInputPoints.empty() ) + { + mPoints.clear(); + + double dx = mMax.x - mMin.x; // locate the first, second and third longest edges and store them in i1, i2, i3 + double dy = mMax.y - mMin.y; + double dz = mMax.z - mMin.z; + + uint32_t i1,i2,i3; + + if ( dx > dy && dx > dz ) + { + i1 = 0; + if ( dy > dz ) + { + i2 = 1; + i3 = 2; + } + else + { + i2 = 2; + i3 = 1; + } + } + else if ( dy > dx && dy > dz ) + { + i1 = 1; + if ( dx > dz ) + { + i2 = 0; + i3 = 2; + } + else + { + i2 = 2; + i3 = 0; + } + } + else + { + i1 = 2; + if ( dx > dy ) + { + i2 = 0; + i3 = 1; + } + else + { + i2 = 1; + i3 = 0; + } + } + + uint32_t pcount = (uint32_t)mInputPoints.size(); + const double *points = &mInputPoints[0].x; + for (uint32_t i=0; i<pcount; i++) + { + TVec v( points[i1], points[i2], points[i3] ); + mPoints.push_back(v); + points+=3; + } + + mIndices.clear(); + triangulate(mIndices); + tcount = (uint32_t)mIndices.size()/3; + if ( tcount ) + { + ret = &mIndices[0]; + } + } + return ret; + } + + virtual const double * getPoint(uint32_t index) + { + return &mInputPoints[index].x; + } + + +private: + double mEpsilon; + TVec mMin; + TVec mMax; + TVecVector mInputPoints; + TVecVector mPoints; + TU32Vector mIndices; + + /// Tests if a point is inside the given triangle + bool _insideTriangle(const TVec& A, const TVec& B, const TVec& C,const TVec& P); + + /// Returns the area of the contour + double _area(); + + bool _snip(int32_t u, int32_t v, int32_t w, int32_t n, int32_t *V); + + /// Processes the triangulation + void _process(TU32Vector &indices); + +}; + +/// Default constructor +CTriangulator::CTriangulator(void) +{ +} + +/// Default destructor +CTriangulator::~CTriangulator() +{ +} + +/// Triangulates the contour +void CTriangulator::triangulate(TU32Vector &indices) +{ + _process(indices); +} + +/// Processes the triangulation +void CTriangulator::_process(TU32Vector &indices) +{ + const int32_t n = (const int32_t)mPoints.size(); + if (n < 3) + return; + int32_t *V = (int32_t *)malloc(sizeof(int32_t)*n); + + bool flipped = false; + + if (0.0f < _area()) + { + for (int32_t v = 0; v < n; v++) + V[v] = v; + } + else + { + flipped = true; + for (int32_t v = 0; v < n; v++) + V[v] = (n - 1) - v; + } + + int32_t nv = n; + int32_t count = 2 * nv; + for (int32_t m = 0, v = nv - 1; nv > 2;) + { + if (0 >= (count--)) + return; + + int32_t u = v; + if (nv <= u) + u = 0; + v = u + 1; + if (nv <= v) + v = 0; + int32_t w = v + 1; + if (nv <= w) + w = 0; + + if (_snip(u, v, w, nv, V)) + { + int32_t a, b, c, s, t; + a = V[u]; + b = V[v]; + c = V[w]; + if ( flipped ) + { + indices.push_back(a); + indices.push_back(b); + indices.push_back(c); + } + else + { + indices.push_back(c); + indices.push_back(b); + indices.push_back(a); + } + m++; + for (s = v, t = v + 1; t < nv; s++, t++) + V[s] = V[t]; + nv--; + count = 2 * nv; + } + } + + free(V); +} + +/// Returns the area of the contour +double CTriangulator::_area() +{ + int32_t n = (uint32_t)mPoints.size(); + double A = 0.0f; + for (int32_t p = n - 1, q = 0; q < n; p = q++) + { + const TVec &pval = mPoints[p]; + const TVec &qval = mPoints[q]; + A += pval.x * qval.y - qval.x * pval.y; + } + A*=0.5f; + return A; +} + +bool CTriangulator::_snip(int32_t u, int32_t v, int32_t w, int32_t n, int32_t *V) +{ + int32_t p; + + const TVec &A = mPoints[ V[u] ]; + const TVec &B = mPoints[ V[v] ]; + const TVec &C = mPoints[ V[w] ]; + + if (mEpsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x))) ) + return false; + + for (p = 0; p < n; p++) + { + if ((p == u) || (p == v) || (p == w)) + continue; + const TVec &P = mPoints[ V[p] ]; + if (_insideTriangle(A, B, C, P)) + return false; + } + return true; +} + +/// Tests if a point is inside the given triangle +bool CTriangulator::_insideTriangle(const TVec& A, const TVec& B, const TVec& C,const TVec& P) +{ + double ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; + double cCROSSap, bCROSScp, aCROSSbp; + + ax = C.x - B.x; ay = C.y - B.y; + bx = A.x - C.x; by = A.y - C.y; + cx = B.x - A.x; cy = B.y - A.y; + apx = P.x - A.x; apy = P.y - A.y; + bpx = P.x - B.x; bpy = P.y - B.y; + cpx = P.x - C.x; cpy = P.y - C.y; + + aCROSSbp = ax * bpy - ay * bpx; + cCROSSap = cx * apy - cy * apx; + bCROSScp = bx * cpy - by * cpx; + + return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); +} + +class Triangulate : public fm_Triangulate +{ +public: + Triangulate(void) + { + mPointsFloat = 0; + mPointsDouble = 0; + } + + virtual ~Triangulate(void) + { + reset(); + } + void reset(void) + { + free(mPointsFloat); + free(mPointsDouble); + mPointsFloat = 0; + mPointsDouble = 0; + } + + virtual const double * triangulate3d(uint32_t pcount, + const double *_points, + uint32_t vstride, + uint32_t &tcount, + bool consolidate, + double epsilon) + { + reset(); + + double *points = (double *)malloc(sizeof(double)*pcount*3); + if ( consolidate ) + { + pcount = fm_consolidatePolygon(pcount,_points,vstride,points,1-epsilon); + } + else + { + double *dest = points; + for (uint32_t i=0; i<pcount; i++) + { + const double *src = fm_getPoint(_points,vstride,i); + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest+=3; + } + vstride = sizeof(double)*3; + } + + if ( pcount >= 3 ) + { + CTriangulator ct; + for (uint32_t i=0; i<pcount; i++) + { + const double *src = fm_getPoint(points,vstride,i); + ct.addPoint( src[0], src[1], src[2] ); + } + uint32_t _tcount; + uint32_t *indices = ct.triangulate(_tcount,epsilon); + if ( indices ) + { + tcount = _tcount; + mPointsDouble = (double *)malloc(sizeof(double)*tcount*3*3); + double *dest = mPointsDouble; + for (uint32_t i=0; i<tcount; i++) + { + uint32_t i1 = indices[i*3+0]; + uint32_t i2 = indices[i*3+1]; + uint32_t i3 = indices[i*3+2]; + const double *p1 = ct.getPoint(i1); + const double *p2 = ct.getPoint(i2); + const double *p3 = ct.getPoint(i3); + + dest[0] = p1[0]; + dest[1] = p1[1]; + dest[2] = p1[2]; + + dest[3] = p2[0]; + dest[4] = p2[1]; + dest[5] = p2[2]; + + dest[6] = p3[0]; + dest[7] = p3[1]; + dest[8] = p3[2]; + dest+=9; + } + } + } + free(points); + + return mPointsDouble; + } + + virtual const float * triangulate3d(uint32_t pcount, + const float *points, + uint32_t vstride, + uint32_t &tcount, + bool consolidate, + float epsilon) + { + reset(); + + double *temp = (double *)malloc(sizeof(double)*pcount*3); + double *dest = temp; + for (uint32_t i=0; i<pcount; i++) + { + const float *p = fm_getPoint(points,vstride,i); + dest[0] = p[0]; + dest[1] = p[1]; + dest[2] = p[2]; + dest+=3; + } + const double *results = triangulate3d(pcount,temp,sizeof(double)*3,tcount,consolidate,epsilon); + if ( results ) + { + uint32_t fcount = tcount*3*3; + mPointsFloat = (float *)malloc(sizeof(float)*tcount*3*3); + for (uint32_t i=0; i<fcount; i++) + { + mPointsFloat[i] = (float) results[i]; + } + free(mPointsDouble); + mPointsDouble = 0; + } + free(temp); + + return mPointsFloat; + } + +private: + float *mPointsFloat; + double *mPointsDouble; +}; + +fm_Triangulate * fm_createTriangulate(void) +{ + Triangulate *t = new Triangulate; + return static_cast< fm_Triangulate *>(t); +} + +void fm_releaseTriangulate(fm_Triangulate *t) +{ + Triangulate *tt = static_cast< Triangulate *>(t); + delete tt; +} + +#endif + +bool validDistance(const REAL *p1,const REAL *p2,REAL epsilon) +{ + bool ret = true; + + REAL dx = p1[0] - p2[0]; + REAL dy = p1[1] - p2[1]; + REAL dz = p1[2] - p2[2]; + REAL dist = dx*dx+dy*dy+dz*dz; + if ( dist < (epsilon*epsilon) ) + { + ret = false; + } + return ret; +} + +bool fm_isValidTriangle(const REAL *p1,const REAL *p2,const REAL *p3,REAL epsilon) +{ + bool ret = false; + + if ( validDistance(p1,p2,epsilon) && + validDistance(p1,p3,epsilon) && + validDistance(p2,p3,epsilon) ) + { + + REAL area = fm_computeArea(p1,p2,p3); + if ( area > epsilon ) + { + REAL _vertices[3*3],vertices[64*3]; + + _vertices[0] = p1[0]; + _vertices[1] = p1[1]; + _vertices[2] = p1[2]; + + _vertices[3] = p2[0]; + _vertices[4] = p2[1]; + _vertices[5] = p2[2]; + + _vertices[6] = p3[0]; + _vertices[7] = p3[1]; + _vertices[8] = p3[2]; + + uint32_t pcount = fm_consolidatePolygon(3,_vertices,sizeof(REAL)*3,vertices,1-epsilon); + if ( pcount == 3 ) + { + ret = true; + } + } + } + return ret; +} + + +void fm_multiplyQuat(const REAL *left,const REAL *right,REAL *quat) +{ + REAL a,b,c,d; + + a = left[3]*right[3] - left[0]*right[0] - left[1]*right[1] - left[2]*right[2]; + b = left[3]*right[0] + right[3]*left[0] + left[1]*right[2] - right[1]*left[2]; + c = left[3]*right[1] + right[3]*left[1] + left[2]*right[0] - right[2]*left[0]; + d = left[3]*right[2] + right[3]*left[2] + left[0]*right[1] - right[0]*left[1]; + + quat[3] = a; + quat[0] = b; + quat[1] = c; + quat[2] = d; +} + +bool fm_computeCentroid(uint32_t vcount, // number of input data points + const REAL *points, // starting address of points array. + REAL *center) + +{ + bool ret = false; + if ( vcount ) + { + center[0] = 0; + center[1] = 0; + center[2] = 0; + const REAL *p = points; + for (uint32_t i=0; i<vcount; i++) + { + center[0]+=p[0]; + center[1]+=p[1]; + center[2]+=p[2]; + p += 3; + } + REAL recip = 1.0f / (REAL)vcount; + center[0]*=recip; + center[1]*=recip; + center[2]*=recip; + ret = true; + } + return ret; +} + +bool fm_computeCentroid(uint32_t vcount, // number of input data points + const REAL *points, // starting address of points array. + uint32_t triCount, + const uint32_t *indices, + REAL *center) + +{ + bool ret = false; + if (vcount) + { + center[0] = 0; + center[1] = 0; + center[2] = 0; + + REAL numerator[3] = { 0, 0, 0 }; + REAL denomintaor = 0; + + for (uint32_t i = 0; i < triCount; i++) + { + uint32_t i1 = indices[i * 3 + 0]; + uint32_t i2 = indices[i * 3 + 1]; + uint32_t i3 = indices[i * 3 + 2]; + + const REAL *p1 = &points[i1 * 3]; + const REAL *p2 = &points[i2 * 3]; + const REAL *p3 = &points[i3 * 3]; + + // Compute the sum of the three positions + REAL sum[3]; + sum[0] = p1[0] + p2[0] + p3[0]; + sum[1] = p1[1] + p2[1] + p3[1]; + sum[2] = p1[2] + p2[2] + p3[2]; + + // Compute the average of the three positions + sum[0] = sum[0] / 3; + sum[1] = sum[1] / 3; + sum[2] = sum[2] / 3; + + // Compute the area of this triangle + REAL area = fm_computeArea(p1, p2, p3); + + numerator[0]+= (sum[0] * area); + numerator[1]+= (sum[1] * area); + numerator[2]+= (sum[2] * area); + + denomintaor += area; + + } + REAL recip = 1 / denomintaor; + center[0] = numerator[0] * recip; + center[1] = numerator[1] * recip; + center[2] = numerator[2] * recip; + ret = true; + } + return ret; +} + + +#ifndef TEMPLATE_VEC3 +#define TEMPLATE_VEC3 +template <class Type> class Vec3 +{ +public: + Vec3(void) + { + + } + Vec3(Type _x,Type _y,Type _z) + { + x = _x; + y = _y; + z = _z; + } + Type x; + Type y; + Type z; +}; +#endif + +void fm_transformAABB(const REAL bmin[3],const REAL bmax[3],const REAL matrix[16],REAL tbmin[3],REAL tbmax[3]) +{ + Vec3<REAL> box[8]; + box[0] = Vec3< REAL >( bmin[0], bmin[1], bmin[2] ); + box[1] = Vec3< REAL >( bmax[0], bmin[1], bmin[2] ); + box[2] = Vec3< REAL >( bmax[0], bmax[1], bmin[2] ); + box[3] = Vec3< REAL >( bmin[0], bmax[1], bmin[2] ); + box[4] = Vec3< REAL >( bmin[0], bmin[1], bmax[2] ); + box[5] = Vec3< REAL >( bmax[0], bmin[1], bmax[2] ); + box[6] = Vec3< REAL >( bmax[0], bmax[1], bmax[2] ); + box[7] = Vec3< REAL >( bmin[0], bmax[1], bmax[2] ); + // transform all 8 corners of the box and then recompute a new AABB + for (unsigned int i=0; i<8; i++) + { + Vec3< REAL > &p = box[i]; + fm_transform(matrix,&p.x,&p.x); + if ( i == 0 ) + { + tbmin[0] = tbmax[0] = p.x; + tbmin[1] = tbmax[1] = p.y; + tbmin[2] = tbmax[2] = p.z; + } + else + { + if ( p.x < tbmin[0] ) tbmin[0] = p.x; + if ( p.y < tbmin[1] ) tbmin[1] = p.y; + if ( p.z < tbmin[2] ) tbmin[2] = p.z; + if ( p.x > tbmax[0] ) tbmax[0] = p.x; + if ( p.y > tbmax[1] ) tbmax[1] = p.y; + if ( p.z > tbmax[2] ) tbmax[2] = p.z; + } + } +} + +REAL fm_normalizeQuat(REAL n[4]) // normalize this quat +{ + REAL dx = n[0]*n[0]; + REAL dy = n[1]*n[1]; + REAL dz = n[2]*n[2]; + REAL dw = n[3]*n[3]; + + REAL dist = dx*dx+dy*dy+dz*dz+dw*dw; + + dist = (REAL)sqrt(dist); + + REAL recip = 1.0f / dist; + + n[0]*=recip; + n[1]*=recip; + n[2]*=recip; + n[3]*=recip; + + return dist; +} + + +}; // end of namespace diff --git a/thirdparty/vhacd/src/VHACD-ASYNC.cpp b/thirdparty/vhacd/src/VHACD-ASYNC.cpp new file mode 100644 index 0000000000..baf309d6a1 --- /dev/null +++ b/thirdparty/vhacd/src/VHACD-ASYNC.cpp @@ -0,0 +1,334 @@ +#include "../public/VHACD.h" +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <thread> +#include <atomic> +#include <mutex> +#include <string> +#include <float.h> + +#define ENABLE_ASYNC 1 + +#define HACD_ALLOC(x) malloc(x) +#define HACD_FREE(x) free(x) +#define HACD_ASSERT(x) assert(x) + +namespace VHACD +{ + +class MyHACD_API : public VHACD::IVHACD, public VHACD::IVHACD::IUserCallback, VHACD::IVHACD::IUserLogger +{ +public: + MyHACD_API(void) + { + mVHACD = VHACD::CreateVHACD(); + } + + virtual ~MyHACD_API(void) + { + releaseHACD(); + Cancel(); + mVHACD->Release(); + } + + + virtual bool Compute(const double* const _points, + const uint32_t countPoints, + const uint32_t* const _triangles, + const uint32_t countTriangles, + const Parameters& _desc) final + { +#if ENABLE_ASYNC + Cancel(); // if we previously had a solution running; cancel it. + releaseHACD(); + + // We need to copy the input vertices and triangles into our own buffers so we can operate + // on them safely from the background thread. + mVertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3); + mIndices = (uint32_t *)HACD_ALLOC(sizeof(uint32_t)*countTriangles * 3); + memcpy(mVertices, _points, sizeof(double)*countPoints * 3); + memcpy(mIndices, _triangles, sizeof(uint32_t)*countTriangles * 3); + mRunning = true; + mThread = new std::thread([this, countPoints, countTriangles, _desc]() + { + ComputeNow(mVertices, countPoints, mIndices, countTriangles, _desc); + mRunning = false; + }); +#else + releaseHACD(); + ComputeNow(_points, countPoints, _triangles, countTriangles, _desc); +#endif + return true; + } + + bool ComputeNow(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& _desc) + { + uint32_t ret = 0; + + mHullCount = 0; + mCallback = _desc.m_callback; + mLogger = _desc.m_logger; + + IVHACD::Parameters desc = _desc; + // Set our intercepting callback interfaces if non-null + desc.m_callback = desc.m_callback ? this : nullptr; + desc.m_logger = desc.m_logger ? this : nullptr; + + if ( countPoints ) + { + bool ok = mVHACD->Compute(points, countPoints, triangles, countTriangles, desc); + if (ok) + { + ret = mVHACD->GetNConvexHulls(); + mHulls = new IVHACD::ConvexHull[ret]; + for (uint32_t i = 0; i < ret; i++) + { + VHACD::IVHACD::ConvexHull vhull; + mVHACD->GetConvexHull(i, vhull); + VHACD::IVHACD::ConvexHull h; + h.m_nPoints = vhull.m_nPoints; + h.m_points = (double *)HACD_ALLOC(sizeof(double) * 3 * h.m_nPoints); + memcpy(h.m_points, vhull.m_points, sizeof(double) * 3 * h.m_nPoints); + h.m_nTriangles = vhull.m_nTriangles; + h.m_triangles = (uint32_t *)HACD_ALLOC(sizeof(uint32_t) * 3 * h.m_nTriangles); + memcpy(h.m_triangles, vhull.m_triangles, sizeof(uint32_t) * 3 * h.m_nTriangles); + h.m_volume = vhull.m_volume; + h.m_center[0] = vhull.m_center[0]; + h.m_center[1] = vhull.m_center[1]; + h.m_center[2] = vhull.m_center[2]; + mHulls[i] = h; + if (mCancel) + { + ret = 0; + break; + } + } + } + } + + mHullCount = ret; + return ret ? true : false; + } + + void releaseHull(VHACD::IVHACD::ConvexHull &h) + { + HACD_FREE((void *)h.m_triangles); + HACD_FREE((void *)h.m_points); + h.m_triangles = nullptr; + h.m_points = nullptr; + } + + virtual void GetConvexHull(const uint32_t index, VHACD::IVHACD::ConvexHull& ch) const final + { + if ( index < mHullCount ) + { + ch = mHulls[index]; + } + } + + void releaseHACD(void) // release memory associated with the last HACD request + { + for (uint32_t i=0; i<mHullCount; i++) + { + releaseHull(mHulls[i]); + } + delete[]mHulls; + mHulls = nullptr; + mHullCount = 0; + HACD_FREE(mVertices); + mVertices = nullptr; + HACD_FREE(mIndices); + mIndices = nullptr; + } + + + virtual void release(void) // release the HACD_API interface + { + delete this; + } + + virtual uint32_t getHullCount(void) + { + return mHullCount; + } + + virtual void Cancel() final + { + if (mRunning) + { + mVHACD->Cancel(); // Set the cancel signal to the base VHACD + } + if (mThread) + { + mThread->join(); // Wait for the thread to fully exit before we delete the instance + delete mThread; + mThread = nullptr; + Log("Convex Decomposition thread canceled\n"); + } + mCancel = false; // clear the cancel semaphore + } + + virtual bool Compute(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) final + { + + double *vertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3); + const float *source = points; + double *dest = vertices; + for (uint32_t i = 0; i < countPoints; i++) + { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest += 3; + source += 3; + } + + bool ret = Compute(vertices, countPoints, triangles, countTriangles, params); + HACD_FREE(vertices); + return ret; + } + + virtual uint32_t GetNConvexHulls() const final + { + processPendingMessages(); + return mHullCount; + } + + virtual void Clean(void) final // release internally allocated memory + { + Cancel(); + releaseHACD(); + mVHACD->Clean(); + } + + virtual void Release(void) final // release IVHACD + { + delete this; + } + + virtual bool OCLInit(void* const oclDevice, + IVHACD::IUserLogger* const logger = 0) final + { + return mVHACD->OCLInit(oclDevice, logger); + } + + virtual bool OCLRelease(IVHACD::IUserLogger* const logger = 0) final + { + return mVHACD->OCLRelease(logger); + } + + virtual void Update(const double overallProgress, + const double stageProgress, + const double operationProgress, + const char* const stage, + const char* const operation) final + { + mMessageMutex.lock(); + mHaveUpdateMessage = true; + mOverallProgress = overallProgress; + mStageProgress = stageProgress; + mOperationProgress = operationProgress; + mStage = std::string(stage); + mOperation = std::string(operation); + mMessageMutex.unlock(); + } + + virtual void Log(const char* const msg) final + { + mMessageMutex.lock(); + mHaveLogMessage = true; + mMessage = std::string(msg); + mMessageMutex.unlock(); + } + + virtual bool IsReady(void) const final + { + processPendingMessages(); + return !mRunning; + } + + // As a convenience for the calling application we only send it update and log messages from it's own main + // thread. This reduces the complexity burden on the caller by making sure it only has to deal with log + // messages in it's main application thread. + void processPendingMessages(void) const + { + // If we have a new update message and the user has specified a callback we send the message and clear the semaphore + if (mHaveUpdateMessage && mCallback) + { + mMessageMutex.lock(); + mCallback->Update(mOverallProgress, mStageProgress, mOperationProgress, mStage.c_str(), mOperation.c_str()); + mHaveUpdateMessage = false; + mMessageMutex.unlock(); + } + // If we have a new log message and the user has specified a callback we send the message and clear the semaphore + if (mHaveLogMessage && mLogger) + { + mMessageMutex.lock(); + mLogger->Log(mMessage.c_str()); + mHaveLogMessage = false; + mMessageMutex.unlock(); + } + } + + // Will compute the center of mass of the convex hull decomposition results and return it + // in 'centerOfMass'. Returns false if the center of mass could not be computed. + virtual bool ComputeCenterOfMass(double centerOfMass[3]) const + { + bool ret = false; + + centerOfMass[0] = 0; + centerOfMass[1] = 0; + centerOfMass[2] = 0; + + if (mVHACD && IsReady() ) + { + ret = mVHACD->ComputeCenterOfMass(centerOfMass); + } + return ret; + } + +private: + double *mVertices{ nullptr }; + uint32_t *mIndices{ nullptr }; + std::atomic< uint32_t> mHullCount{ 0 }; + VHACD::IVHACD::ConvexHull *mHulls{ nullptr }; + VHACD::IVHACD::IUserCallback *mCallback{ nullptr }; + VHACD::IVHACD::IUserLogger *mLogger{ nullptr }; + VHACD::IVHACD *mVHACD{ nullptr }; + std::thread *mThread{ nullptr }; + std::atomic< bool > mRunning{ false }; + std::atomic<bool> mCancel{ false }; + + // Thread safe caching mechanism for messages and update status. + // This is so that caller always gets messages in his own thread + // Member variables are marked as 'mutable' since the message dispatch function + // is called from const query methods. + mutable std::mutex mMessageMutex; + mutable std::atomic< bool > mHaveUpdateMessage{ false }; + mutable std::atomic< bool > mHaveLogMessage{ false }; + mutable double mOverallProgress{ 0 }; + mutable double mStageProgress{ 0 }; + mutable double mOperationProgress{ 0 }; + mutable std::string mStage; + mutable std::string mOperation; + mutable std::string mMessage; +}; + +IVHACD* CreateVHACD_ASYNC(void) +{ + MyHACD_API *m = new MyHACD_API; + return static_cast<IVHACD *>(m); +} + + +}; // end of VHACD namespace + diff --git a/thirdparty/vhacd/src/VHACD.cpp b/thirdparty/vhacd/src/VHACD.cpp new file mode 100644 index 0000000000..54cc6e3761 --- /dev/null +++ b/thirdparty/vhacd/src/VHACD.cpp @@ -0,0 +1,1589 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include <algorithm> +#include <fstream> +#include <iomanip> +#include <limits> +#include <sstream> +#if _OPENMP +#include <omp.h> +#endif // _OPENMP + +#include "../public/VHACD.h" +#include "btConvexHullComputer.h" +#include "vhacdICHull.h" +#include "vhacdMesh.h" +#include "vhacdSArray.h" +#include "vhacdTimer.h" +#include "vhacdVHACD.h" +#include "vhacdVector.h" +#include "vhacdVolume.h" +#include "FloatMath.h" + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define ABS(a) (((a) < 0) ? -(a) : (a)) +#define ZSGN(a) (((a) < 0) ? -1 : (a) > 0 ? 1 : 0) +#define MAX_DOUBLE (1.79769e+308) + +#ifdef _MSC_VER +#pragma warning(disable:4267 4100 4244 4456) +#endif + +#ifdef USE_SSE +#include <immintrin.h> + +const int32_t SIMD_WIDTH = 4; +inline int32_t FindMinimumElement(const float* const d, float* const _, const int32_t n) +{ + // Min within vectors + __m128 min_i = _mm_set1_ps(-1.0f); + __m128 min_v = _mm_set1_ps(std::numeric_limits<float>::max()); + for (int32_t i = 0; i <= n - SIMD_WIDTH; i += SIMD_WIDTH) { + const __m128 data = _mm_load_ps(&d[i]); + const __m128 pred = _mm_cmplt_ps(data, min_v); + + min_i = _mm_blendv_ps(min_i, _mm_set1_ps(i), pred); + min_v = _mm_min_ps(data, min_v); + } + + /* Min within vector */ + const __m128 min1 = _mm_shuffle_ps(min_v, min_v, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 min2 = _mm_min_ps(min_v, min1); + const __m128 min3 = _mm_shuffle_ps(min2, min2, _MM_SHUFFLE(0, 1, 0, 1)); + const __m128 min4 = _mm_min_ps(min2, min3); + float min_d = _mm_cvtss_f32(min4); + + // Min index + const int32_t min_idx = __builtin_ctz(_mm_movemask_ps(_mm_cmpeq_ps(min_v, min4))); + int32_t ret = min_i[min_idx] + min_idx; + + // Trailing elements + for (int32_t i = (n & ~(SIMD_WIDTH - 1)); i < n; ++i) { + if (d[i] < min_d) { + min_d = d[i]; + ret = i; + } + } + + *m = min_d; + return ret; +} + +inline int32_t FindMinimumElement(const float* const d, float* const m, const int32_t begin, const int32_t end) +{ + // Leading elements + int32_t min_i = -1; + float min_d = std::numeric_limits<float>::max(); + const int32_t aligned = (begin & ~(SIMD_WIDTH - 1)) + ((begin & (SIMD_WIDTH - 1)) ? SIMD_WIDTH : 0); + for (int32_t i = begin; i < std::min(end, aligned); ++i) { + if (d[i] < min_d) { + min_d = d[i]; + min_i = i; + } + } + + // Middle and trailing elements + float r_m = std::numeric_limits<float>::max(); + const int32_t n = end - aligned; + const int32_t r_i = (n > 0) ? FindMinimumElement(&d[aligned], &r_m, n) : 0; + + // Pick the lowest + if (r_m < min_d) { + *m = r_m; + return r_i + aligned; + } + else { + *m = min_d; + return min_i; + } +} +#else +inline int32_t FindMinimumElement(const float* const d, float* const m, const int32_t begin, const int32_t end) +{ + int32_t idx = -1; + float min = (std::numeric_limits<float>::max)(); + for (size_t i = begin; i < size_t(end); ++i) { + if (d[i] < min) { + idx = i; + min = d[i]; + } + } + + *m = min; + return idx; +} +#endif + +//#define OCL_SOURCE_FROM_FILE +#ifndef OCL_SOURCE_FROM_FILE +const char* oclProgramSource = "\ +__kernel void ComputePartialVolumes(__global short4 * voxels, \ + const int numVoxels, \ + const float4 plane, \ + const float4 minBB, \ + const float4 scale, \ + __local uint4 * localPartialVolumes, \ + __global uint4 * partialVolumes) \ +{ \ + int localId = get_local_id(0); \ + int groupSize = get_local_size(0); \ + int i0 = get_global_id(0) << 2; \ + float4 voxel; \ + uint4 v; \ + voxel = convert_float4(voxels[i0]); \ + v.s0 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 < numVoxels);\ + voxel = convert_float4(voxels[i0 + 1]); \ + v.s1 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 1 < numVoxels);\ + voxel = convert_float4(voxels[i0 + 2]); \ + v.s2 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 2 < numVoxels);\ + voxel = convert_float4(voxels[i0 + 3]); \ + v.s3 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 3 < numVoxels);\ + localPartialVolumes[localId] = v; \ + barrier(CLK_LOCAL_MEM_FENCE); \ + for (int i = groupSize >> 1; i > 0; i >>= 1) \ + { \ + if (localId < i) \ + { \ + localPartialVolumes[localId] += localPartialVolumes[localId + i]; \ + } \ + barrier(CLK_LOCAL_MEM_FENCE); \ + } \ + if (localId == 0) \ + { \ + partialVolumes[get_group_id(0)] = localPartialVolumes[0]; \ + } \ +} \ +__kernel void ComputePartialSums(__global uint4 * data, \ + const int dataSize, \ + __local uint4 * partialSums) \ +{ \ + int globalId = get_global_id(0); \ + int localId = get_local_id(0); \ + int groupSize = get_local_size(0); \ + int i; \ + if (globalId < dataSize) \ + { \ + partialSums[localId] = data[globalId]; \ + } \ + else \ + { \ + partialSums[localId] = (0, 0, 0, 0); \ + } \ + barrier(CLK_LOCAL_MEM_FENCE); \ + for (i = groupSize >> 1; i > 0; i >>= 1) \ + { \ + if (localId < i) \ + { \ + partialSums[localId] += partialSums[localId + i]; \ + } \ + barrier(CLK_LOCAL_MEM_FENCE); \ + } \ + if (localId == 0) \ + { \ + data[get_group_id(0)] = partialSums[0]; \ + } \ +}"; +#endif //OCL_SOURCE_FROM_FILE + +namespace VHACD { +IVHACD* CreateVHACD(void) +{ + return new VHACD(); +} +bool VHACD::OCLInit(void* const oclDevice, IUserLogger* const logger) +{ +#ifdef CL_VERSION_1_1 + m_oclDevice = (cl_device_id*)oclDevice; + cl_int error; + m_oclContext = clCreateContext(NULL, 1, m_oclDevice, NULL, NULL, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create context\n"); + } + return false; + } + +#ifdef OCL_SOURCE_FROM_FILE + std::string cl_files = OPENCL_CL_FILES; +// read kernal from file +#ifdef _WIN32 + std::replace(cl_files.begin(), cl_files.end(), '/', '\\'); +#endif // _WIN32 + + FILE* program_handle = fopen(cl_files.c_str(), "rb"); + fseek(program_handle, 0, SEEK_END); + size_t program_size = ftell(program_handle); + rewind(program_handle); + char* program_buffer = new char[program_size + 1]; + program_buffer[program_size] = '\0'; + fread(program_buffer, sizeof(char), program_size, program_handle); + fclose(program_handle); + // create program + m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&program_buffer, &program_size, &error); + delete[] program_buffer; +#else + size_t program_size = strlen(oclProgramSource); + m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&oclProgramSource, &program_size, &error); +#endif + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create program\n"); + } + return false; + } + + /* Build program */ + error = clBuildProgram(m_oclProgram, 1, m_oclDevice, "-cl-denorms-are-zero", NULL, NULL); + if (error != CL_SUCCESS) { + size_t log_size; + /* Find Size of log and print to std output */ + clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size); + char* program_log = new char[log_size + 2]; + program_log[log_size] = '\n'; + program_log[log_size + 1] = '\0'; + clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, log_size + 1, program_log, NULL); + if (logger) { + logger->Log("Couldn't build program\n"); + logger->Log(program_log); + } + delete[] program_log; + return false; + } + + delete[] m_oclQueue; + delete[] m_oclKernelComputePartialVolumes; + delete[] m_oclKernelComputeSum; + m_oclQueue = new cl_command_queue[m_ompNumProcessors]; + m_oclKernelComputePartialVolumes = new cl_kernel[m_ompNumProcessors]; + m_oclKernelComputeSum = new cl_kernel[m_ompNumProcessors]; + + const char nameKernelComputePartialVolumes[] = "ComputePartialVolumes"; + const char nameKernelComputeSum[] = "ComputePartialSums"; + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + m_oclKernelComputePartialVolumes[k] = clCreateKernel(m_oclProgram, nameKernelComputePartialVolumes, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create kernel\n"); + } + return false; + } + m_oclKernelComputeSum[k] = clCreateKernel(m_oclProgram, nameKernelComputeSum, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create kernel\n"); + } + return false; + } + } + + error = clGetKernelWorkGroupInfo(m_oclKernelComputePartialVolumes[0], + *m_oclDevice, + CL_KERNEL_WORK_GROUP_SIZE, + sizeof(size_t), + &m_oclWorkGroupSize, + NULL); + size_t workGroupSize = 0; + error = clGetKernelWorkGroupInfo(m_oclKernelComputeSum[0], + *m_oclDevice, + CL_KERNEL_WORK_GROUP_SIZE, + sizeof(size_t), + &workGroupSize, + NULL); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't query work group info\n"); + } + return false; + } + + if (workGroupSize < m_oclWorkGroupSize) { + m_oclWorkGroupSize = workGroupSize; + } + + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + m_oclQueue[k] = clCreateCommandQueue(m_oclContext, *m_oclDevice, 0 /*CL_QUEUE_PROFILING_ENABLE*/, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create queue\n"); + } + return false; + } + } + return true; +#else //CL_VERSION_1_1 + return false; +#endif //CL_VERSION_1_1 +} +bool VHACD::OCLRelease(IUserLogger* const logger) +{ +#ifdef CL_VERSION_1_1 + cl_int error; + if (m_oclKernelComputePartialVolumes) { + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + error = clReleaseKernel(m_oclKernelComputePartialVolumes[k]); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release kernal\n"); + } + return false; + } + } + delete[] m_oclKernelComputePartialVolumes; + } + if (m_oclKernelComputeSum) { + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + error = clReleaseKernel(m_oclKernelComputeSum[k]); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release kernal\n"); + } + return false; + } + } + delete[] m_oclKernelComputeSum; + } + if (m_oclQueue) { + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + error = clReleaseCommandQueue(m_oclQueue[k]); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release queue\n"); + } + return false; + } + } + delete[] m_oclQueue; + } + error = clReleaseProgram(m_oclProgram); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release program\n"); + } + return false; + } + error = clReleaseContext(m_oclContext); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release context\n"); + } + return false; + } + + return true; +#else //CL_VERSION_1_1 + return false; +#endif //CL_VERSION_1_1 +} +void VHACD::ComputePrimitiveSet(const Parameters& params) +{ + if (GetCancel()) { + return; + } + m_timer.Tic(); + + m_stage = "Compute primitive set"; + m_operation = "Convert volume to pset"; + + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(0.0, 0.0, params); + if (params.m_mode == 0) { + VoxelSet* vset = new VoxelSet; + m_volume->Convert(*vset); + m_pset = vset; + } + else { + TetrahedronSet* tset = new TetrahedronSet; + m_volume->Convert(*tset); + m_pset = tset; + } + + delete m_volume; + m_volume = 0; + + if (params.m_logger) { + msg.str(""); + msg << "\t # primitives " << m_pset->GetNPrimitives() << std::endl; + msg << "\t # inside surface " << m_pset->GetNPrimitivesInsideSurf() << std::endl; + msg << "\t # on surface " << m_pset->GetNPrimitivesOnSurf() << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + m_overallProgress = 15.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } +} +bool VHACD::Compute(const double* const points, const uint32_t nPoints, + const uint32_t* const triangles,const uint32_t nTriangles, const Parameters& params) +{ + return ComputeACD(points, nPoints, triangles, nTriangles, params); +} +bool VHACD::Compute(const float* const points,const uint32_t nPoints, + const uint32_t* const triangles,const uint32_t nTriangles, const Parameters& params) +{ + return ComputeACD(points, nPoints, triangles, nTriangles, params); +} +double ComputePreferredCuttingDirection(const PrimitiveSet* const tset, Vec3<double>& dir) +{ + double ex = tset->GetEigenValue(AXIS_X); + double ey = tset->GetEigenValue(AXIS_Y); + double ez = tset->GetEigenValue(AXIS_Z); + double vx = (ey - ez) * (ey - ez); + double vy = (ex - ez) * (ex - ez); + double vz = (ex - ey) * (ex - ey); + if (vx < vy && vx < vz) { + double e = ey * ey + ez * ez; + dir[0] = 1.0; + dir[1] = 0.0; + dir[2] = 0.0; + return (e == 0.0) ? 0.0 : 1.0 - vx / e; + } + else if (vy < vx && vy < vz) { + double e = ex * ex + ez * ez; + dir[0] = 0.0; + dir[1] = 1.0; + dir[2] = 0.0; + return (e == 0.0) ? 0.0 : 1.0 - vy / e; + } + else { + double e = ex * ex + ey * ey; + dir[0] = 0.0; + dir[1] = 0.0; + dir[2] = 1.0; + return (e == 0.0) ? 0.0 : 1.0 - vz / e; + } +} +void ComputeAxesAlignedClippingPlanes(const VoxelSet& vset, const short downsampling, SArray<Plane>& planes) +{ + const Vec3<short> minV = vset.GetMinBBVoxels(); + const Vec3<short> maxV = vset.GetMaxBBVoxels(); + Vec3<double> pt; + Plane plane; + const short i0 = minV[0]; + const short i1 = maxV[0]; + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; i += downsampling) { + pt = vset.GetPoint(Vec3<double>(i + 0.5, 0.0, 0.0)); + plane.m_d = -pt[0]; + plane.m_index = i; + planes.PushBack(plane); + } + const short j0 = minV[1]; + const short j1 = maxV[1]; + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; j += downsampling) { + pt = vset.GetPoint(Vec3<double>(0.0, j + 0.5, 0.0)); + plane.m_d = -pt[1]; + plane.m_index = j; + planes.PushBack(plane); + } + const short k0 = minV[2]; + const short k1 = maxV[2]; + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; k += downsampling) { + pt = vset.GetPoint(Vec3<double>(0.0, 0.0, k + 0.5)); + plane.m_d = -pt[2]; + plane.m_index = k; + planes.PushBack(plane); + } +} +void ComputeAxesAlignedClippingPlanes(const TetrahedronSet& tset, const short downsampling, SArray<Plane>& planes) +{ + const Vec3<double> minV = tset.GetMinBB(); + const Vec3<double> maxV = tset.GetMaxBB(); + const double scale = tset.GetSacle(); + const short i0 = 0; + const short j0 = 0; + const short k0 = 0; + const short i1 = static_cast<short>((maxV[0] - minV[0]) / scale + 0.5); + const short j1 = static_cast<short>((maxV[1] - minV[1]) / scale + 0.5); + const short k1 = static_cast<short>((maxV[2] - minV[2]) / scale + 0.5); + + Plane plane; + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; i += downsampling) { + double x = minV[0] + scale * i; + plane.m_d = -x; + plane.m_index = i; + planes.PushBack(plane); + } + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; j += downsampling) { + double y = minV[1] + scale * j; + plane.m_d = -y; + plane.m_index = j; + planes.PushBack(plane); + } + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; k += downsampling) { + double z = minV[2] + scale * k; + plane.m_d = -z; + plane.m_index = k; + planes.PushBack(plane); + } +} +void RefineAxesAlignedClippingPlanes(const VoxelSet& vset, const Plane& bestPlane, const short downsampling, + SArray<Plane>& planes) +{ + const Vec3<short> minV = vset.GetMinBBVoxels(); + const Vec3<short> maxV = vset.GetMaxBBVoxels(); + Vec3<double> pt; + Plane plane; + + if (bestPlane.m_axis == AXIS_X) { + const short i0 = MAX(minV[0], bestPlane.m_index - downsampling); + const short i1 = MIN(maxV[0], bestPlane.m_index + downsampling); + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; ++i) { + pt = vset.GetPoint(Vec3<double>(i + 0.5, 0.0, 0.0)); + plane.m_d = -pt[0]; + plane.m_index = i; + planes.PushBack(plane); + } + } + else if (bestPlane.m_axis == AXIS_Y) { + const short j0 = MAX(minV[1], bestPlane.m_index - downsampling); + const short j1 = MIN(maxV[1], bestPlane.m_index + downsampling); + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; ++j) { + pt = vset.GetPoint(Vec3<double>(0.0, j + 0.5, 0.0)); + plane.m_d = -pt[1]; + plane.m_index = j; + planes.PushBack(plane); + } + } + else { + const short k0 = MAX(minV[2], bestPlane.m_index - downsampling); + const short k1 = MIN(maxV[2], bestPlane.m_index + downsampling); + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; ++k) { + pt = vset.GetPoint(Vec3<double>(0.0, 0.0, k + 0.5)); + plane.m_d = -pt[2]; + plane.m_index = k; + planes.PushBack(plane); + } + } +} +void RefineAxesAlignedClippingPlanes(const TetrahedronSet& tset, const Plane& bestPlane, const short downsampling, + SArray<Plane>& planes) +{ + const Vec3<double> minV = tset.GetMinBB(); + const Vec3<double> maxV = tset.GetMaxBB(); + const double scale = tset.GetSacle(); + Plane plane; + + if (bestPlane.m_axis == AXIS_X) { + const short i0 = MAX(0, bestPlane.m_index - downsampling); + const short i1 = static_cast<short>(MIN((maxV[0] - minV[0]) / scale + 0.5, bestPlane.m_index + downsampling)); + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; ++i) { + double x = minV[0] + scale * i; + plane.m_d = -x; + plane.m_index = i; + planes.PushBack(plane); + } + } + else if (bestPlane.m_axis == AXIS_Y) { + const short j0 = MAX(0, bestPlane.m_index - downsampling); + const short j1 = static_cast<short>(MIN((maxV[1] - minV[1]) / scale + 0.5, bestPlane.m_index + downsampling)); + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; ++j) { + double y = minV[1] + scale * j; + plane.m_d = -y; + plane.m_index = j; + planes.PushBack(plane); + } + } + else { + const short k0 = MAX(0, bestPlane.m_index - downsampling); + const short k1 = static_cast<short>(MIN((maxV[2] - minV[2]) / scale + 0.5, bestPlane.m_index + downsampling)); + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; ++k) { + double z = minV[2] + scale * k; + plane.m_d = -z; + plane.m_index = k; + planes.PushBack(plane); + } + } +} +inline double ComputeLocalConcavity(const double volume, const double volumeCH) +{ + return fabs(volumeCH - volume) / volumeCH; +} +inline double ComputeConcavity(const double volume, const double volumeCH, const double volume0) +{ + return fabs(volumeCH - volume) / volume0; +} + +//#define DEBUG_TEMP +void VHACD::ComputeBestClippingPlane(const PrimitiveSet* inputPSet, const double volume, const SArray<Plane>& planes, + const Vec3<double>& preferredCuttingDirection, const double w, const double alpha, const double beta, + const int32_t convexhullDownsampling, const double progress0, const double progress1, Plane& bestPlane, + double& minConcavity, const Parameters& params) +{ + if (GetCancel()) { + return; + } + char msg[256]; + size_t nPrimitives = inputPSet->GetNPrimitives(); + bool oclAcceleration = (nPrimitives > OCL_MIN_NUM_PRIMITIVES && params.m_oclAcceleration && params.m_mode == 0) ? true : false; + int32_t iBest = -1; + int32_t nPlanes = static_cast<int32_t>(planes.Size()); + bool cancel = false; + int32_t done = 0; + double minTotal = MAX_DOUBLE; + double minBalance = MAX_DOUBLE; + double minSymmetry = MAX_DOUBLE; + minConcavity = MAX_DOUBLE; + + SArray<Vec3<double> >* chPts = new SArray<Vec3<double> >[2 * m_ompNumProcessors]; + Mesh* chs = new Mesh[2 * m_ompNumProcessors]; + PrimitiveSet* onSurfacePSet = inputPSet->Create(); + inputPSet->SelectOnSurface(onSurfacePSet); + + PrimitiveSet** psets = 0; + if (!params.m_convexhullApproximation) { + psets = new PrimitiveSet*[2 * m_ompNumProcessors]; + for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) { + psets[i] = inputPSet->Create(); + } + } + +#ifdef CL_VERSION_1_1 + // allocate OpenCL data structures + cl_mem voxels; + cl_mem* partialVolumes = 0; + size_t globalSize = 0; + size_t nWorkGroups = 0; + double unitVolume = 0.0; + if (oclAcceleration) { + VoxelSet* vset = (VoxelSet*)inputPSet; + const Vec3<double> minBB = vset->GetMinBB(); + const float fMinBB[4] = { (float)minBB[0], (float)minBB[1], (float)minBB[2], 1.0f }; + const float fSclae[4] = { (float)vset->GetScale(), (float)vset->GetScale(), (float)vset->GetScale(), 0.0f }; + const int32_t nVoxels = (int32_t)nPrimitives; + unitVolume = vset->GetUnitVolume(); + nWorkGroups = (nPrimitives + 4 * m_oclWorkGroupSize - 1) / (4 * m_oclWorkGroupSize); + globalSize = nWorkGroups * m_oclWorkGroupSize; + cl_int error; + voxels = clCreateBuffer(m_oclContext, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + sizeof(Voxel) * nPrimitives, + vset->GetVoxels(), + &error); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't create buffer\n"); + } + SetCancel(true); + } + + partialVolumes = new cl_mem[m_ompNumProcessors]; + for (int32_t i = 0; i < m_ompNumProcessors; ++i) { + partialVolumes[i] = clCreateBuffer(m_oclContext, + CL_MEM_WRITE_ONLY, + sizeof(uint32_t) * 4 * nWorkGroups, + NULL, + &error); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't create buffer\n"); + } + SetCancel(true); + break; + } + error = clSetKernelArg(m_oclKernelComputePartialVolumes[i], 0, sizeof(cl_mem), &voxels); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 1, sizeof(uint32_t), &nVoxels); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 3, sizeof(float) * 4, fMinBB); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 4, sizeof(float) * 4, &fSclae); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 5, sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 6, sizeof(cl_mem), &(partialVolumes[i])); + error |= clSetKernelArg(m_oclKernelComputeSum[i], 0, sizeof(cl_mem), &(partialVolumes[i])); + error |= clSetKernelArg(m_oclKernelComputeSum[i], 2, sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel arguments \n"); + } + SetCancel(true); + } + } + } +#else // CL_VERSION_1_1 + oclAcceleration = false; +#endif // CL_VERSION_1_1 + +#ifdef DEBUG_TEMP + Timer timerComputeCost; + timerComputeCost.Tic(); +#endif // DEBUG_TEMP + +#if USE_THREAD == 1 && _OPENMP +#pragma omp parallel for +#endif + for (int32_t x = 0; x < nPlanes; ++x) { + int32_t threadID = 0; +#if USE_THREAD == 1 && _OPENMP + threadID = omp_get_thread_num(); +#pragma omp flush(cancel) +#endif + if (!cancel) { + //Update progress + if (GetCancel()) { + cancel = true; +#if USE_THREAD == 1 && _OPENMP +#pragma omp flush(cancel) +#endif + } + Plane plane = planes[x]; + + if (oclAcceleration) { +#ifdef CL_VERSION_1_1 + const float fPlane[4] = { (float)plane.m_a, (float)plane.m_b, (float)plane.m_c, (float)plane.m_d }; + cl_int error = clSetKernelArg(m_oclKernelComputePartialVolumes[threadID], 2, sizeof(float) * 4, fPlane); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel atguments \n"); + } + SetCancel(true); + } + + error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputePartialVolumes[threadID], + 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't run kernel \n"); + } + SetCancel(true); + } + int32_t nValues = (int32_t)nWorkGroups; + while (nValues > 1) { + error = clSetKernelArg(m_oclKernelComputeSum[threadID], 1, sizeof(int32_t), &nValues); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel atguments \n"); + } + SetCancel(true); + } + size_t nWorkGroups = (nValues + m_oclWorkGroupSize - 1) / m_oclWorkGroupSize; + size_t globalSize = nWorkGroups * m_oclWorkGroupSize; + error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputeSum[threadID], + 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't run kernel \n"); + } + SetCancel(true); + } + nValues = (int32_t)nWorkGroups; + } +#endif // CL_VERSION_1_1 + } + + Mesh& leftCH = chs[threadID]; + Mesh& rightCH = chs[threadID + m_ompNumProcessors]; + rightCH.ResizePoints(0); + leftCH.ResizePoints(0); + rightCH.ResizeTriangles(0); + leftCH.ResizeTriangles(0); + +// compute convex-hulls +#ifdef TEST_APPROX_CH + double volumeLeftCH1; + double volumeRightCH1; +#endif //TEST_APPROX_CH + if (params.m_convexhullApproximation) { + SArray<Vec3<double> >& leftCHPts = chPts[threadID]; + SArray<Vec3<double> >& rightCHPts = chPts[threadID + m_ompNumProcessors]; + rightCHPts.Resize(0); + leftCHPts.Resize(0); + onSurfacePSet->Intersect(plane, &rightCHPts, &leftCHPts, convexhullDownsampling * 32); + inputPSet->GetConvexHull().Clip(plane, rightCHPts, leftCHPts); + rightCH.ComputeConvexHull((double*)rightCHPts.Data(), rightCHPts.Size()); + leftCH.ComputeConvexHull((double*)leftCHPts.Data(), leftCHPts.Size()); +#ifdef TEST_APPROX_CH + Mesh leftCH1; + Mesh rightCH1; + VoxelSet right; + VoxelSet left; + onSurfacePSet->Clip(plane, &right, &left); + right.ComputeConvexHull(rightCH1, convexhullDownsampling); + left.ComputeConvexHull(leftCH1, convexhullDownsampling); + + volumeLeftCH1 = leftCH1.ComputeVolume(); + volumeRightCH1 = rightCH1.ComputeVolume(); +#endif //TEST_APPROX_CH + } + else { + PrimitiveSet* const right = psets[threadID]; + PrimitiveSet* const left = psets[threadID + m_ompNumProcessors]; + onSurfacePSet->Clip(plane, right, left); + right->ComputeConvexHull(rightCH, convexhullDownsampling); + left->ComputeConvexHull(leftCH, convexhullDownsampling); + } + double volumeLeftCH = leftCH.ComputeVolume(); + double volumeRightCH = rightCH.ComputeVolume(); + + // compute clipped volumes + double volumeLeft = 0.0; + double volumeRight = 0.0; + if (oclAcceleration) { +#ifdef CL_VERSION_1_1 + uint32_t volumes[4]; + cl_int error = clEnqueueReadBuffer(m_oclQueue[threadID], partialVolumes[threadID], CL_TRUE, + 0, sizeof(uint32_t) * 4, volumes, 0, NULL, NULL); + size_t nPrimitivesRight = volumes[0] + volumes[1] + volumes[2] + volumes[3]; + size_t nPrimitivesLeft = nPrimitives - nPrimitivesRight; + volumeRight = nPrimitivesRight * unitVolume; + volumeLeft = nPrimitivesLeft * unitVolume; + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't read buffer \n"); + } + SetCancel(true); + } +#endif // CL_VERSION_1_1 + } + else { + inputPSet->ComputeClippedVolumes(plane, volumeRight, volumeLeft); + } + double concavityLeft = ComputeConcavity(volumeLeft, volumeLeftCH, m_volumeCH0); + double concavityRight = ComputeConcavity(volumeRight, volumeRightCH, m_volumeCH0); + double concavity = (concavityLeft + concavityRight); + + // compute cost + double balance = alpha * fabs(volumeLeft - volumeRight) / m_volumeCH0; + double d = w * (preferredCuttingDirection[0] * plane.m_a + preferredCuttingDirection[1] * plane.m_b + preferredCuttingDirection[2] * plane.m_c); + double symmetry = beta * d; + double total = concavity + balance + symmetry; + +#if USE_THREAD == 1 && _OPENMP +#pragma omp critical +#endif + { + if (total < minTotal || (total == minTotal && x < iBest)) { + minConcavity = concavity; + minBalance = balance; + minSymmetry = symmetry; + bestPlane = plane; + minTotal = total; + iBest = x; + } + ++done; + if (!(done & 127)) // reduce update frequency + { + double progress = done * (progress1 - progress0) / nPlanes + progress0; + Update(m_stageProgress, progress, params); + } + } + } + } + +#ifdef DEBUG_TEMP + timerComputeCost.Toc(); + printf_s("Cost[%i] = %f\n", nPlanes, timerComputeCost.GetElapsedTime()); +#endif // DEBUG_TEMP + +#ifdef CL_VERSION_1_1 + if (oclAcceleration) { + clReleaseMemObject(voxels); + for (int32_t i = 0; i < m_ompNumProcessors; ++i) { + clReleaseMemObject(partialVolumes[i]); + } + delete[] partialVolumes; + } +#endif // CL_VERSION_1_1 + + if (psets) { + for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) { + delete psets[i]; + } + delete[] psets; + } + delete onSurfacePSet; + delete[] chPts; + delete[] chs; + if (params.m_logger) { + sprintf(msg, "\n\t\t\t Best %04i T=%2.6f C=%2.6f B=%2.6f S=%2.6f (%1.1f, %1.1f, %1.1f, %3.3f)\n\n", iBest, minTotal, minConcavity, minBalance, minSymmetry, bestPlane.m_a, bestPlane.m_b, bestPlane.m_c, bestPlane.m_d); + params.m_logger->Log(msg); + } +} +void VHACD::ComputeACD(const Parameters& params) +{ + if (GetCancel()) { + return; + } + m_timer.Tic(); + + m_stage = "Approximate Convex Decomposition"; + m_stageProgress = 0.0; + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + SArray<PrimitiveSet*> parts; + SArray<PrimitiveSet*> inputParts; + SArray<PrimitiveSet*> temp; + inputParts.PushBack(m_pset); + m_pset = 0; + SArray<Plane> planes; + SArray<Plane> planesRef; + uint32_t sub = 0; + bool firstIteration = true; + m_volumeCH0 = 1.0; + + // Compute the decomposition depth based on the number of convex hulls being requested.. + uint32_t hullCount = 2; + uint32_t depth = 1; + while (params.m_maxConvexHulls > hullCount) + { + depth++; + hullCount *= 2; + } + // We must always increment the decomposition depth one higher than the maximum number of hulls requested. + // The reason for this is as follows. + // Say, for example, the user requests 32 convex hulls exactly. This would be a decomposition depth of 5. + // However, when we do that, we do *not* necessarily get 32 hulls as a result. This is because, during + // the recursive descent of the binary tree, one or more of the leaf nodes may have no concavity and + // will not be split. So, in this way, even with a decomposition depth of 5, you can produce fewer than + // 32 hulls. So, in this case, we would set the decomposition depth to 6 (producing up to as high as 64 convex hulls). + // Then, the merge step which combines over-described hulls down to the user requested amount, we will end up + // getting exactly 32 convex hulls as a result. + // We could just allow the artist to directly control the decomposition depth directly, but this would be a bit + // too complex and the preference is simply to let them specify how many hulls they want and derive the solution + // from that. + depth++; + + + while (sub++ < depth && inputParts.Size() > 0 && !m_cancel) { + msg.str(""); + msg << "Subdivision level " << sub; + m_operation = msg.str(); + + if (params.m_logger) { + msg.str(""); + msg << "\t Subdivision level " << sub << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + double maxConcavity = 0.0; + const size_t nInputParts = inputParts.Size(); + Update(m_stageProgress, 0.0, params); + for (size_t p = 0; p < nInputParts && !m_cancel; ++p) { + const double progress0 = p * 100.0 / nInputParts; + const double progress1 = (p + 0.75) * 100.0 / nInputParts; + const double progress2 = (p + 1.00) * 100.0 / nInputParts; + + Update(m_stageProgress, progress0, params); + + PrimitiveSet* pset = inputParts[p]; + inputParts[p] = 0; + double volume = pset->ComputeVolume(); + pset->ComputeBB(); + pset->ComputePrincipalAxes(); + if (params.m_pca) { + pset->AlignToPrincipalAxes(); + } + + pset->ComputeConvexHull(pset->GetConvexHull()); + double volumeCH = fabs(pset->GetConvexHull().ComputeVolume()); + if (firstIteration) { + m_volumeCH0 = volumeCH; + } + + double concavity = ComputeConcavity(volume, volumeCH, m_volumeCH0); + double error = 1.01 * pset->ComputeMaxVolumeError() / m_volumeCH0; + + if (firstIteration) { + firstIteration = false; + } + + if (params.m_logger) { + msg.str(""); + msg << "\t -> Part[" << p + << "] C = " << concavity + << ", E = " << error + << ", VS = " << pset->GetNPrimitivesOnSurf() + << ", VI = " << pset->GetNPrimitivesInsideSurf() + << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + if (concavity > params.m_concavity && concavity > error) { + Vec3<double> preferredCuttingDirection; + double w = ComputePreferredCuttingDirection(pset, preferredCuttingDirection); + planes.Resize(0); + if (params.m_mode == 0) { + VoxelSet* vset = (VoxelSet*)pset; + ComputeAxesAlignedClippingPlanes(*vset, params.m_planeDownsampling, planes); + } + else { + TetrahedronSet* tset = (TetrahedronSet*)pset; + ComputeAxesAlignedClippingPlanes(*tset, params.m_planeDownsampling, planes); + } + + if (params.m_logger) { + msg.str(""); + msg << "\t\t [Regular sampling] Number of clipping planes " << planes.Size() << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Plane bestPlane; + double minConcavity = MAX_DOUBLE; + ComputeBestClippingPlane(pset, + volume, + planes, + preferredCuttingDirection, + w, + concavity * params.m_alpha, + concavity * params.m_beta, + params.m_convexhullDownsampling, + progress0, + progress1, + bestPlane, + minConcavity, + params); + if (!m_cancel && (params.m_planeDownsampling > 1 || params.m_convexhullDownsampling > 1)) { + planesRef.Resize(0); + + if (params.m_mode == 0) { + VoxelSet* vset = (VoxelSet*)pset; + RefineAxesAlignedClippingPlanes(*vset, bestPlane, params.m_planeDownsampling, planesRef); + } + else { + TetrahedronSet* tset = (TetrahedronSet*)pset; + RefineAxesAlignedClippingPlanes(*tset, bestPlane, params.m_planeDownsampling, planesRef); + } + + if (params.m_logger) { + msg.str(""); + msg << "\t\t [Refining] Number of clipping planes " << planesRef.Size() << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + ComputeBestClippingPlane(pset, + volume, + planesRef, + preferredCuttingDirection, + w, + concavity * params.m_alpha, + concavity * params.m_beta, + 1, // convexhullDownsampling = 1 + progress1, + progress2, + bestPlane, + minConcavity, + params); + } + if (GetCancel()) { + delete pset; // clean up + break; + } + else { + if (maxConcavity < minConcavity) { + maxConcavity = minConcavity; + } + PrimitiveSet* bestLeft = pset->Create(); + PrimitiveSet* bestRight = pset->Create(); + temp.PushBack(bestLeft); + temp.PushBack(bestRight); + pset->Clip(bestPlane, bestRight, bestLeft); + if (params.m_pca) { + bestRight->RevertAlignToPrincipalAxes(); + bestLeft->RevertAlignToPrincipalAxes(); + } + delete pset; + } + } + else { + if (params.m_pca) { + pset->RevertAlignToPrincipalAxes(); + } + parts.PushBack(pset); + } + } + + Update(95.0 * (1.0 - maxConcavity) / (1.0 - params.m_concavity), 100.0, params); + if (GetCancel()) { + const size_t nTempParts = temp.Size(); + for (size_t p = 0; p < nTempParts; ++p) { + delete temp[p]; + } + temp.Resize(0); + } + else { + inputParts = temp; + temp.Resize(0); + } + } + const size_t nInputParts = inputParts.Size(); + for (size_t p = 0; p < nInputParts; ++p) { + parts.PushBack(inputParts[p]); + } + + if (GetCancel()) { + const size_t nParts = parts.Size(); + for (size_t p = 0; p < nParts; ++p) { + delete parts[p]; + } + return; + } + + m_overallProgress = 90.0; + Update(m_stageProgress, 100.0, params); + + msg.str(""); + msg << "Generate convex-hulls"; + m_operation = msg.str(); + size_t nConvexHulls = parts.Size(); + if (params.m_logger) { + msg.str(""); + msg << "+ Generate " << nConvexHulls << " convex-hulls " << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(m_stageProgress, 0.0, params); + m_convexHulls.Resize(0); + for (size_t p = 0; p < nConvexHulls && !m_cancel; ++p) { + Update(m_stageProgress, p * 100.0 / nConvexHulls, params); + m_convexHulls.PushBack(new Mesh); + parts[p]->ComputeConvexHull(*m_convexHulls[p]); + size_t nv = m_convexHulls[p]->GetNPoints(); + double x, y, z; + for (size_t i = 0; i < nv; ++i) { + Vec3<double>& pt = m_convexHulls[p]->GetPoint(i); + x = pt[0]; + y = pt[1]; + z = pt[2]; + pt[0] = m_rot[0][0] * x + m_rot[0][1] * y + m_rot[0][2] * z + m_barycenter[0]; + pt[1] = m_rot[1][0] * x + m_rot[1][1] * y + m_rot[1][2] * z + m_barycenter[1]; + pt[2] = m_rot[2][0] * x + m_rot[2][1] * y + m_rot[2][2] * z + m_barycenter[2]; + } + } + + const size_t nParts = parts.Size(); + for (size_t p = 0; p < nParts; ++p) { + delete parts[p]; + parts[p] = 0; + } + parts.Resize(0); + + if (GetCancel()) { + const size_t nConvexHulls = m_convexHulls.Size(); + for (size_t p = 0; p < nConvexHulls; ++p) { + delete m_convexHulls[p]; + } + m_convexHulls.Clear(); + return; + } + + m_overallProgress = 95.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } +} +void AddPoints(const Mesh* const mesh, SArray<Vec3<double> >& pts) +{ + const int32_t n = (int32_t)mesh->GetNPoints(); + for (int32_t i = 0; i < n; ++i) { + pts.PushBack(mesh->GetPoint(i)); + } +} +void ComputeConvexHull(const Mesh* const ch1, const Mesh* const ch2, SArray<Vec3<double> >& pts, Mesh* const combinedCH) +{ + pts.Resize(0); + AddPoints(ch1, pts); + AddPoints(ch2, pts); + + btConvexHullComputer ch; + ch.compute((double*)pts.Data(), 3 * sizeof(double), (int32_t)pts.Size(), -1.0, -1.0); + combinedCH->ResizePoints(0); + combinedCH->ResizeTriangles(0); + for (int32_t v = 0; v < ch.vertices.size(); v++) { + combinedCH->AddPoint(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int32_t nt = ch.faces.size(); + for (int32_t t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int32_t a = sourceEdge->getSourceVertex(); + int32_t b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int32_t c = edge->getTargetVertex(); + while (c != a) { + combinedCH->AddTriangle(Vec3<int32_t>(a, b, c)); + edge = edge->getNextEdgeOfFace(); + b = c; + c = edge->getTargetVertex(); + } + } +} +void VHACD::MergeConvexHulls(const Parameters& params) +{ + if (GetCancel()) { + return; + } + m_timer.Tic(); + + m_stage = "Merge Convex Hulls"; + + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + // Get the current number of convex hulls + size_t nConvexHulls = m_convexHulls.Size(); + // Iteration counter + int32_t iteration = 0; + // While we have more than at least one convex hull and the user has not asked us to cancel the operation + if (nConvexHulls > 1 && !m_cancel) + { + // Get the gamma error threshold for when to exit + SArray<Vec3<double> > pts; + Mesh combinedCH; + + // Populate the cost matrix + size_t idx = 0; + SArray<float> costMatrix; + costMatrix.Resize(((nConvexHulls * nConvexHulls) - nConvexHulls) >> 1); + for (size_t p1 = 1; p1 < nConvexHulls; ++p1) + { + const float volume1 = m_convexHulls[p1]->ComputeVolume(); + for (size_t p2 = 0; p2 < p1; ++p2) + { + ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, &combinedCH); + costMatrix[idx++] = ComputeConcavity(volume1 + m_convexHulls[p2]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); + } + } + + // Until we cant merge below the maximum cost + size_t costSize = m_convexHulls.Size(); + while (!m_cancel) + { + msg.str(""); + msg << "Iteration " << iteration++; + m_operation = msg.str(); + + // Search for lowest cost + float bestCost = (std::numeric_limits<float>::max)(); + const size_t addr = FindMinimumElement(costMatrix.Data(), &bestCost, 0, costMatrix.Size()); + if ( (costSize-1) < params.m_maxConvexHulls) + { + break; + } + const size_t addrI = (static_cast<int32_t>(sqrt(1 + (8 * addr))) - 1) >> 1; + const size_t p1 = addrI + 1; + const size_t p2 = addr - ((addrI * (addrI + 1)) >> 1); + assert(p1 >= 0); + assert(p2 >= 0); + assert(p1 < costSize); + assert(p2 < costSize); + + if (params.m_logger) + { + msg.str(""); + msg << "\t\t Merging (" << p1 << ", " << p2 << ") " << bestCost << std::endl + << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + // Make the lowest cost row and column into a new hull + Mesh* cch = new Mesh; + ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, cch); + delete m_convexHulls[p2]; + m_convexHulls[p2] = cch; + + delete m_convexHulls[p1]; + std::swap(m_convexHulls[p1], m_convexHulls[m_convexHulls.Size() - 1]); + m_convexHulls.PopBack(); + + costSize = costSize - 1; + + // Calculate costs versus the new hull + size_t rowIdx = ((p2 - 1) * p2) >> 1; + const float volume1 = m_convexHulls[p2]->ComputeVolume(); + for (size_t i = 0; (i < p2) && (!m_cancel); ++i) + { + ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH); + costMatrix[rowIdx++] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); + } + + rowIdx += p2; + for (size_t i = p2 + 1; (i < costSize) && (!m_cancel); ++i) + { + ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH); + costMatrix[rowIdx] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); + rowIdx += i; + assert(rowIdx >= 0); + } + + // Move the top column in to replace its space + const size_t erase_idx = ((costSize - 1) * costSize) >> 1; + if (p1 < costSize) { + rowIdx = (addrI * p1) >> 1; + size_t top_row = erase_idx; + for (size_t i = 0; i < p1; ++i) { + if (i != p2) { + costMatrix[rowIdx] = costMatrix[top_row]; + } + ++rowIdx; + ++top_row; + } + + ++top_row; + rowIdx += p1; + for (size_t i = p1 + 1; i < (costSize + 1); ++i) { + costMatrix[rowIdx] = costMatrix[top_row++]; + rowIdx += i; + assert(rowIdx >= 0); + } + } + costMatrix.Resize(erase_idx); + } + } + m_overallProgress = 99.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } +} +void VHACD::SimplifyConvexHull(Mesh* const ch, const size_t nvertices, const double minVolume) +{ + if (nvertices <= 4) { + return; + } + ICHull icHull; + if (mRaycastMesh) + { + // We project these points onto the original source mesh to increase precision + // The voxelization process drops floating point precision so returned data points are not exactly lying on the + // surface of the original source mesh. + // The first step is we need to compute the bounding box of the mesh we are trying to build a convex hull for. + // From this bounding box, we compute the length of the diagonal to get a relative size and center for point projection + uint32_t nPoints = ch->GetNPoints(); + Vec3<double> *inputPoints = ch->GetPointsBuffer(); + Vec3<double> bmin(inputPoints[0]); + Vec3<double> bmax(inputPoints[1]); + for (uint32_t i = 1; i < nPoints; i++) + { + const Vec3<double> &p = inputPoints[i]; + p.UpdateMinMax(bmin, bmax); + } + Vec3<double> center; + double diagonalLength = center.GetCenter(bmin, bmax); // Get the center of the bounding box + // This is the error threshold for determining if we should use the raycast result data point vs. the voxelized result. + double pointDistanceThreshold = diagonalLength * 0.05; + // If a new point is within 1/100th the diagonal length of the bounding volume we do not add it. To do so would create a + // thin sliver in the resulting convex hull + double snapDistanceThreshold = diagonalLength * 0.01; + double snapDistanceThresholdSquared = snapDistanceThreshold*snapDistanceThreshold; + + // Allocate buffer for projected vertices + Vec3<double> *outputPoints = new Vec3<double>[nPoints]; + uint32_t outCount = 0; + for (uint32_t i = 0; i < nPoints; i++) + { + Vec3<double> &inputPoint = inputPoints[i]; + Vec3<double> &outputPoint = outputPoints[outCount]; + // Compute the direction vector from the center of this mesh to the vertex + Vec3<double> dir = inputPoint - center; + // Normalize the direction vector. + dir.Normalize(); + // Multiply times the diagonal length of the mesh + dir *= diagonalLength; + // Add the center back in again to get the destination point + dir += center; + // By default the output point is equal to the input point + outputPoint = inputPoint; + double pointDistance; + if (mRaycastMesh->raycast(center.GetData(), dir.GetData(), inputPoint.GetData(), outputPoint.GetData(),&pointDistance) ) + { + // If the nearest intersection point is too far away, we keep the original source data point. + // Not all points lie directly on the original mesh surface + if (pointDistance > pointDistanceThreshold) + { + outputPoint = inputPoint; + } + } + // Ok, before we add this point, we do not want to create points which are extremely close to each other. + // This will result in tiny sliver triangles which are really bad for collision detection. + bool foundNearbyPoint = false; + for (uint32_t j = 0; j < outCount; j++) + { + // If this new point is extremely close to an existing point, we do not add it! + double squaredDistance = outputPoints[j].GetDistanceSquared(outputPoint); + if (squaredDistance < snapDistanceThresholdSquared ) + { + foundNearbyPoint = true; + break; + } + } + if (!foundNearbyPoint) + { + outCount++; + } + } + icHull.AddPoints(outputPoints, outCount); + delete[]outputPoints; + } + else + { + icHull.AddPoints(ch->GetPointsBuffer(), ch->GetNPoints()); + } + icHull.Process((uint32_t)nvertices, minVolume); + TMMesh& mesh = icHull.GetMesh(); + const size_t nT = mesh.GetNTriangles(); + const size_t nV = mesh.GetNVertices(); + ch->ResizePoints(nV); + ch->ResizeTriangles(nT); + mesh.GetIFS(ch->GetPointsBuffer(), ch->GetTrianglesBuffer()); +} +void VHACD::SimplifyConvexHulls(const Parameters& params) +{ + if (m_cancel || params.m_maxNumVerticesPerCH < 4) { + return; + } + m_timer.Tic(); + + m_stage = "Simplify convex-hulls"; + m_operation = "Simplify convex-hulls"; + + std::ostringstream msg; + const size_t nConvexHulls = m_convexHulls.Size(); + if (params.m_logger) { + msg << "+ Simplify " << nConvexHulls << " convex-hulls " << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(0.0, 0.0, params); + for (size_t i = 0; i < nConvexHulls && !m_cancel; ++i) { + if (params.m_logger) { + msg.str(""); + msg << "\t\t Simplify CH[" << std::setfill('0') << std::setw(5) << i << "] " << m_convexHulls[i]->GetNPoints() << " V, " << m_convexHulls[i]->GetNTriangles() << " T" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + SimplifyConvexHull(m_convexHulls[i], params.m_maxNumVerticesPerCH, m_volumeCH0 * params.m_minVolumePerCH); + } + + m_overallProgress = 100.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } +} + +bool VHACD::ComputeCenterOfMass(double centerOfMass[3]) const +{ + bool ret = false; + + centerOfMass[0] = 0; + centerOfMass[1] = 0; + centerOfMass[2] = 0; + // Get number of convex hulls in the result + uint32_t hullCount = GetNConvexHulls(); + if (hullCount) // if we have results + { + ret = true; + double totalVolume = 0; + // Initialize the center of mass to zero + centerOfMass[0] = 0; + centerOfMass[1] = 0; + centerOfMass[2] = 0; + // Compute the total volume of all convex hulls + for (uint32_t i = 0; i < hullCount; i++) + { + ConvexHull ch; + GetConvexHull(i, ch); + totalVolume += ch.m_volume; + } + // compute the reciprocal of the total volume + double recipVolume = 1.0 / totalVolume; + // Add in the weighted by volume average of the center point of each convex hull + for (uint32_t i = 0; i < hullCount; i++) + { + ConvexHull ch; + GetConvexHull(i, ch); + double ratio = ch.m_volume*recipVolume; + centerOfMass[0] += ch.m_center[0] * ratio; + centerOfMass[1] += ch.m_center[1] * ratio; + centerOfMass[2] += ch.m_center[2] * ratio; + } + } + return ret; +} + +} // end of VHACD namespace diff --git a/thirdparty/vhacd/src/btAlignedAllocator.cpp b/thirdparty/vhacd/src/btAlignedAllocator.cpp new file mode 100644 index 0000000000..8dee31e7e3 --- /dev/null +++ b/thirdparty/vhacd/src/btAlignedAllocator.cpp @@ -0,0 +1,192 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include "btAlignedAllocator.h" + +// -- GODOT start -- +namespace VHACD { +// -- GODOT end -- + +#ifdef _MSC_VER +#pragma warning(disable:4311 4302) +#endif + +int32_t gNumAlignedAllocs = 0; +int32_t gNumAlignedFree = 0; +int32_t gTotalBytesAlignedAllocs = 0; //detect memory leaks + +static void* btAllocDefault(size_t size) +{ + return malloc(size); +} + +static void btFreeDefault(void* ptr) +{ + free(ptr); +} + +static btAllocFunc* sAllocFunc = btAllocDefault; +static btFreeFunc* sFreeFunc = btFreeDefault; + +#if defined(BT_HAS_ALIGNED_ALLOCATOR) +#include <malloc.h> +static void* btAlignedAllocDefault(size_t size, int32_t alignment) +{ + return _aligned_malloc(size, (size_t)alignment); +} + +static void btAlignedFreeDefault(void* ptr) +{ + _aligned_free(ptr); +} +#elif defined(__CELLOS_LV2__) +#include <stdlib.h> + +static inline void* btAlignedAllocDefault(size_t size, int32_t alignment) +{ + return memalign(alignment, size); +} + +static inline void btAlignedFreeDefault(void* ptr) +{ + free(ptr); +} +#else +static inline void* btAlignedAllocDefault(size_t size, int32_t alignment) +{ + void* ret; + char* real; + unsigned long offset; + + real = (char*)sAllocFunc(size + sizeof(void*) + (alignment - 1)); + if (real) { + // -- GODOT start -- + // Synced with Bullet 2.88 to fix GH-27926 + //offset = (alignment - (unsigned long)(real + sizeof(void*))) & (alignment - 1); + //ret = (void*)((real + sizeof(void*)) + offset); + ret = btAlignPointer(real + sizeof(void *), alignment); + // -- GODOT end -- + *((void**)(ret)-1) = (void*)(real); + } + else { + ret = (void*)(real); + } + return (ret); +} + +static inline void btAlignedFreeDefault(void* ptr) +{ + void* real; + + if (ptr) { + real = *((void**)(ptr)-1); + sFreeFunc(real); + } +} +#endif + +static btAlignedAllocFunc* sAlignedAllocFunc = btAlignedAllocDefault; +static btAlignedFreeFunc* sAlignedFreeFunc = btAlignedFreeDefault; + +void btAlignedAllocSetCustomAligned(btAlignedAllocFunc* allocFunc, btAlignedFreeFunc* freeFunc) +{ + sAlignedAllocFunc = allocFunc ? allocFunc : btAlignedAllocDefault; + sAlignedFreeFunc = freeFunc ? freeFunc : btAlignedFreeDefault; +} + +void btAlignedAllocSetCustom(btAllocFunc* allocFunc, btFreeFunc* freeFunc) +{ + sAllocFunc = allocFunc ? allocFunc : btAllocDefault; + sFreeFunc = freeFunc ? freeFunc : btFreeDefault; +} + +#ifdef BT_DEBUG_MEMORY_ALLOCATIONS +//this generic allocator provides the total allocated number of bytes +#include <stdio.h> + +void* btAlignedAllocInternal(size_t size, int32_t alignment, int32_t line, char* filename) +{ + void* ret; + char* real; + unsigned long offset; + + gTotalBytesAlignedAllocs += size; + gNumAlignedAllocs++; + + real = (char*)sAllocFunc(size + 2 * sizeof(void*) + (alignment - 1)); + if (real) { + offset = (alignment - (unsigned long)(real + 2 * sizeof(void*))) & (alignment - 1); + ret = (void*)((real + 2 * sizeof(void*)) + offset); + *((void**)(ret)-1) = (void*)(real); + *((int32_t*)(ret)-2) = size; + } + else { + ret = (void*)(real); //?? + } + + printf("allocation#%d at address %x, from %s,line %d, size %d\n", gNumAlignedAllocs, real, filename, line, size); + + int32_t* ptr = (int32_t*)ret; + *ptr = 12; + return (ret); +} + +void btAlignedFreeInternal(void* ptr, int32_t line, char* filename) +{ + + void* real; + gNumAlignedFree++; + + if (ptr) { + real = *((void**)(ptr)-1); + int32_t size = *((int32_t*)(ptr)-2); + gTotalBytesAlignedAllocs -= size; + + printf("free #%d at address %x, from %s,line %d, size %d\n", gNumAlignedFree, real, filename, line, size); + + sFreeFunc(real); + } + else { + printf("NULL ptr\n"); + } +} + +#else //BT_DEBUG_MEMORY_ALLOCATIONS + +void* btAlignedAllocInternal(size_t size, int32_t alignment) +{ + gNumAlignedAllocs++; + void* ptr; + ptr = sAlignedAllocFunc(size, alignment); + // printf("btAlignedAllocInternal %d, %x\n",size,ptr); + return ptr; +} + +void btAlignedFreeInternal(void* ptr) +{ + if (!ptr) { + return; + } + + gNumAlignedFree++; + // printf("btAlignedFreeInternal %x\n",ptr); + sAlignedFreeFunc(ptr); +} + +// -- GODOT start -- +}; // namespace VHACD +// -- GODOT end -- + +#endif //BT_DEBUG_MEMORY_ALLOCATIONS diff --git a/thirdparty/vhacd/src/btConvexHullComputer.cpp b/thirdparty/vhacd/src/btConvexHullComputer.cpp new file mode 100644 index 0000000000..8ab34af2ab --- /dev/null +++ b/thirdparty/vhacd/src/btConvexHullComputer.cpp @@ -0,0 +1,2487 @@ +/* +Copyright (c) 2011 Ole Kniemeyer, MAXON, www.maxon.net + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include <string.h> + +#include "btAlignedObjectArray.h" +#include "btConvexHullComputer.h" +#include "btMinMax.h" +#include "btVector3.h" + +#ifdef __GNUC__ +#include <stdint.h> +#elif defined(_MSC_VER) +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#else +typedef int32_t int32_t; +typedef long long int32_t int64_t; +typedef uint32_t uint32_t; +typedef unsigned long long int32_t uint64_t; +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4458) +#endif + +//The definition of USE_X86_64_ASM is moved into the build system. You can enable it manually by commenting out the following lines +//#if (defined(__GNUC__) && defined(__x86_64__) && !defined(__ICL)) // || (defined(__ICL) && defined(_M_X64)) bug in Intel compiler, disable inline assembly +// #define USE_X86_64_ASM +//#endif + +//#define DEBUG_CONVEX_HULL +//#define SHOW_ITERATIONS + +#if defined(DEBUG_CONVEX_HULL) || defined(SHOW_ITERATIONS) +#include <stdio.h> +#endif + +// -- GODOT start -- +namespace VHACD { +// -- GODOT end -- + +// Convex hull implementation based on Preparata and Hong +// Ole Kniemeyer, MAXON Computer GmbH +class btConvexHullInternal { +public: + class Point64 { + public: + int64_t x; + int64_t y; + int64_t z; + + Point64(int64_t x, int64_t y, int64_t z) + : x(x) + , y(y) + , z(z) + { + } + + bool isZero() + { + return (x == 0) && (y == 0) && (z == 0); + } + + int64_t dot(const Point64& b) const + { + return x * b.x + y * b.y + z * b.z; + } + }; + + class Point32 { + public: + int32_t x; + int32_t y; + int32_t z; + int32_t index; + + Point32() + { + } + + Point32(int32_t x, int32_t y, int32_t z) + : x(x) + , y(y) + , z(z) + , index(-1) + { + } + + bool operator==(const Point32& b) const + { + return (x == b.x) && (y == b.y) && (z == b.z); + } + + bool operator!=(const Point32& b) const + { + return (x != b.x) || (y != b.y) || (z != b.z); + } + + bool isZero() + { + return (x == 0) && (y == 0) && (z == 0); + } + + Point64 cross(const Point32& b) const + { + return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); + } + + Point64 cross(const Point64& b) const + { + return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); + } + + int64_t dot(const Point32& b) const + { + return x * b.x + y * b.y + z * b.z; + } + + int64_t dot(const Point64& b) const + { + return x * b.x + y * b.y + z * b.z; + } + + Point32 operator+(const Point32& b) const + { + return Point32(x + b.x, y + b.y, z + b.z); + } + + Point32 operator-(const Point32& b) const + { + return Point32(x - b.x, y - b.y, z - b.z); + } + }; + + class Int128 { + public: + uint64_t low; + uint64_t high; + + Int128() + { + } + + Int128(uint64_t low, uint64_t high) + : low(low) + , high(high) + { + } + + Int128(uint64_t low) + : low(low) + , high(0) + { + } + + Int128(int64_t value) + : low(value) + , high((value >= 0) ? 0 : (uint64_t)-1LL) + { + } + + static Int128 mul(int64_t a, int64_t b); + + static Int128 mul(uint64_t a, uint64_t b); + + Int128 operator-() const + { + return Int128((uint64_t) - (int64_t)low, ~high + (low == 0)); + } + + Int128 operator+(const Int128& b) const + { +#ifdef USE_X86_64_ASM + Int128 result; + __asm__("addq %[bl], %[rl]\n\t" + "adcq %[bh], %[rh]\n\t" + : [rl] "=r"(result.low), [rh] "=r"(result.high) + : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) + : "cc"); + return result; +#else + uint64_t lo = low + b.low; + return Int128(lo, high + b.high + (lo < low)); +#endif + } + + Int128 operator-(const Int128& b) const + { +#ifdef USE_X86_64_ASM + Int128 result; + __asm__("subq %[bl], %[rl]\n\t" + "sbbq %[bh], %[rh]\n\t" + : [rl] "=r"(result.low), [rh] "=r"(result.high) + : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) + : "cc"); + return result; +#else + return *this + -b; +#endif + } + + Int128& operator+=(const Int128& b) + { +#ifdef USE_X86_64_ASM + __asm__("addq %[bl], %[rl]\n\t" + "adcq %[bh], %[rh]\n\t" + : [rl] "=r"(low), [rh] "=r"(high) + : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) + : "cc"); +#else + uint64_t lo = low + b.low; + if (lo < low) { + ++high; + } + low = lo; + high += b.high; +#endif + return *this; + } + + Int128& operator++() + { + if (++low == 0) { + ++high; + } + return *this; + } + + Int128 operator*(int64_t b) const; + + btScalar toScalar() const + { + return ((int64_t)high >= 0) ? btScalar(high) * (btScalar(0x100000000LL) * btScalar(0x100000000LL)) + btScalar(low) + : -(-*this).toScalar(); + } + + int32_t getSign() const + { + return ((int64_t)high < 0) ? -1 : (high || low) ? 1 : 0; + } + + bool operator<(const Int128& b) const + { + return (high < b.high) || ((high == b.high) && (low < b.low)); + } + + int32_t ucmp(const Int128& b) const + { + if (high < b.high) { + return -1; + } + if (high > b.high) { + return 1; + } + if (low < b.low) { + return -1; + } + if (low > b.low) { + return 1; + } + return 0; + } + }; + + class Rational64 { + private: + uint64_t m_numerator; + uint64_t m_denominator; + int32_t sign; + + public: + Rational64(int64_t numerator, int64_t denominator) + { + if (numerator > 0) { + sign = 1; + m_numerator = (uint64_t)numerator; + } + else if (numerator < 0) { + sign = -1; + m_numerator = (uint64_t)-numerator; + } + else { + sign = 0; + m_numerator = 0; + } + if (denominator > 0) { + m_denominator = (uint64_t)denominator; + } + else if (denominator < 0) { + sign = -sign; + m_denominator = (uint64_t)-denominator; + } + else { + m_denominator = 0; + } + } + + bool isNegativeInfinity() const + { + return (sign < 0) && (m_denominator == 0); + } + + bool isNaN() const + { + return (sign == 0) && (m_denominator == 0); + } + + int32_t compare(const Rational64& b) const; + + btScalar toScalar() const + { + return sign * ((m_denominator == 0) ? SIMD_INFINITY : (btScalar)m_numerator / m_denominator); + } + }; + + class Rational128 { + private: + Int128 numerator; + Int128 denominator; + int32_t sign; + bool isInt64; + + public: + Rational128(int64_t value) + { + if (value > 0) { + sign = 1; + this->numerator = value; + } + else if (value < 0) { + sign = -1; + this->numerator = -value; + } + else { + sign = 0; + this->numerator = (uint64_t)0; + } + this->denominator = (uint64_t)1; + isInt64 = true; + } + + Rational128(const Int128& numerator, const Int128& denominator) + { + sign = numerator.getSign(); + if (sign >= 0) { + this->numerator = numerator; + } + else { + this->numerator = -numerator; + } + int32_t dsign = denominator.getSign(); + if (dsign >= 0) { + this->denominator = denominator; + } + else { + sign = -sign; + this->denominator = -denominator; + } + isInt64 = false; + } + + int32_t compare(const Rational128& b) const; + + int32_t compare(int64_t b) const; + + btScalar toScalar() const + { + return sign * ((denominator.getSign() == 0) ? SIMD_INFINITY : numerator.toScalar() / denominator.toScalar()); + } + }; + + class PointR128 { + public: + Int128 x; + Int128 y; + Int128 z; + Int128 denominator; + + PointR128() + { + } + + PointR128(Int128 x, Int128 y, Int128 z, Int128 denominator) + : x(x) + , y(y) + , z(z) + , denominator(denominator) + { + } + + btScalar xvalue() const + { + return x.toScalar() / denominator.toScalar(); + } + + btScalar yvalue() const + { + return y.toScalar() / denominator.toScalar(); + } + + btScalar zvalue() const + { + return z.toScalar() / denominator.toScalar(); + } + }; + + class Edge; + class Face; + + class Vertex { + public: + Vertex* next; + Vertex* prev; + Edge* edges; + Face* firstNearbyFace; + Face* lastNearbyFace; + PointR128 point128; + Point32 point; + int32_t copy; + + Vertex() + : next(NULL) + , prev(NULL) + , edges(NULL) + , firstNearbyFace(NULL) + , lastNearbyFace(NULL) + , copy(-1) + { + } + +#ifdef DEBUG_CONVEX_HULL + void print() + { + printf("V%d (%d, %d, %d)", point.index, point.x, point.y, point.z); + } + + void printGraph(); +#endif + + Point32 operator-(const Vertex& b) const + { + return point - b.point; + } + + Rational128 dot(const Point64& b) const + { + return (point.index >= 0) ? Rational128(point.dot(b)) + : Rational128(point128.x * b.x + point128.y * b.y + point128.z * b.z, point128.denominator); + } + + btScalar xvalue() const + { + return (point.index >= 0) ? btScalar(point.x) : point128.xvalue(); + } + + btScalar yvalue() const + { + return (point.index >= 0) ? btScalar(point.y) : point128.yvalue(); + } + + btScalar zvalue() const + { + return (point.index >= 0) ? btScalar(point.z) : point128.zvalue(); + } + + void receiveNearbyFaces(Vertex* src) + { + if (lastNearbyFace) { + lastNearbyFace->nextWithSameNearbyVertex = src->firstNearbyFace; + } + else { + firstNearbyFace = src->firstNearbyFace; + } + if (src->lastNearbyFace) { + lastNearbyFace = src->lastNearbyFace; + } + for (Face* f = src->firstNearbyFace; f; f = f->nextWithSameNearbyVertex) { + btAssert(f->nearbyVertex == src); + f->nearbyVertex = this; + } + src->firstNearbyFace = NULL; + src->lastNearbyFace = NULL; + } + }; + + class Edge { + public: + Edge* next; + Edge* prev; + Edge* reverse; + Vertex* target; + Face* face; + int32_t copy; + + ~Edge() + { + next = NULL; + prev = NULL; + reverse = NULL; + target = NULL; + face = NULL; + } + + void link(Edge* n) + { + btAssert(reverse->target == n->reverse->target); + next = n; + n->prev = this; + } + +#ifdef DEBUG_CONVEX_HULL + void print() + { + printf("E%p : %d -> %d, n=%p p=%p (0 %d\t%d\t%d) -> (%d %d %d)", this, reverse->target->point.index, target->point.index, next, prev, + reverse->target->point.x, reverse->target->point.y, reverse->target->point.z, target->point.x, target->point.y, target->point.z); + } +#endif + }; + + class Face { + public: + Face* next; + Vertex* nearbyVertex; + Face* nextWithSameNearbyVertex; + Point32 origin; + Point32 dir0; + Point32 dir1; + + Face() + : next(NULL) + , nearbyVertex(NULL) + , nextWithSameNearbyVertex(NULL) + { + } + + void init(Vertex* a, Vertex* b, Vertex* c) + { + nearbyVertex = a; + origin = a->point; + dir0 = *b - *a; + dir1 = *c - *a; + if (a->lastNearbyFace) { + a->lastNearbyFace->nextWithSameNearbyVertex = this; + } + else { + a->firstNearbyFace = this; + } + a->lastNearbyFace = this; + } + + Point64 getNormal() + { + return dir0.cross(dir1); + } + }; + + template <typename UWord, typename UHWord> + class DMul { + private: + static uint32_t high(uint64_t value) + { + return (uint32_t)(value >> 32); + } + + static uint32_t low(uint64_t value) + { + return (uint32_t)value; + } + + static uint64_t mul(uint32_t a, uint32_t b) + { + return (uint64_t)a * (uint64_t)b; + } + + static void shlHalf(uint64_t& value) + { + value <<= 32; + } + + static uint64_t high(Int128 value) + { + return value.high; + } + + static uint64_t low(Int128 value) + { + return value.low; + } + + static Int128 mul(uint64_t a, uint64_t b) + { + return Int128::mul(a, b); + } + + static void shlHalf(Int128& value) + { + value.high = value.low; + value.low = 0; + } + + public: + static void mul(UWord a, UWord b, UWord& resLow, UWord& resHigh) + { + UWord p00 = mul(low(a), low(b)); + UWord p01 = mul(low(a), high(b)); + UWord p10 = mul(high(a), low(b)); + UWord p11 = mul(high(a), high(b)); + UWord p0110 = UWord(low(p01)) + UWord(low(p10)); + p11 += high(p01); + p11 += high(p10); + p11 += high(p0110); + shlHalf(p0110); + p00 += p0110; + if (p00 < p0110) { + ++p11; + } + resLow = p00; + resHigh = p11; + } + }; + +private: + class IntermediateHull { + public: + Vertex* minXy; + Vertex* maxXy; + Vertex* minYx; + Vertex* maxYx; + + IntermediateHull() + : minXy(NULL) + , maxXy(NULL) + , minYx(NULL) + , maxYx(NULL) + { + } + + void print(); + }; + + enum Orientation { NONE, + CLOCKWISE, + COUNTER_CLOCKWISE }; + + template <typename T> + class PoolArray { + private: + T* array; + int32_t size; + + public: + PoolArray<T>* next; + + PoolArray(int32_t size) + : size(size) + , next(NULL) + { + array = (T*)btAlignedAlloc(sizeof(T) * size, 16); + } + + ~PoolArray() + { + btAlignedFree(array); + } + + T* init() + { + T* o = array; + for (int32_t i = 0; i < size; i++, o++) { + o->next = (i + 1 < size) ? o + 1 : NULL; + } + return array; + } + }; + + template <typename T> + class Pool { + private: + PoolArray<T>* arrays; + PoolArray<T>* nextArray; + T* freeObjects; + int32_t arraySize; + + public: + Pool() + : arrays(NULL) + , nextArray(NULL) + , freeObjects(NULL) + , arraySize(256) + { + } + + ~Pool() + { + while (arrays) { + PoolArray<T>* p = arrays; + arrays = p->next; + p->~PoolArray<T>(); + btAlignedFree(p); + } + } + + void reset() + { + nextArray = arrays; + freeObjects = NULL; + } + + void setArraySize(int32_t arraySize) + { + this->arraySize = arraySize; + } + + T* newObject() + { + T* o = freeObjects; + if (!o) { + PoolArray<T>* p = nextArray; + if (p) { + nextArray = p->next; + } + else { + p = new (btAlignedAlloc(sizeof(PoolArray<T>), 16)) PoolArray<T>(arraySize); + p->next = arrays; + arrays = p; + } + o = p->init(); + } + freeObjects = o->next; + return new (o) T(); + }; + + void freeObject(T* object) + { + object->~T(); + object->next = freeObjects; + freeObjects = object; + } + }; + + btVector3 scaling; + btVector3 center; + Pool<Vertex> vertexPool; + Pool<Edge> edgePool; + Pool<Face> facePool; + btAlignedObjectArray<Vertex*> originalVertices; + int32_t mergeStamp; + int32_t minAxis; + int32_t medAxis; + int32_t maxAxis; + int32_t usedEdgePairs; + int32_t maxUsedEdgePairs; + + static Orientation getOrientation(const Edge* prev, const Edge* next, const Point32& s, const Point32& t); + Edge* findMaxAngle(bool ccw, const Vertex* start, const Point32& s, const Point64& rxs, const Point64& sxrxs, Rational64& minCot); + void findEdgeForCoplanarFaces(Vertex* c0, Vertex* c1, Edge*& e0, Edge*& e1, Vertex* stop0, Vertex* stop1); + + Edge* newEdgePair(Vertex* from, Vertex* to); + + void removeEdgePair(Edge* edge) + { + Edge* n = edge->next; + Edge* r = edge->reverse; + + btAssert(edge->target && r->target); + + if (n != edge) { + n->prev = edge->prev; + edge->prev->next = n; + r->target->edges = n; + } + else { + r->target->edges = NULL; + } + + n = r->next; + + if (n != r) { + n->prev = r->prev; + r->prev->next = n; + edge->target->edges = n; + } + else { + edge->target->edges = NULL; + } + + edgePool.freeObject(edge); + edgePool.freeObject(r); + usedEdgePairs--; + } + + void computeInternal(int32_t start, int32_t end, IntermediateHull& result); + + bool mergeProjection(IntermediateHull& h0, IntermediateHull& h1, Vertex*& c0, Vertex*& c1); + + void merge(IntermediateHull& h0, IntermediateHull& h1); + + btVector3 toBtVector(const Point32& v); + + btVector3 getBtNormal(Face* face); + + bool shiftFace(Face* face, btScalar amount, btAlignedObjectArray<Vertex*> stack); + +public: + Vertex* vertexList; + + void compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count); + + btVector3 getCoordinates(const Vertex* v); + + btScalar shrink(btScalar amount, btScalar clampAmount); +}; + +btConvexHullInternal::Int128 btConvexHullInternal::Int128::operator*(int64_t b) const +{ + bool negative = (int64_t)high < 0; + Int128 a = negative ? -*this : *this; + if (b < 0) { + negative = !negative; + b = -b; + } + Int128 result = mul(a.low, (uint64_t)b); + result.high += a.high * (uint64_t)b; + return negative ? -result : result; +} + +btConvexHullInternal::Int128 btConvexHullInternal::Int128::mul(int64_t a, int64_t b) +{ + Int128 result; + +#ifdef USE_X86_64_ASM + __asm__("imulq %[b]" + : "=a"(result.low), "=d"(result.high) + : "0"(a), [b] "r"(b) + : "cc"); + return result; + +#else + bool negative = a < 0; + if (negative) { + a = -a; + } + if (b < 0) { + negative = !negative; + b = -b; + } + DMul<uint64_t, uint32_t>::mul((uint64_t)a, (uint64_t)b, result.low, result.high); + return negative ? -result : result; +#endif +} + +btConvexHullInternal::Int128 btConvexHullInternal::Int128::mul(uint64_t a, uint64_t b) +{ + Int128 result; + +#ifdef USE_X86_64_ASM + __asm__("mulq %[b]" + : "=a"(result.low), "=d"(result.high) + : "0"(a), [b] "r"(b) + : "cc"); + +#else + DMul<uint64_t, uint32_t>::mul(a, b, result.low, result.high); +#endif + + return result; +} + +int32_t btConvexHullInternal::Rational64::compare(const Rational64& b) const +{ + if (sign != b.sign) { + return sign - b.sign; + } + else if (sign == 0) { + return 0; + } + +// return (numerator * b.denominator > b.numerator * denominator) ? sign : (numerator * b.denominator < b.numerator * denominator) ? -sign : 0; + +#ifdef USE_X86_64_ASM + + int32_t result; + int64_t tmp; + int64_t dummy; + __asm__("mulq %[bn]\n\t" + "movq %%rax, %[tmp]\n\t" + "movq %%rdx, %%rbx\n\t" + "movq %[tn], %%rax\n\t" + "mulq %[bd]\n\t" + "subq %[tmp], %%rax\n\t" + "sbbq %%rbx, %%rdx\n\t" // rdx:rax contains 128-bit-difference "numerator*b.denominator - b.numerator*denominator" + "setnsb %%bh\n\t" // bh=1 if difference is non-negative, bh=0 otherwise + "orq %%rdx, %%rax\n\t" + "setnzb %%bl\n\t" // bl=1 if difference if non-zero, bl=0 if it is zero + "decb %%bh\n\t" // now bx=0x0000 if difference is zero, 0xff01 if it is negative, 0x0001 if it is positive (i.e., same sign as difference) + "shll $16, %%ebx\n\t" // ebx has same sign as difference + : "=&b"(result), [tmp] "=&r"(tmp), "=a"(dummy) + : "a"(denominator), [bn] "g"(b.numerator), [tn] "g"(numerator), [bd] "g"(b.denominator) + : "%rdx", "cc"); + return result ? result ^ sign // if sign is +1, only bit 0 of result is inverted, which does not change the sign of result (and cannot result in zero) + // if sign is -1, all bits of result are inverted, which changes the sign of result (and again cannot result in zero) + : 0; + +#else + + return sign * Int128::mul(m_numerator, b.m_denominator).ucmp(Int128::mul(m_denominator, b.m_numerator)); + +#endif +} + +int32_t btConvexHullInternal::Rational128::compare(const Rational128& b) const +{ + if (sign != b.sign) { + return sign - b.sign; + } + else if (sign == 0) { + return 0; + } + if (isInt64) { + return -b.compare(sign * (int64_t)numerator.low); + } + + Int128 nbdLow, nbdHigh, dbnLow, dbnHigh; + DMul<Int128, uint64_t>::mul(numerator, b.denominator, nbdLow, nbdHigh); + DMul<Int128, uint64_t>::mul(denominator, b.numerator, dbnLow, dbnHigh); + + int32_t cmp = nbdHigh.ucmp(dbnHigh); + if (cmp) { + return cmp * sign; + } + return nbdLow.ucmp(dbnLow) * sign; +} + +int32_t btConvexHullInternal::Rational128::compare(int64_t b) const +{ + if (isInt64) { + int64_t a = sign * (int64_t)numerator.low; + return (a > b) ? 1 : (a < b) ? -1 : 0; + } + if (b > 0) { + if (sign <= 0) { + return -1; + } + } + else if (b < 0) { + if (sign >= 0) { + return 1; + } + b = -b; + } + else { + return sign; + } + + return numerator.ucmp(denominator * b) * sign; +} + +btConvexHullInternal::Edge* btConvexHullInternal::newEdgePair(Vertex* from, Vertex* to) +{ + btAssert(from && to); + Edge* e = edgePool.newObject(); + Edge* r = edgePool.newObject(); + e->reverse = r; + r->reverse = e; + e->copy = mergeStamp; + r->copy = mergeStamp; + e->target = to; + r->target = from; + e->face = NULL; + r->face = NULL; + usedEdgePairs++; + if (usedEdgePairs > maxUsedEdgePairs) { + maxUsedEdgePairs = usedEdgePairs; + } + return e; +} + +bool btConvexHullInternal::mergeProjection(IntermediateHull& h0, IntermediateHull& h1, Vertex*& c0, Vertex*& c1) +{ + Vertex* v0 = h0.maxYx; + Vertex* v1 = h1.minYx; + if ((v0->point.x == v1->point.x) && (v0->point.y == v1->point.y)) { + btAssert(v0->point.z < v1->point.z); + Vertex* v1p = v1->prev; + if (v1p == v1) { + c0 = v0; + if (v1->edges) { + btAssert(v1->edges->next == v1->edges); + v1 = v1->edges->target; + btAssert(v1->edges->next == v1->edges); + } + c1 = v1; + return false; + } + Vertex* v1n = v1->next; + v1p->next = v1n; + v1n->prev = v1p; + if (v1 == h1.minXy) { + if ((v1n->point.x < v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y < v1p->point.y))) { + h1.minXy = v1n; + } + else { + h1.minXy = v1p; + } + } + if (v1 == h1.maxXy) { + if ((v1n->point.x > v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y > v1p->point.y))) { + h1.maxXy = v1n; + } + else { + h1.maxXy = v1p; + } + } + } + + v0 = h0.maxXy; + v1 = h1.maxXy; + Vertex* v00 = NULL; + Vertex* v10 = NULL; + int32_t sign = 1; + + for (int32_t side = 0; side <= 1; side++) { + int32_t dx = (v1->point.x - v0->point.x) * sign; + if (dx > 0) { + while (true) { + int32_t dy = v1->point.y - v0->point.y; + + Vertex* w0 = side ? v0->next : v0->prev; + if (w0 != v0) { + int32_t dx0 = (w0->point.x - v0->point.x) * sign; + int32_t dy0 = w0->point.y - v0->point.y; + if ((dy0 <= 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx <= dy * dx0)))) { + v0 = w0; + dx = (v1->point.x - v0->point.x) * sign; + continue; + } + } + + Vertex* w1 = side ? v1->next : v1->prev; + if (w1 != v1) { + int32_t dx1 = (w1->point.x - v1->point.x) * sign; + int32_t dy1 = w1->point.y - v1->point.y; + int32_t dxn = (w1->point.x - v0->point.x) * sign; + if ((dxn > 0) && (dy1 < 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx < dy * dx1)))) { + v1 = w1; + dx = dxn; + continue; + } + } + + break; + } + } + else if (dx < 0) { + while (true) { + int32_t dy = v1->point.y - v0->point.y; + + Vertex* w1 = side ? v1->prev : v1->next; + if (w1 != v1) { + int32_t dx1 = (w1->point.x - v1->point.x) * sign; + int32_t dy1 = w1->point.y - v1->point.y; + if ((dy1 >= 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx <= dy * dx1)))) { + v1 = w1; + dx = (v1->point.x - v0->point.x) * sign; + continue; + } + } + + Vertex* w0 = side ? v0->prev : v0->next; + if (w0 != v0) { + int32_t dx0 = (w0->point.x - v0->point.x) * sign; + int32_t dy0 = w0->point.y - v0->point.y; + int32_t dxn = (v1->point.x - w0->point.x) * sign; + if ((dxn < 0) && (dy0 > 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx < dy * dx0)))) { + v0 = w0; + dx = dxn; + continue; + } + } + + break; + } + } + else { + int32_t x = v0->point.x; + int32_t y0 = v0->point.y; + Vertex* w0 = v0; + Vertex* t; + while (((t = side ? w0->next : w0->prev) != v0) && (t->point.x == x) && (t->point.y <= y0)) { + w0 = t; + y0 = t->point.y; + } + v0 = w0; + + int32_t y1 = v1->point.y; + Vertex* w1 = v1; + while (((t = side ? w1->prev : w1->next) != v1) && (t->point.x == x) && (t->point.y >= y1)) { + w1 = t; + y1 = t->point.y; + } + v1 = w1; + } + + if (side == 0) { + v00 = v0; + v10 = v1; + + v0 = h0.minXy; + v1 = h1.minXy; + sign = -1; + } + } + + v0->prev = v1; + v1->next = v0; + + v00->next = v10; + v10->prev = v00; + + if (h1.minXy->point.x < h0.minXy->point.x) { + h0.minXy = h1.minXy; + } + if (h1.maxXy->point.x >= h0.maxXy->point.x) { + h0.maxXy = h1.maxXy; + } + + h0.maxYx = h1.maxYx; + + c0 = v00; + c1 = v10; + + return true; +} + +void btConvexHullInternal::computeInternal(int32_t start, int32_t end, IntermediateHull& result) +{ + int32_t n = end - start; + switch (n) { + case 0: + result.minXy = NULL; + result.maxXy = NULL; + result.minYx = NULL; + result.maxYx = NULL; + return; + case 2: { + Vertex* v = originalVertices[start]; + Vertex* w = v + 1; + if (v->point != w->point) { + int32_t dx = v->point.x - w->point.x; + int32_t dy = v->point.y - w->point.y; + + if ((dx == 0) && (dy == 0)) { + if (v->point.z > w->point.z) { + Vertex* t = w; + w = v; + v = t; + } + btAssert(v->point.z < w->point.z); + v->next = v; + v->prev = v; + result.minXy = v; + result.maxXy = v; + result.minYx = v; + result.maxYx = v; + } + else { + v->next = w; + v->prev = w; + w->next = v; + w->prev = v; + + if ((dx < 0) || ((dx == 0) && (dy < 0))) { + result.minXy = v; + result.maxXy = w; + } + else { + result.minXy = w; + result.maxXy = v; + } + + if ((dy < 0) || ((dy == 0) && (dx < 0))) { + result.minYx = v; + result.maxYx = w; + } + else { + result.minYx = w; + result.maxYx = v; + } + } + + Edge* e = newEdgePair(v, w); + e->link(e); + v->edges = e; + + e = e->reverse; + e->link(e); + w->edges = e; + + return; + } + } + // lint -fallthrough + case 1: { + Vertex* v = originalVertices[start]; + v->edges = NULL; + v->next = v; + v->prev = v; + + result.minXy = v; + result.maxXy = v; + result.minYx = v; + result.maxYx = v; + + return; + } + } + + int32_t split0 = start + n / 2; + Point32 p = originalVertices[split0 - 1]->point; + int32_t split1 = split0; + while ((split1 < end) && (originalVertices[split1]->point == p)) { + split1++; + } + computeInternal(start, split0, result); + IntermediateHull hull1; + computeInternal(split1, end, hull1); +#ifdef DEBUG_CONVEX_HULL + printf("\n\nMerge\n"); + result.print(); + hull1.print(); +#endif + merge(result, hull1); +#ifdef DEBUG_CONVEX_HULL + printf("\n Result\n"); + result.print(); +#endif +} + +#ifdef DEBUG_CONVEX_HULL +void btConvexHullInternal::IntermediateHull::print() +{ + printf(" Hull\n"); + for (Vertex* v = minXy; v;) { + printf(" "); + v->print(); + if (v == maxXy) { + printf(" maxXy"); + } + if (v == minYx) { + printf(" minYx"); + } + if (v == maxYx) { + printf(" maxYx"); + } + if (v->next->prev != v) { + printf(" Inconsistency"); + } + printf("\n"); + v = v->next; + if (v == minXy) { + break; + } + } + if (minXy) { + minXy->copy = (minXy->copy == -1) ? -2 : -1; + minXy->printGraph(); + } +} + +void btConvexHullInternal::Vertex::printGraph() +{ + print(); + printf("\nEdges\n"); + Edge* e = edges; + if (e) { + do { + e->print(); + printf("\n"); + e = e->next; + } while (e != edges); + do { + Vertex* v = e->target; + if (v->copy != copy) { + v->copy = copy; + v->printGraph(); + } + e = e->next; + } while (e != edges); + } +} +#endif + +btConvexHullInternal::Orientation btConvexHullInternal::getOrientation(const Edge* prev, const Edge* next, const Point32& s, const Point32& t) +{ + btAssert(prev->reverse->target == next->reverse->target); + if (prev->next == next) { + if (prev->prev == next) { + Point64 n = t.cross(s); + Point64 m = (*prev->target - *next->reverse->target).cross(*next->target - *next->reverse->target); + btAssert(!m.isZero()); + int64_t dot = n.dot(m); + btAssert(dot != 0); + return (dot > 0) ? COUNTER_CLOCKWISE : CLOCKWISE; + } + return COUNTER_CLOCKWISE; + } + else if (prev->prev == next) { + return CLOCKWISE; + } + else { + return NONE; + } +} + +btConvexHullInternal::Edge* btConvexHullInternal::findMaxAngle(bool ccw, const Vertex* start, const Point32& s, const Point64& rxs, const Point64& sxrxs, Rational64& minCot) +{ + Edge* minEdge = NULL; + +#ifdef DEBUG_CONVEX_HULL + printf("find max edge for %d\n", start->point.index); +#endif + Edge* e = start->edges; + if (e) { + do { + if (e->copy > mergeStamp) { + Point32 t = *e->target - *start; + Rational64 cot(t.dot(sxrxs), t.dot(rxs)); +#ifdef DEBUG_CONVEX_HULL + printf(" Angle is %f (%d) for ", (float)btAtan(cot.toScalar()), (int32_t)cot.isNaN()); + e->print(); +#endif + if (cot.isNaN()) { + btAssert(ccw ? (t.dot(s) < 0) : (t.dot(s) > 0)); + } + else { + int32_t cmp; + if (minEdge == NULL) { + minCot = cot; + minEdge = e; + } + else if ((cmp = cot.compare(minCot)) < 0) { + minCot = cot; + minEdge = e; + } + else if ((cmp == 0) && (ccw == (getOrientation(minEdge, e, s, t) == COUNTER_CLOCKWISE))) { + minEdge = e; + } + } +#ifdef DEBUG_CONVEX_HULL + printf("\n"); +#endif + } + e = e->next; + } while (e != start->edges); + } + return minEdge; +} + +void btConvexHullInternal::findEdgeForCoplanarFaces(Vertex* c0, Vertex* c1, Edge*& e0, Edge*& e1, Vertex* stop0, Vertex* stop1) +{ + Edge* start0 = e0; + Edge* start1 = e1; + Point32 et0 = start0 ? start0->target->point : c0->point; + Point32 et1 = start1 ? start1->target->point : c1->point; + Point32 s = c1->point - c0->point; + Point64 normal = ((start0 ? start0 : start1)->target->point - c0->point).cross(s); + int64_t dist = c0->point.dot(normal); + btAssert(!start1 || (start1->target->point.dot(normal) == dist)); + Point64 perp = s.cross(normal); + btAssert(!perp.isZero()); + +#ifdef DEBUG_CONVEX_HULL + printf(" Advancing %d %d (%p %p, %d %d)\n", c0->point.index, c1->point.index, start0, start1, start0 ? start0->target->point.index : -1, start1 ? start1->target->point.index : -1); +#endif + + int64_t maxDot0 = et0.dot(perp); + if (e0) { + while (e0->target != stop0) { + Edge* e = e0->reverse->prev; + if (e->target->point.dot(normal) < dist) { + break; + } + btAssert(e->target->point.dot(normal) == dist); + if (e->copy == mergeStamp) { + break; + } + int64_t dot = e->target->point.dot(perp); + if (dot <= maxDot0) { + break; + } + maxDot0 = dot; + e0 = e; + et0 = e->target->point; + } + } + + int64_t maxDot1 = et1.dot(perp); + if (e1) { + while (e1->target != stop1) { + Edge* e = e1->reverse->next; + if (e->target->point.dot(normal) < dist) { + break; + } + btAssert(e->target->point.dot(normal) == dist); + if (e->copy == mergeStamp) { + break; + } + int64_t dot = e->target->point.dot(perp); + if (dot <= maxDot1) { + break; + } + maxDot1 = dot; + e1 = e; + et1 = e->target->point; + } + } + +#ifdef DEBUG_CONVEX_HULL + printf(" Starting at %d %d\n", et0.index, et1.index); +#endif + + int64_t dx = maxDot1 - maxDot0; + if (dx > 0) { + while (true) { + int64_t dy = (et1 - et0).dot(s); + + if (e0 && (e0->target != stop0)) { + Edge* f0 = e0->next->reverse; + if (f0->copy > mergeStamp) { + int64_t dx0 = (f0->target->point - et0).dot(perp); + int64_t dy0 = (f0->target->point - et0).dot(s); + if ((dx0 == 0) ? (dy0 < 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) >= 0))) { + et0 = f0->target->point; + dx = (et1 - et0).dot(perp); + e0 = (e0 == start0) ? NULL : f0; + continue; + } + } + } + + if (e1 && (e1->target != stop1)) { + Edge* f1 = e1->reverse->next; + if (f1->copy > mergeStamp) { + Point32 d1 = f1->target->point - et1; + if (d1.dot(normal) == 0) { + int64_t dx1 = d1.dot(perp); + int64_t dy1 = d1.dot(s); + int64_t dxn = (f1->target->point - et0).dot(perp); + if ((dxn > 0) && ((dx1 == 0) ? (dy1 < 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) > 0)))) { + e1 = f1; + et1 = e1->target->point; + dx = dxn; + continue; + } + } + else { + btAssert((e1 == start1) && (d1.dot(normal) < 0)); + } + } + } + + break; + } + } + else if (dx < 0) { + while (true) { + int64_t dy = (et1 - et0).dot(s); + + if (e1 && (e1->target != stop1)) { + Edge* f1 = e1->prev->reverse; + if (f1->copy > mergeStamp) { + int64_t dx1 = (f1->target->point - et1).dot(perp); + int64_t dy1 = (f1->target->point - et1).dot(s); + if ((dx1 == 0) ? (dy1 > 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) <= 0))) { + et1 = f1->target->point; + dx = (et1 - et0).dot(perp); + e1 = (e1 == start1) ? NULL : f1; + continue; + } + } + } + + if (e0 && (e0->target != stop0)) { + Edge* f0 = e0->reverse->prev; + if (f0->copy > mergeStamp) { + Point32 d0 = f0->target->point - et0; + if (d0.dot(normal) == 0) { + int64_t dx0 = d0.dot(perp); + int64_t dy0 = d0.dot(s); + int64_t dxn = (et1 - f0->target->point).dot(perp); + if ((dxn < 0) && ((dx0 == 0) ? (dy0 > 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) < 0)))) { + e0 = f0; + et0 = e0->target->point; + dx = dxn; + continue; + } + } + else { + btAssert((e0 == start0) && (d0.dot(normal) < 0)); + } + } + } + + break; + } + } +#ifdef DEBUG_CONVEX_HULL + printf(" Advanced edges to %d %d\n", et0.index, et1.index); +#endif +} + +void btConvexHullInternal::merge(IntermediateHull& h0, IntermediateHull& h1) +{ + if (!h1.maxXy) { + return; + } + if (!h0.maxXy) { + h0 = h1; + return; + } + + mergeStamp--; + + Vertex* c0 = NULL; + Edge* toPrev0 = NULL; + Edge* firstNew0 = NULL; + Edge* pendingHead0 = NULL; + Edge* pendingTail0 = NULL; + Vertex* c1 = NULL; + Edge* toPrev1 = NULL; + Edge* firstNew1 = NULL; + Edge* pendingHead1 = NULL; + Edge* pendingTail1 = NULL; + Point32 prevPoint; + + if (mergeProjection(h0, h1, c0, c1)) { + Point32 s = *c1 - *c0; + Point64 normal = Point32(0, 0, -1).cross(s); + Point64 t = s.cross(normal); + btAssert(!t.isZero()); + + Edge* e = c0->edges; + Edge* start0 = NULL; + if (e) { + do { + int64_t dot = (*e->target - *c0).dot(normal); + btAssert(dot <= 0); + if ((dot == 0) && ((*e->target - *c0).dot(t) > 0)) { + if (!start0 || (getOrientation(start0, e, s, Point32(0, 0, -1)) == CLOCKWISE)) { + start0 = e; + } + } + e = e->next; + } while (e != c0->edges); + } + + e = c1->edges; + Edge* start1 = NULL; + if (e) { + do { + int64_t dot = (*e->target - *c1).dot(normal); + btAssert(dot <= 0); + if ((dot == 0) && ((*e->target - *c1).dot(t) > 0)) { + if (!start1 || (getOrientation(start1, e, s, Point32(0, 0, -1)) == COUNTER_CLOCKWISE)) { + start1 = e; + } + } + e = e->next; + } while (e != c1->edges); + } + + if (start0 || start1) { + findEdgeForCoplanarFaces(c0, c1, start0, start1, NULL, NULL); + if (start0) { + c0 = start0->target; + } + if (start1) { + c1 = start1->target; + } + } + + prevPoint = c1->point; + prevPoint.z++; + } + else { + prevPoint = c1->point; + prevPoint.x++; + } + + Vertex* first0 = c0; + Vertex* first1 = c1; + bool firstRun = true; + + while (true) { + Point32 s = *c1 - *c0; + Point32 r = prevPoint - c0->point; + Point64 rxs = r.cross(s); + Point64 sxrxs = s.cross(rxs); + +#ifdef DEBUG_CONVEX_HULL + printf("\n Checking %d %d\n", c0->point.index, c1->point.index); +#endif + Rational64 minCot0(0, 0); + Edge* min0 = findMaxAngle(false, c0, s, rxs, sxrxs, minCot0); + Rational64 minCot1(0, 0); + Edge* min1 = findMaxAngle(true, c1, s, rxs, sxrxs, minCot1); + if (!min0 && !min1) { + Edge* e = newEdgePair(c0, c1); + e->link(e); + c0->edges = e; + + e = e->reverse; + e->link(e); + c1->edges = e; + return; + } + else { + int32_t cmp = !min0 ? 1 : !min1 ? -1 : minCot0.compare(minCot1); +#ifdef DEBUG_CONVEX_HULL + printf(" -> Result %d\n", cmp); +#endif + if (firstRun || ((cmp >= 0) ? !minCot1.isNegativeInfinity() : !minCot0.isNegativeInfinity())) { + Edge* e = newEdgePair(c0, c1); + if (pendingTail0) { + pendingTail0->prev = e; + } + else { + pendingHead0 = e; + } + e->next = pendingTail0; + pendingTail0 = e; + + e = e->reverse; + if (pendingTail1) { + pendingTail1->next = e; + } + else { + pendingHead1 = e; + } + e->prev = pendingTail1; + pendingTail1 = e; + } + + Edge* e0 = min0; + Edge* e1 = min1; + +#ifdef DEBUG_CONVEX_HULL + printf(" Found min edges to %d %d\n", e0 ? e0->target->point.index : -1, e1 ? e1->target->point.index : -1); +#endif + + if (cmp == 0) { + findEdgeForCoplanarFaces(c0, c1, e0, e1, NULL, NULL); + } + + if ((cmp >= 0) && e1) { + if (toPrev1) { + for (Edge *e = toPrev1->next, *n = NULL; e != min1; e = n) { + n = e->next; + removeEdgePair(e); + } + } + + if (pendingTail1) { + if (toPrev1) { + toPrev1->link(pendingHead1); + } + else { + min1->prev->link(pendingHead1); + firstNew1 = pendingHead1; + } + pendingTail1->link(min1); + pendingHead1 = NULL; + pendingTail1 = NULL; + } + else if (!toPrev1) { + firstNew1 = min1; + } + + prevPoint = c1->point; + c1 = e1->target; + toPrev1 = e1->reverse; + } + + if ((cmp <= 0) && e0) { + if (toPrev0) { + for (Edge *e = toPrev0->prev, *n = NULL; e != min0; e = n) { + n = e->prev; + removeEdgePair(e); + } + } + + if (pendingTail0) { + if (toPrev0) { + pendingHead0->link(toPrev0); + } + else { + pendingHead0->link(min0->next); + firstNew0 = pendingHead0; + } + min0->link(pendingTail0); + pendingHead0 = NULL; + pendingTail0 = NULL; + } + else if (!toPrev0) { + firstNew0 = min0; + } + + prevPoint = c0->point; + c0 = e0->target; + toPrev0 = e0->reverse; + } + } + + if ((c0 == first0) && (c1 == first1)) { + if (toPrev0 == NULL) { + pendingHead0->link(pendingTail0); + c0->edges = pendingTail0; + } + else { + for (Edge *e = toPrev0->prev, *n = NULL; e != firstNew0; e = n) { + n = e->prev; + removeEdgePair(e); + } + if (pendingTail0) { + pendingHead0->link(toPrev0); + firstNew0->link(pendingTail0); + } + } + + if (toPrev1 == NULL) { + pendingTail1->link(pendingHead1); + c1->edges = pendingTail1; + } + else { + for (Edge *e = toPrev1->next, *n = NULL; e != firstNew1; e = n) { + n = e->next; + removeEdgePair(e); + } + if (pendingTail1) { + toPrev1->link(pendingHead1); + pendingTail1->link(firstNew1); + } + } + + return; + } + + firstRun = false; + } +} + +static bool pointCmp(const btConvexHullInternal::Point32& p, const btConvexHullInternal::Point32& q) +{ + return (p.y < q.y) || ((p.y == q.y) && ((p.x < q.x) || ((p.x == q.x) && (p.z < q.z)))); +} + +void btConvexHullInternal::compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count) +{ + btVector3 min(btScalar(1e30), btScalar(1e30), btScalar(1e30)), max(btScalar(-1e30), btScalar(-1e30), btScalar(-1e30)); + const char* ptr = (const char*)coords; + if (doubleCoords) { + for (int32_t i = 0; i < count; i++) { + const double* v = (const double*)ptr; + btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]); + ptr += stride; + min.setMin(p); + max.setMax(p); + } + } + else { + for (int32_t i = 0; i < count; i++) { + const float* v = (const float*)ptr; + btVector3 p(v[0], v[1], v[2]); + ptr += stride; + min.setMin(p); + max.setMax(p); + } + } + + btVector3 s = max - min; + maxAxis = s.maxAxis(); + minAxis = s.minAxis(); + if (minAxis == maxAxis) { + minAxis = (maxAxis + 1) % 3; + } + medAxis = 3 - maxAxis - minAxis; + + s /= btScalar(10216); + if (((medAxis + 1) % 3) != maxAxis) { + s *= -1; + } + scaling = s; + + if (s[0] != 0) { + s[0] = btScalar(1) / s[0]; + } + if (s[1] != 0) { + s[1] = btScalar(1) / s[1]; + } + if (s[2] != 0) { + s[2] = btScalar(1) / s[2]; + } + + center = (min + max) * btScalar(0.5); + + btAlignedObjectArray<Point32> points; + points.resize(count); + ptr = (const char*)coords; + if (doubleCoords) { + for (int32_t i = 0; i < count; i++) { + const double* v = (const double*)ptr; + btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]); + ptr += stride; + p = (p - center) * s; + points[i].x = (int32_t)p[medAxis]; + points[i].y = (int32_t)p[maxAxis]; + points[i].z = (int32_t)p[minAxis]; + points[i].index = i; + } + } + else { + for (int32_t i = 0; i < count; i++) { + const float* v = (const float*)ptr; + btVector3 p(v[0], v[1], v[2]); + ptr += stride; + p = (p - center) * s; + points[i].x = (int32_t)p[medAxis]; + points[i].y = (int32_t)p[maxAxis]; + points[i].z = (int32_t)p[minAxis]; + points[i].index = i; + } + } + points.quickSort(pointCmp); + + vertexPool.reset(); + vertexPool.setArraySize(count); + originalVertices.resize(count); + for (int32_t i = 0; i < count; i++) { + Vertex* v = vertexPool.newObject(); + v->edges = NULL; + v->point = points[i]; + v->copy = -1; + originalVertices[i] = v; + } + + points.clear(); + + edgePool.reset(); + edgePool.setArraySize(6 * count); + + usedEdgePairs = 0; + maxUsedEdgePairs = 0; + + mergeStamp = -3; + + IntermediateHull hull; + computeInternal(0, count, hull); + vertexList = hull.minXy; +#ifdef DEBUG_CONVEX_HULL + printf("max. edges %d (3v = %d)", maxUsedEdgePairs, 3 * count); +#endif +} + +btVector3 btConvexHullInternal::toBtVector(const Point32& v) +{ + btVector3 p; + p[medAxis] = btScalar(v.x); + p[maxAxis] = btScalar(v.y); + p[minAxis] = btScalar(v.z); + return p * scaling; +} + +btVector3 btConvexHullInternal::getBtNormal(Face* face) +{ + return toBtVector(face->dir0).cross(toBtVector(face->dir1)).normalized(); +} + +btVector3 btConvexHullInternal::getCoordinates(const Vertex* v) +{ + btVector3 p; + p[medAxis] = v->xvalue(); + p[maxAxis] = v->yvalue(); + p[minAxis] = v->zvalue(); + return p * scaling + center; +} + +btScalar btConvexHullInternal::shrink(btScalar amount, btScalar clampAmount) +{ + if (!vertexList) { + return 0; + } + int32_t stamp = --mergeStamp; + btAlignedObjectArray<Vertex*> stack; + vertexList->copy = stamp; + stack.push_back(vertexList); + btAlignedObjectArray<Face*> faces; + + Point32 ref = vertexList->point; + Int128 hullCenterX(0, 0); + Int128 hullCenterY(0, 0); + Int128 hullCenterZ(0, 0); + Int128 volume(0, 0); + + while (stack.size() > 0) { + Vertex* v = stack[stack.size() - 1]; + stack.pop_back(); + Edge* e = v->edges; + if (e) { + do { + if (e->target->copy != stamp) { + e->target->copy = stamp; + stack.push_back(e->target); + } + if (e->copy != stamp) { + Face* face = facePool.newObject(); + face->init(e->target, e->reverse->prev->target, v); + faces.push_back(face); + Edge* f = e; + + Vertex* a = NULL; + Vertex* b = NULL; + do { + if (a && b) { + int64_t vol = (v->point - ref).dot((a->point - ref).cross(b->point - ref)); + btAssert(vol >= 0); + Point32 c = v->point + a->point + b->point + ref; + hullCenterX += vol * c.x; + hullCenterY += vol * c.y; + hullCenterZ += vol * c.z; + volume += vol; + } + + btAssert(f->copy != stamp); + f->copy = stamp; + f->face = face; + + a = b; + b = f->target; + + f = f->reverse->prev; + } while (f != e); + } + e = e->next; + } while (e != v->edges); + } + } + + if (volume.getSign() <= 0) { + return 0; + } + + btVector3 hullCenter; + hullCenter[medAxis] = hullCenterX.toScalar(); + hullCenter[maxAxis] = hullCenterY.toScalar(); + hullCenter[minAxis] = hullCenterZ.toScalar(); + hullCenter /= 4 * volume.toScalar(); + hullCenter *= scaling; + + int32_t faceCount = faces.size(); + + if (clampAmount > 0) { + btScalar minDist = SIMD_INFINITY; + for (int32_t i = 0; i < faceCount; i++) { + btVector3 normal = getBtNormal(faces[i]); + btScalar dist = normal.dot(toBtVector(faces[i]->origin) - hullCenter); + if (dist < minDist) { + minDist = dist; + } + } + + if (minDist <= 0) { + return 0; + } + + amount = btMin(amount, minDist * clampAmount); + } + + uint32_t seed = 243703; + for (int32_t i = 0; i < faceCount; i++, seed = 1664525 * seed + 1013904223) { + btSwap(faces[i], faces[seed % faceCount]); + } + + for (int32_t i = 0; i < faceCount; i++) { + if (!shiftFace(faces[i], amount, stack)) { + return -amount; + } + } + + return amount; +} + +bool btConvexHullInternal::shiftFace(Face* face, btScalar amount, btAlignedObjectArray<Vertex*> stack) +{ + btVector3 origShift = getBtNormal(face) * -amount; + if (scaling[0] != 0) { + origShift[0] /= scaling[0]; + } + if (scaling[1] != 0) { + origShift[1] /= scaling[1]; + } + if (scaling[2] != 0) { + origShift[2] /= scaling[2]; + } + Point32 shift((int32_t)origShift[medAxis], (int32_t)origShift[maxAxis], (int32_t)origShift[minAxis]); + if (shift.isZero()) { + return true; + } + Point64 normal = face->getNormal(); +#ifdef DEBUG_CONVEX_HULL + printf("\nShrinking face (%d %d %d) (%d %d %d) (%d %d %d) by (%d %d %d)\n", + face->origin.x, face->origin.y, face->origin.z, face->dir0.x, face->dir0.y, face->dir0.z, face->dir1.x, face->dir1.y, face->dir1.z, shift.x, shift.y, shift.z); +#endif + int64_t origDot = face->origin.dot(normal); + Point32 shiftedOrigin = face->origin + shift; + int64_t shiftedDot = shiftedOrigin.dot(normal); + btAssert(shiftedDot <= origDot); + if (shiftedDot >= origDot) { + return false; + } + + Edge* intersection = NULL; + + Edge* startEdge = face->nearbyVertex->edges; +#ifdef DEBUG_CONVEX_HULL + printf("Start edge is "); + startEdge->print(); + printf(", normal is (%lld %lld %lld), shifted dot is %lld\n", normal.x, normal.y, normal.z, shiftedDot); +#endif + Rational128 optDot = face->nearbyVertex->dot(normal); + int32_t cmp = optDot.compare(shiftedDot); +#ifdef SHOW_ITERATIONS + int32_t n = 0; +#endif + if (cmp >= 0) { + Edge* e = startEdge; + do { +#ifdef SHOW_ITERATIONS + n++; +#endif + Rational128 dot = e->target->dot(normal); + btAssert(dot.compare(origDot) <= 0); +#ifdef DEBUG_CONVEX_HULL + printf("Moving downwards, edge is "); + e->print(); + printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot); +#endif + if (dot.compare(optDot) < 0) { + int32_t c = dot.compare(shiftedDot); + optDot = dot; + e = e->reverse; + startEdge = e; + if (c < 0) { + intersection = e; + break; + } + cmp = c; + } + e = e->prev; + } while (e != startEdge); + + if (!intersection) { + return false; + } + } + else { + Edge* e = startEdge; + do { +#ifdef SHOW_ITERATIONS + n++; +#endif + Rational128 dot = e->target->dot(normal); + btAssert(dot.compare(origDot) <= 0); +#ifdef DEBUG_CONVEX_HULL + printf("Moving upwards, edge is "); + e->print(); + printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot); +#endif + if (dot.compare(optDot) > 0) { + cmp = dot.compare(shiftedDot); + if (cmp >= 0) { + intersection = e; + break; + } + optDot = dot; + e = e->reverse; + startEdge = e; + } + e = e->prev; + } while (e != startEdge); + + if (!intersection) { + return true; + } + } + +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to find initial intersection\n", n); +#endif + + if (cmp == 0) { + Edge* e = intersection->reverse->next; +#ifdef SHOW_ITERATIONS + n = 0; +#endif + while (e->target->dot(normal).compare(shiftedDot) <= 0) { +#ifdef SHOW_ITERATIONS + n++; +#endif + e = e->next; + if (e == intersection->reverse) { + return true; + } +#ifdef DEBUG_CONVEX_HULL + printf("Checking for outwards edge, current edge is "); + e->print(); + printf("\n"); +#endif + } +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to check for complete containment\n", n); +#endif + } + + Edge* firstIntersection = NULL; + Edge* faceEdge = NULL; + Edge* firstFaceEdge = NULL; + +#ifdef SHOW_ITERATIONS + int32_t m = 0; +#endif + while (true) { +#ifdef SHOW_ITERATIONS + m++; +#endif +#ifdef DEBUG_CONVEX_HULL + printf("Intersecting edge is "); + intersection->print(); + printf("\n"); +#endif + if (cmp == 0) { + Edge* e = intersection->reverse->next; + startEdge = e; +#ifdef SHOW_ITERATIONS + n = 0; +#endif + while (true) { +#ifdef SHOW_ITERATIONS + n++; +#endif + if (e->target->dot(normal).compare(shiftedDot) >= 0) { + break; + } + intersection = e->reverse; + e = e->next; + if (e == startEdge) { + return true; + } + } +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to advance intersection\n", n); +#endif + } + +#ifdef DEBUG_CONVEX_HULL + printf("Advanced intersecting edge to "); + intersection->print(); + printf(", cmp = %d\n", cmp); +#endif + + if (!firstIntersection) { + firstIntersection = intersection; + } + else if (intersection == firstIntersection) { + break; + } + + int32_t prevCmp = cmp; + Edge* prevIntersection = intersection; + Edge* prevFaceEdge = faceEdge; + + Edge* e = intersection->reverse; +#ifdef SHOW_ITERATIONS + n = 0; +#endif + while (true) { +#ifdef SHOW_ITERATIONS + n++; +#endif + e = e->reverse->prev; + btAssert(e != intersection->reverse); + cmp = e->target->dot(normal).compare(shiftedDot); +#ifdef DEBUG_CONVEX_HULL + printf("Testing edge "); + e->print(); + printf(" -> cmp = %d\n", cmp); +#endif + if (cmp >= 0) { + intersection = e; + break; + } + } +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to find other intersection of face\n", n); +#endif + + if (cmp > 0) { + Vertex* removed = intersection->target; + e = intersection->reverse; + if (e->prev == e) { + removed->edges = NULL; + } + else { + removed->edges = e->prev; + e->prev->link(e->next); + e->link(e); + } +#ifdef DEBUG_CONVEX_HULL + printf("1: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); +#endif + + Point64 n0 = intersection->face->getNormal(); + Point64 n1 = intersection->reverse->face->getNormal(); + int64_t m00 = face->dir0.dot(n0); + int64_t m01 = face->dir1.dot(n0); + int64_t m10 = face->dir0.dot(n1); + int64_t m11 = face->dir1.dot(n1); + int64_t r0 = (intersection->face->origin - shiftedOrigin).dot(n0); + int64_t r1 = (intersection->reverse->face->origin - shiftedOrigin).dot(n1); + Int128 det = Int128::mul(m00, m11) - Int128::mul(m01, m10); + btAssert(det.getSign() != 0); + Vertex* v = vertexPool.newObject(); + v->point.index = -1; + v->copy = -1; + v->point128 = PointR128(Int128::mul(face->dir0.x * r0, m11) - Int128::mul(face->dir0.x * r1, m01) + + Int128::mul(face->dir1.x * r1, m00) - Int128::mul(face->dir1.x * r0, m10) + det * shiftedOrigin.x, + Int128::mul(face->dir0.y * r0, m11) - Int128::mul(face->dir0.y * r1, m01) + + Int128::mul(face->dir1.y * r1, m00) - Int128::mul(face->dir1.y * r0, m10) + det * shiftedOrigin.y, + Int128::mul(face->dir0.z * r0, m11) - Int128::mul(face->dir0.z * r1, m01) + + Int128::mul(face->dir1.z * r1, m00) - Int128::mul(face->dir1.z * r0, m10) + det * shiftedOrigin.z, + det); + v->point.x = (int32_t)v->point128.xvalue(); + v->point.y = (int32_t)v->point128.yvalue(); + v->point.z = (int32_t)v->point128.zvalue(); + intersection->target = v; + v->edges = e; + + stack.push_back(v); + stack.push_back(removed); + stack.push_back(NULL); + } + + if (cmp || prevCmp || (prevIntersection->reverse->next->target != intersection->target)) { + faceEdge = newEdgePair(prevIntersection->target, intersection->target); + if (prevCmp == 0) { + faceEdge->link(prevIntersection->reverse->next); + } + if ((prevCmp == 0) || prevFaceEdge) { + prevIntersection->reverse->link(faceEdge); + } + if (cmp == 0) { + intersection->reverse->prev->link(faceEdge->reverse); + } + faceEdge->reverse->link(intersection->reverse); + } + else { + faceEdge = prevIntersection->reverse->next; + } + + if (prevFaceEdge) { + if (prevCmp > 0) { + faceEdge->link(prevFaceEdge->reverse); + } + else if (faceEdge != prevFaceEdge->reverse) { + stack.push_back(prevFaceEdge->target); + while (faceEdge->next != prevFaceEdge->reverse) { + Vertex* removed = faceEdge->next->target; + removeEdgePair(faceEdge->next); + stack.push_back(removed); +#ifdef DEBUG_CONVEX_HULL + printf("2: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); +#endif + } + stack.push_back(NULL); + } + } + faceEdge->face = face; + faceEdge->reverse->face = intersection->face; + + if (!firstFaceEdge) { + firstFaceEdge = faceEdge; + } + } +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to process all intersections\n", m); +#endif + + if (cmp > 0) { + firstFaceEdge->reverse->target = faceEdge->target; + firstIntersection->reverse->link(firstFaceEdge); + firstFaceEdge->link(faceEdge->reverse); + } + else if (firstFaceEdge != faceEdge->reverse) { + stack.push_back(faceEdge->target); + while (firstFaceEdge->next != faceEdge->reverse) { + Vertex* removed = firstFaceEdge->next->target; + removeEdgePair(firstFaceEdge->next); + stack.push_back(removed); +#ifdef DEBUG_CONVEX_HULL + printf("3: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); +#endif + } + stack.push_back(NULL); + } + + btAssert(stack.size() > 0); + vertexList = stack[0]; + +#ifdef DEBUG_CONVEX_HULL + printf("Removing part\n"); +#endif +#ifdef SHOW_ITERATIONS + n = 0; +#endif + int32_t pos = 0; + while (pos < stack.size()) { + int32_t end = stack.size(); + while (pos < end) { + Vertex* kept = stack[pos++]; +#ifdef DEBUG_CONVEX_HULL + kept->print(); +#endif + bool deeper = false; + Vertex* removed; + while ((removed = stack[pos++]) != NULL) { +#ifdef SHOW_ITERATIONS + n++; +#endif + kept->receiveNearbyFaces(removed); + while (removed->edges) { + if (!deeper) { + deeper = true; + stack.push_back(kept); + } + stack.push_back(removed->edges->target); + removeEdgePair(removed->edges); + } + } + if (deeper) { + stack.push_back(NULL); + } + } + } +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to remove part\n", n); +#endif + + stack.resize(0); + face->origin = shiftedOrigin; + + return true; +} + +static int32_t getVertexCopy(btConvexHullInternal::Vertex* vertex, btAlignedObjectArray<btConvexHullInternal::Vertex*>& vertices) +{ + int32_t index = vertex->copy; + if (index < 0) { + index = vertices.size(); + vertex->copy = index; + vertices.push_back(vertex); +#ifdef DEBUG_CONVEX_HULL + printf("Vertex %d gets index *%d\n", vertex->point.index, index); +#endif + } + return index; +} + +btScalar btConvexHullComputer::compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) +{ + if (count <= 0) { + vertices.clear(); + edges.clear(); + faces.clear(); + return 0; + } + + btConvexHullInternal hull; + hull.compute(coords, doubleCoords, stride, count); + + btScalar shift = 0; + if ((shrink > 0) && ((shift = hull.shrink(shrink, shrinkClamp)) < 0)) { + vertices.clear(); + edges.clear(); + faces.clear(); + return shift; + } + + vertices.resize(0); + edges.resize(0); + faces.resize(0); + + btAlignedObjectArray<btConvexHullInternal::Vertex*> oldVertices; + getVertexCopy(hull.vertexList, oldVertices); + int32_t copied = 0; + while (copied < oldVertices.size()) { + btConvexHullInternal::Vertex* v = oldVertices[copied]; + vertices.push_back(hull.getCoordinates(v)); + btConvexHullInternal::Edge* firstEdge = v->edges; + if (firstEdge) { + int32_t firstCopy = -1; + int32_t prevCopy = -1; + btConvexHullInternal::Edge* e = firstEdge; + do { + if (e->copy < 0) { + int32_t s = edges.size(); + edges.push_back(Edge()); + edges.push_back(Edge()); + Edge* c = &edges[s]; + Edge* r = &edges[s + 1]; + e->copy = s; + e->reverse->copy = s + 1; + c->reverse = 1; + r->reverse = -1; + c->targetVertex = getVertexCopy(e->target, oldVertices); + r->targetVertex = copied; +#ifdef DEBUG_CONVEX_HULL + printf(" CREATE: Vertex *%d has edge to *%d\n", copied, c->getTargetVertex()); +#endif + } + if (prevCopy >= 0) { + edges[e->copy].next = prevCopy - e->copy; + } + else { + firstCopy = e->copy; + } + prevCopy = e->copy; + e = e->next; + } while (e != firstEdge); + edges[firstCopy].next = prevCopy - firstCopy; + } + copied++; + } + + for (int32_t i = 0; i < copied; i++) { + btConvexHullInternal::Vertex* v = oldVertices[i]; + btConvexHullInternal::Edge* firstEdge = v->edges; + if (firstEdge) { + btConvexHullInternal::Edge* e = firstEdge; + do { + if (e->copy >= 0) { +#ifdef DEBUG_CONVEX_HULL + printf("Vertex *%d has edge to *%d\n", i, edges[e->copy].getTargetVertex()); +#endif + faces.push_back(e->copy); + btConvexHullInternal::Edge* f = e; + do { +#ifdef DEBUG_CONVEX_HULL + printf(" Face *%d\n", edges[f->copy].getTargetVertex()); +#endif + f->copy = -1; + f = f->reverse->prev; + } while (f != e); + } + e = e->next; + } while (e != firstEdge); + } + } + + return shift; +} + +// -- GODOT start -- +}; // namespace VHACD +// -- GODOT end -- diff --git a/thirdparty/vhacd/src/vhacdICHull.cpp b/thirdparty/vhacd/src/vhacdICHull.cpp new file mode 100644 index 0000000000..989587c124 --- /dev/null +++ b/thirdparty/vhacd/src/vhacdICHull.cpp @@ -0,0 +1,731 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "vhacdICHull.h" +#include <limits> + +#ifdef _MSC_VER +#pragma warning(disable:4456 4706) +#endif + + +namespace VHACD { +const double ICHull::sc_eps = 1.0e-15; +const int32_t ICHull::sc_dummyIndex = std::numeric_limits<int32_t>::max(); +ICHull::ICHull() +{ + m_isFlat = false; +} +bool ICHull::AddPoints(const Vec3<double>* points, size_t nPoints) +{ + if (!points) { + return false; + } + CircularListElement<TMMVertex>* vertex = NULL; + for (size_t i = 0; i < nPoints; i++) { + vertex = m_mesh.AddVertex(); + vertex->GetData().m_pos.X() = points[i].X(); + vertex->GetData().m_pos.Y() = points[i].Y(); + vertex->GetData().m_pos.Z() = points[i].Z(); + vertex->GetData().m_name = static_cast<int32_t>(i); + } + return true; +} +bool ICHull::AddPoint(const Vec3<double>& point, int32_t id) +{ + if (AddPoints(&point, 1)) { + m_mesh.m_vertices.GetData().m_name = id; + return true; + } + return false; +} + +ICHullError ICHull::Process() +{ + uint32_t addedPoints = 0; + if (m_mesh.GetNVertices() < 3) { + return ICHullErrorNotEnoughPoints; + } + if (m_mesh.GetNVertices() == 3) { + m_isFlat = true; + CircularListElement<TMMTriangle>* t1 = m_mesh.AddTriangle(); + CircularListElement<TMMTriangle>* t2 = m_mesh.AddTriangle(); + CircularListElement<TMMVertex>* v0 = m_mesh.m_vertices.GetHead(); + CircularListElement<TMMVertex>* v1 = v0->GetNext(); + CircularListElement<TMMVertex>* v2 = v1->GetNext(); + // Compute the normal to the plane + Vec3<double> p0 = v0->GetData().m_pos; + Vec3<double> p1 = v1->GetData().m_pos; + Vec3<double> p2 = v2->GetData().m_pos; + m_normal = (p1 - p0) ^ (p2 - p0); + m_normal.Normalize(); + t1->GetData().m_vertices[0] = v0; + t1->GetData().m_vertices[1] = v1; + t1->GetData().m_vertices[2] = v2; + t2->GetData().m_vertices[0] = v1; + t2->GetData().m_vertices[1] = v2; + t2->GetData().m_vertices[2] = v2; + return ICHullErrorOK; + } + if (m_isFlat) { + m_mesh.m_edges.Clear(); + m_mesh.m_triangles.Clear(); + m_isFlat = false; + } + if (m_mesh.GetNTriangles() == 0) // we have to create the first polyhedron + { + ICHullError res = DoubleTriangle(); + if (res != ICHullErrorOK) { + return res; + } + else { + addedPoints += 3; + } + } + CircularList<TMMVertex>& vertices = m_mesh.GetVertices(); + // go to the first added and not processed vertex + while (!(vertices.GetHead()->GetPrev()->GetData().m_tag)) { + vertices.Prev(); + } + while (!vertices.GetData().m_tag) // not processed + { + vertices.GetData().m_tag = true; + if (ProcessPoint()) { + addedPoints++; + CleanUp(addedPoints); + vertices.Next(); + if (!GetMesh().CheckConsistancy()) { + size_t nV = m_mesh.GetNVertices(); + CircularList<TMMVertex>& vertices = m_mesh.GetVertices(); + for (size_t v = 0; v < nV; ++v) { + if (vertices.GetData().m_name == sc_dummyIndex) { + vertices.Delete(); + break; + } + vertices.Next(); + } + return ICHullErrorInconsistent; + } + } + } + if (m_isFlat) { + SArray<CircularListElement<TMMTriangle>*> trianglesToDuplicate; + size_t nT = m_mesh.GetNTriangles(); + for (size_t f = 0; f < nT; f++) { + TMMTriangle& currentTriangle = m_mesh.m_triangles.GetHead()->GetData(); + if (currentTriangle.m_vertices[0]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[1]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[2]->GetData().m_name == sc_dummyIndex) { + m_trianglesToDelete.PushBack(m_mesh.m_triangles.GetHead()); + for (int32_t k = 0; k < 3; k++) { + for (int32_t h = 0; h < 2; h++) { + if (currentTriangle.m_edges[k]->GetData().m_triangles[h] == m_mesh.m_triangles.GetHead()) { + currentTriangle.m_edges[k]->GetData().m_triangles[h] = 0; + break; + } + } + } + } + else { + trianglesToDuplicate.PushBack(m_mesh.m_triangles.GetHead()); + } + m_mesh.m_triangles.Next(); + } + size_t nE = m_mesh.GetNEdges(); + for (size_t e = 0; e < nE; e++) { + TMMEdge& currentEdge = m_mesh.m_edges.GetHead()->GetData(); + if (currentEdge.m_triangles[0] == 0 && currentEdge.m_triangles[1] == 0) { + m_edgesToDelete.PushBack(m_mesh.m_edges.GetHead()); + } + m_mesh.m_edges.Next(); + } + size_t nV = m_mesh.GetNVertices(); + CircularList<TMMVertex>& vertices = m_mesh.GetVertices(); + for (size_t v = 0; v < nV; ++v) { + if (vertices.GetData().m_name == sc_dummyIndex) { + vertices.Delete(); + } + else { + vertices.GetData().m_tag = false; + vertices.Next(); + } + } + CleanEdges(); + CleanTriangles(); + CircularListElement<TMMTriangle>* newTriangle; + for (size_t t = 0; t < trianglesToDuplicate.Size(); t++) { + newTriangle = m_mesh.AddTriangle(); + newTriangle->GetData().m_vertices[0] = trianglesToDuplicate[t]->GetData().m_vertices[1]; + newTriangle->GetData().m_vertices[1] = trianglesToDuplicate[t]->GetData().m_vertices[0]; + newTriangle->GetData().m_vertices[2] = trianglesToDuplicate[t]->GetData().m_vertices[2]; + } + } + return ICHullErrorOK; +} +ICHullError ICHull::Process(const uint32_t nPointsCH, + const double minVolume) +{ + uint32_t addedPoints = 0; + if (nPointsCH < 3 || m_mesh.GetNVertices() < 3) { + return ICHullErrorNotEnoughPoints; + } + if (m_mesh.GetNVertices() == 3) { + m_isFlat = true; + CircularListElement<TMMTriangle>* t1 = m_mesh.AddTriangle(); + CircularListElement<TMMTriangle>* t2 = m_mesh.AddTriangle(); + CircularListElement<TMMVertex>* v0 = m_mesh.m_vertices.GetHead(); + CircularListElement<TMMVertex>* v1 = v0->GetNext(); + CircularListElement<TMMVertex>* v2 = v1->GetNext(); + // Compute the normal to the plane + Vec3<double> p0 = v0->GetData().m_pos; + Vec3<double> p1 = v1->GetData().m_pos; + Vec3<double> p2 = v2->GetData().m_pos; + m_normal = (p1 - p0) ^ (p2 - p0); + m_normal.Normalize(); + t1->GetData().m_vertices[0] = v0; + t1->GetData().m_vertices[1] = v1; + t1->GetData().m_vertices[2] = v2; + t2->GetData().m_vertices[0] = v1; + t2->GetData().m_vertices[1] = v0; + t2->GetData().m_vertices[2] = v2; + return ICHullErrorOK; + } + + if (m_isFlat) { + m_mesh.m_triangles.Clear(); + m_mesh.m_edges.Clear(); + m_isFlat = false; + } + + if (m_mesh.GetNTriangles() == 0) // we have to create the first polyhedron + { + ICHullError res = DoubleTriangle(); + if (res != ICHullErrorOK) { + return res; + } + else { + addedPoints += 3; + } + } + CircularList<TMMVertex>& vertices = m_mesh.GetVertices(); + while (!vertices.GetData().m_tag && addedPoints < nPointsCH) // not processed + { + if (!FindMaxVolumePoint((addedPoints > 4) ? minVolume : 0.0)) { + break; + } + vertices.GetData().m_tag = true; + if (ProcessPoint()) { + addedPoints++; + CleanUp(addedPoints); + if (!GetMesh().CheckConsistancy()) { + size_t nV = m_mesh.GetNVertices(); + CircularList<TMMVertex>& vertices = m_mesh.GetVertices(); + for (size_t v = 0; v < nV; ++v) { + if (vertices.GetData().m_name == sc_dummyIndex) { + vertices.Delete(); + break; + } + vertices.Next(); + } + return ICHullErrorInconsistent; + } + vertices.Next(); + } + } + // delete remaining points + while (!vertices.GetData().m_tag) { + vertices.Delete(); + } + if (m_isFlat) { + SArray<CircularListElement<TMMTriangle>*> trianglesToDuplicate; + size_t nT = m_mesh.GetNTriangles(); + for (size_t f = 0; f < nT; f++) { + TMMTriangle& currentTriangle = m_mesh.m_triangles.GetHead()->GetData(); + if (currentTriangle.m_vertices[0]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[1]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[2]->GetData().m_name == sc_dummyIndex) { + m_trianglesToDelete.PushBack(m_mesh.m_triangles.GetHead()); + for (int32_t k = 0; k < 3; k++) { + for (int32_t h = 0; h < 2; h++) { + if (currentTriangle.m_edges[k]->GetData().m_triangles[h] == m_mesh.m_triangles.GetHead()) { + currentTriangle.m_edges[k]->GetData().m_triangles[h] = 0; + break; + } + } + } + } + else { + trianglesToDuplicate.PushBack(m_mesh.m_triangles.GetHead()); + } + m_mesh.m_triangles.Next(); + } + size_t nE = m_mesh.GetNEdges(); + for (size_t e = 0; e < nE; e++) { + TMMEdge& currentEdge = m_mesh.m_edges.GetHead()->GetData(); + if (currentEdge.m_triangles[0] == 0 && currentEdge.m_triangles[1] == 0) { + m_edgesToDelete.PushBack(m_mesh.m_edges.GetHead()); + } + m_mesh.m_edges.Next(); + } + size_t nV = m_mesh.GetNVertices(); + CircularList<TMMVertex>& vertices = m_mesh.GetVertices(); + for (size_t v = 0; v < nV; ++v) { + if (vertices.GetData().m_name == sc_dummyIndex) { + vertices.Delete(); + } + else { + vertices.GetData().m_tag = false; + vertices.Next(); + } + } + CleanEdges(); + CleanTriangles(); + CircularListElement<TMMTriangle>* newTriangle; + for (size_t t = 0; t < trianglesToDuplicate.Size(); t++) { + newTriangle = m_mesh.AddTriangle(); + newTriangle->GetData().m_vertices[0] = trianglesToDuplicate[t]->GetData().m_vertices[1]; + newTriangle->GetData().m_vertices[1] = trianglesToDuplicate[t]->GetData().m_vertices[0]; + newTriangle->GetData().m_vertices[2] = trianglesToDuplicate[t]->GetData().m_vertices[2]; + } + } + return ICHullErrorOK; +} +bool ICHull::FindMaxVolumePoint(const double minVolume) +{ + CircularList<TMMVertex>& vertices = m_mesh.GetVertices(); + CircularListElement<TMMVertex>* vMaxVolume = 0; + CircularListElement<TMMVertex>* vHeadPrev = vertices.GetHead()->GetPrev(); + + double maxVolume = minVolume; + double volume = 0.0; + while (!vertices.GetData().m_tag) // not processed + { + if (ComputePointVolume(volume, false)) { + if (maxVolume < volume) { + maxVolume = volume; + vMaxVolume = vertices.GetHead(); + } + vertices.Next(); + } + } + CircularListElement<TMMVertex>* vHead = vHeadPrev->GetNext(); + vertices.GetHead() = vHead; + if (!vMaxVolume) { + return false; + } + if (vMaxVolume != vHead) { + Vec3<double> pos = vHead->GetData().m_pos; + int32_t id = vHead->GetData().m_name; + vHead->GetData().m_pos = vMaxVolume->GetData().m_pos; + vHead->GetData().m_name = vMaxVolume->GetData().m_name; + vMaxVolume->GetData().m_pos = pos; + vHead->GetData().m_name = id; + } + return true; +} +ICHullError ICHull::DoubleTriangle() +{ + // find three non colinear points + m_isFlat = false; + CircularList<TMMVertex>& vertices = m_mesh.GetVertices(); + CircularListElement<TMMVertex>* v0 = vertices.GetHead(); + while (Colinear(v0->GetData().m_pos, + v0->GetNext()->GetData().m_pos, + v0->GetNext()->GetNext()->GetData().m_pos)) { + if ((v0 = v0->GetNext()) == vertices.GetHead()) { + return ICHullErrorCoplanarPoints; + } + } + CircularListElement<TMMVertex>* v1 = v0->GetNext(); + CircularListElement<TMMVertex>* v2 = v1->GetNext(); + // mark points as processed + v0->GetData().m_tag = v1->GetData().m_tag = v2->GetData().m_tag = true; + + // create two triangles + CircularListElement<TMMTriangle>* f0 = MakeFace(v0, v1, v2, 0); + MakeFace(v2, v1, v0, f0); + + // find a fourth non-coplanar point to form tetrahedron + CircularListElement<TMMVertex>* v3 = v2->GetNext(); + vertices.GetHead() = v3; + + double vol = ComputeVolume4(v0->GetData().m_pos, v1->GetData().m_pos, v2->GetData().m_pos, v3->GetData().m_pos); + while (fabs(vol) < sc_eps && !v3->GetNext()->GetData().m_tag) { + v3 = v3->GetNext(); + vol = ComputeVolume4(v0->GetData().m_pos, v1->GetData().m_pos, v2->GetData().m_pos, v3->GetData().m_pos); + } + if (fabs(vol) < sc_eps) { + // compute the barycenter + Vec3<double> bary(0.0, 0.0, 0.0); + CircularListElement<TMMVertex>* vBary = v0; + do { + bary += vBary->GetData().m_pos; + } while ((vBary = vBary->GetNext()) != v0); + bary /= static_cast<double>(vertices.GetSize()); + + // Compute the normal to the plane + Vec3<double> p0 = v0->GetData().m_pos; + Vec3<double> p1 = v1->GetData().m_pos; + Vec3<double> p2 = v2->GetData().m_pos; + m_normal = (p1 - p0) ^ (p2 - p0); + m_normal.Normalize(); + // add dummy vertex placed at (bary + normal) + vertices.GetHead() = v2; + Vec3<double> newPt = bary + m_normal; + AddPoint(newPt, sc_dummyIndex); + m_isFlat = true; + return ICHullErrorOK; + } + else if (v3 != vertices.GetHead()) { + TMMVertex temp; + temp.m_name = v3->GetData().m_name; + temp.m_pos = v3->GetData().m_pos; + v3->GetData().m_name = vertices.GetHead()->GetData().m_name; + v3->GetData().m_pos = vertices.GetHead()->GetData().m_pos; + vertices.GetHead()->GetData().m_name = temp.m_name; + vertices.GetHead()->GetData().m_pos = temp.m_pos; + } + return ICHullErrorOK; +} +CircularListElement<TMMTriangle>* ICHull::MakeFace(CircularListElement<TMMVertex>* v0, + CircularListElement<TMMVertex>* v1, + CircularListElement<TMMVertex>* v2, + CircularListElement<TMMTriangle>* fold) +{ + CircularListElement<TMMEdge>* e0; + CircularListElement<TMMEdge>* e1; + CircularListElement<TMMEdge>* e2; + int32_t index = 0; + if (!fold) // if first face to be created + { + e0 = m_mesh.AddEdge(); // create the three edges + e1 = m_mesh.AddEdge(); + e2 = m_mesh.AddEdge(); + } + else // otherwise re-use existing edges (in reverse order) + { + e0 = fold->GetData().m_edges[2]; + e1 = fold->GetData().m_edges[1]; + e2 = fold->GetData().m_edges[0]; + index = 1; + } + e0->GetData().m_vertices[0] = v0; + e0->GetData().m_vertices[1] = v1; + e1->GetData().m_vertices[0] = v1; + e1->GetData().m_vertices[1] = v2; + e2->GetData().m_vertices[0] = v2; + e2->GetData().m_vertices[1] = v0; + // create the new face + CircularListElement<TMMTriangle>* f = m_mesh.AddTriangle(); + f->GetData().m_edges[0] = e0; + f->GetData().m_edges[1] = e1; + f->GetData().m_edges[2] = e2; + f->GetData().m_vertices[0] = v0; + f->GetData().m_vertices[1] = v1; + f->GetData().m_vertices[2] = v2; + // link edges to face f + e0->GetData().m_triangles[index] = e1->GetData().m_triangles[index] = e2->GetData().m_triangles[index] = f; + return f; +} +CircularListElement<TMMTriangle>* ICHull::MakeConeFace(CircularListElement<TMMEdge>* e, CircularListElement<TMMVertex>* p) +{ + // create two new edges if they don't already exist + CircularListElement<TMMEdge>* newEdges[2]; + for (int32_t i = 0; i < 2; ++i) { + if (!(newEdges[i] = e->GetData().m_vertices[i]->GetData().m_duplicate)) { // if the edge doesn't exits add it and mark the vertex as duplicated + newEdges[i] = m_mesh.AddEdge(); + newEdges[i]->GetData().m_vertices[0] = e->GetData().m_vertices[i]; + newEdges[i]->GetData().m_vertices[1] = p; + e->GetData().m_vertices[i]->GetData().m_duplicate = newEdges[i]; + } + } + // make the new face + CircularListElement<TMMTriangle>* newFace = m_mesh.AddTriangle(); + newFace->GetData().m_edges[0] = e; + newFace->GetData().m_edges[1] = newEdges[0]; + newFace->GetData().m_edges[2] = newEdges[1]; + MakeCCW(newFace, e, p); + for (int32_t i = 0; i < 2; ++i) { + for (int32_t j = 0; j < 2; ++j) { + if (!newEdges[i]->GetData().m_triangles[j]) { + newEdges[i]->GetData().m_triangles[j] = newFace; + break; + } + } + } + return newFace; +} +bool ICHull::ComputePointVolume(double& totalVolume, bool markVisibleFaces) +{ + // mark visible faces + CircularListElement<TMMTriangle>* fHead = m_mesh.GetTriangles().GetHead(); + CircularListElement<TMMTriangle>* f = fHead; + CircularList<TMMVertex>& vertices = m_mesh.GetVertices(); + CircularListElement<TMMVertex>* vertex0 = vertices.GetHead(); + bool visible = false; + Vec3<double> pos0 = Vec3<double>(vertex0->GetData().m_pos.X(), + vertex0->GetData().m_pos.Y(), + vertex0->GetData().m_pos.Z()); + double vol = 0.0; + totalVolume = 0.0; + Vec3<double> ver0, ver1, ver2; + do { + ver0.X() = f->GetData().m_vertices[0]->GetData().m_pos.X(); + ver0.Y() = f->GetData().m_vertices[0]->GetData().m_pos.Y(); + ver0.Z() = f->GetData().m_vertices[0]->GetData().m_pos.Z(); + ver1.X() = f->GetData().m_vertices[1]->GetData().m_pos.X(); + ver1.Y() = f->GetData().m_vertices[1]->GetData().m_pos.Y(); + ver1.Z() = f->GetData().m_vertices[1]->GetData().m_pos.Z(); + ver2.X() = f->GetData().m_vertices[2]->GetData().m_pos.X(); + ver2.Y() = f->GetData().m_vertices[2]->GetData().m_pos.Y(); + ver2.Z() = f->GetData().m_vertices[2]->GetData().m_pos.Z(); + vol = ComputeVolume4(ver0, ver1, ver2, pos0); + if (vol < -sc_eps) { + vol = fabs(vol); + totalVolume += vol; + if (markVisibleFaces) { + f->GetData().m_visible = true; + m_trianglesToDelete.PushBack(f); + } + visible = true; + } + f = f->GetNext(); + } while (f != fHead); + + if (m_trianglesToDelete.Size() == m_mesh.m_triangles.GetSize()) { + for (size_t i = 0; i < m_trianglesToDelete.Size(); i++) { + m_trianglesToDelete[i]->GetData().m_visible = false; + } + visible = false; + } + // if no faces visible from p then p is inside the hull + if (!visible && markVisibleFaces) { + vertices.Delete(); + m_trianglesToDelete.Resize(0); + return false; + } + return true; +} +bool ICHull::ProcessPoint() +{ + double totalVolume = 0.0; + if (!ComputePointVolume(totalVolume, true)) { + return false; + } + // Mark edges in interior of visible region for deletion. + // Create a new face based on each border edge + CircularListElement<TMMVertex>* v0 = m_mesh.GetVertices().GetHead(); + CircularListElement<TMMEdge>* eHead = m_mesh.GetEdges().GetHead(); + CircularListElement<TMMEdge>* e = eHead; + CircularListElement<TMMEdge>* tmp = 0; + int32_t nvisible = 0; + m_edgesToDelete.Resize(0); + m_edgesToUpdate.Resize(0); + do { + tmp = e->GetNext(); + nvisible = 0; + for (int32_t k = 0; k < 2; k++) { + if (e->GetData().m_triangles[k]->GetData().m_visible) { + nvisible++; + } + } + if (nvisible == 2) { + m_edgesToDelete.PushBack(e); + } + else if (nvisible == 1) { + e->GetData().m_newFace = MakeConeFace(e, v0); + m_edgesToUpdate.PushBack(e); + } + e = tmp; + } while (e != eHead); + return true; +} +bool ICHull::MakeCCW(CircularListElement<TMMTriangle>* f, + CircularListElement<TMMEdge>* e, + CircularListElement<TMMVertex>* v) +{ + // the visible face adjacent to e + CircularListElement<TMMTriangle>* fv; + if (e->GetData().m_triangles[0]->GetData().m_visible) { + fv = e->GetData().m_triangles[0]; + } + else { + fv = e->GetData().m_triangles[1]; + } + + // set vertex[0] and vertex[1] to have the same orientation as the corresponding vertices of fv. + int32_t i; // index of e->m_vertices[0] in fv + CircularListElement<TMMVertex>* v0 = e->GetData().m_vertices[0]; + CircularListElement<TMMVertex>* v1 = e->GetData().m_vertices[1]; + for (i = 0; fv->GetData().m_vertices[i] != v0; i++) + ; + + if (fv->GetData().m_vertices[(i + 1) % 3] != e->GetData().m_vertices[1]) { + f->GetData().m_vertices[0] = v1; + f->GetData().m_vertices[1] = v0; + } + else { + f->GetData().m_vertices[0] = v0; + f->GetData().m_vertices[1] = v1; + // swap edges + CircularListElement<TMMEdge>* tmp = f->GetData().m_edges[0]; + f->GetData().m_edges[0] = f->GetData().m_edges[1]; + f->GetData().m_edges[1] = tmp; + } + f->GetData().m_vertices[2] = v; + return true; +} +bool ICHull::CleanUp(uint32_t& addedPoints) +{ + bool r0 = CleanEdges(); + bool r1 = CleanTriangles(); + bool r2 = CleanVertices(addedPoints); + return r0 && r1 && r2; +} +bool ICHull::CleanEdges() +{ + // integrate the new faces into the data structure + CircularListElement<TMMEdge>* e; + const size_t ne_update = m_edgesToUpdate.Size(); + for (size_t i = 0; i < ne_update; ++i) { + e = m_edgesToUpdate[i]; + if (e->GetData().m_newFace) { + if (e->GetData().m_triangles[0]->GetData().m_visible) { + e->GetData().m_triangles[0] = e->GetData().m_newFace; + } + else { + e->GetData().m_triangles[1] = e->GetData().m_newFace; + } + e->GetData().m_newFace = 0; + } + } + // delete edges maked for deletion + CircularList<TMMEdge>& edges = m_mesh.GetEdges(); + const size_t ne_delete = m_edgesToDelete.Size(); + for (size_t i = 0; i < ne_delete; ++i) { + edges.Delete(m_edgesToDelete[i]); + } + m_edgesToDelete.Resize(0); + m_edgesToUpdate.Resize(0); + return true; +} +bool ICHull::CleanTriangles() +{ + CircularList<TMMTriangle>& triangles = m_mesh.GetTriangles(); + const size_t nt_delete = m_trianglesToDelete.Size(); + for (size_t i = 0; i < nt_delete; ++i) { + triangles.Delete(m_trianglesToDelete[i]); + } + m_trianglesToDelete.Resize(0); + return true; +} +bool ICHull::CleanVertices(uint32_t& addedPoints) +{ + // mark all vertices incident to some undeleted edge as on the hull + CircularList<TMMEdge>& edges = m_mesh.GetEdges(); + CircularListElement<TMMEdge>* e = edges.GetHead(); + size_t nE = edges.GetSize(); + for (size_t i = 0; i < nE; i++) { + e->GetData().m_vertices[0]->GetData().m_onHull = true; + e->GetData().m_vertices[1]->GetData().m_onHull = true; + e = e->GetNext(); + } + // delete all the vertices that have been processed but are not on the hull + CircularList<TMMVertex>& vertices = m_mesh.GetVertices(); + CircularListElement<TMMVertex>* vHead = vertices.GetHead(); + CircularListElement<TMMVertex>* v = vHead; + v = v->GetPrev(); + do { + if (v->GetData().m_tag && !v->GetData().m_onHull) { + CircularListElement<TMMVertex>* tmp = v->GetPrev(); + vertices.Delete(v); + v = tmp; + addedPoints--; + } + else { + v->GetData().m_duplicate = 0; + v->GetData().m_onHull = false; + v = v->GetPrev(); + } + } while (v->GetData().m_tag && v != vHead); + return true; +} +void ICHull::Clear() +{ + m_mesh.Clear(); + m_edgesToDelete.Resize(0); + m_edgesToUpdate.Resize(0); + m_trianglesToDelete.Resize(0); + m_isFlat = false; +} +const ICHull& ICHull::operator=(ICHull& rhs) +{ + if (&rhs != this) { + m_mesh.Copy(rhs.m_mesh); + m_edgesToDelete = rhs.m_edgesToDelete; + m_edgesToUpdate = rhs.m_edgesToUpdate; + m_trianglesToDelete = rhs.m_trianglesToDelete; + m_isFlat = rhs.m_isFlat; + } + return (*this); +} +bool ICHull::IsInside(const Vec3<double>& pt0, const double eps) +{ + const Vec3<double> pt(pt0.X(), pt0.Y(), pt0.Z()); + if (m_isFlat) { + size_t nT = m_mesh.m_triangles.GetSize(); + Vec3<double> ver0, ver1, ver2, a, b, c; + double u, v; + for (size_t t = 0; t < nT; t++) { + ver0.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.X(); + ver0.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Y(); + ver0.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Z(); + ver1.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.X(); + ver1.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Y(); + ver1.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Z(); + ver2.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.X(); + ver2.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Y(); + ver2.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Z(); + a = ver1 - ver0; + b = ver2 - ver0; + c = pt - ver0; + u = c * a; + v = c * b; + if (u >= 0.0 && u <= 1.0 && v >= 0.0 && u + v <= 1.0) { + return true; + } + m_mesh.m_triangles.Next(); + } + return false; + } + else { + size_t nT = m_mesh.m_triangles.GetSize(); + Vec3<double> ver0, ver1, ver2; + double vol; + for (size_t t = 0; t < nT; t++) { + ver0.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.X(); + ver0.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Y(); + ver0.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Z(); + ver1.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.X(); + ver1.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Y(); + ver1.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Z(); + ver2.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.X(); + ver2.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Y(); + ver2.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Z(); + vol = ComputeVolume4(ver0, ver1, ver2, pt); + if (vol < eps) { + return false; + } + m_mesh.m_triangles.Next(); + } + return true; + } +} +} diff --git a/thirdparty/vhacd/src/vhacdManifoldMesh.cpp b/thirdparty/vhacd/src/vhacdManifoldMesh.cpp new file mode 100644 index 0000000000..7aac9c0d17 --- /dev/null +++ b/thirdparty/vhacd/src/vhacdManifoldMesh.cpp @@ -0,0 +1,202 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "vhacdManifoldMesh.h" +namespace VHACD { +TMMVertex::TMMVertex(void) +{ + Initialize(); +} +void TMMVertex::Initialize() +{ + m_name = 0; + m_id = 0; + m_duplicate = 0; + m_onHull = false; + m_tag = false; +} + +TMMVertex::~TMMVertex(void) +{ +} +TMMEdge::TMMEdge(void) +{ + Initialize(); +} +void TMMEdge::Initialize() +{ + m_id = 0; + m_triangles[0] = m_triangles[1] = m_newFace = 0; + m_vertices[0] = m_vertices[1] = 0; +} +TMMEdge::~TMMEdge(void) +{ +} +void TMMTriangle::Initialize() +{ + m_id = 0; + for (int32_t i = 0; i < 3; i++) { + m_edges[i] = 0; + m_vertices[0] = 0; + } + m_visible = false; +} +TMMTriangle::TMMTriangle(void) +{ + Initialize(); +} +TMMTriangle::~TMMTriangle(void) +{ +} +TMMesh::TMMesh() +{ +} +TMMesh::~TMMesh(void) +{ +} +void TMMesh::GetIFS(Vec3<double>* const points, Vec3<int32_t>* const triangles) +{ + size_t nV = m_vertices.GetSize(); + size_t nT = m_triangles.GetSize(); + + for (size_t v = 0; v < nV; v++) { + points[v] = m_vertices.GetData().m_pos; + m_vertices.GetData().m_id = v; + m_vertices.Next(); + } + for (size_t f = 0; f < nT; f++) { + TMMTriangle& currentTriangle = m_triangles.GetData(); + triangles[f].X() = static_cast<int32_t>(currentTriangle.m_vertices[0]->GetData().m_id); + triangles[f].Y() = static_cast<int32_t>(currentTriangle.m_vertices[1]->GetData().m_id); + triangles[f].Z() = static_cast<int32_t>(currentTriangle.m_vertices[2]->GetData().m_id); + m_triangles.Next(); + } +} +void TMMesh::Clear() +{ + m_vertices.Clear(); + m_edges.Clear(); + m_triangles.Clear(); +} +void TMMesh::Copy(TMMesh& mesh) +{ + Clear(); + // updating the id's + size_t nV = mesh.m_vertices.GetSize(); + size_t nE = mesh.m_edges.GetSize(); + size_t nT = mesh.m_triangles.GetSize(); + for (size_t v = 0; v < nV; v++) { + mesh.m_vertices.GetData().m_id = v; + mesh.m_vertices.Next(); + } + for (size_t e = 0; e < nE; e++) { + mesh.m_edges.GetData().m_id = e; + mesh.m_edges.Next(); + } + for (size_t f = 0; f < nT; f++) { + mesh.m_triangles.GetData().m_id = f; + mesh.m_triangles.Next(); + } + // copying data + m_vertices = mesh.m_vertices; + m_edges = mesh.m_edges; + m_triangles = mesh.m_triangles; + + // generate mapping + CircularListElement<TMMVertex>** vertexMap = new CircularListElement<TMMVertex>*[nV]; + CircularListElement<TMMEdge>** edgeMap = new CircularListElement<TMMEdge>*[nE]; + CircularListElement<TMMTriangle>** triangleMap = new CircularListElement<TMMTriangle>*[nT]; + for (size_t v = 0; v < nV; v++) { + vertexMap[v] = m_vertices.GetHead(); + m_vertices.Next(); + } + for (size_t e = 0; e < nE; e++) { + edgeMap[e] = m_edges.GetHead(); + m_edges.Next(); + } + for (size_t f = 0; f < nT; f++) { + triangleMap[f] = m_triangles.GetHead(); + m_triangles.Next(); + } + + // updating pointers + for (size_t v = 0; v < nV; v++) { + if (vertexMap[v]->GetData().m_duplicate) { + vertexMap[v]->GetData().m_duplicate = edgeMap[vertexMap[v]->GetData().m_duplicate->GetData().m_id]; + } + } + for (size_t e = 0; e < nE; e++) { + if (edgeMap[e]->GetData().m_newFace) { + edgeMap[e]->GetData().m_newFace = triangleMap[edgeMap[e]->GetData().m_newFace->GetData().m_id]; + } + if (nT > 0) { + for (int32_t f = 0; f < 2; f++) { + if (edgeMap[e]->GetData().m_triangles[f]) { + edgeMap[e]->GetData().m_triangles[f] = triangleMap[edgeMap[e]->GetData().m_triangles[f]->GetData().m_id]; + } + } + } + for (int32_t v = 0; v < 2; v++) { + if (edgeMap[e]->GetData().m_vertices[v]) { + edgeMap[e]->GetData().m_vertices[v] = vertexMap[edgeMap[e]->GetData().m_vertices[v]->GetData().m_id]; + } + } + } + for (size_t f = 0; f < nT; f++) { + if (nE > 0) { + for (int32_t e = 0; e < 3; e++) { + if (triangleMap[f]->GetData().m_edges[e]) { + triangleMap[f]->GetData().m_edges[e] = edgeMap[triangleMap[f]->GetData().m_edges[e]->GetData().m_id]; + } + } + } + for (int32_t v = 0; v < 3; v++) { + if (triangleMap[f]->GetData().m_vertices[v]) { + triangleMap[f]->GetData().m_vertices[v] = vertexMap[triangleMap[f]->GetData().m_vertices[v]->GetData().m_id]; + } + } + } + delete[] vertexMap; + delete[] edgeMap; + delete[] triangleMap; +} +bool TMMesh::CheckConsistancy() +{ + size_t nE = m_edges.GetSize(); + size_t nT = m_triangles.GetSize(); + for (size_t e = 0; e < nE; e++) { + for (int32_t f = 0; f < 2; f++) { + if (!m_edges.GetHead()->GetData().m_triangles[f]) { + return false; + } + } + m_edges.Next(); + } + for (size_t f = 0; f < nT; f++) { + for (int32_t e = 0; e < 3; e++) { + int32_t found = 0; + for (int32_t k = 0; k < 2; k++) { + if (m_triangles.GetHead()->GetData().m_edges[e]->GetData().m_triangles[k] == m_triangles.GetHead()) { + found++; + } + } + if (found != 1) { + return false; + } + } + m_triangles.Next(); + } + return true; +} +}
\ No newline at end of file diff --git a/thirdparty/vhacd/src/vhacdMesh.cpp b/thirdparty/vhacd/src/vhacdMesh.cpp new file mode 100644 index 0000000000..5d03989fda --- /dev/null +++ b/thirdparty/vhacd/src/vhacdMesh.cpp @@ -0,0 +1,376 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "btConvexHullComputer.h" +#include "vhacdMesh.h" +#include "FloatMath.h" +#include <fstream> +#include <iosfwd> +#include <iostream> +#include <stdio.h> +#include <stdlib.h> +#include <string> + +namespace VHACD { +Mesh::Mesh() +{ + m_diag = 1.0; +} +Mesh::~Mesh() +{ +} + +Vec3<double>& Mesh::ComputeCenter(void) +{ + const size_t nV = GetNPoints(); + if (nV) + { + double center[3]; + uint32_t pcount = uint32_t(GetNPoints()); + const double *points = GetPoints(); + uint32_t tcount = uint32_t(GetNTriangles()); + const uint32_t *indices = (const uint32_t *)GetTriangles(); + FLOAT_MATH::fm_computeCentroid(pcount, points, tcount, indices, center); + m_center.X() = center[0]; + m_center.Y() = center[1]; + m_center.Z() = center[2]; + m_minBB = GetPoint(0); + m_maxBB = GetPoint(0); + for (size_t v = 1; v < nV; v++) + { + Vec3<double> p = GetPoint(v); + if (p.X() < m_minBB.X()) + { + m_minBB.X() = p.X(); + } + if (p.Y() < m_minBB.Y()) + { + m_minBB.Y() = p.Y(); + } + if (p.Z() < m_minBB.Z()) + { + m_minBB.Z() = p.Z(); + } + if (p.X() > m_maxBB.X()) + { + m_maxBB.X() = p.X(); + } + if (p.Y() > m_maxBB.Y()) + { + m_maxBB.Y() = p.Y(); + } + if (p.Z() > m_maxBB.Z()) + { + m_maxBB.Z() = p.Z(); + } + } + } + return m_center; +} + +double Mesh::ComputeVolume() const +{ + const size_t nV = GetNPoints(); + const size_t nT = GetNTriangles(); + if (nV == 0 || nT == 0) { + return 0.0; + } + + Vec3<double> bary(0.0, 0.0, 0.0); + for (size_t v = 0; v < nV; v++) { + bary += GetPoint(v); + } + bary /= static_cast<double>(nV); + + Vec3<double> ver0, ver1, ver2; + double totalVolume = 0.0; + for (int32_t t = 0; t < int32_t(nT); t++) { + const Vec3<int32_t>& tri = GetTriangle(t); + ver0 = GetPoint(tri[0]); + ver1 = GetPoint(tri[1]); + ver2 = GetPoint(tri[2]); + totalVolume += ComputeVolume4(ver0, ver1, ver2, bary); + } + return totalVolume / 6.0; +} + +void Mesh::ComputeConvexHull(const double* const pts, + const size_t nPts) +{ + ResizePoints(0); + ResizeTriangles(0); + btConvexHullComputer ch; + ch.compute(pts, 3 * sizeof(double), (int32_t)nPts, -1.0, -1.0); + for (int32_t v = 0; v < ch.vertices.size(); v++) { + AddPoint(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int32_t nt = ch.faces.size(); + for (int32_t t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int32_t a = sourceEdge->getSourceVertex(); + int32_t b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int32_t c = edge->getTargetVertex(); + while (c != a) { + AddTriangle(Vec3<int32_t>(a, b, c)); + edge = edge->getNextEdgeOfFace(); + b = c; + c = edge->getTargetVertex(); + } + } +} +void Mesh::Clip(const Plane& plane, + SArray<Vec3<double> >& positivePart, + SArray<Vec3<double> >& negativePart) const +{ + const size_t nV = GetNPoints(); + if (nV == 0) { + return; + } + double d; + for (size_t v = 0; v < nV; v++) { + const Vec3<double>& pt = GetPoint(v); + d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; + if (d > 0.0) { + positivePart.PushBack(pt); + } + else if (d < 0.0) { + negativePart.PushBack(pt); + } + else { + positivePart.PushBack(pt); + negativePart.PushBack(pt); + } + } +} +bool Mesh::IsInside(const Vec3<double>& pt) const +{ + const size_t nV = GetNPoints(); + const size_t nT = GetNTriangles(); + if (nV == 0 || nT == 0) { + return false; + } + Vec3<double> ver0, ver1, ver2; + double volume; + for (int32_t t = 0; t < int32_t(nT); t++) { + const Vec3<int32_t>& tri = GetTriangle(t); + ver0 = GetPoint(tri[0]); + ver1 = GetPoint(tri[1]); + ver2 = GetPoint(tri[2]); + volume = ComputeVolume4(ver0, ver1, ver2, pt); + if (volume < 0.0) { + return false; + } + } + return true; +} +double Mesh::ComputeDiagBB() +{ + const size_t nPoints = GetNPoints(); + if (nPoints == 0) + return 0.0; + Vec3<double> minBB = m_points[0]; + Vec3<double> maxBB = m_points[0]; + double x, y, z; + for (size_t v = 1; v < nPoints; v++) { + x = m_points[v][0]; + y = m_points[v][1]; + z = m_points[v][2]; + if (x < minBB[0]) + minBB[0] = x; + else if (x > maxBB[0]) + maxBB[0] = x; + if (y < minBB[1]) + minBB[1] = y; + else if (y > maxBB[1]) + maxBB[1] = y; + if (z < minBB[2]) + minBB[2] = z; + else if (z > maxBB[2]) + maxBB[2] = z; + } + return (m_diag = (maxBB - minBB).GetNorm()); +} + +#ifdef VHACD_DEBUG_MESH +bool Mesh::SaveVRML2(const std::string& fileName) const +{ + std::ofstream fout(fileName.c_str()); + if (fout.is_open()) { + const Material material; + + if (SaveVRML2(fout, material)) { + fout.close(); + return true; + } + return false; + } + return false; +} +bool Mesh::SaveVRML2(std::ofstream& fout, const Material& material) const +{ + if (fout.is_open()) { + fout.setf(std::ios::fixed, std::ios::floatfield); + fout.setf(std::ios::showpoint); + fout.precision(6); + size_t nV = m_points.Size(); + size_t nT = m_triangles.Size(); + fout << "#VRML V2.0 utf8" << std::endl; + fout << "" << std::endl; + fout << "# Vertices: " << nV << std::endl; + fout << "# Triangles: " << nT << std::endl; + fout << "" << std::endl; + fout << "Group {" << std::endl; + fout << " children [" << std::endl; + fout << " Shape {" << std::endl; + fout << " appearance Appearance {" << std::endl; + fout << " material Material {" << std::endl; + fout << " diffuseColor " << material.m_diffuseColor[0] << " " + << material.m_diffuseColor[1] << " " + << material.m_diffuseColor[2] << std::endl; + fout << " ambientIntensity " << material.m_ambientIntensity << std::endl; + fout << " specularColor " << material.m_specularColor[0] << " " + << material.m_specularColor[1] << " " + << material.m_specularColor[2] << std::endl; + fout << " emissiveColor " << material.m_emissiveColor[0] << " " + << material.m_emissiveColor[1] << " " + << material.m_emissiveColor[2] << std::endl; + fout << " shininess " << material.m_shininess << std::endl; + fout << " transparency " << material.m_transparency << std::endl; + fout << " }" << std::endl; + fout << " }" << std::endl; + fout << " geometry IndexedFaceSet {" << std::endl; + fout << " ccw TRUE" << std::endl; + fout << " solid TRUE" << std::endl; + fout << " convex TRUE" << std::endl; + if (nV > 0) { + fout << " coord DEF co Coordinate {" << std::endl; + fout << " point [" << std::endl; + for (size_t v = 0; v < nV; v++) { + fout << " " << m_points[v][0] << " " + << m_points[v][1] << " " + << m_points[v][2] << "," << std::endl; + } + fout << " ]" << std::endl; + fout << " }" << std::endl; + } + if (nT > 0) { + fout << " coordIndex [ " << std::endl; + for (size_t f = 0; f < nT; f++) { + fout << " " << m_triangles[f][0] << ", " + << m_triangles[f][1] << ", " + << m_triangles[f][2] << ", -1," << std::endl; + } + fout << " ]" << std::endl; + } + fout << " }" << std::endl; + fout << " }" << std::endl; + fout << " ]" << std::endl; + fout << "}" << std::endl; + return true; + } + return false; +} +bool Mesh::SaveOFF(const std::string& fileName) const +{ + std::ofstream fout(fileName.c_str()); + if (fout.is_open()) { + size_t nV = m_points.Size(); + size_t nT = m_triangles.Size(); + fout << "OFF" << std::endl; + fout << nV << " " << nT << " " << 0 << std::endl; + for (size_t v = 0; v < nV; v++) { + fout << m_points[v][0] << " " + << m_points[v][1] << " " + << m_points[v][2] << std::endl; + } + for (size_t f = 0; f < nT; f++) { + fout << "3 " << m_triangles[f][0] << " " + << m_triangles[f][1] << " " + << m_triangles[f][2] << std::endl; + } + fout.close(); + return true; + } + return false; +} + +bool Mesh::LoadOFF(const std::string& fileName, bool invert) +{ + FILE* fid = fopen(fileName.c_str(), "r"); + if (fid) { + const std::string strOFF("OFF"); + char temp[1024]; + fscanf(fid, "%s", temp); + if (std::string(temp) != strOFF) { + fclose(fid); + return false; + } + else { + int32_t nv = 0; + int32_t nf = 0; + int32_t ne = 0; + fscanf(fid, "%i", &nv); + fscanf(fid, "%i", &nf); + fscanf(fid, "%i", &ne); + m_points.Resize(nv); + m_triangles.Resize(nf); + Vec3<double> coord; + float x, y, z; + for (int32_t p = 0; p < nv; p++) { + fscanf(fid, "%f", &x); + fscanf(fid, "%f", &y); + fscanf(fid, "%f", &z); + m_points[p][0] = x; + m_points[p][1] = y; + m_points[p][2] = z; + } + int32_t i, j, k, s; + for (int32_t t = 0; t < nf; ++t) { + fscanf(fid, "%i", &s); + if (s == 3) { + fscanf(fid, "%i", &i); + fscanf(fid, "%i", &j); + fscanf(fid, "%i", &k); + m_triangles[t][0] = i; + if (invert) { + m_triangles[t][1] = k; + m_triangles[t][2] = j; + } + else { + m_triangles[t][1] = j; + m_triangles[t][2] = k; + } + } + else // Fix me: support only triangular meshes + { + for (int32_t h = 0; h < s; ++h) + fscanf(fid, "%i", &s); + } + } + fclose(fid); + } + } + else { + return false; + } + return true; +} +#endif // VHACD_DEBUG_MESH +} diff --git a/thirdparty/vhacd/src/vhacdRaycastMesh.cpp b/thirdparty/vhacd/src/vhacdRaycastMesh.cpp new file mode 100644 index 0000000000..e8b9435d5f --- /dev/null +++ b/thirdparty/vhacd/src/vhacdRaycastMesh.cpp @@ -0,0 +1,208 @@ +#include "vhacdRaycastMesh.h" +#include <math.h> +#include <assert.h> + +namespace RAYCAST_MESH +{ + +/* a = b - c */ +#define vector(a,b,c) \ + (a)[0] = (b)[0] - (c)[0]; \ + (a)[1] = (b)[1] - (c)[1]; \ + (a)[2] = (b)[2] - (c)[2]; + +#define innerProduct(v,q) \ + ((v)[0] * (q)[0] + \ + (v)[1] * (q)[1] + \ + (v)[2] * (q)[2]) + +#define crossProduct(a,b,c) \ + (a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \ + (a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \ + (a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1]; + + +static inline bool rayIntersectsTriangle(const double *p,const double *d,const double *v0,const double *v1,const double *v2,double &t) +{ + double e1[3],e2[3],h[3],s[3],q[3]; + double a,f,u,v; + + vector(e1,v1,v0); + vector(e2,v2,v0); + crossProduct(h,d,e2); + a = innerProduct(e1,h); + + if (a > -0.00001 && a < 0.00001) + return(false); + + f = 1/a; + vector(s,p,v0); + u = f * (innerProduct(s,h)); + + if (u < 0.0 || u > 1.0) + return(false); + + crossProduct(q,s,e1); + v = f * innerProduct(d,q); + if (v < 0.0 || u + v > 1.0) + return(false); + // at this stage we can compute t to find out where + // the intersection point is on the line + t = f * innerProduct(e2,q); + if (t > 0) // ray intersection + return(true); + else // this means that there is a line intersection + // but not a ray intersection + return (false); +} + +static double getPointDistance(const double *p1, const double *p2) +{ + double dx = p1[0] - p2[0]; + double dy = p1[1] - p2[1]; + double dz = p1[2] - p2[2]; + return sqrt(dx*dx + dy*dy + dz*dz); +} + +class MyRaycastMesh : public VHACD::RaycastMesh +{ +public: + + template <class T> + MyRaycastMesh(uint32_t vcount, + const T *vertices, + uint32_t tcount, + const uint32_t *indices) + { + mVcount = vcount; + mVertices = new double[mVcount * 3]; + for (uint32_t i = 0; i < mVcount; i++) + { + mVertices[i * 3 + 0] = vertices[0]; + mVertices[i * 3 + 1] = vertices[1]; + mVertices[i * 3 + 2] = vertices[2]; + vertices += 3; + } + mTcount = tcount; + mIndices = new uint32_t[mTcount * 3]; + for (uint32_t i = 0; i < mTcount; i++) + { + mIndices[i * 3 + 0] = indices[0]; + mIndices[i * 3 + 1] = indices[1]; + mIndices[i * 3 + 2] = indices[2]; + indices += 3; + } + } + + + ~MyRaycastMesh(void) + { + delete[]mVertices; + delete[]mIndices; + } + + virtual void release(void) + { + delete this; + } + + virtual bool raycast(const double *from, // The starting point of the raycast + const double *to, // The ending point of the raycast + const double *closestToPoint, // The point to match the nearest hit location (can just be the 'from' location of no specific point) + double *hitLocation, // The point where the ray hit nearest to the 'closestToPoint' location + double *hitDistance) final // The distance the ray traveled to the hit location + { + bool ret = false; + + double dir[3]; + + dir[0] = to[0] - from[0]; + dir[1] = to[1] - from[1]; + dir[2] = to[2] - from[2]; + + double distance = sqrt( dir[0]*dir[0] + dir[1]*dir[1]+dir[2]*dir[2] ); + if ( distance < 0.0000000001f ) return false; + double recipDistance = 1.0f / distance; + dir[0]*=recipDistance; + dir[1]*=recipDistance; + dir[2]*=recipDistance; + const uint32_t *indices = mIndices; + const double *vertices = mVertices; + double nearestDistance = distance; + + for (uint32_t tri=0; tri<mTcount; tri++) + { + uint32_t i1 = indices[tri*3+0]; + uint32_t i2 = indices[tri*3+1]; + uint32_t i3 = indices[tri*3+2]; + + const double *p1 = &vertices[i1*3]; + const double *p2 = &vertices[i2*3]; + const double *p3 = &vertices[i3*3]; + + double t; + if ( rayIntersectsTriangle(from,dir,p1,p2,p3,t)) + { + double hitPos[3]; + + hitPos[0] = from[0] + dir[0] * t; + hitPos[1] = from[1] + dir[1] * t; + hitPos[2] = from[2] + dir[2] * t; + + double pointDistance = getPointDistance(hitPos, closestToPoint); + + if (pointDistance < nearestDistance ) + { + nearestDistance = pointDistance; + if ( hitLocation ) + { + hitLocation[0] = hitPos[0]; + hitLocation[1] = hitPos[1]; + hitLocation[2] = hitPos[2]; + } + if ( hitDistance ) + { + *hitDistance = pointDistance; + } + ret = true; + } + } + } + return ret; + } + + uint32_t mVcount; + double *mVertices; + uint32_t mTcount; + uint32_t *mIndices; +}; + +}; + + + +using namespace RAYCAST_MESH; + +namespace VHACD +{ + + RaycastMesh * RaycastMesh::createRaycastMesh(uint32_t vcount, // The number of vertices in the source triangle mesh + const double *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc. + uint32_t tcount, // The number of triangles in the source triangle mesh + const uint32_t *indices) // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ... + { + MyRaycastMesh *m = new MyRaycastMesh(vcount, vertices, tcount, indices); + return static_cast<RaycastMesh *>(m); + } + + RaycastMesh * RaycastMesh::createRaycastMesh(uint32_t vcount, // The number of vertices in the source triangle mesh + const float *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc. + uint32_t tcount, // The number of triangles in the source triangle mesh + const uint32_t *indices) // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ... + { + MyRaycastMesh *m = new MyRaycastMesh(vcount, vertices, tcount, indices); + return static_cast<RaycastMesh *>(m); + } + + +} // end of VHACD namespace
\ No newline at end of file diff --git a/thirdparty/vhacd/src/vhacdVolume.cpp b/thirdparty/vhacd/src/vhacdVolume.cpp new file mode 100644 index 0000000000..a250e5acaa --- /dev/null +++ b/thirdparty/vhacd/src/vhacdVolume.cpp @@ -0,0 +1,1626 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "btConvexHullComputer.h" +#include "vhacdVolume.h" +#include <algorithm> +#include <float.h> +#include <math.h> +#include <queue> +#include <string.h> + +#ifdef _MSC_VER +#pragma warning(disable:4458 4100) +#endif + + +namespace VHACD { +/********************************************************/ +/* AABB-triangle overlap test code */ +/* by Tomas Akenine-M�ller */ +/* Function: int32_t triBoxOverlap(float boxcenter[3], */ +/* float boxhalfsize[3],float triverts[3][3]); */ +/* History: */ +/* 2001-03-05: released the code in its first version */ +/* 2001-06-18: changed the order of the tests, faster */ +/* */ +/* Acknowledgement: Many thanks to Pierre Terdiman for */ +/* suggestions and discussions on how to optimize code. */ +/* Thanks to David Hunt for finding a ">="-bug! */ +/********************************************************/ + +#define X 0 +#define Y 1 +#define Z 2 +#define FINDMINMAX(x0, x1, x2, min, max) \ + min = max = x0; \ + if (x1 < min) \ + min = x1; \ + if (x1 > max) \ + max = x1; \ + if (x2 < min) \ + min = x2; \ + if (x2 > max) \ + max = x2; + +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a * v0[Y] - b * v0[Z]; \ + p2 = a * v2[Y] - b * v2[Z]; \ + if (p0 < p2) { \ + min = p0; \ + max = p2; \ + } \ + else { \ + min = p2; \ + max = p0; \ + } \ + rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \ + if (min > rad || max < -rad) \ + return 0; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a * v0[Y] - b * v0[Z]; \ + p1 = a * v1[Y] - b * v1[Z]; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } \ + else { \ + min = p1; \ + max = p0; \ + } \ + rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \ + if (min > rad || max < -rad) \ + return 0; + +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a * v0[X] + b * v0[Z]; \ + p2 = -a * v2[X] + b * v2[Z]; \ + if (p0 < p2) { \ + min = p0; \ + max = p2; \ + } \ + else { \ + min = p2; \ + max = p0; \ + } \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \ + if (min > rad || max < -rad) \ + return 0; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a * v0[X] + b * v0[Z]; \ + p1 = -a * v1[X] + b * v1[Z]; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } \ + else { \ + min = p1; \ + max = p0; \ + } \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \ + if (min > rad || max < -rad) \ + return 0; + +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a * v1[X] - b * v1[Y]; \ + p2 = a * v2[X] - b * v2[Y]; \ + if (p2 < p1) { \ + min = p2; \ + max = p1; \ + } \ + else { \ + min = p1; \ + max = p2; \ + } \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \ + if (min > rad || max < -rad) \ + return 0; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a * v0[X] - b * v0[Y]; \ + p1 = a * v1[X] - b * v1[Y]; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } \ + else { \ + min = p1; \ + max = p0; \ + } \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \ + if (min > rad || max < -rad) \ + return 0; + +int32_t PlaneBoxOverlap(const Vec3<double>& normal, + const Vec3<double>& vert, + const Vec3<double>& maxbox) +{ + int32_t q; + Vec3<double> vmin, vmax; + double v; + for (q = X; q <= Z; q++) { + v = vert[q]; + if (normal[q] > 0.0) { + vmin[q] = -maxbox[q] - v; + vmax[q] = maxbox[q] - v; + } + else { + vmin[q] = maxbox[q] - v; + vmax[q] = -maxbox[q] - v; + } + } + if (normal * vmin > 0.0) + return 0; + if (normal * vmax >= 0.0) + return 1; + return 0; +} + +int32_t TriBoxOverlap(const Vec3<double>& boxcenter, + const Vec3<double>& boxhalfsize, + const Vec3<double>& triver0, + const Vec3<double>& triver1, + const Vec3<double>& triver2) +{ + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + + Vec3<double> v0, v1, v2; + double min, max, p0, p1, p2, rad, fex, fey, fez; // -NJMP- "d" local variable removed + Vec3<double> normal, e0, e1, e2; + + /* This is the fastest branch on Sun */ + /* move everything so that the boxcenter is in (0,0,0) */ + + v0 = triver0 - boxcenter; + v1 = triver1 - boxcenter; + v2 = triver2 - boxcenter; + + /* compute triangle edges */ + e0 = v1 - v0; /* tri edge 0 */ + e1 = v2 - v1; /* tri edge 1 */ + e2 = v0 - v2; /* tri edge 2 */ + + /* Bullet 3: */ + /* test the 9 tests first (this was faster) */ + fex = fabs(e0[X]); + fey = fabs(e0[Y]); + fez = fabs(e0[Z]); + + AXISTEST_X01(e0[Z], e0[Y], fez, fey); + AXISTEST_Y02(e0[Z], e0[X], fez, fex); + AXISTEST_Z12(e0[Y], e0[X], fey, fex); + + fex = fabs(e1[X]); + fey = fabs(e1[Y]); + fez = fabs(e1[Z]); + + AXISTEST_X01(e1[Z], e1[Y], fez, fey); + AXISTEST_Y02(e1[Z], e1[X], fez, fex); + AXISTEST_Z0(e1[Y], e1[X], fey, fex); + + fex = fabs(e2[X]); + fey = fabs(e2[Y]); + fez = fabs(e2[Z]); + + AXISTEST_X2(e2[Z], e2[Y], fez, fey); + AXISTEST_Y1(e2[Z], e2[X], fez, fex); + AXISTEST_Z12(e2[Y], e2[X], fey, fex); + + /* Bullet 1: */ + /* first test overlap in the {x,y,z}-directions */ + /* find min, max of the triangle each direction, and test for overlap in */ + /* that direction -- this is equivalent to testing a minimal AABB around */ + /* the triangle against the AABB */ + + /* test in X-direction */ + FINDMINMAX(v0[X], v1[X], v2[X], min, max); + if (min > boxhalfsize[X] || max < -boxhalfsize[X]) + return 0; + + /* test in Y-direction */ + FINDMINMAX(v0[Y], v1[Y], v2[Y], min, max); + if (min > boxhalfsize[Y] || max < -boxhalfsize[Y]) + return 0; + + /* test in Z-direction */ + FINDMINMAX(v0[Z], v1[Z], v2[Z], min, max); + if (min > boxhalfsize[Z] || max < -boxhalfsize[Z]) + return 0; + + /* Bullet 2: */ + /* test if the box intersects the plane of the triangle */ + /* compute plane equation of triangle: normal*x+d=0 */ + normal = e0 ^ e1; + + if (!PlaneBoxOverlap(normal, v0, boxhalfsize)) + return 0; + return 1; /* box and triangle overlaps */ +} + +// Slightly modified version of Stan Melax's code for 3x3 matrix diagonalization (Thanks Stan!) +// source: http://www.melax.com/diag.html?attredirects=0 +void Diagonalize(const double (&A)[3][3], double (&Q)[3][3], double (&D)[3][3]) +{ + // A must be a symmetric matrix. + // returns Q and D such that + // Diagonal matrix D = QT * A * Q; and A = Q*D*QT + const int32_t maxsteps = 24; // certainly wont need that many. + int32_t k0, k1, k2; + double o[3], m[3]; + double q[4] = { 0.0, 0.0, 0.0, 1.0 }; + double jr[4]; + double sqw, sqx, sqy, sqz; + double tmp1, tmp2, mq; + double AQ[3][3]; + double thet, sgn, t, c; + for (int32_t i = 0; i < maxsteps; ++i) { + // quat to matrix + sqx = q[0] * q[0]; + sqy = q[1] * q[1]; + sqz = q[2] * q[2]; + sqw = q[3] * q[3]; + Q[0][0] = (sqx - sqy - sqz + sqw); + Q[1][1] = (-sqx + sqy - sqz + sqw); + Q[2][2] = (-sqx - sqy + sqz + sqw); + tmp1 = q[0] * q[1]; + tmp2 = q[2] * q[3]; + Q[1][0] = 2.0 * (tmp1 + tmp2); + Q[0][1] = 2.0 * (tmp1 - tmp2); + tmp1 = q[0] * q[2]; + tmp2 = q[1] * q[3]; + Q[2][0] = 2.0 * (tmp1 - tmp2); + Q[0][2] = 2.0 * (tmp1 + tmp2); + tmp1 = q[1] * q[2]; + tmp2 = q[0] * q[3]; + Q[2][1] = 2.0 * (tmp1 + tmp2); + Q[1][2] = 2.0 * (tmp1 - tmp2); + + // AQ = A * Q + AQ[0][0] = Q[0][0] * A[0][0] + Q[1][0] * A[0][1] + Q[2][0] * A[0][2]; + AQ[0][1] = Q[0][1] * A[0][0] + Q[1][1] * A[0][1] + Q[2][1] * A[0][2]; + AQ[0][2] = Q[0][2] * A[0][0] + Q[1][2] * A[0][1] + Q[2][2] * A[0][2]; + AQ[1][0] = Q[0][0] * A[0][1] + Q[1][0] * A[1][1] + Q[2][0] * A[1][2]; + AQ[1][1] = Q[0][1] * A[0][1] + Q[1][1] * A[1][1] + Q[2][1] * A[1][2]; + AQ[1][2] = Q[0][2] * A[0][1] + Q[1][2] * A[1][1] + Q[2][2] * A[1][2]; + AQ[2][0] = Q[0][0] * A[0][2] + Q[1][0] * A[1][2] + Q[2][0] * A[2][2]; + AQ[2][1] = Q[0][1] * A[0][2] + Q[1][1] * A[1][2] + Q[2][1] * A[2][2]; + AQ[2][2] = Q[0][2] * A[0][2] + Q[1][2] * A[1][2] + Q[2][2] * A[2][2]; + // D = Qt * AQ + D[0][0] = AQ[0][0] * Q[0][0] + AQ[1][0] * Q[1][0] + AQ[2][0] * Q[2][0]; + D[0][1] = AQ[0][0] * Q[0][1] + AQ[1][0] * Q[1][1] + AQ[2][0] * Q[2][1]; + D[0][2] = AQ[0][0] * Q[0][2] + AQ[1][0] * Q[1][2] + AQ[2][0] * Q[2][2]; + D[1][0] = AQ[0][1] * Q[0][0] + AQ[1][1] * Q[1][0] + AQ[2][1] * Q[2][0]; + D[1][1] = AQ[0][1] * Q[0][1] + AQ[1][1] * Q[1][1] + AQ[2][1] * Q[2][1]; + D[1][2] = AQ[0][1] * Q[0][2] + AQ[1][1] * Q[1][2] + AQ[2][1] * Q[2][2]; + D[2][0] = AQ[0][2] * Q[0][0] + AQ[1][2] * Q[1][0] + AQ[2][2] * Q[2][0]; + D[2][1] = AQ[0][2] * Q[0][1] + AQ[1][2] * Q[1][1] + AQ[2][2] * Q[2][1]; + D[2][2] = AQ[0][2] * Q[0][2] + AQ[1][2] * Q[1][2] + AQ[2][2] * Q[2][2]; + o[0] = D[1][2]; + o[1] = D[0][2]; + o[2] = D[0][1]; + m[0] = fabs(o[0]); + m[1] = fabs(o[1]); + m[2] = fabs(o[2]); + + k0 = (m[0] > m[1] && m[0] > m[2]) ? 0 : (m[1] > m[2]) ? 1 : 2; // index of largest element of offdiag + k1 = (k0 + 1) % 3; + k2 = (k0 + 2) % 3; + if (o[k0] == 0.0) { + break; // diagonal already + } + thet = (D[k2][k2] - D[k1][k1]) / (2.0 * o[k0]); + sgn = (thet > 0.0) ? 1.0 : -1.0; + thet *= sgn; // make it positive + t = sgn / (thet + ((thet < 1.E6) ? sqrt(thet * thet + 1.0) : thet)); // sign(T)/(|T|+sqrt(T^2+1)) + c = 1.0 / sqrt(t * t + 1.0); // c= 1/(t^2+1) , t=s/c + if (c == 1.0) { + break; // no room for improvement - reached machine precision. + } + jr[0] = jr[1] = jr[2] = jr[3] = 0.0; + jr[k0] = sgn * sqrt((1.0 - c) / 2.0); // using 1/2 angle identity sin(a/2) = sqrt((1-cos(a))/2) + jr[k0] *= -1.0; // since our quat-to-matrix convention was for v*M instead of M*v + jr[3] = sqrt(1.0 - jr[k0] * jr[k0]); + if (jr[3] == 1.0) { + break; // reached limits of floating point precision + } + q[0] = (q[3] * jr[0] + q[0] * jr[3] + q[1] * jr[2] - q[2] * jr[1]); + q[1] = (q[3] * jr[1] - q[0] * jr[2] + q[1] * jr[3] + q[2] * jr[0]); + q[2] = (q[3] * jr[2] + q[0] * jr[1] - q[1] * jr[0] + q[2] * jr[3]); + q[3] = (q[3] * jr[3] - q[0] * jr[0] - q[1] * jr[1] - q[2] * jr[2]); + mq = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); + q[0] /= mq; + q[1] /= mq; + q[2] /= mq; + q[3] /= mq; + } +} +const double TetrahedronSet::EPS = 0.0000000000001; +VoxelSet::VoxelSet() +{ + m_minBB[0] = m_minBB[1] = m_minBB[2] = 0.0; + m_minBBVoxels[0] = m_minBBVoxels[1] = m_minBBVoxels[2] = 0; + m_maxBBVoxels[0] = m_maxBBVoxels[1] = m_maxBBVoxels[2] = 1; + m_minBBPts[0] = m_minBBPts[1] = m_minBBPts[2] = 0; + m_maxBBPts[0] = m_maxBBPts[1] = m_maxBBPts[2] = 1; + m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0; + m_barycenterPCA[0] = m_barycenterPCA[1] = m_barycenterPCA[2] = 0.0; + m_scale = 1.0; + m_unitVolume = 1.0; + m_numVoxelsOnSurface = 0; + m_numVoxelsInsideSurface = 0; + memset(m_Q, 0, sizeof(double) * 9); + memset(m_D, 0, sizeof(double) * 9); +} +VoxelSet::~VoxelSet(void) +{ +} +void VoxelSet::ComputeBB() +{ + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + for (int32_t h = 0; h < 3; ++h) { + m_minBBVoxels[h] = m_voxels[0].m_coord[h]; + m_maxBBVoxels[h] = m_voxels[0].m_coord[h]; + } + Vec3<double> bary(0.0); + for (size_t p = 0; p < nVoxels; ++p) { + for (int32_t h = 0; h < 3; ++h) { + bary[h] += m_voxels[p].m_coord[h]; + if (m_minBBVoxels[h] > m_voxels[p].m_coord[h]) + m_minBBVoxels[h] = m_voxels[p].m_coord[h]; + if (m_maxBBVoxels[h] < m_voxels[p].m_coord[h]) + m_maxBBVoxels[h] = m_voxels[p].m_coord[h]; + } + } + bary /= (double)nVoxels; + for (int32_t h = 0; h < 3; ++h) { + m_minBBPts[h] = m_minBBVoxels[h] * m_scale + m_minBB[h]; + m_maxBBPts[h] = m_maxBBVoxels[h] * m_scale + m_minBB[h]; + m_barycenter[h] = (short)(bary[h] + 0.5); + } +} +void VoxelSet::ComputeConvexHull(Mesh& meshCH, const size_t sampling) const +{ + const size_t CLUSTER_SIZE = 65536; + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + + SArray<Vec3<double> > cpoints; + + Vec3<double>* points = new Vec3<double>[CLUSTER_SIZE]; + size_t p = 0; + size_t s = 0; + short i, j, k; + while (p < nVoxels) { + size_t q = 0; + while (q < CLUSTER_SIZE && p < nVoxels) { + if (m_voxels[p].m_data == PRIMITIVE_ON_SURFACE) { + ++s; + if (s == sampling) { + s = 0; + i = m_voxels[p].m_coord[0]; + j = m_voxels[p].m_coord[1]; + k = m_voxels[p].m_coord[2]; + Vec3<double> p0((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3<double> p1((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3<double> p2((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3<double> p3((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3<double> p4((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3<double> p5((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3<double> p6((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3<double> p7((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); + points[q++] = p0 + m_minBB; + points[q++] = p1 + m_minBB; + points[q++] = p2 + m_minBB; + points[q++] = p3 + m_minBB; + points[q++] = p4 + m_minBB; + points[q++] = p5 + m_minBB; + points[q++] = p6 + m_minBB; + points[q++] = p7 + m_minBB; + } + } + ++p; + } + btConvexHullComputer ch; + ch.compute((double*)points, 3 * sizeof(double), (int32_t)q, -1.0, -1.0); + for (int32_t v = 0; v < ch.vertices.size(); v++) { + cpoints.PushBack(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + } + delete[] points; + + points = cpoints.Data(); + btConvexHullComputer ch; + ch.compute((double*)points, 3 * sizeof(double), (int32_t)cpoints.Size(), -1.0, -1.0); + meshCH.ResizePoints(0); + meshCH.ResizeTriangles(0); + for (int32_t v = 0; v < ch.vertices.size(); v++) { + meshCH.AddPoint(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int32_t nt = ch.faces.size(); + for (int32_t t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int32_t a = sourceEdge->getSourceVertex(); + int32_t b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int32_t c = edge->getTargetVertex(); + while (c != a) { + meshCH.AddTriangle(Vec3<int32_t>(a, b, c)); + edge = edge->getNextEdgeOfFace(); + b = c; + c = edge->getTargetVertex(); + } + } +} +void VoxelSet::GetPoints(const Voxel& voxel, + Vec3<double>* const pts) const +{ + short i = voxel.m_coord[0]; + short j = voxel.m_coord[1]; + short k = voxel.m_coord[2]; + pts[0][0] = (i - 0.5) * m_scale + m_minBB[0]; + pts[1][0] = (i + 0.5) * m_scale + m_minBB[0]; + pts[2][0] = (i + 0.5) * m_scale + m_minBB[0]; + pts[3][0] = (i - 0.5) * m_scale + m_minBB[0]; + pts[4][0] = (i - 0.5) * m_scale + m_minBB[0]; + pts[5][0] = (i + 0.5) * m_scale + m_minBB[0]; + pts[6][0] = (i + 0.5) * m_scale + m_minBB[0]; + pts[7][0] = (i - 0.5) * m_scale + m_minBB[0]; + pts[0][1] = (j - 0.5) * m_scale + m_minBB[1]; + pts[1][1] = (j - 0.5) * m_scale + m_minBB[1]; + pts[2][1] = (j + 0.5) * m_scale + m_minBB[1]; + pts[3][1] = (j + 0.5) * m_scale + m_minBB[1]; + pts[4][1] = (j - 0.5) * m_scale + m_minBB[1]; + pts[5][1] = (j - 0.5) * m_scale + m_minBB[1]; + pts[6][1] = (j + 0.5) * m_scale + m_minBB[1]; + pts[7][1] = (j + 0.5) * m_scale + m_minBB[1]; + pts[0][2] = (k - 0.5) * m_scale + m_minBB[2]; + pts[1][2] = (k - 0.5) * m_scale + m_minBB[2]; + pts[2][2] = (k - 0.5) * m_scale + m_minBB[2]; + pts[3][2] = (k - 0.5) * m_scale + m_minBB[2]; + pts[4][2] = (k + 0.5) * m_scale + m_minBB[2]; + pts[5][2] = (k + 0.5) * m_scale + m_minBB[2]; + pts[6][2] = (k + 0.5) * m_scale + m_minBB[2]; + pts[7][2] = (k + 0.5) * m_scale + m_minBB[2]; +} +void VoxelSet::Intersect(const Plane& plane, + SArray<Vec3<double> >* const positivePts, + SArray<Vec3<double> >* const negativePts, + const size_t sampling) const +{ + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + const double d0 = m_scale; + double d; + Vec3<double> pts[8]; + Vec3<double> pt; + Voxel voxel; + size_t sp = 0; + size_t sn = 0; + for (size_t v = 0; v < nVoxels; ++v) { + voxel = m_voxels[v]; + pt = GetPoint(voxel); + d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; + // if (d >= 0.0 && d <= d0) positivePts->PushBack(pt); + // else if (d < 0.0 && -d <= d0) negativePts->PushBack(pt); + if (d >= 0.0) { + if (d <= d0) { + GetPoints(voxel, pts); + for (int32_t k = 0; k < 8; ++k) { + positivePts->PushBack(pts[k]); + } + } + else { + if (++sp == sampling) { + // positivePts->PushBack(pt); + GetPoints(voxel, pts); + for (int32_t k = 0; k < 8; ++k) { + positivePts->PushBack(pts[k]); + } + sp = 0; + } + } + } + else { + if (-d <= d0) { + GetPoints(voxel, pts); + for (int32_t k = 0; k < 8; ++k) { + negativePts->PushBack(pts[k]); + } + } + else { + if (++sn == sampling) { + // negativePts->PushBack(pt); + GetPoints(voxel, pts); + for (int32_t k = 0; k < 8; ++k) { + negativePts->PushBack(pts[k]); + } + sn = 0; + } + } + } + } +} +void VoxelSet::ComputeExteriorPoints(const Plane& plane, + const Mesh& mesh, + SArray<Vec3<double> >* const exteriorPts) const +{ + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + double d; + Vec3<double> pt; + Vec3<double> pts[8]; + Voxel voxel; + for (size_t v = 0; v < nVoxels; ++v) { + voxel = m_voxels[v]; + pt = GetPoint(voxel); + d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; + if (d >= 0.0) { + if (!mesh.IsInside(pt)) { + GetPoints(voxel, pts); + for (int32_t k = 0; k < 8; ++k) { + exteriorPts->PushBack(pts[k]); + } + } + } + } +} +void VoxelSet::ComputeClippedVolumes(const Plane& plane, + double& positiveVolume, + double& negativeVolume) const +{ + negativeVolume = 0.0; + positiveVolume = 0.0; + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + double d; + Vec3<double> pt; + size_t nPositiveVoxels = 0; + for (size_t v = 0; v < nVoxels; ++v) { + pt = GetPoint(m_voxels[v]); + d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; + nPositiveVoxels += (d >= 0.0); + } + size_t nNegativeVoxels = nVoxels - nPositiveVoxels; + positiveVolume = m_unitVolume * nPositiveVoxels; + negativeVolume = m_unitVolume * nNegativeVoxels; +} +void VoxelSet::SelectOnSurface(PrimitiveSet* const onSurfP) const +{ + VoxelSet* const onSurf = (VoxelSet*)onSurfP; + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + + for (int32_t h = 0; h < 3; ++h) { + onSurf->m_minBB[h] = m_minBB[h]; + } + onSurf->m_voxels.Resize(0); + onSurf->m_scale = m_scale; + onSurf->m_unitVolume = m_unitVolume; + onSurf->m_numVoxelsOnSurface = 0; + onSurf->m_numVoxelsInsideSurface = 0; + Voxel voxel; + for (size_t v = 0; v < nVoxels; ++v) { + voxel = m_voxels[v]; + if (voxel.m_data == PRIMITIVE_ON_SURFACE) { + onSurf->m_voxels.PushBack(voxel); + ++onSurf->m_numVoxelsOnSurface; + } + } +} +void VoxelSet::Clip(const Plane& plane, + PrimitiveSet* const positivePartP, + PrimitiveSet* const negativePartP) const +{ + VoxelSet* const positivePart = (VoxelSet*)positivePartP; + VoxelSet* const negativePart = (VoxelSet*)negativePartP; + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + + for (int32_t h = 0; h < 3; ++h) { + negativePart->m_minBB[h] = positivePart->m_minBB[h] = m_minBB[h]; + } + positivePart->m_voxels.Resize(0); + negativePart->m_voxels.Resize(0); + positivePart->m_voxels.Allocate(nVoxels); + negativePart->m_voxels.Allocate(nVoxels); + negativePart->m_scale = positivePart->m_scale = m_scale; + negativePart->m_unitVolume = positivePart->m_unitVolume = m_unitVolume; + negativePart->m_numVoxelsOnSurface = positivePart->m_numVoxelsOnSurface = 0; + negativePart->m_numVoxelsInsideSurface = positivePart->m_numVoxelsInsideSurface = 0; + + double d; + Vec3<double> pt; + Voxel voxel; + const double d0 = m_scale; + for (size_t v = 0; v < nVoxels; ++v) { + voxel = m_voxels[v]; + pt = GetPoint(voxel); + d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; + if (d >= 0.0) { + if (voxel.m_data == PRIMITIVE_ON_SURFACE || d <= d0) { + voxel.m_data = PRIMITIVE_ON_SURFACE; + positivePart->m_voxels.PushBack(voxel); + ++positivePart->m_numVoxelsOnSurface; + } + else { + positivePart->m_voxels.PushBack(voxel); + ++positivePart->m_numVoxelsInsideSurface; + } + } + else { + if (voxel.m_data == PRIMITIVE_ON_SURFACE || -d <= d0) { + voxel.m_data = PRIMITIVE_ON_SURFACE; + negativePart->m_voxels.PushBack(voxel); + ++negativePart->m_numVoxelsOnSurface; + } + else { + negativePart->m_voxels.PushBack(voxel); + ++negativePart->m_numVoxelsInsideSurface; + } + } + } +} +void VoxelSet::Convert(Mesh& mesh, const VOXEL_VALUE value) const +{ + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + Voxel voxel; + Vec3<double> pts[8]; + for (size_t v = 0; v < nVoxels; ++v) { + voxel = m_voxels[v]; + if (voxel.m_data == value) { + GetPoints(voxel, pts); + int32_t s = (int32_t)mesh.GetNPoints(); + for (int32_t k = 0; k < 8; ++k) { + mesh.AddPoint(pts[k]); + } + mesh.AddTriangle(Vec3<int32_t>(s + 0, s + 2, s + 1)); + mesh.AddTriangle(Vec3<int32_t>(s + 0, s + 3, s + 2)); + mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 5, s + 6)); + mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 6, s + 7)); + mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 6, s + 2)); + mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 2, s + 3)); + mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 1, s + 5)); + mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 0, s + 1)); + mesh.AddTriangle(Vec3<int32_t>(s + 6, s + 5, s + 1)); + mesh.AddTriangle(Vec3<int32_t>(s + 6, s + 1, s + 2)); + mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 0, s + 4)); + mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 3, s + 0)); + } + } +} +void VoxelSet::ComputePrincipalAxes() +{ + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + m_barycenterPCA[0] = m_barycenterPCA[1] = m_barycenterPCA[2] = 0.0; + for (size_t v = 0; v < nVoxels; ++v) { + Voxel& voxel = m_voxels[v]; + m_barycenterPCA[0] += voxel.m_coord[0]; + m_barycenterPCA[1] += voxel.m_coord[1]; + m_barycenterPCA[2] += voxel.m_coord[2]; + } + m_barycenterPCA /= (double)nVoxels; + + double covMat[3][3] = { { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 } }; + double x, y, z; + for (size_t v = 0; v < nVoxels; ++v) { + Voxel& voxel = m_voxels[v]; + x = voxel.m_coord[0] - m_barycenter[0]; + y = voxel.m_coord[1] - m_barycenter[1]; + z = voxel.m_coord[2] - m_barycenter[2]; + covMat[0][0] += x * x; + covMat[1][1] += y * y; + covMat[2][2] += z * z; + covMat[0][1] += x * y; + covMat[0][2] += x * z; + covMat[1][2] += y * z; + } + covMat[0][0] /= nVoxels; + covMat[1][1] /= nVoxels; + covMat[2][2] /= nVoxels; + covMat[0][1] /= nVoxels; + covMat[0][2] /= nVoxels; + covMat[1][2] /= nVoxels; + covMat[1][0] = covMat[0][1]; + covMat[2][0] = covMat[0][2]; + covMat[2][1] = covMat[1][2]; + Diagonalize(covMat, m_Q, m_D); +} +Volume::Volume() +{ + m_dim[0] = m_dim[1] = m_dim[2] = 0; + m_minBB[0] = m_minBB[1] = m_minBB[2] = 0.0; + m_maxBB[0] = m_maxBB[1] = m_maxBB[2] = 1.0; + m_numVoxelsOnSurface = 0; + m_numVoxelsInsideSurface = 0; + m_numVoxelsOutsideSurface = 0; + m_scale = 1.0; + m_data = 0; +} +Volume::~Volume(void) +{ + delete[] m_data; +} +void Volume::Allocate() +{ + delete[] m_data; + size_t size = m_dim[0] * m_dim[1] * m_dim[2]; + m_data = new unsigned char[size]; + memset(m_data, PRIMITIVE_UNDEFINED, sizeof(unsigned char) * size); +} +void Volume::Free() +{ + delete[] m_data; + m_data = 0; +} +void Volume::FillOutsideSurface(const size_t i0, + const size_t j0, + const size_t k0, + const size_t i1, + const size_t j1, + const size_t k1) +{ + const short neighbours[6][3] = { { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 }, + { -1, 0, 0 }, + { 0, -1, 0 }, + { 0, 0, -1 } }; + std::queue<Vec3<short> > fifo; + Vec3<short> current; + short a, b, c; + for (size_t i = i0; i < i1; ++i) { + for (size_t j = j0; j < j1; ++j) { + for (size_t k = k0; k < k1; ++k) { + + if (GetVoxel(i, j, k) == PRIMITIVE_UNDEFINED) { + current[0] = (short)i; + current[1] = (short)j; + current[2] = (short)k; + fifo.push(current); + GetVoxel(current[0], current[1], current[2]) = PRIMITIVE_OUTSIDE_SURFACE; + ++m_numVoxelsOutsideSurface; + while (fifo.size() > 0) { + current = fifo.front(); + fifo.pop(); + for (int32_t h = 0; h < 6; ++h) { + a = current[0] + neighbours[h][0]; + b = current[1] + neighbours[h][1]; + c = current[2] + neighbours[h][2]; + if (a < 0 || a >= (int32_t)m_dim[0] || b < 0 || b >= (int32_t)m_dim[1] || c < 0 || c >= (int32_t)m_dim[2]) { + continue; + } + unsigned char& v = GetVoxel(a, b, c); + if (v == PRIMITIVE_UNDEFINED) { + v = PRIMITIVE_OUTSIDE_SURFACE; + ++m_numVoxelsOutsideSurface; + fifo.push(Vec3<short>(a, b, c)); + } + } + } + } + } + } + } +} +void Volume::FillInsideSurface() +{ + const size_t i0 = m_dim[0]; + const size_t j0 = m_dim[1]; + const size_t k0 = m_dim[2]; + for (size_t i = 0; i < i0; ++i) { + for (size_t j = 0; j < j0; ++j) { + for (size_t k = 0; k < k0; ++k) { + unsigned char& v = GetVoxel(i, j, k); + if (v == PRIMITIVE_UNDEFINED) { + v = PRIMITIVE_INSIDE_SURFACE; + ++m_numVoxelsInsideSurface; + } + } + } + } +} +void Volume::Convert(Mesh& mesh, const VOXEL_VALUE value) const +{ + const size_t i0 = m_dim[0]; + const size_t j0 = m_dim[1]; + const size_t k0 = m_dim[2]; + for (size_t i = 0; i < i0; ++i) { + for (size_t j = 0; j < j0; ++j) { + for (size_t k = 0; k < k0; ++k) { + const unsigned char& voxel = GetVoxel(i, j, k); + if (voxel == value) { + Vec3<double> p0((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3<double> p1((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3<double> p2((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3<double> p3((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3<double> p4((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3<double> p5((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3<double> p6((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3<double> p7((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); + int32_t s = (int32_t)mesh.GetNPoints(); + mesh.AddPoint(p0 + m_minBB); + mesh.AddPoint(p1 + m_minBB); + mesh.AddPoint(p2 + m_minBB); + mesh.AddPoint(p3 + m_minBB); + mesh.AddPoint(p4 + m_minBB); + mesh.AddPoint(p5 + m_minBB); + mesh.AddPoint(p6 + m_minBB); + mesh.AddPoint(p7 + m_minBB); + mesh.AddTriangle(Vec3<int32_t>(s + 0, s + 2, s + 1)); + mesh.AddTriangle(Vec3<int32_t>(s + 0, s + 3, s + 2)); + mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 5, s + 6)); + mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 6, s + 7)); + mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 6, s + 2)); + mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 2, s + 3)); + mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 1, s + 5)); + mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 0, s + 1)); + mesh.AddTriangle(Vec3<int32_t>(s + 6, s + 5, s + 1)); + mesh.AddTriangle(Vec3<int32_t>(s + 6, s + 1, s + 2)); + mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 0, s + 4)); + mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 3, s + 0)); + } + } + } + } +} +void Volume::Convert(VoxelSet& vset) const +{ + for (int32_t h = 0; h < 3; ++h) { + vset.m_minBB[h] = m_minBB[h]; + } + vset.m_voxels.Allocate(m_numVoxelsInsideSurface + m_numVoxelsOnSurface); + vset.m_scale = m_scale; + vset.m_unitVolume = m_scale * m_scale * m_scale; + const short i0 = (short)m_dim[0]; + const short j0 = (short)m_dim[1]; + const short k0 = (short)m_dim[2]; + Voxel voxel; + vset.m_numVoxelsOnSurface = 0; + vset.m_numVoxelsInsideSurface = 0; + for (short i = 0; i < i0; ++i) { + for (short j = 0; j < j0; ++j) { + for (short k = 0; k < k0; ++k) { + const unsigned char& value = GetVoxel(i, j, k); + if (value == PRIMITIVE_INSIDE_SURFACE) { + voxel.m_coord[0] = i; + voxel.m_coord[1] = j; + voxel.m_coord[2] = k; + voxel.m_data = PRIMITIVE_INSIDE_SURFACE; + vset.m_voxels.PushBack(voxel); + ++vset.m_numVoxelsInsideSurface; + } + else if (value == PRIMITIVE_ON_SURFACE) { + voxel.m_coord[0] = i; + voxel.m_coord[1] = j; + voxel.m_coord[2] = k; + voxel.m_data = PRIMITIVE_ON_SURFACE; + vset.m_voxels.PushBack(voxel); + ++vset.m_numVoxelsOnSurface; + } + } + } + } +} + +void Volume::Convert(TetrahedronSet& tset) const +{ + tset.m_tetrahedra.Allocate(5 * (m_numVoxelsInsideSurface + m_numVoxelsOnSurface)); + tset.m_scale = m_scale; + const short i0 = (short)m_dim[0]; + const short j0 = (short)m_dim[1]; + const short k0 = (short)m_dim[2]; + tset.m_numTetrahedraOnSurface = 0; + tset.m_numTetrahedraInsideSurface = 0; + Tetrahedron tetrahedron; + for (short i = 0; i < i0; ++i) { + for (short j = 0; j < j0; ++j) { + for (short k = 0; k < k0; ++k) { + const unsigned char& value = GetVoxel(i, j, k); + if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) { + tetrahedron.m_data = value; + Vec3<double> p1((i - 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); + Vec3<double> p2((i + 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); + Vec3<double> p3((i + 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); + Vec3<double> p4((i - 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); + Vec3<double> p5((i - 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); + Vec3<double> p6((i + 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); + Vec3<double> p7((i + 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); + Vec3<double> p8((i - 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); + + tetrahedron.m_pts[0] = p2; + tetrahedron.m_pts[1] = p4; + tetrahedron.m_pts[2] = p7; + tetrahedron.m_pts[3] = p5; + tset.m_tetrahedra.PushBack(tetrahedron); + + tetrahedron.m_pts[0] = p6; + tetrahedron.m_pts[1] = p2; + tetrahedron.m_pts[2] = p7; + tetrahedron.m_pts[3] = p5; + tset.m_tetrahedra.PushBack(tetrahedron); + + tetrahedron.m_pts[0] = p3; + tetrahedron.m_pts[1] = p4; + tetrahedron.m_pts[2] = p7; + tetrahedron.m_pts[3] = p2; + tset.m_tetrahedra.PushBack(tetrahedron); + + tetrahedron.m_pts[0] = p1; + tetrahedron.m_pts[1] = p4; + tetrahedron.m_pts[2] = p2; + tetrahedron.m_pts[3] = p5; + tset.m_tetrahedra.PushBack(tetrahedron); + + tetrahedron.m_pts[0] = p8; + tetrahedron.m_pts[1] = p5; + tetrahedron.m_pts[2] = p7; + tetrahedron.m_pts[3] = p4; + tset.m_tetrahedra.PushBack(tetrahedron); + if (value == PRIMITIVE_INSIDE_SURFACE) { + tset.m_numTetrahedraInsideSurface += 5; + } + else { + tset.m_numTetrahedraOnSurface += 5; + } + } + } + } + } +} + +void Volume::AlignToPrincipalAxes(double (&rot)[3][3]) const +{ + const short i0 = (short)m_dim[0]; + const short j0 = (short)m_dim[1]; + const short k0 = (short)m_dim[2]; + Vec3<double> barycenter(0.0); + size_t nVoxels = 0; + for (short i = 0; i < i0; ++i) { + for (short j = 0; j < j0; ++j) { + for (short k = 0; k < k0; ++k) { + const unsigned char& value = GetVoxel(i, j, k); + if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) { + barycenter[0] += i; + barycenter[1] += j; + barycenter[2] += k; + ++nVoxels; + } + } + } + } + barycenter /= (double)nVoxels; + + double covMat[3][3] = { { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 } }; + double x, y, z; + for (short i = 0; i < i0; ++i) { + for (short j = 0; j < j0; ++j) { + for (short k = 0; k < k0; ++k) { + const unsigned char& value = GetVoxel(i, j, k); + if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) { + x = i - barycenter[0]; + y = j - barycenter[1]; + z = k - barycenter[2]; + covMat[0][0] += x * x; + covMat[1][1] += y * y; + covMat[2][2] += z * z; + covMat[0][1] += x * y; + covMat[0][2] += x * z; + covMat[1][2] += y * z; + } + } + } + } + covMat[1][0] = covMat[0][1]; + covMat[2][0] = covMat[0][2]; + covMat[2][1] = covMat[1][2]; + double D[3][3]; + Diagonalize(covMat, rot, D); +} +TetrahedronSet::TetrahedronSet() +{ + m_minBB[0] = m_minBB[1] = m_minBB[2] = 0.0; + m_maxBB[0] = m_maxBB[1] = m_maxBB[2] = 1.0; + m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0; + m_scale = 1.0; + m_numTetrahedraOnSurface = 0; + m_numTetrahedraInsideSurface = 0; + memset(m_Q, 0, sizeof(double) * 9); + memset(m_D, 0, sizeof(double) * 9); +} +TetrahedronSet::~TetrahedronSet(void) +{ +} +void TetrahedronSet::ComputeBB() +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + + for (int32_t h = 0; h < 3; ++h) { + m_minBB[h] = m_maxBB[h] = m_tetrahedra[0].m_pts[0][h]; + m_barycenter[h] = 0.0; + } + for (size_t p = 0; p < nTetrahedra; ++p) { + for (int32_t i = 0; i < 4; ++i) { + for (int32_t h = 0; h < 3; ++h) { + if (m_minBB[h] > m_tetrahedra[p].m_pts[i][h]) + m_minBB[h] = m_tetrahedra[p].m_pts[i][h]; + if (m_maxBB[h] < m_tetrahedra[p].m_pts[i][h]) + m_maxBB[h] = m_tetrahedra[p].m_pts[i][h]; + m_barycenter[h] += m_tetrahedra[p].m_pts[i][h]; + } + } + } + m_barycenter /= (double)(4 * nTetrahedra); +} +void TetrahedronSet::ComputeConvexHull(Mesh& meshCH, const size_t sampling) const +{ + const size_t CLUSTER_SIZE = 65536; + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + + SArray<Vec3<double> > cpoints; + + Vec3<double>* points = new Vec3<double>[CLUSTER_SIZE]; + size_t p = 0; + while (p < nTetrahedra) { + size_t q = 0; + size_t s = 0; + while (q < CLUSTER_SIZE && p < nTetrahedra) { + if (m_tetrahedra[p].m_data == PRIMITIVE_ON_SURFACE) { + ++s; + if (s == sampling) { + s = 0; + for (int32_t a = 0; a < 4; ++a) { + points[q++] = m_tetrahedra[p].m_pts[a]; + for (int32_t xx = 0; xx < 3; ++xx) { + assert(m_tetrahedra[p].m_pts[a][xx] + EPS >= m_minBB[xx]); + assert(m_tetrahedra[p].m_pts[a][xx] <= m_maxBB[xx] + EPS); + } + } + } + } + ++p; + } + btConvexHullComputer ch; + ch.compute((double*)points, 3 * sizeof(double), (int32_t)q, -1.0, -1.0); + for (int32_t v = 0; v < ch.vertices.size(); v++) { + cpoints.PushBack(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + } + delete[] points; + + points = cpoints.Data(); + btConvexHullComputer ch; + ch.compute((double*)points, 3 * sizeof(double), (int32_t)cpoints.Size(), -1.0, -1.0); + meshCH.ResizePoints(0); + meshCH.ResizeTriangles(0); + for (int32_t v = 0; v < ch.vertices.size(); v++) { + meshCH.AddPoint(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int32_t nt = ch.faces.size(); + for (int32_t t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int32_t a = sourceEdge->getSourceVertex(); + int32_t b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int32_t c = edge->getTargetVertex(); + while (c != a) { + meshCH.AddTriangle(Vec3<int32_t>(a, b, c)); + edge = edge->getNextEdgeOfFace(); + b = c; + c = edge->getTargetVertex(); + } + } +} +inline bool TetrahedronSet::Add(Tetrahedron& tetrahedron) +{ + double v = ComputeVolume4(tetrahedron.m_pts[0], tetrahedron.m_pts[1], tetrahedron.m_pts[2], tetrahedron.m_pts[3]); + + const double EPS = 0.0000000001; + if (fabs(v) < EPS) { + return false; + } + else if (v < 0.0) { + Vec3<double> tmp = tetrahedron.m_pts[0]; + tetrahedron.m_pts[0] = tetrahedron.m_pts[1]; + tetrahedron.m_pts[1] = tmp; + } + + for (int32_t a = 0; a < 4; ++a) { + for (int32_t xx = 0; xx < 3; ++xx) { + assert(tetrahedron.m_pts[a][xx] + EPS >= m_minBB[xx]); + assert(tetrahedron.m_pts[a][xx] <= m_maxBB[xx] + EPS); + } + } + m_tetrahedra.PushBack(tetrahedron); + return true; +} + +void TetrahedronSet::AddClippedTetrahedra(const Vec3<double> (&pts)[10], const int32_t nPts) +{ + const int32_t tetF[4][3] = { { 0, 1, 2 }, { 2, 1, 3 }, { 3, 1, 0 }, { 3, 0, 2 } }; + if (nPts < 4) { + return; + } + else if (nPts == 4) { + Tetrahedron tetrahedron; + tetrahedron.m_data = PRIMITIVE_ON_SURFACE; + tetrahedron.m_pts[0] = pts[0]; + tetrahedron.m_pts[1] = pts[1]; + tetrahedron.m_pts[2] = pts[2]; + tetrahedron.m_pts[3] = pts[3]; + if (Add(tetrahedron)) { + ++m_numTetrahedraOnSurface; + } + } + else if (nPts == 5) { + const int32_t tet[15][4] = { + { 0, 1, 2, 3 }, { 1, 2, 3, 4 }, { 0, 2, 3, 4 }, { 0, 1, 3, 4 }, { 0, 1, 2, 4 }, + }; + const int32_t rem[5] = { 4, 0, 1, 2, 3 }; + double maxVol = 0.0; + int32_t h0 = -1; + Tetrahedron tetrahedron0; + tetrahedron0.m_data = PRIMITIVE_ON_SURFACE; + for (int32_t h = 0; h < 5; ++h) { + double v = ComputeVolume4(pts[tet[h][0]], pts[tet[h][1]], pts[tet[h][2]], pts[tet[h][3]]); + if (v > maxVol) { + h0 = h; + tetrahedron0.m_pts[0] = pts[tet[h][0]]; + tetrahedron0.m_pts[1] = pts[tet[h][1]]; + tetrahedron0.m_pts[2] = pts[tet[h][2]]; + tetrahedron0.m_pts[3] = pts[tet[h][3]]; + maxVol = v; + } + else if (-v > maxVol) { + h0 = h; + tetrahedron0.m_pts[0] = pts[tet[h][1]]; + tetrahedron0.m_pts[1] = pts[tet[h][0]]; + tetrahedron0.m_pts[2] = pts[tet[h][2]]; + tetrahedron0.m_pts[3] = pts[tet[h][3]]; + maxVol = -v; + } + } + if (h0 == -1) + return; + if (Add(tetrahedron0)) { + ++m_numTetrahedraOnSurface; + } + else { + return; + } + int32_t a = rem[h0]; + maxVol = 0.0; + int32_t h1 = -1; + Tetrahedron tetrahedron1; + tetrahedron1.m_data = PRIMITIVE_ON_SURFACE; + for (int32_t h = 0; h < 4; ++h) { + double v = ComputeVolume4(pts[a], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]); + if (v > maxVol) { + h1 = h; + tetrahedron1.m_pts[0] = pts[a]; + tetrahedron1.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]]; + tetrahedron1.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]]; + tetrahedron1.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]]; + maxVol = v; + } + } + if (h1 == -1 && Add(tetrahedron1)) { + ++m_numTetrahedraOnSurface; + } + } + else if (nPts == 6) { + + const int32_t tet[15][4] = { { 2, 3, 4, 5 }, { 1, 3, 4, 5 }, { 1, 2, 4, 5 }, { 1, 2, 3, 5 }, { 1, 2, 3, 4 }, + { 0, 3, 4, 5 }, { 0, 2, 4, 5 }, { 0, 2, 3, 5 }, { 0, 2, 3, 4 }, { 0, 1, 4, 5 }, + { 0, 1, 3, 5 }, { 0, 1, 3, 4 }, { 0, 1, 2, 5 }, { 0, 1, 2, 4 }, { 0, 1, 2, 3 } }; + const int32_t rem[15][2] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 0, 5 }, + { 1, 2 }, { 1, 3 }, { 1, 4 }, { 1, 5 }, { 2, 3 }, + { 2, 4 }, { 2, 5 }, { 3, 4 }, { 3, 5 }, { 4, 5 } }; + double maxVol = 0.0; + int32_t h0 = -1; + Tetrahedron tetrahedron0; + tetrahedron0.m_data = PRIMITIVE_ON_SURFACE; + for (int32_t h = 0; h < 15; ++h) { + double v = ComputeVolume4(pts[tet[h][0]], pts[tet[h][1]], pts[tet[h][2]], pts[tet[h][3]]); + if (v > maxVol) { + h0 = h; + tetrahedron0.m_pts[0] = pts[tet[h][0]]; + tetrahedron0.m_pts[1] = pts[tet[h][1]]; + tetrahedron0.m_pts[2] = pts[tet[h][2]]; + tetrahedron0.m_pts[3] = pts[tet[h][3]]; + maxVol = v; + } + else if (-v > maxVol) { + h0 = h; + tetrahedron0.m_pts[0] = pts[tet[h][1]]; + tetrahedron0.m_pts[1] = pts[tet[h][0]]; + tetrahedron0.m_pts[2] = pts[tet[h][2]]; + tetrahedron0.m_pts[3] = pts[tet[h][3]]; + maxVol = -v; + } + } + if (h0 == -1) + return; + if (Add(tetrahedron0)) { + ++m_numTetrahedraOnSurface; + } + else { + return; + } + + int32_t a0 = rem[h0][0]; + int32_t a1 = rem[h0][1]; + int32_t h1 = -1; + Tetrahedron tetrahedron1; + tetrahedron1.m_data = PRIMITIVE_ON_SURFACE; + maxVol = 0.0; + for (int32_t h = 0; h < 4; ++h) { + double v = ComputeVolume4(pts[a0], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]); + if (v > maxVol) { + h1 = h; + tetrahedron1.m_pts[0] = pts[a0]; + tetrahedron1.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]]; + tetrahedron1.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]]; + tetrahedron1.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]]; + maxVol = v; + } + } + if (h1 != -1 && Add(tetrahedron1)) { + ++m_numTetrahedraOnSurface; + } + else { + h1 = -1; + } + maxVol = 0.0; + int32_t h2 = -1; + Tetrahedron tetrahedron2; + tetrahedron2.m_data = PRIMITIVE_ON_SURFACE; + for (int32_t h = 0; h < 4; ++h) { + double v = ComputeVolume4(pts[a0], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]); + if (h == h1) + continue; + if (v > maxVol) { + h2 = h; + tetrahedron2.m_pts[0] = pts[a1]; + tetrahedron2.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]]; + tetrahedron2.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]]; + tetrahedron2.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]]; + maxVol = v; + } + } + if (h1 != -1) { + for (int32_t h = 0; h < 4; ++h) { + double v = ComputeVolume4(pts[a1], tetrahedron1.m_pts[tetF[h][0]], tetrahedron1.m_pts[tetF[h][1]], tetrahedron1.m_pts[tetF[h][2]]); + if (h == 1) + continue; + if (v > maxVol) { + h2 = h; + tetrahedron2.m_pts[0] = pts[a1]; + tetrahedron2.m_pts[1] = tetrahedron1.m_pts[tetF[h][0]]; + tetrahedron2.m_pts[2] = tetrahedron1.m_pts[tetF[h][1]]; + tetrahedron2.m_pts[3] = tetrahedron1.m_pts[tetF[h][2]]; + maxVol = v; + } + } + } + if (h2 != -1 && Add(tetrahedron2)) { + ++m_numTetrahedraOnSurface; + } + } + else { + assert(0); + } +} + +void TetrahedronSet::Intersect(const Plane& plane, + SArray<Vec3<double> >* const positivePts, + SArray<Vec3<double> >* const negativePts, + const size_t sampling) const +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; +} +void TetrahedronSet::ComputeExteriorPoints(const Plane& plane, + const Mesh& mesh, + SArray<Vec3<double> >* const exteriorPts) const +{ +} +void TetrahedronSet::ComputeClippedVolumes(const Plane& plane, + double& positiveVolume, + double& negativeVolume) const +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; +} + +void TetrahedronSet::SelectOnSurface(PrimitiveSet* const onSurfP) const +{ + TetrahedronSet* const onSurf = (TetrahedronSet*)onSurfP; + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + onSurf->m_tetrahedra.Resize(0); + onSurf->m_scale = m_scale; + onSurf->m_numTetrahedraOnSurface = 0; + onSurf->m_numTetrahedraInsideSurface = 0; + onSurf->m_barycenter = m_barycenter; + onSurf->m_minBB = m_minBB; + onSurf->m_maxBB = m_maxBB; + for (int32_t i = 0; i < 3; ++i) { + for (int32_t j = 0; j < 3; ++j) { + onSurf->m_Q[i][j] = m_Q[i][j]; + onSurf->m_D[i][j] = m_D[i][j]; + } + } + Tetrahedron tetrahedron; + for (size_t v = 0; v < nTetrahedra; ++v) { + tetrahedron = m_tetrahedra[v]; + if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { + onSurf->m_tetrahedra.PushBack(tetrahedron); + ++onSurf->m_numTetrahedraOnSurface; + } + } +} +void TetrahedronSet::Clip(const Plane& plane, + PrimitiveSet* const positivePartP, + PrimitiveSet* const negativePartP) const +{ + TetrahedronSet* const positivePart = (TetrahedronSet*)positivePartP; + TetrahedronSet* const negativePart = (TetrahedronSet*)negativePartP; + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + positivePart->m_tetrahedra.Resize(0); + negativePart->m_tetrahedra.Resize(0); + positivePart->m_tetrahedra.Allocate(nTetrahedra); + negativePart->m_tetrahedra.Allocate(nTetrahedra); + negativePart->m_scale = positivePart->m_scale = m_scale; + negativePart->m_numTetrahedraOnSurface = positivePart->m_numTetrahedraOnSurface = 0; + negativePart->m_numTetrahedraInsideSurface = positivePart->m_numTetrahedraInsideSurface = 0; + negativePart->m_barycenter = m_barycenter; + positivePart->m_barycenter = m_barycenter; + negativePart->m_minBB = m_minBB; + positivePart->m_minBB = m_minBB; + negativePart->m_maxBB = m_maxBB; + positivePart->m_maxBB = m_maxBB; + for (int32_t i = 0; i < 3; ++i) { + for (int32_t j = 0; j < 3; ++j) { + negativePart->m_Q[i][j] = positivePart->m_Q[i][j] = m_Q[i][j]; + negativePart->m_D[i][j] = positivePart->m_D[i][j] = m_D[i][j]; + } + } + + Tetrahedron tetrahedron; + double delta, alpha; + int32_t sign[4]; + int32_t npos, nneg; + Vec3<double> posPts[10]; + Vec3<double> negPts[10]; + Vec3<double> P0, P1, M; + const Vec3<double> n(plane.m_a, plane.m_b, plane.m_c); + const int32_t edges[6][2] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 2 }, { 1, 3 }, { 2, 3 } }; + double dist; + for (size_t v = 0; v < nTetrahedra; ++v) { + tetrahedron = m_tetrahedra[v]; + npos = nneg = 0; + for (int32_t i = 0; i < 4; ++i) { + dist = plane.m_a * tetrahedron.m_pts[i][0] + plane.m_b * tetrahedron.m_pts[i][1] + plane.m_c * tetrahedron.m_pts[i][2] + plane.m_d; + if (dist > 0.0) { + sign[i] = 1; + posPts[npos] = tetrahedron.m_pts[i]; + ++npos; + } + else { + sign[i] = -1; + negPts[nneg] = tetrahedron.m_pts[i]; + ++nneg; + } + } + + if (npos == 4) { + positivePart->Add(tetrahedron); + if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { + ++positivePart->m_numTetrahedraOnSurface; + } + else { + ++positivePart->m_numTetrahedraInsideSurface; + } + } + else if (nneg == 4) { + negativePart->Add(tetrahedron); + if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { + ++negativePart->m_numTetrahedraOnSurface; + } + else { + ++negativePart->m_numTetrahedraInsideSurface; + } + } + else { + int32_t nnew = 0; + for (int32_t j = 0; j < 6; ++j) { + if (sign[edges[j][0]] * sign[edges[j][1]] == -1) { + P0 = tetrahedron.m_pts[edges[j][0]]; + P1 = tetrahedron.m_pts[edges[j][1]]; + delta = (P0 - P1) * n; + alpha = -(plane.m_d + (n * P1)) / delta; + assert(alpha >= 0.0 && alpha <= 1.0); + M = alpha * P0 + (1 - alpha) * P1; + for (int32_t xx = 0; xx < 3; ++xx) { + assert(M[xx] + EPS >= m_minBB[xx]); + assert(M[xx] <= m_maxBB[xx] + EPS); + } + posPts[npos++] = M; + negPts[nneg++] = M; + ++nnew; + } + } + negativePart->AddClippedTetrahedra(negPts, nneg); + positivePart->AddClippedTetrahedra(posPts, npos); + } + } +} +void TetrahedronSet::Convert(Mesh& mesh, const VOXEL_VALUE value) const +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + for (size_t v = 0; v < nTetrahedra; ++v) { + const Tetrahedron& tetrahedron = m_tetrahedra[v]; + if (tetrahedron.m_data == value) { + int32_t s = (int32_t)mesh.GetNPoints(); + mesh.AddPoint(tetrahedron.m_pts[0]); + mesh.AddPoint(tetrahedron.m_pts[1]); + mesh.AddPoint(tetrahedron.m_pts[2]); + mesh.AddPoint(tetrahedron.m_pts[3]); + mesh.AddTriangle(Vec3<int32_t>(s + 0, s + 1, s + 2)); + mesh.AddTriangle(Vec3<int32_t>(s + 2, s + 1, s + 3)); + mesh.AddTriangle(Vec3<int32_t>(s + 3, s + 1, s + 0)); + mesh.AddTriangle(Vec3<int32_t>(s + 3, s + 0, s + 2)); + } + } +} +const double TetrahedronSet::ComputeVolume() const +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return 0.0; + double volume = 0.0; + for (size_t v = 0; v < nTetrahedra; ++v) { + const Tetrahedron& tetrahedron = m_tetrahedra[v]; + volume += fabs(ComputeVolume4(tetrahedron.m_pts[0], tetrahedron.m_pts[1], tetrahedron.m_pts[2], tetrahedron.m_pts[3])); + } + return volume / 6.0; +} +const double TetrahedronSet::ComputeMaxVolumeError() const +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return 0.0; + double volume = 0.0; + for (size_t v = 0; v < nTetrahedra; ++v) { + const Tetrahedron& tetrahedron = m_tetrahedra[v]; + if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { + volume += fabs(ComputeVolume4(tetrahedron.m_pts[0], tetrahedron.m_pts[1], tetrahedron.m_pts[2], tetrahedron.m_pts[3])); + } + } + return volume / 6.0; +} +void TetrahedronSet::RevertAlignToPrincipalAxes() +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + double x, y, z; + for (size_t v = 0; v < nTetrahedra; ++v) { + Tetrahedron& tetrahedron = m_tetrahedra[v]; + for (int32_t i = 0; i < 4; ++i) { + x = tetrahedron.m_pts[i][0] - m_barycenter[0]; + y = tetrahedron.m_pts[i][1] - m_barycenter[1]; + z = tetrahedron.m_pts[i][2] - m_barycenter[2]; + tetrahedron.m_pts[i][0] = m_Q[0][0] * x + m_Q[0][1] * y + m_Q[0][2] * z + m_barycenter[0]; + tetrahedron.m_pts[i][1] = m_Q[1][0] * x + m_Q[1][1] * y + m_Q[1][2] * z + m_barycenter[1]; + tetrahedron.m_pts[i][2] = m_Q[2][0] * x + m_Q[2][1] * y + m_Q[2][2] * z + m_barycenter[2]; + } + } + ComputeBB(); +} +void TetrahedronSet::ComputePrincipalAxes() +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + double covMat[3][3] = { { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 } }; + double x, y, z; + for (size_t v = 0; v < nTetrahedra; ++v) { + Tetrahedron& tetrahedron = m_tetrahedra[v]; + for (int32_t i = 0; i < 4; ++i) { + x = tetrahedron.m_pts[i][0] - m_barycenter[0]; + y = tetrahedron.m_pts[i][1] - m_barycenter[1]; + z = tetrahedron.m_pts[i][2] - m_barycenter[2]; + covMat[0][0] += x * x; + covMat[1][1] += y * y; + covMat[2][2] += z * z; + covMat[0][1] += x * y; + covMat[0][2] += x * z; + covMat[1][2] += y * z; + } + } + double n = nTetrahedra * 4.0; + covMat[0][0] /= n; + covMat[1][1] /= n; + covMat[2][2] /= n; + covMat[0][1] /= n; + covMat[0][2] /= n; + covMat[1][2] /= n; + covMat[1][0] = covMat[0][1]; + covMat[2][0] = covMat[0][2]; + covMat[2][1] = covMat[1][2]; + Diagonalize(covMat, m_Q, m_D); +} +void TetrahedronSet::AlignToPrincipalAxes() +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + double x, y, z; + for (size_t v = 0; v < nTetrahedra; ++v) { + Tetrahedron& tetrahedron = m_tetrahedra[v]; + for (int32_t i = 0; i < 4; ++i) { + x = tetrahedron.m_pts[i][0] - m_barycenter[0]; + y = tetrahedron.m_pts[i][1] - m_barycenter[1]; + z = tetrahedron.m_pts[i][2] - m_barycenter[2]; + tetrahedron.m_pts[i][0] = m_Q[0][0] * x + m_Q[1][0] * y + m_Q[2][0] * z + m_barycenter[0]; + tetrahedron.m_pts[i][1] = m_Q[0][1] * x + m_Q[1][1] * y + m_Q[2][1] * z + m_barycenter[1]; + tetrahedron.m_pts[i][2] = m_Q[0][2] * x + m_Q[1][2] * y + m_Q[2][2] * z + m_barycenter[2]; + } + } + ComputeBB(); +} +} diff --git a/thirdparty/thekla_atlas/LICENSE b/thirdparty/xatlas/LICENSE index 164e7d3a2b..94d0c60485 100644 --- a/thirdparty/thekla_atlas/LICENSE +++ b/thirdparty/xatlas/LICENSE @@ -1,5 +1,11 @@ -Copyright (c) 2013 Thekla, Inc +xatlas +https://github.com/jpcy/xatlas +Copyright (c) 2018 Jonathan Young +thekla_atlas +https://github.com/Thekla/thekla_atlas +Copyright (c) 2013 Thekla, Inc +Copyright NVIDIA Corporation 2006 -- Ignacio Castano <icastano@nvidia.com> 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: diff --git a/thirdparty/xatlas/avoid-failing-on-bad-geometry.patch b/thirdparty/xatlas/avoid-failing-on-bad-geometry.patch new file mode 100644 index 0000000000..a28cd9f82b --- /dev/null +++ b/thirdparty/xatlas/avoid-failing-on-bad-geometry.patch @@ -0,0 +1,157 @@ +diff --git a/thirdparty/xatlas/xatlas.cpp b/thirdparty/xatlas/xatlas.cpp +index df5ef94db..eb0824a51 100644 +--- a/thirdparty/xatlas/xatlas.cpp ++++ b/thirdparty/xatlas/xatlas.cpp +@@ -1276,6 +1276,9 @@ class Vertex + { + public: + uint32_t id; ++ // -- GODOT start -- ++ uint32_t original_id; ++ // -- GODOT end -- + Edge *edge; + Vertex *next; + Vertex *prev; +@@ -1283,7 +1286,10 @@ public: + Vector3 nor; + Vector2 tex; + +- Vertex(uint32_t id) : id(id), edge(NULL), pos(0.0f), nor(0.0f), tex(0.0f) ++ // -- GODOT start -- ++ //Vertex(uint32_t id) : id(id), edge(NULL), pos(0.0f), nor(0.0f), tex(0.0f) ++ Vertex(uint32_t id) : id(id), original_id(id), edge(NULL), pos(0.0f), nor(0.0f), tex(0.0f) ++ // -- GODOT end -- + { + next = this; + prev = this; +@@ -1934,6 +1940,64 @@ public: + return f; + } + ++ // -- GODOT start -- ++ 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); ++ } ++ // -- GODOT end -- ++ + // These functions disconnect the given element from the mesh and delete it. + + // @@ We must always disconnect edge pairs simultaneously. +@@ -2915,6 +2979,14 @@ Mesh *triangulate(const Mesh *inputMesh) + Vector2 p0 = polygonPoints[i0]; + Vector2 p1 = polygonPoints[i1]; + Vector2 p2 = polygonPoints[i2]; ++ ++ // -- GODOT start -- ++ bool degenerate = distance(p0, p1) < NV_EPSILON || distance(p0, p2) < NV_EPSILON || distance(p1, p2) < NV_EPSILON; ++ if (degenerate) { ++ continue; ++ } ++ // -- GODOT end -- ++ + 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); +@@ -2938,6 +3010,11 @@ Mesh *triangulate(const Mesh *inputMesh) + } + } + } ++ // -- GODOT start -- ++ if (!bestIsValid) ++ break; ++ // -- GODOT end -- ++ + xaDebugAssert(minAngle <= 2 * PI); + // Clip best ear: + uint32_t i0 = (bestEar + size - 1) % size; +@@ -5606,7 +5683,10 @@ public: + } + if (chartMeshIndices[vertex->id] == ~0) { + chartMeshIndices[vertex->id] = m_chartMesh->vertexCount(); +- m_chartToOriginalMap.push_back(vertex->id); ++ // -- GODOT start -- ++ //m_chartToOriginalMap.push_back(vertex->id); ++ m_chartToOriginalMap.push_back(vertex->original_id); ++ // -- GODOT end -- + m_chartToUnifiedMap.push_back(unifiedMeshIndices[unifiedVertex->id]); + halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos); + v->nor = vertex->nor; +@@ -5699,7 +5779,10 @@ public: + const halfedge::Vertex *vertex = it.current()->vertex; + if (chartMeshIndices[vertex->id] == ~0) { + chartMeshIndices[vertex->id] = m_chartMesh->vertexCount(); +- m_chartToOriginalMap.push_back(vertex->id); ++ // -- GODOT start -- ++ //m_chartToOriginalMap.push_back(vertex->id); ++ m_chartToOriginalMap.push_back(vertex->original_id); ++ // -- GODOT end -- + halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos); + v->nor = vertex->nor; + v->tex = vertex->tex; // @@ Not necessary. +@@ -7573,6 +7656,14 @@ AddMeshError AddMesh(Atlas *atlas, const InputMesh &mesh, bool useColocalVertice + } + } + internal::halfedge::Face *face = heMesh->addFace(tri[0], tri[1], tri[2]); ++ ++ // -- GODOT start -- ++ 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]); ++ } ++ // -- GODOT end -- ++ + if (!face) { + if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::AlreadyAddedEdge) + error.code = AddMeshErrorCode::AlreadyAddedEdge; diff --git a/thirdparty/xatlas/build-fix-limits.patch b/thirdparty/xatlas/build-fix-limits.patch new file mode 100644 index 0000000000..00d07371c0 --- /dev/null +++ b/thirdparty/xatlas/build-fix-limits.patch @@ -0,0 +1,14 @@ +diff --git a/thirdparty/xatlas/xatlas.h b/thirdparty/xatlas/xatlas.h +index 7e556c6c3..dbf8ca08c 100644 +--- a/thirdparty/xatlas/xatlas.h ++++ b/thirdparty/xatlas/xatlas.h +@@ -3,6 +3,9 @@ + #ifndef XATLAS_H + #define XATLAS_H + #include <float.h> // FLT_MAX ++// -- GODOT start -- ++#include <limits.h> // INT_MAX, UINT_MAX ++// -- GODOT end -- + + namespace xatlas { + diff --git a/thirdparty/xatlas/xatlas.cpp b/thirdparty/xatlas/xatlas.cpp index f6a9ce64dc..eb0824a517 100644 --- a/thirdparty/xatlas/xatlas.cpp +++ b/thirdparty/xatlas/xatlas.cpp @@ -1,5 +1,9 @@ // This code is in the public domain -- castanyo@yahoo.es -#include "xatlas.h" +#include <algorithm> +#include <cmath> +#include <memory> +#include <unordered_map> +#include <vector> #include <assert.h> #include <float.h> #include <math.h> @@ -8,29 +12,19 @@ #include <stdio.h> #include <string.h> #include <time.h> -#include <algorithm> -#include <cmath> -#include <memory> -#include <unordered_map> -#include <vector> +#include "xatlas.h" #undef min #undef max #ifndef xaAssert -#define xaAssert(exp) \ - if (!(exp)) { \ - xaPrint("%s %s %s\n", #exp, __FILE__, __LINE__); \ - } +#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__); \ - } +#define xaPrint(...) if (xatlas::internal::s_print) { xatlas::internal::s_print(__VA_ARGS__); } #endif #ifdef _MSC_VER @@ -43,87 +37,101 @@ #define NV_FORCEINLINE __forceinline #else #define restrict __restrict__ -#define NV_FORCEINLINE __attribute__((always_inline)) inline +#define NV_FORCEINLINE __attribute__((always_inline)) inline #endif -#define NV_UINT32_MAX 0xffffffff -#define NV_FLOAT_MAX 3.402823466e+38F +#define NV_UINT32_MAX 0xffffffff +#define NV_FLOAT_MAX 3.402823466e+38F #ifndef PI -#define PI float(3.1415926535897932384626433833) +#define PI float(3.1415926535897932384626433833) #endif -#define NV_EPSILON (0.0001f) -#define NV_NORMAL_EPSILON (0.001f) +#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) { +static int align(int x, int a) +{ return (x + a - 1) & ~(a - 1); } -static bool isAligned(int x, int a) { +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) { +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) { +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) { +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) { +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) { +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) { +NV_FORCEINLINE static int ftoi_floor(float val) +{ return (int)val; } -NV_FORCEINLINE static int ftoi_ceil(float val) { +NV_FORCEINLINE static int ftoi_ceil(float val) +{ return (int)ceilf(val); } -NV_FORCEINLINE static int ftoi_round(float f) { +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) { +static bool isZero(const float f, const float epsilon = NV_EPSILON) +{ return fabs(f) <= epsilon; } -static float lerp(float f0, float f1, float t) { +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) { +static float square(float f) +{ return f * f; } -static int square(int i) { +static int square(int i) +{ return i * i; } @@ -133,8 +141,9 @@ static int square(int i) { * @note isPowerOfTwo(x) == true -> nextPowerOfTwo(x) == x * @note nextPowerOfTwo(x) = 2 << log2(x-1) */ -static uint32_t nextPowerOfTwo(uint32_t x) { - xaDebugAssert(x != 0); +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; @@ -145,7 +154,8 @@ static uint32_t nextPowerOfTwo(uint32_t x) { return x + 1; } -static uint64_t nextPowerOfTwo(uint64_t x) { +static uint64_t nextPowerOfTwo(uint64_t x) +{ xaDebugAssert(x != 0); uint32_t p = 1; while (x > p) { @@ -154,17 +164,19 @@ static uint64_t nextPowerOfTwo(uint64_t x) { 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; +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++]; + 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) { +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; @@ -177,85 +189,93 @@ static uint32_t sdbmFloatHash(const float *f, uint32_t count, uint32_t h = 5381) } template <typename T> -static uint32_t hash(const T &t, uint32_t h = 5381) { +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) { +static uint32_t hash(const float &f, uint32_t h) +{ return sdbmFloatHash(&f, 1, h); } // Functors for hash table: -template <typename Key> -struct Hash { +template <typename Key> struct Hash +{ uint32_t operator()(const Key &k) const { return hash(k); } }; -template <typename Key> -struct Equal { +template <typename Key> struct Equal +{ bool operator()(const Key &k0, const Key &k1) const { return k0 == k1; } }; -class Vector2 { +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) { + 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; + return + *this; } const float *ptr() const { return &x; } - void set(float _x, float _y) { + void set(float _x, float _y) + { x = _x; y = _y; } - Vector2 operator-() const { + Vector2 operator-() const + { return Vector2(-x, -y); } - void operator+=(Vector2::Arg v) { + void operator+=(Vector2::Arg v) + { x += v.x; y += v.y; } - void operator-=(Vector2::Arg v) { + void operator-=(Vector2::Arg v) + { x -= v.x; y -= v.y; } - void operator*=(float s) { + void operator*=(float s) + { x *= s; y *= s; } - void operator*=(Vector2::Arg v) { + void operator*=(Vector2::Arg v) + { x *= v.x; y *= v.y; } - friend bool operator==(Vector2::Arg a, Vector2::Arg b) { + 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) { + friend bool operator!=(Vector2::Arg a, Vector2::Arg b) + { return a.x != b.x || a.y != b.y; } - union { + union + { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4201) @@ -272,52 +292,64 @@ public: }; }; -Vector2 operator+(Vector2::Arg a, Vector2::Arg b) { +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) { +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) { +Vector2 operator*(Vector2::Arg v, float s) +{ return Vector2(v.x * s, v.y * s); } -Vector2 operator*(Vector2::Arg v1, Vector2::Arg v2) { +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) { +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) { +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) { +float dot(Vector2::Arg a, Vector2::Arg b) +{ return a.x * b.x + a.y * b.y; } -float lengthSquared(Vector2::Arg v) { +float lengthSquared(Vector2::Arg v) +{ return v.x * v.x + v.y * v.y; } -float length(Vector2::Arg v) { +float length(Vector2::Arg v) +{ return sqrtf(lengthSquared(v)); } -float distance(Vector2::Arg a, Vector2::Arg b) { +float distance(Vector2::Arg a, Vector2::Arg b) +{ return length(a - b); } -bool isNormalized(Vector2::Arg v, float epsilon = NV_NORMAL_EPSILON) { +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) { +Vector2 normalize(Vector2::Arg v, float epsilon = NV_EPSILON) +{ float l = length(v); xaDebugAssert(!isZero(l, epsilon)); #ifdef NDEBUG @@ -328,7 +360,8 @@ Vector2 normalize(Vector2::Arg v, float epsilon = NV_EPSILON) { return n; } -Vector2 normalizeSafe(Vector2::Arg v, Vector2::Arg fallback, float epsilon = NV_EPSILON) { +Vector2 normalizeSafe(Vector2::Arg v, Vector2::Arg fallback, float epsilon = NV_EPSILON) +{ float l = length(v); if (isZero(l, epsilon)) { return fallback; @@ -336,23 +369,28 @@ Vector2 normalizeSafe(Vector2::Arg v, Vector2::Arg fallback, float epsilon = NV_ return v * (1.0f / l); } -bool equal(Vector2::Arg v1, Vector2::Arg v2, float epsilon = NV_EPSILON) { +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) { +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) { +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) { +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) { +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 @@ -364,105 +402,109 @@ float triangleArea(Vector2::Arg a, Vector2::Arg b, Vector2::Arg c) { return triangleArea(a - c, b - c); } -float triangleArea2(Vector2::Arg v1, Vector2::Arg v2, Vector2::Arg v3) { +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) { +static uint32_t hash(const Vector2 &v, uint32_t h) +{ return sdbmFloatHash(v.component, 2, h); } -class Vector3 { +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) { + 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 { + Vector2 xy() const + { return Vector2(x, y); } const float *ptr() const { return &x; } - void set(float _x, float _y, float _z) { + void set(float _x, float _y, float _z) + { x = _x; y = _y; z = _z; } - Vector3 operator-() const { + Vector3 operator-() const + { return Vector3(-x, -y, -z); } - void operator+=(Vector3::Arg v) { + void operator+=(Vector3::Arg v) + { x += v.x; y += v.y; z += v.z; } - void operator-=(Vector3::Arg v) { + void operator-=(Vector3::Arg v) + { x -= v.x; y -= v.y; z -= v.z; } - void operator*=(float s) { + void operator*=(float s) + { x *= s; y *= s; z *= s; } - void operator/=(float s) { + void operator/=(float s) + { float is = 1.0f / s; x *= is; y *= is; z *= is; } - void operator*=(Vector3::Arg v) { + void operator*=(Vector3::Arg v) + { x *= v.x; y *= v.y; z *= v.z; } - void operator/=(Vector3::Arg v) { + void operator/=(Vector3::Arg v) + { x /= v.x; y /= v.y; z /= v.z; } - friend bool operator==(Vector3::Arg a, Vector3::Arg b) { + 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) { + friend bool operator!=(Vector3::Arg a, Vector3::Arg b) + { return a.x != b.x || a.y != b.y || a.z != b.z; } - union { + union + { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4201) @@ -479,85 +521,106 @@ public: }; }; -Vector3 add(Vector3::Arg a, Vector3::Arg b) { +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) { +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) { +Vector3 operator+(Vector3::Arg a, Vector3::Arg b) +{ return add(a, b); } -Vector3 operator+(Vector3::Arg a, float b) { +Vector3 operator+(Vector3::Arg a, float b) +{ return add(a, b); } -Vector3 sub(Vector3::Arg a, Vector3::Arg 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) { +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) { +Vector3 operator-(Vector3::Arg a, Vector3::Arg b) +{ return sub(a, b); } -Vector3 operator-(Vector3::Arg a, float b) { +Vector3 operator-(Vector3::Arg a, float b) +{ return sub(a, b); } -Vector3 cross(Vector3::Arg a, Vector3::Arg 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) { +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) { +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) { +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) { +Vector3 operator/(Vector3::Arg v, float s) +{ return v * (1.0f / s); } -Vector3 lerp(Vector3::Arg v1, Vector3::Arg v2, float t) { +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) { +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) { +float lengthSquared(Vector3::Arg v) +{ return v.x * v.x + v.y * v.y + v.z * v.z; } -float length(Vector3::Arg v) { +float length(Vector3::Arg v) +{ return sqrtf(lengthSquared(v)); } -float distance(Vector3::Arg a, Vector3::Arg b) { +float distance(Vector3::Arg a, Vector3::Arg b) +{ return length(a - b); } -float distanceSquared(Vector3::Arg a, Vector3::Arg b) { +float distanceSquared(Vector3::Arg a, Vector3::Arg b) +{ return lengthSquared(a - b); } -bool isNormalized(Vector3::Arg v, float epsilon = NV_NORMAL_EPSILON) { +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) { +Vector3 normalize(Vector3::Arg v, float epsilon = NV_EPSILON) +{ float l = length(v); xaDebugAssert(!isZero(l, epsilon)); #ifdef NDEBUG @@ -568,7 +631,8 @@ Vector3 normalize(Vector3::Arg v, float epsilon = NV_EPSILON) { return n; } -Vector3 normalizeSafe(Vector3::Arg v, Vector3::Arg fallback, float epsilon = NV_EPSILON) { +Vector3 normalizeSafe(Vector3::Arg v, Vector3::Arg fallback, float epsilon = NV_EPSILON) +{ float l = length(v); if (isZero(l, epsilon)) { return fallback; @@ -576,49 +640,56 @@ Vector3 normalizeSafe(Vector3::Arg v, Vector3::Arg fallback, float epsilon = NV_ return v * (1.0f / l); } -bool equal(Vector3::Arg v1, Vector3::Arg v2, float epsilon = NV_EPSILON) { +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) { +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) { +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) { +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) { +Vector3 saturate(Vector3::Arg v) +{ return Vector3(saturate(v.x), saturate(v.y), saturate(v.z)); } -Vector3 floor(Vector3::Arg v) { +Vector3 floor(Vector3::Arg v) +{ return Vector3(floorf(v.x), floorf(v.y), floorf(v.z)); } -bool isFinite(Vector3::Arg v) { +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) { +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 { +class Basis +{ public: /// Create a null basis. - Basis() : - tangent(0, 0, 0), - bitangent(0, 0, 0), - normal(0, 0, 0) {} + Basis() : tangent(0, 0, 0), bitangent(0, 0, 0), normal(0, 0, 0) {} - void buildFrameForDirection(Vector3::Arg d, float angle = 0) { + void buildFrameForDirection(Vector3::Arg d, float angle = 0) + { xaAssert(isNormalized(d)); normal = d; // Choose minimum axis. @@ -649,65 +720,76 @@ public: }; // Simple bit array. -class BitArray { +class BitArray +{ public: - BitArray() : - m_size(0) {} - BitArray(uint32_t sz) { + BitArray() : m_size(0) {} + BitArray(uint32_t sz) + { resize(sz); } - uint32_t size() const { + uint32_t size() const + { return m_size; } - void clear() { + void clear() + { resize(0); } - void resize(uint32_t new_size) { + void resize(uint32_t new_size) + { m_size = new_size; - m_wordArray.resize((m_size + 31) >> 5); + m_wordArray.resize( (m_size + 31) >> 5 ); } /// Get bit. - bool bitAt(uint32_t b) const { - xaDebugAssert(b < m_size); + 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) { + void setBitAt(uint32_t idx) + { xaDebugAssert(idx < m_size); - m_wordArray[idx >> 5] |= (1 << (idx & 31)); + m_wordArray[idx >> 5] |= (1 << (idx & 31)); } // Toggle a bit. - void toggleBitAt(uint32_t idx) { + 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) { + 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)); + 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)); + 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) { + uint32_t setBits(uint32_t w, uint32_t m, bool b) + { return (w & ~m) | (-int(b) & m); } @@ -719,29 +801,26 @@ private: }; /// Bit map. This should probably be called BitImage. -class BitMap { +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 { + 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 { + uint32_t height() const + { return m_height; } - void resize(uint32_t w, uint32_t h, bool initValue) { + void resize(uint32_t w, uint32_t h, bool initValue) + { BitArray tmp(w * h); - if (initValue) - tmp.setAll(); - else - tmp.clearAll(); + 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++) { @@ -754,17 +833,20 @@ public: m_height = h; } - bool bitAt(uint32_t x, uint32_t y) const { + 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) { + void setBitAt(uint32_t x, uint32_t y) + { xaDebugAssert(x < m_width && y < m_height); m_bitArray.setBitAt(y * m_width + x); } - void clearAll() { + void clearAll() + { m_bitArray.clearAll(); } @@ -775,39 +857,41 @@ private: }; // Axis Aligned Bounding Box. -class 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 { + 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() { + 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 { + Vector3 extents() const + { return (maxCorner - minCorner) * 0.5f; } // Add a point to this box. - void addPointToBounds(const Vector3 &p) { + void addPointToBounds(const Vector3 &p) + { minCorner = min(minCorner, p); maxCorner = max(maxCorner, p); } // Get the volume of the box. - float volume() const { + float volume() const + { Vector3 d = extents(); return 8.0f * (d.x * d.y * d.z); } @@ -816,9 +900,11 @@ public: Vector3 maxCorner; }; -class Fit { +class Fit +{ public: - static Vector3 computeCentroid(int n, const Vector3 *__restrict points) { + static Vector3 computeCentroid(int n, const Vector3 *__restrict points) + { Vector3 centroid(0.0f); for (int i = 0; i < n; i++) { centroid += points[i]; @@ -827,7 +913,8 @@ public: return centroid; } - static Vector3 computeCovariance(int n, const Vector3 *__restrict points, float *__restrict covariance) { + static Vector3 computeCovariance(int n, const Vector3 *__restrict points, float *__restrict covariance) + { // compute the centroid Vector3 centroid = computeCentroid(n, points); // compute covariance matrix @@ -846,7 +933,8 @@ public: return centroid; } - static bool isPlanar(int n, const Vector3 *points, float epsilon = NV_EPSILON) { + static bool isPlanar(int n, const Vector3 *points, float epsilon = NV_EPSILON) + { // compute the centroid and covariance float matrix[6]; computeCovariance(n, points, matrix); @@ -861,7 +949,8 @@ public: // 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]) { + 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]; @@ -886,7 +975,7 @@ public: // 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]; + eigenVectors[j].component[i] = (float) work[i][j]; } } // shuffle to sort by singular value : @@ -908,7 +997,8 @@ public: } private: - static void EigenSolver3_Tridiagonal(float mat[3][3], float *diag, float *subd) { + 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 @@ -960,7 +1050,8 @@ private: } } - static bool EigenSolver3_QLAlgorithm(float mat[3][3], float *diag, float *subd) { + 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; @@ -970,21 +1061,21 @@ private: int m; for (m = ell; m <= 1; m++) { float dd = fabsf(diag[m]) + fabsf(diag[m + 1]); - if (fabsf(subd[m]) + dd == dd) + if ( fabsf(subd[m]) + dd == dd ) break; } - if (m == ell) + if ( m == ell ) break; float g = (diag[ell + 1] - diag[ell]) / (2 * subd[ell]); float r = sqrtf(g * g + 1); - if (g < 0) + 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)) { + if ( fabsf(f) >= fabsf(g) ) { c = g / f; r = sqrtf(c * c + 1); subd[i + 1] = f * r; @@ -1010,7 +1101,7 @@ private: subd[ell] = g; subd[m] = 0; } - if (iter == maxiter) + if ( iter == maxiter ) // should not get here under normal circumstances return false; } @@ -1019,30 +1110,33 @@ private: }; /// Fixed size vector class. -class FullVector { +class FullVector +{ public: FullVector(uint32_t dim) { m_array.resize(dim); } - FullVector(const FullVector &v) : - m_array(v.m_array) {} + FullVector(const FullVector &v) : m_array(v.m_array) {} - const FullVector &operator=(const FullVector &v) { + 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]; } + const float &operator[]( uint32_t index ) const { return m_array[index]; } + float &operator[] ( uint32_t index ) { return m_array[index]; } - void fill(float f) { + 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) { + void operator+=(const FullVector &v) + { xaDebugAssert(dimension() == v.dimension()); const uint32_t dim = dimension(); for (uint32_t i = 0; i < dim; i++) { @@ -1050,7 +1144,8 @@ public: } } - void operator-=(const FullVector &v) { + void operator-=(const FullVector &v) + { xaDebugAssert(dimension() == v.dimension()); const uint32_t dim = dimension(); for (uint32_t i = 0; i < dim; i++) { @@ -1058,7 +1153,8 @@ public: } } - void operator*=(const FullVector &v) { + void operator*=(const FullVector &v) + { xaDebugAssert(dimension() == v.dimension()); const uint32_t dim = dimension(); for (uint32_t i = 0; i < dim; i++) { @@ -1066,21 +1162,24 @@ public: } } - void operator+=(float 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) { + 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) { + void operator*=(float f) + { const uint32_t dim = dimension(); for (uint32_t i = 0; i < dim; i++) { m_array[i] *= f; @@ -1095,66 +1194,70 @@ namespace halfedge { class Face; class Vertex; -class Edge { +class Edge +{ public: uint32_t id; Edge *next; - Edge *prev; // This is not strictly half-edge, but makes algorithms easier and faster. + 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) {} + Edge(uint32_t id) : id(id), next(NULL), prev(NULL), pair(NULL), vertex(NULL), face(NULL) {} // Vertex queries. - const Vertex *from() const { + const Vertex *from() const + { return vertex; } - Vertex *from() { + 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. + 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() { + Vertex *to() + { return pair->vertex; } // Edge queries. - void setNext(Edge *e) { + void setNext(Edge *e) + { next = e; if (e != NULL) e->prev = this; } - void setPrev(Edge *e) { + 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 { + bool isBoundary() const + { return !(face && pair->face); } // @@ This is not exactly accurate, we should compare the texture coordinates... - bool isSeam() const { + bool isSeam() const + { return vertex != pair->next->vertex || next->vertex != pair->vertex; } bool isNormalSeam() const; bool isTextureSeam() const; - bool isValid() 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; @@ -1169,10 +1272,13 @@ public: float angle() const; }; -class Vertex { +class Vertex +{ public: uint32_t id; + // -- GODOT start -- uint32_t original_id; + // -- GODOT end -- Edge *edge; Vertex *next; Vertex *prev; @@ -1180,36 +1286,38 @@ public: Vector3 nor; Vector2 tex; - Vertex(uint32_t id) : - id(id), - original_id(id), - edge(NULL), - pos(0.0f), - nor(0.0f), - tex(0.0f) { + // -- GODOT start -- + //Vertex(uint32_t id) : id(id), edge(NULL), pos(0.0f), nor(0.0f), tex(0.0f) + Vertex(uint32_t id) : id(id), original_id(id), edge(NULL), pos(0.0f), nor(0.0f), tex(0.0f) + // -- GODOT end -- + { next = this; prev = this; } // Set first edge of all colocals. - void setEdge(Edge *e) { + 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) { + void setPos(const Vector3 &p) + { for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { it.current()->pos = p; } } - bool isFirstColocal() const { + bool isFirstColocal() const + { return firstColocal() == this; } - const Vertex *firstColocal() const { + const Vertex *firstColocal() const + { uint32_t firstId = id; const Vertex *vertex = this; for (ConstVertexIterator it(colocals()); !it.isDone(); it.advance()) { @@ -1221,7 +1329,8 @@ public: return vertex; } - Vertex *firstColocal() { + Vertex *firstColocal() + { Vertex *vertex = this; uint32_t firstId = id; for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { @@ -1233,7 +1342,8 @@ public: return vertex; } - bool isColocal(const Vertex *v) const { + 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()) { @@ -1244,13 +1354,15 @@ public: return false; } - void linkColocal(Vertex *v) { + void linkColocal(Vertex *v) + { next->prev = v; v->next = next; next = v; v->prev = this; } - void unlinkColocal() { + void unlinkColocal() + { next->prev = prev; prev->next = next; next = this; @@ -1258,7 +1370,8 @@ public: } // @@ Note: This only works if linkBoundary has been called. - bool isBoundary() const { + bool isBoundary() const + { return (edge && !edge->face); } @@ -1266,23 +1379,25 @@ public: class EdgeIterator //: public Iterator<Edge *> { public: - EdgeIterator(Edge *e) : - m_end(NULL), - m_current(e) {} + EdgeIterator(Edge *e) : m_end(NULL), m_current(e) { } - virtual void advance() { + 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 { + virtual bool isDone() const + { return m_end == m_current; } - virtual Edge *current() const { + virtual Edge *current() const + { return m_current; } - Vertex *vertex() const { + Vertex *vertex() const + { return m_current->vertex; } @@ -1291,10 +1406,12 @@ public: Edge *m_current; }; - EdgeIterator edges() { + EdgeIterator edges() + { return EdgeIterator(edge); } - EdgeIterator edges(Edge *e) { + EdgeIterator edges(Edge *e) + { return EdgeIterator(e); } @@ -1302,26 +1419,26 @@ public: 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() { + 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 { + virtual bool isDone() const + { return m_end == m_current; } - virtual const Edge *current() const { + virtual const Edge *current() const + { return m_current; } - const Vertex *vertex() const { + const Vertex *vertex() const + { return m_current->to(); } @@ -1330,10 +1447,12 @@ public: const Edge *m_current; }; - ConstEdgeIterator edges() const { + ConstEdgeIterator edges() const + { return ConstEdgeIterator(edge); } - ConstEdgeIterator edges(const Edge *e) const { + ConstEdgeIterator edges(const Edge *e) const + { return ConstEdgeIterator(e); } @@ -1341,19 +1460,20 @@ public: class VertexIterator //: public Iterator<Edge *> { public: - VertexIterator(Vertex *v) : - m_end(NULL), - m_current(v) {} + VertexIterator(Vertex *v) : m_end(NULL), m_current(v) { } - virtual void advance() { + virtual void advance() + { if (m_end == NULL) m_end = m_current; m_current = m_current->next; } - virtual bool isDone() const { + virtual bool isDone() const + { return m_end == m_current; } - virtual Vertex *current() const { + virtual Vertex *current() const + { return m_current; } @@ -1362,7 +1482,8 @@ public: Vertex *m_current; }; - VertexIterator colocals() { + VertexIterator colocals() + { return VertexIterator(this); } @@ -1370,19 +1491,20 @@ public: class ConstVertexIterator //: public Iterator<Edge *> { public: - ConstVertexIterator(const Vertex *v) : - m_end(NULL), - m_current(v) {} + ConstVertexIterator(const Vertex *v) : m_end(NULL), m_current(v) { } - virtual void advance() { + virtual void advance() + { if (m_end == NULL) m_end = m_current; m_current = m_current->next; } - virtual bool isDone() const { + virtual bool isDone() const + { return m_end == m_current; } - virtual const Vertex *current() const { + virtual const Vertex *current() const + { return m_current; } @@ -1391,24 +1513,29 @@ public: const Vertex *m_current; }; - ConstVertexIterator colocals() const { + ConstVertexIterator colocals() const + { return ConstVertexIterator(this); } }; -bool Edge::isNormalSeam() const { +bool Edge::isNormalSeam() const +{ return (vertex->nor != pair->next->vertex->nor || next->vertex->nor != pair->vertex->nor); } -bool Edge::isTextureSeam() const { +bool Edge::isTextureSeam() const +{ return (vertex->tex != pair->next->vertex->tex || next->vertex->tex != pair->vertex->tex); } -float Edge::length() const { +float Edge::length() const +{ return internal::length(to()->pos - from()->pos); } -float Edge::angle() const { +float Edge::angle() const +{ Vector3 p = vertex->pos; Vector3 a = prev->vertex->pos; Vector3 b = next->vertex->pos; @@ -1417,20 +1544,18 @@ float Edge::angle() const { return acosf(dot(v0, v1) / (internal::length(v0) * internal::length(v1))); } -class Face { +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) {} + Face(uint32_t id) : id(id), group(uint16_t(~0)), material(uint16_t(~0)), edge(NULL) {} - float area() const { + float area() const + { float area = 0; const Vector3 &v0 = edge->from()->pos; for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance()) { @@ -1442,7 +1567,8 @@ public: return area * 0.5f; } - float parametricArea() const { + float parametricArea() const + { float area = 0; const Vector2 &v0 = edge->from()->tex; for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance()) { @@ -1454,7 +1580,8 @@ public: return area * 0.5f; } - Vector3 normal() const { + Vector3 normal() const + { Vector3 n(0); const Vertex *vertex0 = NULL; for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { @@ -1476,7 +1603,8 @@ public: return normalizeSafe(n, Vector3(0, 0, 1), 0.0f); } - Vector3 centroid() const { + Vector3 centroid() const + { Vector3 sum(0.0f); uint32_t count = 0; for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { @@ -1488,7 +1616,8 @@ public: } // Unnormalized face normal assuming it's a triangle. - Vector3 triangleNormal() const { + Vector3 triangleNormal() const + { Vector3 p0 = edge->vertex->pos; Vector3 p1 = edge->next->vertex->pos; Vector3 p2 = edge->next->next->vertex->pos; @@ -1497,7 +1626,8 @@ public: return normalizeSafe(cross(e0, e1), Vector3(0), 0.0f); } - Vector3 triangleNormalAreaScaled() const { + Vector3 triangleNormalAreaScaled() const + { Vector3 p0 = edge->vertex->pos; Vector3 p1 = edge->next->vertex->pos; Vector3 p2 = edge->next->next->vertex->pos; @@ -1508,7 +1638,8 @@ public: // 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 triangleCenter() const + { Vector3 p0 = edge->vertex->pos; Vector3 p1 = edge->next->vertex->pos; Vector3 p2 = edge->next->next->vertex->pos; @@ -1521,7 +1652,8 @@ public: return m0 + m1 + m2; } - bool isValid() const { + bool isValid() const + { uint32_t count = 0; for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { const Edge *e = it.current(); @@ -1534,14 +1666,16 @@ public: return true; } - bool contains(const Edge *e) const { + 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 edgeCount() const + { uint32_t count = 0; for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { ++count; @@ -1553,22 +1687,24 @@ public: class EdgeIterator //: public Iterator<Edge *> { public: - EdgeIterator(Edge *e) : - m_end(NULL), - m_current(e) {} + EdgeIterator(Edge *e) : m_end(NULL), m_current(e) { } - virtual void advance() { + virtual void advance() + { if (m_end == NULL) m_end = m_current; m_current = m_current->next; } - virtual bool isDone() const { + virtual bool isDone() const + { return m_end == m_current; } - virtual Edge *current() const { + virtual Edge *current() const + { return m_current; } - Vertex *vertex() const { + Vertex *vertex() const + { return m_current->vertex; } @@ -1577,10 +1713,12 @@ public: Edge *m_current; }; - EdgeIterator edges() { + EdgeIterator edges() + { return EdgeIterator(edge); } - EdgeIterator edges(Edge *e) { + EdgeIterator edges(Edge *e) + { xaDebugAssert(contains(e)); return EdgeIterator(e); } @@ -1589,25 +1727,25 @@ public: 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() { + 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 { + virtual bool isDone() const + { return m_end == m_current; } - virtual const Edge *current() const { + virtual const Edge *current() const + { return m_current; } - const Vertex *vertex() const { + const Vertex *vertex() const + { return m_current->vertex; } @@ -1616,22 +1754,25 @@ public: const Edge *m_current; }; - ConstEdgeIterator edges() const { + ConstEdgeIterator edges() const + { return ConstEdgeIterator(edge); } - ConstEdgeIterator edges(const Edge *e) const { + ConstEdgeIterator edges(const Edge *e) const + { xaDebugAssert(contains(e)); return ConstEdgeIterator(e); } }; /// Simple half edge mesh designed for dynamic mesh manipulation. -class Mesh { +class Mesh +{ public: - Mesh() : - m_colocalVertexCount(0) {} + Mesh() : m_colocalVertexCount(0) {} - Mesh(const Mesh *mesh) { + Mesh(const Mesh *mesh) + { // Copy mesh vertices. const uint32_t vertexCount = mesh->vertexCount(); m_vertexArray.resize(vertexCount); @@ -1659,11 +1800,13 @@ public: } } - ~Mesh() { + ~Mesh() + { clear(); } - void clear() { + void clear() + { for (size_t i = 0; i < m_vertexArray.size(); i++) delete m_vertexArray[i]; m_vertexArray.clear(); @@ -1676,7 +1819,8 @@ public: m_faceArray.clear(); } - Vertex *addVertex(const Vector3 &pos) { + Vertex *addVertex(const Vector3 &pos) + { xaDebugAssert(isFinite(pos)); Vertex *v = new Vertex(m_vertexArray.size()); v->pos = pos; @@ -1685,7 +1829,8 @@ public: } /// Link colocal vertices based on geometric location only. - void linkColocals() { + void linkColocals() + { xaPrint("--- Linking colocals:\n"); const uint32_t vertexCount = this->vertexCount(); std::unordered_map<Vector3, Vertex *, Hash<Vector3>, Equal<Vector3> > vertexMap; @@ -1704,7 +1849,8 @@ public: // @@ Remove duplicated vertices? or just leave them as colocals? } - void linkColocalsWithCanonicalMap(const std::vector<uint32_t> &canonicalMap) { + 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++) { @@ -1728,13 +1874,15 @@ public: xaPrint("--- %d vertex positions.\n", m_colocalVertexCount); } - Face *addFace() { + 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) { + Face *addFace(uint32_t v0, uint32_t v1, uint32_t v2) + { uint32_t indexArray[3]; indexArray[0] = v0; indexArray[1] = v1; @@ -1742,6 +1890,57 @@ public: 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; + } + + // -- GODOT start -- Face *addUniqueFace(uint32_t v0, uint32_t v1, uint32_t v2) { int base_vertex = m_vertexArray.size(); @@ -1797,59 +1996,13 @@ public: 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; - } + // -- GODOT end -- // These functions disconnect the given element from the mesh and delete it. // @@ We must always disconnect edge pairs simultaneously. - void disconnect(Edge *edge) { + void disconnect(Edge *edge) + { xaDebugAssert(edge != NULL); // Remove from edge list. if ((edge->id & 1) == 0) { @@ -1905,13 +2058,15 @@ public: } } - void remove(Edge *edge) { + void remove(Edge *edge) + { xaDebugAssert(edge != NULL); disconnect(edge); delete edge; } - void remove(Vertex *vertex) { + void remove(Vertex *vertex) + { xaDebugAssert(vertex != NULL); // Remove from vertex list. m_vertexArray[vertex->id] = NULL; @@ -1929,7 +2084,8 @@ public: delete vertex; } - void remove(Face *face) { + void remove(Face *face) + { xaDebugAssert(face != NULL); // Remove from face list. m_faceArray[face->id] = NULL; @@ -1943,7 +2099,8 @@ public: } // Triangulate in place. - void triangulate() { + void triangulate() + { bool all_triangles = true; const uint32_t faceCount = m_faceArray.size(); for (uint32_t f = 0; f < faceCount; f++) { @@ -1984,7 +2141,8 @@ public: } /// Link boundary edges once the mesh has been created. - void linkBoundary() { + void linkBoundary() + { xaPrint("--- Linking boundaries:\n"); int num = 0; // Create boundary edges. @@ -2058,7 +2216,7 @@ public: 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? + vertex->linkColocal(splitVertex); // @@ Should we do this here? splitCount++; } } @@ -2071,59 +2229,70 @@ public: } // Vertices - uint32_t vertexCount() const { + uint32_t vertexCount() const + { return m_vertexArray.size(); } - const Vertex *vertexAt(int i) const { + const Vertex *vertexAt(int i) const + { return m_vertexArray[i]; } - Vertex *vertexAt(int i) { + Vertex *vertexAt(int i) + { return m_vertexArray[i]; } - uint32_t colocalVertexCount() const { + uint32_t colocalVertexCount() const + { return m_colocalVertexCount; } // Faces - uint32_t faceCount() const { + uint32_t faceCount() const + { return m_faceArray.size(); } - const Face *faceAt(int i) const { + const Face *faceAt(int i) const + { return m_faceArray[i]; } - Face *faceAt(int i) { + Face *faceAt(int i) + { return m_faceArray[i]; } // Edges - uint32_t edgeCount() const { + uint32_t edgeCount() const + { return m_edgeArray.size(); } - const Edge *edgeAt(int i) const { + const Edge *edgeAt(int i) const + { return m_edgeArray[i]; } - Edge *edgeAt(int i) { + Edge *edgeAt(int i) + { return m_edgeArray[i]; } class ConstVertexIterator; - class VertexIterator { + class VertexIterator + { friend class ConstVertexIterator; - public: - VertexIterator(Mesh *mesh) : - m_mesh(mesh), - m_current(0) {} + VertexIterator(Mesh *mesh) : m_mesh(mesh), m_current(0) { } - virtual void advance() { + virtual void advance() + { m_current++; } - virtual bool isDone() const { + virtual bool isDone() const + { return m_current == m_mesh->vertexCount(); } - virtual Vertex *current() const { + virtual Vertex *current() const + { return m_mesh->vertexAt(m_current); } @@ -2131,26 +2300,27 @@ public: halfedge::Mesh *m_mesh; uint32_t m_current; }; - VertexIterator vertices() { + VertexIterator vertices() + { return VertexIterator(this); } - class ConstVertexIterator { + 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() { + 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 { + virtual bool isDone() const + { return m_current == m_mesh->vertexCount(); } - virtual const Vertex *current() const { + virtual const Vertex *current() const + { return m_mesh->vertexAt(m_current); } @@ -2158,27 +2328,29 @@ public: const halfedge::Mesh *m_mesh; uint32_t m_current; }; - ConstVertexIterator vertices() const { + ConstVertexIterator vertices() const + { return ConstVertexIterator(this); } class ConstFaceIterator; - class FaceIterator { + class FaceIterator + { friend class ConstFaceIterator; - public: - FaceIterator(Mesh *mesh) : - m_mesh(mesh), - m_current(0) {} + FaceIterator(Mesh *mesh) : m_mesh(mesh), m_current(0) { } - virtual void advance() { + virtual void advance() + { m_current++; } - virtual bool isDone() const { + virtual bool isDone() const + { return m_current == m_mesh->faceCount(); } - virtual Face *current() const { + virtual Face *current() const + { return m_mesh->faceAt(m_current); } @@ -2186,26 +2358,27 @@ public: halfedge::Mesh *m_mesh; uint32_t m_current; }; - FaceIterator faces() { + FaceIterator faces() + { return FaceIterator(this); } - class ConstFaceIterator { + 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() { + 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 { + virtual bool isDone() const + { return m_current == m_mesh->faceCount(); } - virtual const Face *current() const { + virtual const Face *current() const + { return m_mesh->faceAt(m_current); } @@ -2213,27 +2386,29 @@ public: const halfedge::Mesh *m_mesh; uint32_t m_current; }; - ConstFaceIterator faces() const { + ConstFaceIterator faces() const + { return ConstFaceIterator(this); } class ConstEdgeIterator; - class EdgeIterator { + class EdgeIterator + { friend class ConstEdgeIterator; - public: - EdgeIterator(Mesh *mesh) : - m_mesh(mesh), - m_current(0) {} + EdgeIterator(Mesh *mesh) : m_mesh(mesh), m_current(0) { } - virtual void advance() { + virtual void advance() + { m_current++; } - virtual bool isDone() const { + virtual bool isDone() const + { return m_current == m_mesh->edgeCount(); } - virtual Edge *current() const { + virtual Edge *current() const + { return m_mesh->edgeAt(m_current); } @@ -2241,26 +2416,27 @@ public: halfedge::Mesh *m_mesh; uint32_t m_current; }; - EdgeIterator edges() { + EdgeIterator edges() + { return EdgeIterator(this); } - class ConstEdgeIterator { + 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() { + 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 { + virtual bool isDone() const + { return m_current == m_mesh->edgeCount(); } - virtual const Edge *current() const { + virtual const Edge *current() const + { return m_mesh->edgeAt(m_current); } @@ -2268,13 +2444,15 @@ public: const halfedge::Mesh *m_mesh; uint32_t m_current; }; - ConstEdgeIterator edges() const { + ConstEdgeIterator edges() const + { return ConstEdgeIterator(this); } // @@ Add half-edge iterator. - bool isValid() const { + bool isValid() const + { // Make sure all edges are valid. const uint32_t edgeCount = m_edgeArray.size(); for (uint32_t e = 0; e < edgeCount; e++) { @@ -2300,9 +2478,11 @@ public: } // Error status: - - struct ErrorCode { - enum Enum { + + struct ErrorCode + { + enum Enum + { AlreadyAddedEdge, DegenerateColocalEdge, DegenerateEdge, @@ -2316,11 +2496,13 @@ public: 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 { + 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 { + 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]; @@ -2347,7 +2529,8 @@ private: } // Return true if the edge doesn't exist or doesn't have any adjacent face. - bool canAddEdge(uint32_t i, uint32_t j) const { + bool canAddEdge(uint32_t i, uint32_t j) const + { if (i == j) { // Skip degenerate edges. errorCode = ErrorCode::DegenerateEdge; @@ -2373,7 +2556,8 @@ private: return true; } - Edge *addEdge(uint32_t i, uint32_t j) { + Edge *addEdge(uint32_t i, uint32_t j) + { xaAssert(i != j); Edge *edge = findEdge(i, j); if (edge != NULL) { @@ -2406,7 +2590,8 @@ private: } /// Find edge, test all colocals. - Edge *findEdge(uint32_t i, uint32_t j) const { + Edge *findEdge(uint32_t i, uint32_t j) const + { Edge *edge = NULL; const Vertex *v0 = vertexAt(i); const Vertex *v1 = vertexAt(j); @@ -2418,9 +2603,9 @@ private: auto edgeIt = m_edgeMap.find(key); if (edgeIt != m_edgeMap.end()) edge = (*edgeIt).second; -#if !defined(_DEBUG) + #if !defined(_DEBUG) if (edge != NULL) return edge; -#endif + #endif } else { // Make sure that only one edge is found. xaDebugAssert(m_edgeMap.find(key) == m_edgeMap.end()); @@ -2431,7 +2616,8 @@ private: } /// Link this boundary edge. - void linkBoundaryEdge(Edge *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; @@ -2451,7 +2637,8 @@ private: } } - Vertex *splitBoundaryEdge(Edge *edge, float t, const Vector3 &pos) { + Vertex *splitBoundaryEdge(Edge *edge, float t, const Vector3 &pos) + { /* We want to go from this configuration: @@ -2521,19 +2708,18 @@ private: std::vector<Edge *> m_edgeArray; std::vector<Face *> m_faceArray; - struct Key { + 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) { + 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 { + bool operator==(const Key &k) const + { return p0 == k.p0 && p1 == k.p1; } @@ -2546,48 +2732,54 @@ private: uint32_t m_colocalVertexCount; }; -class MeshTopology { +class MeshTopology +{ public: - MeshTopology(const Mesh *mesh) { + MeshTopology(const Mesh *mesh) + { buildTopologyInfo(mesh); } /// Determine if the mesh is connected. - bool isConnected() const { + bool isConnected() const + { return m_connectedCount == 1; } /// Determine if the mesh is closed. (Each edge is shared by two faces) - bool isClosed() const { + 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*/; + bool isDisk() const + { + return isConnected() && m_boundaryCount == 1/* && m_eulerNumber == 1*/; } private: - void buildTopologyInfo(const Mesh *mesh) { + 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"); + xaPrint( "--- Building mesh topology:\n" ); std::vector<uint32_t> stack(faceCount); BitArray bitFlags(faceCount); bitFlags.clearAll(); // Compute connectivity. - xaPrint("--- Computing connectivity.\n"); + xaPrint( "--- Computing connectivity.\n" ); m_connectedCount = 0; - for (uint32_t f = 0; f < faceCount; f++) { - if (bitFlags.bitAt(f) == false) { + for (uint32_t f = 0; f < faceCount; f++ ) { + if ( bitFlags.bitAt(f) == false ) { m_connectedCount++; - stack.push_back(f); - while (!stack.empty()) { + 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) { + if ( bitFlags.bitAt(top) == false ) { bitFlags.setBitAt(top); const Face *face = mesh->faceAt(top); const Edge *firstEdge = face->edge; @@ -2604,9 +2796,9 @@ private: } } xaAssert(stack.empty()); - xaPrint("--- %d connected components.\n", m_connectedCount); + xaPrint( "--- %d connected components.\n", m_connectedCount ); // Count boundary loops. - xaPrint("--- Counting boundary loops.\n"); + xaPrint( "--- Counting boundary loops.\n" ); m_boundaryCount = 0; bitFlags.resize(edgeCount); bitFlags.clearAll(); @@ -2625,13 +2817,13 @@ private: } while (startEdge != edge); } } - xaPrint("--- %d boundary loops found.\n", m_boundaryCount); + 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()) { + if ( isClosed() && isConnected() ) { m_genus = (2 - m_eulerNumber) / 2; xaPrint("--- Genus: %d.\n", m_genus); } @@ -2651,7 +2843,8 @@ private: int m_genus; }; -float computeSurfaceArea(const halfedge::Mesh *mesh) { +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(); @@ -2661,7 +2854,8 @@ float computeSurfaceArea(const halfedge::Mesh *mesh) { return area; } -float computeParametricArea(const halfedge::Mesh *mesh) { +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(); @@ -2670,7 +2864,8 @@ float computeParametricArea(const halfedge::Mesh *mesh) { return area; } -uint32_t countMeshTriangles(const Mesh *mesh) { +uint32_t countMeshTriangles(const Mesh *mesh) +{ const uint32_t faceCount = mesh->faceCount(); uint32_t triangleCount = 0; for (uint32_t f = 0; f < faceCount; f++) { @@ -2682,7 +2877,8 @@ uint32_t countMeshTriangles(const Mesh *mesh) { return triangleCount; } -Mesh *unifyVertices(const Mesh *inputMesh) { +Mesh *unifyVertices(const Mesh *inputMesh) +{ Mesh *mesh = new Mesh; // Only add the first colocal. const uint32_t vertexCount = inputMesh->vertexCount(); @@ -2709,15 +2905,17 @@ Mesh *unifyVertices(const Mesh *inputMesh) { return mesh; } -static bool pointInTriangle(const Vector2 &p, const Vector2 &a, const Vector2 &b, const Vector2 &c) { +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; + 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 *triangulate(const Mesh *inputMesh) +{ Mesh *mesh = new Mesh; // Add all vertices. const uint32_t vertexCount = inputMesh->vertexCount(); @@ -2782,10 +2980,12 @@ Mesh *triangulate(const Mesh *inputMesh) { Vector2 p1 = polygonPoints[i1]; Vector2 p2 = polygonPoints[i2]; + // -- GODOT start -- bool degenerate = distance(p0, p1) < NV_EPSILON || distance(p0, p2) < NV_EPSILON || distance(p1, p2) < NV_EPSILON; if (degenerate) { continue; } + // -- GODOT end -- float d = clamp(dot(p0 - p1, p2 - p1) / (length(p0 - p1) * length(p2 - p1)), -1.0f, 1.0f); float angle = acosf(d); @@ -2810,8 +3010,10 @@ Mesh *triangulate(const Mesh *inputMesh) { } } } + // -- GODOT start -- if (!bestIsValid) break; + // -- GODOT end -- xaDebugAssert(minAngle <= 2 * PI); // Clip best ear: @@ -2835,59 +3037,66 @@ Mesh *triangulate(const Mesh *inputMesh) { } // namespace halfedge /// Mersenne twister random number generator. -class MTRand { +class MTRand +{ public: enum time_e { Time }; - enum { N = 624 }; // length of state vector + 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)); + MTRand( time_e ) + { + seed((uint32_t )time(NULL)); } /// Constructor that uses the given seed. - MTRand(uint32_t s = 0) { + MTRand( uint32_t s = 0 ) + { seed(s); } /// Provide a new seed. - void seed(uint32_t s) { + void seed( uint32_t s ) + { initialize(s); reload(); } /// Get a random number between 0 - 65536. - uint32_t get() { + 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) { + if ( left == 0 ) { reload(); } left--; uint32_t s1; s1 = *next++; s1 ^= (s1 >> 11); - s1 ^= (s1 << 7) & 0x9d2c5680U; + s1 ^= (s1 << 7) & 0x9d2c5680U; s1 ^= (s1 << 15) & 0xefc60000U; - return (s1 ^ (s1 >> 18)); + return ( s1 ^ (s1 >> 18) ); }; /// Get a random number on [0, max] interval. - uint32_t getRange(uint32_t max) { + 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 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); + } while ( n > max ); return n; } private: - void initialize(uint32_t seed) { + 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 @@ -2896,44 +3105,50 @@ private: uint32_t *r = state; int i = 1; *s++ = seed & 0xffffffffUL; - for (; i < N; ++i) { - *s++ = (1812433253UL * (*r ^ (*r >> 30)) + i) & 0xffffffffUL; + for ( ; i < N; ++i ) { + *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; r++; } } - void reload() { + 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]); + 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 { + uint32_t hiBit( uint32_t u ) const + { return u & 0x80000000U; } - uint32_t loBit(uint32_t u) const { + uint32_t loBit( uint32_t u ) const + { return u & 0x00000001U; } - uint32_t loBits(uint32_t u) const { + uint32_t loBits( uint32_t u ) const + { return u & 0x7fffffffU; } - uint32_t mixBits(uint32_t u, uint32_t v) const { + 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 { + 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 + 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 { @@ -2941,50 +3156,59 @@ namespace morton { // 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 +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 +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) { +uint32_t decodeMorton2X(uint32_t code) +{ return compact1By1(code >> 0); } -uint32_t decodeMorton2Y(uint32_t code) { +uint32_t decodeMorton2Y(uint32_t code) +{ return compact1By1(code >> 1); } -uint32_t decodeMorton3X(uint32_t code) { +uint32_t decodeMorton3X(uint32_t code) +{ return compact1By2(code >> 0); } -uint32_t decodeMorton3Y(uint32_t code) { +uint32_t decodeMorton3Y(uint32_t code) +{ return compact1By2(code >> 1); } -uint32_t decodeMorton3Z(uint32_t code) { +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) { +struct ProximityGrid +{ + void init(const Box &box, uint32_t count) + { cellArray.clear(); // Determine grid size. float cellWidth; @@ -3021,19 +3245,23 @@ struct ProximityGrid { 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_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_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_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 { + int index(int x, int y, int z) const + { xaDebugAssert(x >= 0 && x < sx); xaDebugAssert(y >= 0 && y < sy); xaDebugAssert(z >= 0 && z < sz); @@ -3042,7 +3270,8 @@ struct ProximityGrid { return idx; } - uint32_t mortonCount() const { + uint32_t mortonCount() const + { uint64_t s = uint64_t(max3(sx, sy, sz)); s = nextPowerOfTwo(s); if (s > 1024) { @@ -3051,7 +3280,8 @@ struct ProximityGrid { return uint32_t(s * s * s); } - int mortonIndex(uint32_t code) const { + int mortonIndex(uint32_t code) const + { uint32_t x, y, z; uint32_t s = uint32_t(max3(sx, sy, sz)); if (s > 1024) { @@ -3084,7 +3314,8 @@ struct ProximityGrid { return index(x, y, z); } - void add(const Vector3 &pos, uint32_t key) { + 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); @@ -3094,7 +3325,8 @@ struct ProximityGrid { // 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) { + 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); @@ -3125,26 +3357,25 @@ struct ProximityGrid { // 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 { +class RadixSort +{ public: - RadixSort() : - m_size(0), - m_ranks(NULL), - m_ranks2(NULL), - m_validRanks(false) {} - ~RadixSort() { + 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) { + 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_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; @@ -3164,16 +3395,19 @@ public: return *this; } - RadixSort &sort(const std::vector<float> &input) { + 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 { + const uint32_t *ranks() const + { xaDebugAssert(m_validRanks); return m_ranks; } - uint32_t *ranks() { + uint32_t *ranks() + { xaDebugAssert(m_validRanks); return m_ranks; } @@ -3184,18 +3418,21 @@ private: uint32_t *m_ranks2; bool m_validRanks; - void FloatFlip(uint32_t &f) { + void FloatFlip(uint32_t &f) + { int32_t mask = (int32_t(f) >> 31) | 0x80000000; // Warren Hunt, Manchor Ko. f ^= mask; } - void IFloatFlip(uint32_t &f) { + 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) { + 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]; @@ -3203,10 +3440,10 @@ private: h[i] = histogram + 256 * i; } // Clear histograms. - memset(histogram, 0, 256 * bucketCount * sizeof(uint32_t)); + 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 *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++]++; @@ -3221,8 +3458,8 @@ private: } } - template <typename T> - void insertionSort(const T *input, uint32_t count) { + 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) { @@ -3252,8 +3489,8 @@ private: } } - template <typename T> - void radixSort(const T *input, uint32_t count) { + 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]; @@ -3271,8 +3508,7 @@ private: } // Create offsets link[0] = m_ranks2; - for (uint32_t i = 1; i < 256; i++) - link[i] = link[i - 1] + h[i - 1]; + 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++) { @@ -3299,9 +3535,11 @@ private: }; namespace raster { -class ClippedTriangle { +class ClippedTriangle +{ public: - ClippedTriangle(Vector2::Arg a, Vector2::Arg b, Vector2::Arg c) { + ClippedTriangle(Vector2::Arg a, Vector2::Arg b, Vector2::Arg c) + { m_numVertices = 3; m_activeVertexBuffer = 0; m_verticesA[0] = a; @@ -3311,27 +3549,30 @@ public: m_vertexBuffers[1] = m_verticesB; } - uint32_t vertexCount() { + uint32_t vertexCount() + { return m_numVertices; } - const Vector2 *vertices() { + const Vector2 *vertices() + { return m_vertexBuffers[m_activeVertexBuffer]; } - void clipHorizontalPlane(float offset, float clipdirection) { - Vector2 *v = 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; + 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; + dy2 = offset - v[k + 1].y; dy2in = clipdirection * dy2 >= 0; if (dy1in) v2[p++] = v[k]; - if (dy1in + dy2in == 1) { // not both in/out + 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); @@ -3343,19 +3584,20 @@ public: //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]; + 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; + 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 + 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)); @@ -3366,8 +3608,9 @@ public: m_numVertices = p; } - void computeAreaCentroid() { - Vector2 *v = m_vertexBuffers[m_activeVertexBuffer]; + void computeAreaCentroid() + { + Vector2 *v = m_vertexBuffers[m_activeVertexBuffer]; v[m_numVertices] = v[0]; m_area = 0; float centroidx = 0, centroidy = 0; @@ -3386,19 +3629,22 @@ public: } } - void clipAABox(float x0, float y0, float x1, float y1) { - clipVerticalPlane(x0, -1); - clipHorizontalPlane(y0, -1); - clipVerticalPlane(x1, 1); - clipHorizontalPlane(y1, 1); + 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() { + Vector2 centroid() + { return m_centroid; } - float area() { + float area() + { return m_area; } @@ -3406,18 +3652,20 @@ 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; + 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); +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) { +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; @@ -3437,7 +3685,8 @@ struct Triangle { /// 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() { + bool computeDeltas() + { Vector2 e0 = v3 - v1; Vector2 e1 = v2 - v1; Vector3 de0 = t3 - t1; @@ -3446,16 +3695,17 @@ struct Triangle { if (!std::isfinite(denom)) { return false; } - float lambda1 = -e1.y * denom; + float lambda1 = - e1.y * denom; float lambda2 = e0.y * denom; float lambda3 = e1.x * denom; - float lambda4 = -e0.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) { + 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); @@ -3479,10 +3729,10 @@ struct Triangle { 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; + 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; @@ -3586,26 +3836,27 @@ struct Triangle { } // 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); + 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); + 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)); + 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)); + 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); @@ -3631,9 +3882,9 @@ struct Triangle { 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; + 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)) { + 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; @@ -3695,9 +3946,10 @@ struct Triangle { return true; } - void flipBackface() { + 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) { + 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 @@ -3708,7 +3960,8 @@ struct Triangle { } // compute unit inward normals for each edge. - void computeUnitInwardNormals() { + void computeUnitInwardNormals() + { n1 = v1 - v2; n1 = Vector2(-n1.y, n1.x); n1 = n1 * (1.0f / sqrtf(n1.x * n1.x + n1.y * n1.y)); @@ -3732,13 +3985,15 @@ struct Triangle { bool valid; }; -enum Mode { +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) { +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. @@ -3754,7 +4009,8 @@ static bool drawTriangle(Mode mode, Vector2::Arg extents, bool enableScissors, c } // 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) { +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. @@ -3786,7 +4042,8 @@ static bool drawQuad(Mode mode, Vector2::Arg extents, bool enableScissors, const // Full and sparse vector and matrix classes. BLAS subset. // Pseudo-BLAS interface. namespace sparse { -enum Transpose { +enum Transpose +{ NoTransposed = 0, Transposed = 1 }; @@ -3799,22 +4056,22 @@ enum Transpose { * elements for each row of the matrix. As with the FullVector the * dimension of the matrix is constant. **/ -class Matrix { +class Matrix +{ public: // An element of the sparse array. - struct Coefficient { - uint32_t x; // column + 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; } + 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) { + const Matrix &operator=(const Matrix &m) + { xaAssert(width() == m.width()); xaAssert(height() == m.height()); m_array = m.m_array; @@ -3826,9 +4083,10 @@ public: 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()); + 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; @@ -3836,9 +4094,10 @@ public: return 0.0f; } - void setCoefficient(uint32_t x, uint32_t y, float f) { - xaDebugAssert(x < width()); - xaDebugAssert(y < height()); + 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) { @@ -3848,12 +4107,13 @@ public: } if (f != 0.0f) { Coefficient c = { x, f }; - m_array[y].push_back(c); + m_array[y].push_back( c ); } } - float dotRow(uint32_t y, const FullVector &v) const { - xaDebugAssert(y < height()); + 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++) { @@ -3862,7 +4122,8 @@ public: return sum; } - void madRow(uint32_t y, float alpha, FullVector &v) const { + 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++) { @@ -3870,13 +4131,15 @@ public: } } - void clearRow(uint32_t y) { - xaDebugAssert(y < height()); + void clearRow(uint32_t y) + { + xaDebugAssert( y < height() ); m_array[y].clear(); } - void scaleRow(uint32_t y, float f) { - xaDebugAssert(y < height()); + 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; @@ -3890,11 +4153,12 @@ private: const uint32_t m_width; /// Array of matrix elements. - std::vector<std::vector<Coefficient> > m_array; + std::vector< std::vector<Coefficient> > m_array; }; // y = a * x + y -static void saxpy(float a, const FullVector &x, FullVector &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++) { @@ -3902,7 +4166,8 @@ static void saxpy(float a, const FullVector &x, FullVector &y) { } } -static void copy(const FullVector &x, FullVector &y) { +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++) { @@ -3910,14 +4175,16 @@ static void copy(const FullVector &x, FullVector &y) { } } -static void scal(float a, FullVector &x) { +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) { +static float dot(const FullVector &x, const FullVector &y) +{ xaDebugAssert(x.dimension() == y.dimension()); const uint32_t dim = x.dimension(); float sum = 0; @@ -3927,19 +4194,20 @@ static float dot(const FullVector &x, const FullVector &y) { return sum; } -static void mult(Transpose TM, const Matrix &M, const FullVector &x, FullVector &y) { +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()); + 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()); + xaDebugAssert( w == x.dimension() ); + xaDebugAssert( h == y.dimension() ); for (uint32_t i = 0; i < h; i++) { y[i] = M.dotRow(i, x); } @@ -3947,22 +4215,24 @@ static void mult(Transpose TM, const Matrix &M, const FullVector &x, FullVector } // y = M * x -static void mult(const Matrix &M, const FullVector &x, FullVector &y) { +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) { +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()); + 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()); + 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]; } @@ -3970,12 +4240,14 @@ static void sgemv(float alpha, Transpose TA, const Matrix &A, const FullVector & } // y = alpha*A*x + beta*y -static void sgemv(float alpha, const Matrix &A, const FullVector &x, float beta, FullVector &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) { +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; @@ -3987,7 +4259,8 @@ static float dotRowColumn(int y, const Matrix &A, int x, const Matrix &B) { } // dot y-row of A by x-row of B -static float dotRowRow(int y, const Matrix &A, int x, const Matrix &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; @@ -3999,7 +4272,8 @@ static float dotRowRow(int y, const Matrix &A, int x, const Matrix &B) { } // dot y-column of A by x-column of B -static float dotColumnColumn(int y, const Matrix &A, int x, const Matrix &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; @@ -4009,7 +4283,8 @@ static float dotColumnColumn(int y, const Matrix &A, int x, const Matrix &B) { return sum; } -static void transpose(const Matrix &A, Matrix &B) { +static void transpose(const Matrix &A, Matrix &B) +{ xaDebugAssert(A.width() == B.height()); xaDebugAssert(B.width() == A.height()); const uint32_t w = A.width(); @@ -4028,7 +4303,8 @@ static void transpose(const Matrix &A, Matrix &B) { } } -static void sgemm(float alpha, Transpose TA, const Matrix &A, Transpose TB, const Matrix &B, float beta, Matrix &C) { +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(); @@ -4063,21 +4339,24 @@ static void sgemm(float alpha, Transpose TA, const Matrix &A, Transpose TB, cons } } -static void mult(Transpose TA, const Matrix &A, Transpose TB, const Matrix &B, Matrix &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) { +static void mult(const Matrix &A, const Matrix &B, Matrix &C) +{ mult(NoTransposed, A, NoTransposed, B, C); } } // namespace sparse -class JacobiPreconditioner { +class JacobiPreconditioner +{ public: - JacobiPreconditioner(const sparse::Matrix &M, bool symmetric) : - m_inverseDiagonal(M.width()) { + 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); @@ -4090,7 +4369,8 @@ public: } } - void apply(const FullVector &x, FullVector &y) const { + 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. @@ -4105,10 +4385,12 @@ private: }; // Linear solvers. -class Solver { +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) { + 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... @@ -4123,7 +4405,8 @@ public: } // 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) { + 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); @@ -4221,16 +4504,17 @@ private: * 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()); + 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); // + 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; @@ -4241,14 +4525,14 @@ private: sparse::sgemv(-1, A, x, 1, r); // p = r; sparse::copy(r, p); - delta_new = sparse::dot(r, r); + 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); + 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 @@ -4260,7 +4544,7 @@ private: sparse::saxpy(-alpha, q, r); } delta_old = delta_new; - delta_new = sparse::dot(r, r); + delta_new = sparse::dot( r, r ); beta = delta_new / delta_old; // p = beta·p + r sparse::scal(beta, p); @@ -4269,18 +4553,20 @@ private: 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()); + 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 + 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; @@ -4312,7 +4598,7 @@ private: // s = M^-1 · r preconditioner.apply(r, s); delta_old = delta_new; - delta_new = sparse::dot(r, s); + delta_new = sparse::dot( r, s ); beta = delta_new / delta_old; // p = s + beta·p sparse::scal(beta, p); @@ -4321,7 +4607,8 @@ private: return delta_new <= epsilon * epsilon * delta_0; } - static bool SymmetricSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f) { + 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()); @@ -4335,7 +4622,8 @@ class Atlas; class Chart; // Fast sweep in 3 directions -static bool findApproximateDiameterVertices(halfedge::Mesh *mesh, halfedge::Vertex **a, halfedge::Vertex **b) { +static bool findApproximateDiameterVertices(halfedge::Mesh *mesh, halfedge::Vertex **a, halfedge::Vertex **b) +{ xaDebugAssert(mesh != NULL); xaDebugAssert(a != NULL); xaDebugAssert(b != NULL); @@ -4364,18 +4652,12 @@ static bool findApproximateDiameterVertices(halfedge::Mesh *mesh, halfedge::Vert // 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; + 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++) { @@ -4396,24 +4678,28 @@ static bool findApproximateDiameterVertices(halfedge::Mesh *mesh, halfedge::Vert // Conformal relations from Brecht Van Lommel (based on ABF): -static float vec_angle_cos(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3) { +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) { +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) { +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) { +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; @@ -4469,7 +4755,8 @@ static void setup_abf_relations(sparse::Matrix &A, int row, const halfedge::Vert A.setCoefficient(v2_id, 2 * row + 1, 1); } -bool computeLeastSquaresConformalMap(halfedge::Mesh *mesh) { +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. @@ -4542,7 +4829,8 @@ bool computeLeastSquaresConformalMap(halfedge::Mesh *mesh) { return true; } -bool computeOrthogonalProjectionMap(halfedge::Mesh *mesh) { +bool computeOrthogonalProjectionMap(halfedge::Mesh *mesh) +{ Vector3 axis[2]; uint32_t vertexCount = mesh->vertexCount(); std::vector<Vector3> points(vertexCount); @@ -4572,7 +4860,8 @@ bool computeOrthogonalProjectionMap(halfedge::Mesh *mesh) { return true; } -void computeSingleFaceMap(halfedge::Mesh *mesh) { +void computeSingleFaceMap(halfedge::Mesh *mesh) +{ xaDebugAssert(mesh != NULL); xaDebugAssert(mesh->faceCount() == 1); halfedge::Face *face = mesh->faceAt(0); @@ -4603,11 +4892,12 @@ void computeSingleFaceMap(halfedge::Mesh *mesh) { // - 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) {} +struct PriorityQueue +{ + PriorityQueue(uint32_t size = UINT_MAX) : maxSize(size) {} - void push(float priority, uint32_t face) { + void push(float priority, uint32_t face) + { uint32_t i = 0; const uint32_t count = pairs.size(); for (; i < count; i++) { @@ -4621,39 +4911,47 @@ struct PriorityQueue { } // push face out of order, to be sorted later. - void push(uint32_t face) { + void push(uint32_t face) + { Pair p = { 0.0f, face }; pairs.push_back(p); } - uint32_t pop() { + uint32_t pop() + { uint32_t f = pairs.back().face; pairs.pop_back(); return f; } - void sort() { + void sort() + { //sort(pairs); // @@ My intro sort appears to be much slower than it should! std::sort(pairs.begin(), pairs.end()); } - void clear() { + void clear() + { pairs.clear(); } - uint32_t count() const { + uint32_t count() const + { return pairs.size(); } - float firstPriority() const { + 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! + struct Pair + { + bool operator <(const Pair &p) const + { + return priority > p.priority; // !! Sort in inverse priority order! } float priority; @@ -4663,9 +4961,10 @@ struct PriorityQueue { std::vector<Pair> pairs; }; -struct ChartBuildData { - ChartBuildData(int p_id) : - id(p_id) { +struct ChartBuildData +{ + ChartBuildData(int id) : id(id) + { planeNormal = Vector3(0); centroid = Vector3(0); coneAxis = Vector3(0); @@ -4689,15 +4988,15 @@ struct ChartBuildData { Vector3 normalSum; Vector3 centroidSum; - std::vector<uint32_t> seeds; // @@ These could be a pointers to the halfedge faces directly. + 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()) { +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); @@ -4720,14 +5019,16 @@ struct AtlasBuilder { } } - ~AtlasBuilder() { + ~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) { + 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]; @@ -4739,7 +5040,8 @@ struct AtlasBuilder { facesLeft -= unchartedFaceCount; } - void computeShortestPaths() { + void computeShortestPaths() + { const uint32_t faceCount = mesh->faceCount(); shortestPaths.resize(faceCount * faceCount, FLT_MAX); // Fill edges: @@ -4767,7 +5069,8 @@ struct AtlasBuilder { } } - void placeSeeds(float threshold, uint32_t maxSeedCount) { + 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. @@ -4783,18 +5086,17 @@ struct AtlasBuilder { } } - void createRandomChart(float 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++; } - 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. @@ -4802,7 +5104,8 @@ struct AtlasBuilder { //growCharts(threshold - threshold * 0.75f / chartCount(), facesLeft); } - void addFaceToChart(ChartBuildData *chart, uint32_t f, bool recomputeProxy = false) { + void addFaceToChart(ChartBuildData *chart, uint32_t f, bool recomputeProxy = false) + { // Add face to chart. chart->faces.push_back(f); xaDebugAssert(faceChartArray[f] == -1); @@ -4824,7 +5127,8 @@ struct AtlasBuilder { } // Returns true if any of the charts can grow more. - bool growCharts(float threshold, uint32_t faceCount) { + bool growCharts(float threshold, uint32_t faceCount) + { // Using one global list. faceCount = std::min(faceCount, facesLeft); for (uint32_t i = 0; i < faceCount; i++) { @@ -4837,9 +5141,10 @@ struct AtlasBuilder { return facesLeft != 0; // Can continue growing. } - bool growChart(ChartBuildData *chart, float threshold, uint32_t faceCount) { + 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;) { + for (uint32_t i = 0; i < faceCount; ) { if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold) { return false; } @@ -4855,7 +5160,8 @@ struct AtlasBuilder { return true; } - void resetCharts() { + void resetCharts() + { const uint32_t faceCount = mesh->faceCount(); for (uint32_t i = 0; i < faceCount; i++) { faceChartArray[i] = -1; @@ -4877,7 +5183,8 @@ struct AtlasBuilder { } } - void updateCandidates(ChartBuildData *chart, uint32_t f) { + 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()) { @@ -4891,20 +5198,23 @@ struct AtlasBuilder { } } - void updateProxies() { + void updateProxies() + { const uint32_t chartCount = chartArray.size(); for (uint32_t i = 0; i < chartCount; i++) { updateProxy(chartArray[i]); } } - void updateProxy(ChartBuildData *chart) { + 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 relocateSeeds() + { bool anySeedChanged = false; const uint32_t chartCount = chartArray.size(); for (uint32_t i = 0; i < chartCount; i++) { @@ -4915,9 +5225,10 @@ struct AtlasBuilder { return anySeedChanged; } - bool relocateSeed(ChartBuildData *chart) { + bool relocateSeed(ChartBuildData *chart) + { Vector3 centroid = computeChartCentroid(chart); - const uint32_t N = 10; // @@ Hardcoded to 10? + 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(); @@ -4953,7 +5264,8 @@ struct AtlasBuilder { } } - void updatePriorities(ChartBuildData *chart) { + void updatePriorities(ChartBuildData *chart) + { // Re-evaluate candidate priorities. uint32_t candidateCount = chart->candidates.count(); for (uint32_t i = 0; i < candidateCount; i++) { @@ -4967,7 +5279,8 @@ struct AtlasBuilder { } // Evaluate combined metric. - float evaluatePriority(ChartBuildData *chart, uint32_t face) { + float evaluatePriority(ChartBuildData *chart, uint32_t face) + { // Estimate boundary length and area: float newBoundaryLength = evaluateBoundaryLength(chart, face); float newChartArea = evaluateChartArea(chart, face); @@ -4984,11 +5297,11 @@ struct AtlasBuilder { // - 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); + 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; @@ -4999,14 +5312,16 @@ struct AtlasBuilder { } // Returns a value in [0-1]. - float evaluateProxyFitMetric(ChartBuildData *chart, uint32_t f) { + 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 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) { @@ -5017,7 +5332,8 @@ struct AtlasBuilder { } } - float evaluateStraightnessMetric(ChartBuildData *chart, uint32_t f) { + float evaluateStraightnessMetric(ChartBuildData *chart, uint32_t f) + { float l_out = 0.0f; float l_in = 0.0f; const halfedge::Face *face = mesh->faceAt(f); @@ -5040,7 +5356,8 @@ struct AtlasBuilder { return std::min(ratio, 0.0f); // Only use the straightness metric to close gaps. } - float evaluateNormalSeamMetric(ChartBuildData *chart, uint32_t f) { + float evaluateNormalSeamMetric(ChartBuildData *chart, uint32_t f) + { float seamFactor = 0.0f; float totalLength = 0.0f; const halfedge::Face *face = mesh->faceAt(f); @@ -5071,7 +5388,8 @@ struct AtlasBuilder { return seamFactor / totalLength; } - float evaluateTextureSeamMetric(ChartBuildData *chart, uint32_t f) { + float evaluateTextureSeamMetric(ChartBuildData *chart, uint32_t f) + { float seamLength = 0.0f; float totalLength = 0.0f; const halfedge::Face *face = mesh->faceAt(f); @@ -5101,12 +5419,14 @@ struct AtlasBuilder { return seamLength / totalLength; } - float evaluateChartArea(ChartBuildData *chart, uint32_t f) { + 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 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); @@ -5125,20 +5445,23 @@ struct AtlasBuilder { } } } - return std::max(0.0f, boundaryLength); // @@ Hack! + return std::max(0.0f, boundaryLength); // @@ Hack! } - Vector3 evaluateChartNormalSum(ChartBuildData *chart, uint32_t f) { + 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) { + 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 computeChartCentroid(const ChartBuildData *chart) + { Vector3 centroid(0); const uint32_t faceCount = chart->faces.size(); for (uint32_t i = 0; i < faceCount; i++) { @@ -5148,12 +5471,14 @@ struct AtlasBuilder { return centroid / float(faceCount); } - void fillHoles(float threshold) { + void fillHoles(float threshold) + { while (facesLeft > 0) createRandomChart(threshold); } - void mergeCharts() { + void mergeCharts() + { std::vector<float> sharedBoundaryLengths; const uint32_t chartCount = chartArray.size(); for (int c = chartCount - 1; c >= 0; c--) { @@ -5219,9 +5544,9 @@ struct AtlasBuilder { // 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())); + xaDebugAssert (faceChartArray[i] != -1); + xaDebugAssert (faceChartArray[i] != c); + xaDebugAssert (faceChartArray[i] <= int32_t(chartArray.size())); if (faceChartArray[i] > c) { faceChartArray[i]--; } @@ -5241,7 +5566,8 @@ struct AtlasBuilder { }; // @@ Get N best candidates in one pass. - const Candidate &getBestCandidate() const { + const Candidate &getBestCandidate() const + { uint32_t best = 0; float bestCandidateMetric = FLT_MAX; const uint32_t candidateCount = candidateArray.size(); @@ -5256,7 +5582,8 @@ struct AtlasBuilder { return candidateArray[best]; } - void removeCandidate(uint32_t f) { + void removeCandidate(uint32_t f) + { int c = faceCandidateArray[f]; if (c != -1) { faceCandidateArray[f] = (uint32_t)-1; @@ -5271,7 +5598,8 @@ struct AtlasBuilder { } } - void updateCandidate(ChartBuildData *chart, uint32_t f, float metric) { + void updateCandidate(ChartBuildData *chart, uint32_t f, float metric) + { if (faceCandidateArray[f] == -1) { const uint32_t index = candidateArray.size(); faceCandidateArray[f] = index; @@ -5291,7 +5619,8 @@ struct AtlasBuilder { } } - void mergeChart(ChartBuildData *owner, ChartBuildData *chart, float sharedBoundaryLength) { + 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]; @@ -5307,6 +5636,7 @@ struct AtlasBuilder { updateProxy(owner); } + uint32_t chartCount() const { return chartArray.size(); } const std::vector<uint32_t> &chartFaces(uint32_t i) const { return chartArray[i]->faces; } @@ -5324,13 +5654,13 @@ struct AtlasBuilder { }; /// A chart is a connected set of faces with a certain topology (usually a disk). -class Chart { +class Chart +{ public: - Chart() : - m_isDisk(false), - m_isVertexMapped(false) {} + Chart() : m_isDisk(false), m_isVertexMapped(false) {} - void build(const halfedge::Mesh *originalMesh, const std::vector<uint32_t> &faceArray) { + void build(const halfedge::Mesh *originalMesh, const std::vector<uint32_t> &faceArray) + { // Copy face indices. m_faceArray = faceArray; const uint32_t meshVertexCount = originalMesh->vertexCount(); @@ -5353,7 +5683,10 @@ public: } if (chartMeshIndices[vertex->id] == ~0) { chartMeshIndices[vertex->id] = m_chartMesh->vertexCount(); + // -- GODOT start -- + //m_chartToOriginalMap.push_back(vertex->id); m_chartToOriginalMap.push_back(vertex->original_id); + // -- GODOT end -- m_chartToUnifiedMap.push_back(unifiedMeshIndices[unifiedVertex->id]); halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos); v->nor = vertex->nor; @@ -5417,7 +5750,8 @@ public: m_isDisk = topology.isDisk(); } - void buildVertexMap(const halfedge::Mesh *originalMesh, const std::vector<uint32_t> &unchartedMaterialArray) { + 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. @@ -5445,7 +5779,10 @@ public: const halfedge::Vertex *vertex = it.current()->vertex; if (chartMeshIndices[vertex->id] == ~0) { chartMeshIndices[vertex->id] = m_chartMesh->vertexCount(); + // -- GODOT start -- + //m_chartToOriginalMap.push_back(vertex->id); m_chartToOriginalMap.push_back(vertex->original_id); + // -- GODOT end -- halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos); v->nor = vertex->nor; v->tex = vertex->tex; // @@ Not necessary. @@ -5505,7 +5842,7 @@ public: halfedge::Vertex *vertex = m_chartMesh->vertexAt(idx); xaDebugAssert(vertexIndexArray[idx] == -1); std::vector<uint32_t> neighbors; - grid.gather(vertex->pos, positionThreshold, /*ref*/ 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]; @@ -5528,7 +5865,7 @@ public: xaDebugAssert(cellsVisited == grid.cellArray.size()); xaDebugAssert(verticesVisited == chartVertexCount); vertexMapWidth = ftoi_ceil(sqrtf(float(texelCount))); - vertexMapWidth = (vertexMapWidth + 3) & ~3; // Width aligned to 4. + 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); @@ -5559,7 +5896,8 @@ public: } } - bool closeHoles() { + bool closeHoles() + { xaDebugAssert(!m_isVertexMapped); std::vector<halfedge::Edge *> boundaryEdges; getBoundaryEdges(m_unifiedMesh.get(), boundaryEdges); @@ -5610,7 +5948,7 @@ public: std::vector<halfedge::Edge *> edgeLoop; halfedge::Edge *edge = startEdge; do { - halfedge::Vertex *vertex = edge->next->vertex; // edge->to() + halfedge::Vertex *vertex = edge->next->vertex; // edge->to() uint32_t j; for (j = 0; j < vertexLoop.size(); j++) { if (vertex->isColocal(vertexLoop[j])) { @@ -5619,8 +5957,8 @@ public: } 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. + 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); @@ -5646,55 +5984,69 @@ public: return boundaryCount == 1; } - bool isDisk() const { + bool isDisk() const + { return m_isDisk; } - bool isVertexMapped() const { + bool isVertexMapped() const + { return m_isVertexMapped; } - uint32_t vertexCount() const { + uint32_t vertexCount() const + { return m_chartMesh->vertexCount(); } - uint32_t colocalVertexCount() const { + uint32_t colocalVertexCount() const + { return m_unifiedMesh->vertexCount(); } - uint32_t faceCount() const { + uint32_t faceCount() const + { return m_faceArray.size(); } - uint32_t faceAt(uint32_t i) const { + uint32_t faceAt(uint32_t i) const + { return m_faceArray[i]; } - const halfedge::Mesh *chartMesh() const { + const halfedge::Mesh *chartMesh() const + { return m_chartMesh.get(); } - halfedge::Mesh *chartMesh() { + halfedge::Mesh *chartMesh() + { return m_chartMesh.get(); } - const halfedge::Mesh *unifiedMesh() const { + const halfedge::Mesh *unifiedMesh() const + { return m_unifiedMesh.get(); } - halfedge::Mesh *unifiedMesh() { + 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 { + uint32_t mapChartVertexToOriginalVertex(uint32_t i) const + { return m_chartToOriginalMap[i]; } - uint32_t mapChartVertexToUnifiedVertex(uint32_t i) const { + uint32_t mapChartVertexToUnifiedVertex(uint32_t i) const + { return m_chartToUnifiedMap[i]; } - const std::vector<uint32_t> &faceArray() const { + const std::vector<uint32_t> &faceArray() const + { return m_faceArray; } // Transfer parameterization from unified mesh to chart mesh. - void transferParameterization() { + void transferParameterization() + { xaDebugAssert(!m_isVertexMapped); uint32_t vertexCount = m_chartMesh->vertexCount(); for (uint32_t v = 0; v < vertexCount; v++) { @@ -5704,18 +6056,21 @@ public: } } - float computeSurfaceArea() const { + float computeSurfaceArea() const + { return halfedge::computeSurfaceArea(m_chartMesh.get()) * scale; } - float computeParametricArea() const { + 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 { + Vector2 computeParametricBounds() const + { // This only makes sense in parameterized meshes. xaDebugAssert(m_isDisk); xaDebugAssert(!m_isVertexMapped); @@ -5735,7 +6090,8 @@ public: bool blockAligned = true; private: - bool closeLoop(uint32_t start, const std::vector<halfedge::Edge *> &loop) { + 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; @@ -5779,7 +6135,8 @@ private: return true; } - static void getBoundaryEdges(halfedge::Mesh *mesh, std::vector<halfedge::Edge *> &boundaryEdges) { + static void getBoundaryEdges(halfedge::Mesh *mesh, std::vector<halfedge::Edge *> &boundaryEdges) + { xaDebugAssert(mesh != NULL); const uint32_t edgeCount = mesh->edgeCount(); BitArray bitFlags(edgeCount); @@ -5810,7 +6167,7 @@ private: 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; @@ -5821,9 +6178,11 @@ private: }; // Estimate quality of existing parameterization. -class ParameterizationQuality { +class ParameterizationQuality +{ public: - ParameterizationQuality() { + ParameterizationQuality() + { m_totalTriangleCount = 0; m_flippedTriangleCount = 0; m_zeroAreaTriangleCount = 0; @@ -5835,7 +6194,8 @@ public: m_authalicMetric = 0.0f; } - ParameterizationQuality(const halfedge::Mesh *mesh) { + ParameterizationQuality(const halfedge::Mesh *mesh) + { xaDebugAssert(mesh != NULL); m_totalTriangleCount = 0; m_flippedTriangleCount = 0; @@ -5879,33 +6239,39 @@ public: xaDebugAssert(std::isfinite(m_authalicMetric)); } - bool isValid() const { + bool isValid() const + { return m_flippedTriangleCount == 0; // @@ Does not test for self-overlaps. } - float rmsStretchMetric() const { + 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 { + float maxStretchMetric() const + { if (m_geometricArea == 0) return 0.0f; float normFactor = sqrtf(m_parametricArea / m_geometricArea); return m_maxStretchMetric * normFactor; } - float rmsConformalMetric() const { + float rmsConformalMetric() const + { if (m_geometricArea == 0) return 0.0f; return sqrtf(m_conformalMetric / m_geometricArea); } - float maxAuthalicMetric() const { + float maxAuthalicMetric() const + { if (m_geometricArea == 0) return 0.0f; return sqrtf(m_authalicMetric / m_geometricArea); } - void operator+=(const ParameterizationQuality &pq) { + void operator+=(const ParameterizationQuality &pq) + { m_totalTriangleCount += pq.m_totalTriangleCount; m_flippedTriangleCount += pq.m_flippedTriangleCount; m_zeroAreaTriangleCount += pq.m_zeroAreaTriangleCount; @@ -5918,7 +6284,8 @@ public: } private: - void processTriangle(Vector3 q[3], Vector2 p[3]) { + void processTriangle(Vector3 q[3], Vector2 p[3]) + { m_totalTriangleCount++; // Evaluate texture stretch metric. See: // - "Texture Mapping Progressive Meshes", Sander, Snyder, Gortler & Hoppe @@ -5983,32 +6350,38 @@ private: }; // Set of charts corresponding to a single mesh. -class MeshCharts { +class MeshCharts +{ public: - MeshCharts(const halfedge::Mesh *mesh) : - m_mesh(mesh) {} + MeshCharts(const halfedge::Mesh *mesh) : m_mesh(mesh) {} - ~MeshCharts() { + ~MeshCharts() + { for (size_t i = 0; i < m_chartArray.size(); i++) delete m_chartArray[i]; } - uint32_t chartCount() const { + uint32_t chartCount() const + { return m_chartArray.size(); } - uint32_t vertexCount() const { + uint32_t vertexCount () const + { return m_totalVertexCount; } - const Chart *chartAt(uint32_t i) const { + const Chart *chartAt(uint32_t i) const + { return m_chartArray[i]; } - Chart *chartAt(uint32_t i) { + Chart *chartAt(uint32_t i) + { return m_chartArray[i]; } // Extract the charts of the input mesh. - void extractCharts() { + void extractCharts() + { const uint32_t faceCount = m_mesh->faceCount(); int first = 0; std::vector<uint32_t> queue; @@ -6108,7 +6481,8 @@ public: - 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) { + void computeCharts(const CharterOptions &options, const std::vector<uint32_t> &unchartedMaterialArray) + { Chart *vertexMap = NULL; if (unchartedMaterialArray.size() != 0) { vertexMap = new Chart(); @@ -6141,7 +6515,7 @@ public: xaPrint("### Placed %d seeds (max = %d)\n", builder.chartCount(), maxSeedCount); builder.updateProxies(); builder.mergeCharts(); -#if 1 + #if 1 xaPrint("### Relocating seeds\n"); builder.relocateSeeds(); xaPrint("### Reset charts\n"); @@ -6183,7 +6557,7 @@ public: xaPrint("### Growing charts\n"); } }; -#endif + #endif // Make sure no holes are left! xaDebugAssert(builder.facesLeft == 0); const uint32_t chartCount = builder.chartArray.size(); @@ -6220,21 +6594,25 @@ public: } } - void parameterizeCharts() { + 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++) { + for (uint32_t i = 0; i < chartCount; i++) + { Chart *chart = m_chartArray[i]; bool isValid = false; - if (chart->isVertexMapped()) { + if (chart->isVertexMapped()) + { continue; } - if (chart->isDisk()) { + if (chart->isDisk()) + { diskCount++; ParameterizationQuality chartParameterizationQuality; if (chart->faceCount() == 1) { @@ -6258,6 +6636,7 @@ public: // 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()); @@ -6266,18 +6645,22 @@ public: xaPrint(" RMS authalic metric: %f\n", globalParameterizationQuality.maxAuthalicMetric()); } - uint32_t faceChartAt(uint32_t i) const { + uint32_t faceChartAt(uint32_t i) const + { return m_faceChart[i]; } - uint32_t faceIndexWithinChartAt(uint32_t i) const { + uint32_t faceIndexWithinChartAt(uint32_t i) const + { return m_faceIndex[i]; } - uint32_t vertexCountBeforeChartAt(uint32_t i) const { + uint32_t vertexCountBeforeChartAt(uint32_t i) const + { return m_chartVertexCountPrefixSum[i]; } private: + const halfedge::Mesh *m_mesh; std::vector<Chart *> m_chartArray; @@ -6290,26 +6673,32 @@ private: }; /// An atlas is a set of charts. -class Atlas { +class Atlas +{ public: - ~Atlas() { + ~Atlas() + { for (size_t i = 0; i < m_meshChartsArray.size(); i++) delete m_meshChartsArray[i]; } - uint32_t meshCount() const { + uint32_t meshCount() const + { return m_meshChartsArray.size(); } - const MeshCharts *meshAt(uint32_t i) const { + const MeshCharts *meshAt(uint32_t i) const + { return m_meshChartsArray[i]; } - MeshCharts *meshAt(uint32_t i) { + MeshCharts *meshAt(uint32_t i) + { return m_meshChartsArray[i]; } - uint32_t chartCount() const { + uint32_t chartCount() const + { uint32_t count = 0; for (uint32_t c = 0; c < m_meshChartsArray.size(); c++) { count += m_meshChartsArray[c]->chartCount(); @@ -6317,7 +6706,8 @@ public: return count; } - const Chart *chartAt(uint32_t i) const { + 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) { @@ -6328,7 +6718,8 @@ public: return NULL; } - Chart *chartAt(uint32_t i) { + 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) { @@ -6341,23 +6732,27 @@ public: // Add mesh charts and takes ownership. // Extract the charts and add to this atlas. - void addMeshCharts(MeshCharts *meshCharts) { + void addMeshCharts(MeshCharts *meshCharts) + { m_meshChartsArray.push_back(meshCharts); } - void extractCharts(const halfedge::Mesh *mesh) { + 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) { + 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() { + void parameterizeCharts() + { for (uint32_t i = 0; i < m_meshChartsArray.size(); i++) { m_meshChartsArray[i]->parameterizeCharts(); } @@ -6367,11 +6762,10 @@ private: std::vector<MeshCharts *> m_meshChartsArray; }; -struct AtlasPacker { - AtlasPacker(Atlas *atlas) : - m_atlas(atlas), - m_width(0), - m_height(0) { +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++) { @@ -6386,7 +6780,8 @@ struct AtlasPacker { uint32_t getHeight() const { return m_height; } // Pack charts in the smallest possible rectangle. - void packCharts(const PackerOptions &options) { + void packCharts(const PackerOptions &options) + { const uint32_t chartCount = m_atlas->chartCount(); if (chartCount == 0) return; float texelsPerUnit = 1; @@ -6415,7 +6810,7 @@ struct AtlasPacker { 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. + 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(); @@ -6564,18 +6959,18 @@ struct AtlasPacker { // 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. + 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. + 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_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) @@ -6629,7 +7024,8 @@ struct AtlasPacker { } } - float computeAtlasUtilization() const { + float computeAtlasUtilization() const + { const uint32_t w = m_width; const uint32_t h = m_height; xaDebugAssert(w <= m_bitmap.width()); @@ -6644,7 +7040,8 @@ struct AtlasPacker { } private: - void resetUvs() { + 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++) @@ -6656,7 +7053,8 @@ private: // 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) { + 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; @@ -6669,7 +7067,8 @@ private: } } - 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) { + 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; @@ -6708,10 +7107,11 @@ private: } } done: - xaDebugAssert(best_metric != INT_MAX); + 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) { + 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++) { @@ -6752,7 +7152,8 @@ private: } } - void drawChartBitmapDilate(const Chart *chart, BitMap *bitmap, int padding) { + 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)); @@ -6802,7 +7203,8 @@ private: } } - void drawChartBitmap(const Chart *chart, BitMap *bitmap, const Vector2 &scale, const Vector2 &offset) { + 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)); @@ -6863,7 +7265,8 @@ private: std::swap(tmp, *bitmap); } - bool canAddChart(const BitMap *bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r) { + 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(); @@ -6904,7 +7307,8 @@ private: return true; } - void addChart(const BitMap *bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r) { + 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(); @@ -6946,8 +7350,9 @@ private: } } - static bool setBitsCallback(void *param, int x, int y, Vector3::Arg, Vector3::Arg, Vector3::Arg, float area) { - BitMap *bitmap = (BitMap *)param; + 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); } @@ -6955,7 +7360,8 @@ private: } // Compute the convex hull using Graham Scan. - static void convexHull(const std::vector<Vector2> &input, std::vector<Vector2> &output, float epsilon) { + 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++) { @@ -6984,7 +7390,7 @@ private: output.clear(); output.push_back(top[0]); output.push_back(top[1]); - for (uint32_t i = 2; i < top.size();) { + for (uint32_t i = 2; i < top.size(); ) { Vector2 a = output[output.size() - 2]; Vector2 b = output[output.size() - 1]; Vector2 c = top[i]; @@ -7000,7 +7406,7 @@ private: uint32_t top_count = output.size(); output.push_back(bottom[1]); // Filter bottom list. - for (uint32_t i = 2; i < bottom.size();) { + for (uint32_t i = 2; i < bottom.size(); ) { Vector2 a = output[output.size() - 2]; Vector2 b = output[output.size() - 1]; Vector2 c = bottom[i]; @@ -7019,7 +7425,8 @@ private: } // 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) { + 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); @@ -7096,7 +7503,8 @@ private: } // namespace param } // namespace internal -struct Atlas { +struct Atlas +{ internal::param::Atlas atlas; std::vector<internal::halfedge::Mesh *> heMeshes; uint32_t width = 0; @@ -7104,65 +7512,75 @@ struct Atlas { OutputMesh **outputMeshes = NULL; }; -void SetPrint(PrintFunc print) { +void SetPrint(PrintFunc print) +{ internal::s_print = print; } -Atlas *Create() { +Atlas *Create() +{ Atlas *atlas = new Atlas(); return atlas; } -void Destroy(Atlas *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->chartArray[j].indexArray; + delete [] outputMesh->chartArray; + delete [] outputMesh->vertexArray; + delete [] outputMesh->indexArray; delete outputMesh; } } - delete[] atlas->outputMeshes; + delete [] atlas->outputMeshes; delete atlas; } -static internal::Vector3 DecodePosition(const InputMesh &mesh, uint32_t index) { +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) { +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) { +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) { +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) { +static float EdgeLength(internal::Vector3 pos1, internal::Vector3 pos2) +{ return internal::length(pos2 - pos1); } -AddMeshError AddMesh(Atlas *atlas, const InputMesh &mesh, bool useColocalVertices) { +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) { + if ((mesh.indexCount % 3) != 0) + { error.code = AddMeshErrorCode::InvalidIndexCount; return error; } @@ -7239,23 +7657,22 @@ AddMeshError AddMesh(Atlas *atlas, const InputMesh &mesh, bool useColocalVertice } internal::halfedge::Face *face = heMesh->addFace(tri[0], tri[1], tri[2]); + // -- GODOT start -- 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]); } + // -- GODOT end -- if (!face) { - //continue; - - if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::AlreadyAddedEdge) { + if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::AlreadyAddedEdge) error.code = AddMeshErrorCode::AlreadyAddedEdge; - } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DegenerateColocalEdge) { + else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DegenerateColocalEdge) error.code = AddMeshErrorCode::DegenerateColocalEdge; - } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DegenerateEdge) { + else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DegenerateEdge) error.code = AddMeshErrorCode::DegenerateEdge; - } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DuplicateEdge) { + else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DuplicateEdge) error.code = AddMeshErrorCode::DuplicateEdge; - } error.face = i; error.index0 = heMesh->errorIndex0; error.index1 = heMesh->errorIndex1; @@ -7270,7 +7687,8 @@ AddMeshError AddMesh(Atlas *atlas, const InputMesh &mesh, bool useColocalVertice return error; } -void Generate(Atlas *atlas, CharterOptions charterOptions, PackerOptions packerOptions) { +void Generate(Atlas *atlas, CharterOptions charterOptions, PackerOptions packerOptions) +{ xaAssert(atlas); xaAssert(packerOptions.texelArea > 0); // Chart meshes. @@ -7285,7 +7703,7 @@ void Generate(Atlas *atlas, CharterOptions charterOptions, PackerOptions packerO atlas->width = packer.getWidth(); atlas->height = packer.getHeight(); // Build output meshes. - atlas->outputMeshes = new OutputMesh *[atlas->heMeshes.size()]; + 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; @@ -7341,27 +7759,32 @@ void Generate(Atlas *atlas, CharterOptions charterOptions, PackerOptions packerO } } -uint32_t GetWidth(const Atlas *atlas) { +uint32_t GetWidth(const Atlas *atlas) +{ xaAssert(atlas); return atlas->width; } -uint32_t GetHeight(const Atlas *atlas) { +uint32_t GetHeight(const Atlas *atlas) +{ xaAssert(atlas); return atlas->height; } -uint32_t GetNumCharts(const Atlas *atlas) { +uint32_t GetNumCharts(const Atlas *atlas) +{ xaAssert(atlas); return atlas->atlas.chartCount(); } -const OutputMesh *const *GetOutputMeshes(const Atlas *atlas) { +const OutputMesh * const *GetOutputMeshes(const Atlas *atlas) +{ xaAssert(atlas); return atlas->outputMeshes; } -const char *StringForEnum(AddMeshErrorCode::Enum error) { +const char *StringForEnum(AddMeshErrorCode::Enum error) +{ if (error == AddMeshErrorCode::AlreadyAddedEdge) return "already added edge"; if (error == AddMeshErrorCode::DegenerateColocalEdge) diff --git a/thirdparty/xatlas/xatlas.h b/thirdparty/xatlas/xatlas.h index 4140429fee..dbf8ca08c7 100644 --- a/thirdparty/xatlas/xatlas.h +++ b/thirdparty/xatlas/xatlas.h @@ -3,15 +3,18 @@ #ifndef XATLAS_H #define XATLAS_H #include <float.h> // FLT_MAX -#include <limits.h> -#include <stdint.h> +// -- GODOT start -- +#include <limits.h> // INT_MAX, UINT_MAX +// -- GODOT end -- + namespace xatlas { typedef void (*PrintFunc)(const char *, ...); struct Atlas; -struct CharterOptions { +struct CharterOptions +{ float proxyFitMetricWeight; float roundnessMetricWeight; float straightnessMetricWeight; @@ -20,7 +23,8 @@ struct CharterOptions { float maxChartArea; float maxBoundaryLength; - CharterOptions() { + CharterOptions() + { // These are the default values we use on The Witness. proxyFitMetricWeight = 2.0f; roundnessMetricWeight = 0.01f; @@ -39,15 +43,18 @@ struct CharterOptions { } }; -struct PackMethod { - enum Enum { +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 { +struct PackerOptions +{ PackMethod::Enum method; // 0 - brute force @@ -59,13 +66,14 @@ struct PackerOptions { // 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? + 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. + bool blockAlign; // Align charts to 4x4 blocks. + bool conservative; // Pack charts with extra padding. int padding; - PackerOptions() { + PackerOptions() + { method = PackMethod::ApproximateResolution; quality = 1; texelArea = 8; @@ -76,8 +84,10 @@ struct PackerOptions { } }; -struct AddMeshErrorCode { - enum Enum { +struct AddMeshErrorCode +{ + enum Enum + { Success, AlreadyAddedEdge, // index0 and index1 are the edge indices DegenerateColocalEdge, // index0 and index1 are the edge indices @@ -90,20 +100,24 @@ struct AddMeshErrorCode { }; }; -struct AddMeshError { +struct AddMeshError +{ AddMeshErrorCode::Enum code; uint32_t face; uint32_t index0, index1; }; -struct IndexFormat { - enum Enum { +struct IndexFormat +{ + enum Enum + { HalfFloat, Float }; }; -struct InputMesh { +struct InputMesh +{ uint32_t vertexCount; const void *vertexPositionData; uint32_t vertexPositionStride; @@ -124,17 +138,20 @@ struct InputMesh { const uint16_t *faceMaterialData; }; -struct OutputChart { +struct OutputChart +{ uint32_t *indexArray; uint32_t indexCount; }; -struct OutputVertex { +struct OutputVertex +{ float uv[2]; - uint32_t xref; // Index of input vertex from which this output vertex originated. + uint32_t xref; // Index of input vertex from which this output vertex originated. }; -struct OutputMesh { +struct OutputMesh +{ OutputChart *chartArray; uint32_t chartCount; uint32_t *indexArray; @@ -152,7 +169,7 @@ void Generate(Atlas *atlas, CharterOptions charterOptions = CharterOptions(), Pa 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 OutputMesh * const *GetOutputMeshes(const Atlas *atlas); const char *StringForEnum(AddMeshErrorCode::Enum error); } // namespace xatlas diff --git a/thirdparty/zstd/common/compiler.h b/thirdparty/zstd/common/compiler.h index 7f561282ca..0836e3ed27 100644 --- a/thirdparty/zstd/common/compiler.h +++ b/thirdparty/zstd/common/compiler.h @@ -40,7 +40,7 @@ /** * FORCE_INLINE_TEMPLATE is used to define C "templates", which take constant - * parameters. They must be inlined for the compiler to elimininate the constant + * parameters. They must be inlined for the compiler to eliminate the constant * branches. */ #define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR diff --git a/thirdparty/zstd/common/fse.h b/thirdparty/zstd/common/fse.h index f72c519b25..811c670bdd 100644 --- a/thirdparty/zstd/common/fse.h +++ b/thirdparty/zstd/common/fse.h @@ -358,7 +358,7 @@ size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size typedef enum { FSE_repeat_none, /**< Cannot use the previous table */ FSE_repeat_check, /**< Can use the previous table but it must be checked */ - FSE_repeat_valid /**< Can use the previous table and it is asumed to be valid */ + FSE_repeat_valid /**< Can use the previous table and it is assumed to be valid */ } FSE_repeat; /* ***************************************** diff --git a/thirdparty/zstd/common/threading.c b/thirdparty/zstd/common/threading.c index 8be8c8da94..f3d4fa8418 100644 --- a/thirdparty/zstd/common/threading.c +++ b/thirdparty/zstd/common/threading.c @@ -14,8 +14,8 @@ * This file will hold wrapper for systems, which do not support pthreads */ -/* create fake symbol to avoid empty trnaslation unit warning */ -int g_ZSTD_threading_useles_symbol; +/* create fake symbol to avoid empty translation unit warning */ +int g_ZSTD_threading_useless_symbol; #if defined(ZSTD_MULTITHREAD) && defined(_WIN32) diff --git a/thirdparty/zstd/common/xxhash.c b/thirdparty/zstd/common/xxhash.c index 532b816192..30599aaae4 100644 --- a/thirdparty/zstd/common/xxhash.c +++ b/thirdparty/zstd/common/xxhash.c @@ -66,10 +66,10 @@ /* #define XXH_ACCEPT_NULL_INPUT_POINTER 1 */ /*!XXH_FORCE_NATIVE_FORMAT : - * By default, xxHash library provides endian-independant Hash values, based on little-endian convention. + * By default, xxHash library provides endian-independent Hash values, based on little-endian convention. * Results are therefore identical for little-endian and big-endian CPU. * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. - * Should endian-independance be of no importance for your application, you may set the #define below to 1, + * Should endian-independence be of no importance for your application, you may set the #define below to 1, * to improve speed for Big-endian CPU. * This option has no impact on Little_Endian CPU. */ diff --git a/thirdparty/zstd/common/zstd_internal.h b/thirdparty/zstd/common/zstd_internal.h index edeb74b9c3..31f756ab58 100644 --- a/thirdparty/zstd/common/zstd_internal.h +++ b/thirdparty/zstd/common/zstd_internal.h @@ -53,8 +53,50 @@ extern "C" { #undef MAX #define MIN(a,b) ((a)<(b) ? (a) : (b)) #define MAX(a,b) ((a)>(b) ? (a) : (b)) -#define CHECK_F(f) { size_t const errcod = f; if (ERR_isError(errcod)) return errcod; } /* check and Forward error code */ -#define CHECK_E(f, e) { size_t const errcod = f; if (ERR_isError(errcod)) return ERROR(e); } /* check and send Error code */ + +/** + * Return the specified error if the condition evaluates to true. + * + * In debug modes, prints additional information. In order to do that + * (particularly, printing the conditional that failed), this can't just wrap + * RETURN_ERROR(). + */ +#define RETURN_ERROR_IF(cond, err, ...) \ + if (cond) { \ + RAWLOG(3, "%s:%d: ERROR!: check %s failed, returning %s", __FILE__, __LINE__, ZSTD_QUOTE(cond), ZSTD_QUOTE(ERROR(err))); \ + RAWLOG(3, ": " __VA_ARGS__); \ + RAWLOG(3, "\n"); \ + return ERROR(err); \ + } + +/** + * Unconditionally return the specified error. + * + * In debug modes, prints additional information. + */ +#define RETURN_ERROR(err, ...) \ + do { \ + RAWLOG(3, "%s:%d: ERROR!: unconditional check failed, returning %s", __FILE__, __LINE__, ZSTD_QUOTE(ERROR(err))); \ + RAWLOG(3, ": " __VA_ARGS__); \ + RAWLOG(3, "\n"); \ + return ERROR(err); \ + } while(0); + +/** + * If the provided expression evaluates to an error code, returns that error code. + * + * In debug modes, prints additional information. + */ +#define FORWARD_IF_ERROR(err, ...) \ + do { \ + size_t const err_code = (err); \ + if (ERR_isError(err_code)) { \ + RAWLOG(3, "%s:%d: ERROR!: forwarding error in %s: %s", __FILE__, __LINE__, ZSTD_QUOTE(err), ERR_getErrorName(err_code)); \ + RAWLOG(3, ": " __VA_ARGS__); \ + RAWLOG(3, "\n"); \ + return err_code; \ + } \ + } while(0); /*-************************************* @@ -200,6 +242,17 @@ typedef struct { U32 longLengthPos; } seqStore_t; +/** + * Contains the compressed frame size and an upper-bound for the decompressed frame size. + * Note: before using `compressedSize`, check for errors using ZSTD_isError(). + * similarly, before using `decompressedBound`, check for errors using: + * `decompressedBound != ZSTD_CONTENTSIZE_ERROR` + */ +typedef struct { + size_t compressedSize; + unsigned long long decompressedBound; +} ZSTD_frameSizeInfo; /* decompress & legacy */ + const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); /* compress & dictBuilder */ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr); /* compress, dictBuilder, decodeCorpus (shouldn't get its definition from here) */ diff --git a/thirdparty/zstd/compress/fse_compress.c b/thirdparty/zstd/compress/fse_compress.c index 60f357bbd2..68b47e1093 100644 --- a/thirdparty/zstd/compress/fse_compress.c +++ b/thirdparty/zstd/compress/fse_compress.c @@ -129,9 +129,9 @@ size_t FSE_buildCTable_wksp(FSE_CTable* ct, { U32 position = 0; U32 symbol; for (symbol=0; symbol<=maxSymbolValue; symbol++) { - int nbOccurences; + int nbOccurrences; int const freq = normalizedCounter[symbol]; - for (nbOccurences=0; nbOccurences<freq; nbOccurences++) { + for (nbOccurrences=0; nbOccurrences<freq; nbOccurrences++) { tableSymbol[position] = (FSE_FUNCTION_TYPE)symbol; position = (position + step) & tableMask; while (position > highThreshold) diff --git a/thirdparty/zstd/compress/zstd_compress.c b/thirdparty/zstd/compress/zstd_compress.c index c2c9d3bc55..2e163c8bf3 100644 --- a/thirdparty/zstd/compress/zstd_compress.c +++ b/thirdparty/zstd/compress/zstd_compress.c @@ -103,12 +103,31 @@ ZSTD_CCtx* ZSTD_initStaticCCtx(void *workspace, size_t workspaceSize) return cctx; } +/** + * Clears and frees all of the dictionaries in the CCtx. + */ +static void ZSTD_clearAllDicts(ZSTD_CCtx* cctx) +{ + ZSTD_free(cctx->localDict.dictBuffer, cctx->customMem); + ZSTD_freeCDict(cctx->localDict.cdict); + memset(&cctx->localDict, 0, sizeof(cctx->localDict)); + memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict)); + cctx->cdict = NULL; +} + +static size_t ZSTD_sizeof_localDict(ZSTD_localDict dict) +{ + size_t const bufferSize = dict.dictBuffer != NULL ? dict.dictSize : 0; + size_t const cdictSize = ZSTD_sizeof_CDict(dict.cdict); + return bufferSize + cdictSize; +} + static void ZSTD_freeCCtxContent(ZSTD_CCtx* cctx) { assert(cctx != NULL); assert(cctx->staticSize == 0); ZSTD_free(cctx->workSpace, cctx->customMem); cctx->workSpace = NULL; - ZSTD_freeCDict(cctx->cdictLocal); cctx->cdictLocal = NULL; + ZSTD_clearAllDicts(cctx); #ifdef ZSTD_MULTITHREAD ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = NULL; #endif @@ -117,7 +136,8 @@ static void ZSTD_freeCCtxContent(ZSTD_CCtx* cctx) size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx) { if (cctx==NULL) return 0; /* support free on NULL */ - if (cctx->staticSize) return ERROR(memory_allocation); /* not compatible with static CCtx */ + RETURN_ERROR_IF(cctx->staticSize, memory_allocation, + "not compatible with static CCtx"); ZSTD_freeCCtxContent(cctx); ZSTD_free(cctx, cctx->customMem); return 0; @@ -139,7 +159,7 @@ size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx) { if (cctx==NULL) return 0; /* support sizeof on NULL */ return sizeof(*cctx) + cctx->workSpaceSize - + ZSTD_sizeof_CDict(cctx->cdictLocal) + + ZSTD_sizeof_localDict(cctx->localDict) + ZSTD_sizeof_mtctx(cctx); } @@ -195,7 +215,7 @@ size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params) } size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) { - if (!cctxParams) { return ERROR(GENERIC); } + RETURN_ERROR_IF(!cctxParams, GENERIC); memset(cctxParams, 0, sizeof(*cctxParams)); cctxParams->compressionLevel = compressionLevel; cctxParams->fParams.contentSizeFlag = 1; @@ -204,8 +224,8 @@ size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params) { - if (!cctxParams) { return ERROR(GENERIC); } - CHECK_F( ZSTD_checkCParams(params.cParams) ); + RETURN_ERROR_IF(!cctxParams, GENERIC); + FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) ); memset(cctxParams, 0, sizeof(*cctxParams)); cctxParams->cParams = params.cParams; cctxParams->fParams = params.fParams; @@ -359,6 +379,12 @@ ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param) bounds.upperBound = ZSTD_dictForceCopy; /* note : how to ensure at compile time that this is the highest value enum ? */ return bounds; + case ZSTD_c_literalCompressionMode: + ZSTD_STATIC_ASSERT(ZSTD_lcm_auto < ZSTD_lcm_huffman && ZSTD_lcm_huffman < ZSTD_lcm_uncompressed); + bounds.lowerBound = ZSTD_lcm_auto; + bounds.upperBound = ZSTD_lcm_uncompressed; + return bounds; + default: { ZSTD_bounds const boundError = { ERROR(parameter_unsupported), 0, 0 }; return boundError; @@ -378,10 +404,22 @@ static int ZSTD_cParam_withinBounds(ZSTD_cParameter cParam, int value) return 1; } -#define BOUNDCHECK(cParam, val) { \ - if (!ZSTD_cParam_withinBounds(cParam,val)) { \ - return ERROR(parameter_outOfBound); \ -} } +/* ZSTD_cParam_clampBounds: + * Clamps the value into the bounded range. + */ +static size_t ZSTD_cParam_clampBounds(ZSTD_cParameter cParam, int* value) +{ + ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam); + if (ZSTD_isError(bounds.error)) return bounds.error; + if (*value < bounds.lowerBound) *value = bounds.lowerBound; + if (*value > bounds.upperBound) *value = bounds.upperBound; + return 0; +} + +#define BOUNDCHECK(cParam, val) { \ + RETURN_ERROR_IF(!ZSTD_cParam_withinBounds(cParam,val), \ + parameter_outOfBound); \ +} static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param) @@ -413,6 +451,7 @@ static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param) case ZSTD_c_ldmBucketSizeLog: case ZSTD_c_ldmHashRateLog: case ZSTD_c_forceAttachDict: + case ZSTD_c_literalCompressionMode: default: return 0; } @@ -425,18 +464,17 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value) if (ZSTD_isUpdateAuthorized(param)) { cctx->cParamsChanged = 1; } else { - return ERROR(stage_wrong); + RETURN_ERROR(stage_wrong); } } switch(param) { - case ZSTD_c_format : - return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); + case ZSTD_c_nbWorkers: + RETURN_ERROR_IF((value!=0) && cctx->staticSize, parameter_unsupported, + "MT not compatible with static alloc"); + break; case ZSTD_c_compressionLevel: - if (cctx->cdict) return ERROR(stage_wrong); - return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); - case ZSTD_c_windowLog: case ZSTD_c_hashLog: case ZSTD_c_chainLog: @@ -444,49 +482,32 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value) case ZSTD_c_minMatch: case ZSTD_c_targetLength: case ZSTD_c_strategy: - if (cctx->cdict) return ERROR(stage_wrong); - return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); - + case ZSTD_c_ldmHashRateLog: + case ZSTD_c_format: case ZSTD_c_contentSizeFlag: case ZSTD_c_checksumFlag: case ZSTD_c_dictIDFlag: - return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); - - case ZSTD_c_forceMaxWindow : /* Force back-references to remain < windowSize, - * even when referencing into Dictionary content. - * default : 0 when using a CDict, 1 when using a Prefix */ - return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); - + case ZSTD_c_forceMaxWindow: case ZSTD_c_forceAttachDict: - return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); - - case ZSTD_c_nbWorkers: - if ((value!=0) && cctx->staticSize) { - return ERROR(parameter_unsupported); /* MT not compatible with static alloc */ - } - return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); - + case ZSTD_c_literalCompressionMode: case ZSTD_c_jobSize: case ZSTD_c_overlapLog: case ZSTD_c_rsyncable: - return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); - case ZSTD_c_enableLongDistanceMatching: case ZSTD_c_ldmHashLog: case ZSTD_c_ldmMinMatch: case ZSTD_c_ldmBucketSizeLog: - case ZSTD_c_ldmHashRateLog: - if (cctx->cdict) return ERROR(stage_wrong); - return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); + break; - default: return ERROR(parameter_unsupported); + default: RETURN_ERROR(parameter_unsupported); } + return ZSTD_CCtxParams_setParameter(&cctx->requestedParams, param, value); } -size_t ZSTD_CCtxParam_setParameter(ZSTD_CCtx_params* CCtxParams, - ZSTD_cParameter param, int value) +size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams, + ZSTD_cParameter param, int value) { - DEBUGLOG(4, "ZSTD_CCtxParam_setParameter (%i, %i)", (int)param, value); + DEBUGLOG(4, "ZSTD_CCtxParams_setParameter (%i, %i)", (int)param, value); switch(param) { case ZSTD_c_format : @@ -495,11 +516,9 @@ size_t ZSTD_CCtxParam_setParameter(ZSTD_CCtx_params* CCtxParams, return (size_t)CCtxParams->format; case ZSTD_c_compressionLevel : { - int cLevel = value; - if (cLevel > ZSTD_maxCLevel()) cLevel = ZSTD_maxCLevel(); - if (cLevel < ZSTD_minCLevel()) cLevel = ZSTD_minCLevel(); - if (cLevel) { /* 0 : does not change current level */ - CCtxParams->compressionLevel = cLevel; + FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value)); + if (value) { /* 0 : does not change current level */ + CCtxParams->compressionLevel = value; } if (CCtxParams->compressionLevel >= 0) return CCtxParams->compressionLevel; return 0; /* return type (size_t) cannot represent negative values */ @@ -573,33 +592,55 @@ size_t ZSTD_CCtxParam_setParameter(ZSTD_CCtx_params* CCtxParams, return CCtxParams->attachDictPref; } + case ZSTD_c_literalCompressionMode : { + const ZSTD_literalCompressionMode_e lcm = (ZSTD_literalCompressionMode_e)value; + BOUNDCHECK(ZSTD_c_literalCompressionMode, lcm); + CCtxParams->literalCompressionMode = lcm; + return CCtxParams->literalCompressionMode; + } + case ZSTD_c_nbWorkers : #ifndef ZSTD_MULTITHREAD - if (value!=0) return ERROR(parameter_unsupported); + RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); return 0; #else - return ZSTDMT_CCtxParam_setNbWorkers(CCtxParams, value); + FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value)); + CCtxParams->nbWorkers = value; + return CCtxParams->nbWorkers; #endif case ZSTD_c_jobSize : #ifndef ZSTD_MULTITHREAD - return ERROR(parameter_unsupported); + RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); + return 0; #else - return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_jobSize, value); + /* Adjust to the minimum non-default value. */ + if (value != 0 && value < ZSTDMT_JOBSIZE_MIN) + value = ZSTDMT_JOBSIZE_MIN; + FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value)); + assert(value >= 0); + CCtxParams->jobSize = value; + return CCtxParams->jobSize; #endif case ZSTD_c_overlapLog : #ifndef ZSTD_MULTITHREAD - return ERROR(parameter_unsupported); + RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); + return 0; #else - return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_overlapLog, value); + FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(ZSTD_c_overlapLog, &value)); + CCtxParams->overlapLog = value; + return CCtxParams->overlapLog; #endif case ZSTD_c_rsyncable : #ifndef ZSTD_MULTITHREAD - return ERROR(parameter_unsupported); + RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); + return 0; #else - return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_rsyncable, value); + FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(ZSTD_c_overlapLog, &value)); + CCtxParams->rsyncable = value; + return CCtxParams->rsyncable; #endif case ZSTD_c_enableLongDistanceMatching : @@ -625,21 +666,21 @@ size_t ZSTD_CCtxParam_setParameter(ZSTD_CCtx_params* CCtxParams, return CCtxParams->ldmParams.bucketSizeLog; case ZSTD_c_ldmHashRateLog : - if (value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN) - return ERROR(parameter_outOfBound); + RETURN_ERROR_IF(value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN, + parameter_outOfBound); CCtxParams->ldmParams.hashRateLog = value; return CCtxParams->ldmParams.hashRateLog; - default: return ERROR(parameter_unsupported); + default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); } } size_t ZSTD_CCtx_getParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value) { - return ZSTD_CCtxParam_getParameter(&cctx->requestedParams, param, value); + return ZSTD_CCtxParams_getParameter(&cctx->requestedParams, param, value); } -size_t ZSTD_CCtxParam_getParameter( +size_t ZSTD_CCtxParams_getParameter( ZSTD_CCtx_params* CCtxParams, ZSTD_cParameter param, int* value) { switch(param) @@ -686,6 +727,9 @@ size_t ZSTD_CCtxParam_getParameter( case ZSTD_c_forceAttachDict : *value = CCtxParams->attachDictPref; break; + case ZSTD_c_literalCompressionMode : + *value = CCtxParams->literalCompressionMode; + break; case ZSTD_c_nbWorkers : #ifndef ZSTD_MULTITHREAD assert(CCtxParams->nbWorkers == 0); @@ -694,7 +738,7 @@ size_t ZSTD_CCtxParam_getParameter( break; case ZSTD_c_jobSize : #ifndef ZSTD_MULTITHREAD - return ERROR(parameter_unsupported); + RETURN_ERROR(parameter_unsupported, "not compiled with multithreading"); #else assert(CCtxParams->jobSize <= INT_MAX); *value = (int)CCtxParams->jobSize; @@ -702,14 +746,14 @@ size_t ZSTD_CCtxParam_getParameter( #endif case ZSTD_c_overlapLog : #ifndef ZSTD_MULTITHREAD - return ERROR(parameter_unsupported); + RETURN_ERROR(parameter_unsupported, "not compiled with multithreading"); #else *value = CCtxParams->overlapLog; break; #endif case ZSTD_c_rsyncable : #ifndef ZSTD_MULTITHREAD - return ERROR(parameter_unsupported); + RETURN_ERROR(parameter_unsupported, "not compiled with multithreading"); #else *value = CCtxParams->rsyncable; break; @@ -729,7 +773,7 @@ size_t ZSTD_CCtxParam_getParameter( case ZSTD_c_ldmHashRateLog : *value = CCtxParams->ldmParams.hashRateLog; break; - default: return ERROR(parameter_unsupported); + default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); } return 0; } @@ -745,8 +789,8 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params) { DEBUGLOG(4, "ZSTD_CCtx_setParametersUsingCCtxParams"); - if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); - if (cctx->cdict) return ERROR(stage_wrong); + RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong); + RETURN_ERROR_IF(cctx->cdict, stage_wrong); cctx->requestedParams = *params; return 0; @@ -755,33 +799,71 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize) { DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %u bytes", (U32)pledgedSrcSize); - if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); + RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong); cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1; return 0; } +/** + * Initializes the local dict using the requested parameters. + * NOTE: This does not use the pledged src size, because it may be used for more + * than one compression. + */ +static size_t ZSTD_initLocalDict(ZSTD_CCtx* cctx) +{ + ZSTD_localDict* const dl = &cctx->localDict; + ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams( + &cctx->requestedParams, 0, dl->dictSize); + if (dl->dict == NULL) { + /* No local dictionary. */ + assert(dl->dictBuffer == NULL); + assert(dl->cdict == NULL); + assert(dl->dictSize == 0); + return 0; + } + if (dl->cdict != NULL) { + assert(cctx->cdict == dl->cdict); + /* Local dictionary already initialized. */ + return 0; + } + assert(dl->dictSize > 0); + assert(cctx->cdict == NULL); + assert(cctx->prefixDict.dict == NULL); + + dl->cdict = ZSTD_createCDict_advanced( + dl->dict, + dl->dictSize, + ZSTD_dlm_byRef, + dl->dictContentType, + cParams, + cctx->customMem); + RETURN_ERROR_IF(!dl->cdict, memory_allocation); + cctx->cdict = dl->cdict; + return 0; +} + size_t ZSTD_CCtx_loadDictionary_advanced( ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) { - if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); - if (cctx->staticSize) return ERROR(memory_allocation); /* no malloc for static CCtx */ + RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong); + RETURN_ERROR_IF(cctx->staticSize, memory_allocation, + "no malloc for static CCtx"); DEBUGLOG(4, "ZSTD_CCtx_loadDictionary_advanced (size: %u)", (U32)dictSize); - ZSTD_freeCDict(cctx->cdictLocal); /* in case one already exists */ - if (dict==NULL || dictSize==0) { /* no dictionary mode */ - cctx->cdictLocal = NULL; - cctx->cdict = NULL; + ZSTD_clearAllDicts(cctx); /* in case one already exists */ + if (dict == NULL || dictSize == 0) /* no dictionary mode */ + return 0; + if (dictLoadMethod == ZSTD_dlm_byRef) { + cctx->localDict.dict = dict; } else { - ZSTD_compressionParameters const cParams = - ZSTD_getCParamsFromCCtxParams(&cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, dictSize); - cctx->cdictLocal = ZSTD_createCDict_advanced( - dict, dictSize, - dictLoadMethod, dictContentType, - cParams, cctx->customMem); - cctx->cdict = cctx->cdictLocal; - if (cctx->cdictLocal == NULL) - return ERROR(memory_allocation); + void* dictBuffer = ZSTD_malloc(dictSize, cctx->customMem); + RETURN_ERROR_IF(!dictBuffer, memory_allocation); + memcpy(dictBuffer, dict, dictSize); + cctx->localDict.dictBuffer = dictBuffer; + cctx->localDict.dict = dictBuffer; } + cctx->localDict.dictSize = dictSize; + cctx->localDict.dictContentType = dictContentType; return 0; } @@ -801,9 +883,10 @@ ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, s size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) { - if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); + RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong); + /* Free the existing local cdict (if any) to save memory. */ + ZSTD_clearAllDicts(cctx); cctx->cdict = cdict; - memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict)); /* exclusive */ return 0; } @@ -815,8 +898,8 @@ size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSiz size_t ZSTD_CCtx_refPrefix_advanced( ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType) { - if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); - cctx->cdict = NULL; /* prefix discards any prior cdict */ + RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong); + ZSTD_clearAllDicts(cctx); cctx->prefixDict.dict = prefix; cctx->prefixDict.dictSize = prefixSize; cctx->prefixDict.dictContentType = dictContentType; @@ -834,8 +917,8 @@ size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset) } if ( (reset == ZSTD_reset_parameters) || (reset == ZSTD_reset_session_and_parameters) ) { - if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); - cctx->cdict = NULL; + RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong); + ZSTD_clearAllDicts(cctx); return ZSTD_CCtxParams_reset(&cctx->requestedParams); } return 0; @@ -888,10 +971,11 @@ static U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat) } /** ZSTD_adjustCParams_internal() : - optimize `cPar` for a given input (`srcSize` and `dictSize`). - mostly downsizing to reduce memory consumption and initialization latency. - Both `srcSize` and `dictSize` are optional (use 0 if unknown). - Note : cPar is assumed validated. Use ZSTD_checkCParams() to ensure this condition. */ + * optimize `cPar` for a specified input (`srcSize` and `dictSize`). + * mostly downsize to reduce memory consumption and initialization latency. + * `srcSize` can be ZSTD_CONTENTSIZE_UNKNOWN when not known. + * note : for the time being, `srcSize==0` means "unknown" too, for compatibility with older convention. + * condition : cPar is presumed validated (can be checked using ZSTD_checkCParams()). */ static ZSTD_compressionParameters ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar, unsigned long long srcSize, @@ -901,7 +985,7 @@ ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar, static const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1); assert(ZSTD_checkCParams(cPar)==0); - if (dictSize && (srcSize+1<2) /* srcSize unknown */ ) + if (dictSize && (srcSize+1<2) /* ZSTD_CONTENTSIZE_UNKNOWN and 0 mean "unknown" */ ) srcSize = minSrcSize; /* presumed small when there is a dictionary */ else if (srcSize == 0) srcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* 0 == unknown : presumed large */ @@ -922,7 +1006,7 @@ ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar, } if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN) - cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN; /* required for frame header */ + cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN; /* minimum wlog required for valid frame header */ return cPar; } @@ -932,7 +1016,7 @@ ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize) { - cPar = ZSTD_clampCParams(cPar); + cPar = ZSTD_clampCParams(cPar); /* resulting cPar is necessarily valid (all parameters within range) */ return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize); } @@ -973,8 +1057,7 @@ ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams, size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params) { - /* Estimate CCtx size is supported for single-threaded compression only. */ - if (params->nbWorkers > 0) { return ERROR(GENERIC); } + RETURN_ERROR_IF(params->nbWorkers > 0, GENERIC, "Estimate CCtx size is supported for single-threaded compression only."); { ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams(params, 0, 0); size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog); @@ -1022,10 +1105,12 @@ size_t ZSTD_estimateCCtxSize(int compressionLevel) size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params) { - if (params->nbWorkers > 0) { return ERROR(GENERIC); } - { size_t const CCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(params); - size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << params->cParams.windowLog); - size_t const inBuffSize = ((size_t)1 << params->cParams.windowLog) + blockSize; + RETURN_ERROR_IF(params->nbWorkers > 0, GENERIC, "Estimate CCtx size is supported for single-threaded compression only."); + { ZSTD_compressionParameters const cParams = + ZSTD_getCParamsFromCCtxParams(params, 0, 0); + size_t const CCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(params); + size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog); + size_t const inBuffSize = ((size_t)1 << cParams.windowLog) + blockSize; size_t const outBuffSize = ZSTD_compressBound(blockSize) + 1; size_t const streamingSize = inBuffSize + outBuffSize; @@ -1367,13 +1452,13 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, DEBUGLOG(4, "Need to resize workSpaceSize from %zuKB to %zuKB", zc->workSpaceSize >> 10, neededSpace >> 10); - /* static cctx : no resize, error out */ - if (zc->staticSize) return ERROR(memory_allocation); + + RETURN_ERROR_IF(zc->staticSize, memory_allocation, "static cctx : no resize"); zc->workSpaceSize = 0; ZSTD_free(zc->workSpace, zc->customMem); zc->workSpace = ZSTD_malloc(neededSpace, zc->customMem); - if (zc->workSpace == NULL) return ERROR(memory_allocation); + RETURN_ERROR_IF(zc->workSpace == NULL, memory_allocation); zc->workSpaceSize = neededSpace; zc->workSpaceOversizedDuration = 0; @@ -1644,7 +1729,7 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, ZSTD_buffered_policy_e zbuff) { DEBUGLOG(5, "ZSTD_copyCCtx_internal"); - if (srcCCtx->stage!=ZSTDcs_init) return ERROR(stage_wrong); + RETURN_ERROR_IF(srcCCtx->stage!=ZSTDcs_init, stage_wrong); memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem)); { ZSTD_CCtx_params params = dstCCtx->requestedParams; @@ -1777,7 +1862,8 @@ static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue) static size_t ZSTD_noCompressBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock) { U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(srcSize << 3); - if (srcSize + ZSTD_blockHeaderSize > dstCapacity) return ERROR(dstSize_tooSmall); + RETURN_ERROR_IF(srcSize + ZSTD_blockHeaderSize > dstCapacity, + dstSize_tooSmall); MEM_writeLE24(dst, cBlockHeader24); memcpy((BYTE*)dst + ZSTD_blockHeaderSize, src, srcSize); return ZSTD_blockHeaderSize + srcSize; @@ -1788,7 +1874,7 @@ static size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void BYTE* const ostart = (BYTE* const)dst; U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); - if (srcSize + flSize > dstCapacity) return ERROR(dstSize_tooSmall); + RETURN_ERROR_IF(srcSize + flSize > dstCapacity, dstSize_tooSmall); switch(flSize) { @@ -1878,7 +1964,7 @@ static size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } - if (dstCapacity < lhSize+1) return ERROR(dstSize_tooSmall); /* not enough space for compression */ + RETURN_ERROR_IF(dstCapacity < lhSize+1, dstSize_tooSmall, "not enough space for compression"); { HUF_repeat repeat = prevHuf->repeatMode; int const preferRepeat = strategy < ZSTD_lazy ? srcSize <= 1024 : 0; if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1; @@ -1960,7 +2046,7 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) * If x == 0: Return 0 * Else: Return floor(-log2(x / 256) * 256) */ -static unsigned const kInverseProbabiltyLog256[256] = { +static unsigned const kInverseProbabilityLog256[256] = { 0, 2048, 1792, 1642, 1536, 1453, 1386, 1329, 1280, 1236, 1197, 1162, 1130, 1100, 1073, 1047, 1024, 1001, 980, 960, 941, 923, 906, 889, 874, 859, 844, 830, 817, 804, 791, 779, 768, 756, 745, 734, @@ -1999,7 +2085,7 @@ static size_t ZSTD_entropyCost(unsigned const* count, unsigned const max, size_t if (count[s] != 0 && norm == 0) norm = 1; assert(count[s] < total); - cost += count[s] * kInverseProbabiltyLog256[norm]; + cost += count[s] * kInverseProbabilityLog256[norm]; } return cost >> 8; } @@ -2022,7 +2108,7 @@ static size_t ZSTD_crossEntropyCost(short const* norm, unsigned accuracyLog, unsigned const norm256 = normAcc << shift; assert(norm256 > 0); assert(norm256 < 256); - cost += count[s] * kInverseProbabiltyLog256[norm256]; + cost += count[s] * kInverseProbabilityLog256[norm256]; } return cost >> 8; } @@ -2050,21 +2136,17 @@ static size_t ZSTD_fseBitCost( unsigned s; FSE_CState_t cstate; FSE_initCState(&cstate, ctable); - if (ZSTD_getFSEMaxSymbolValue(ctable) < max) { - DEBUGLOG(5, "Repeat FSE_CTable has maxSymbolValue %u < %u", + RETURN_ERROR_IF(ZSTD_getFSEMaxSymbolValue(ctable) < max, GENERIC, + "Repeat FSE_CTable has maxSymbolValue %u < %u", ZSTD_getFSEMaxSymbolValue(ctable), max); - return ERROR(GENERIC); - } for (s = 0; s <= max; ++s) { unsigned const tableLog = cstate.stateLog; unsigned const badCost = (tableLog + 1) << kAccuracyLog; unsigned const bitCost = FSE_bitCost(cstate.symbolTT, tableLog, s, kAccuracyLog); if (count[s] == 0) continue; - if (bitCost >= badCost) { - DEBUGLOG(5, "Repeat FSE_CTable has Prob[%u] == 0", s); - return ERROR(GENERIC); - } + RETURN_ERROR_IF(bitCost >= badCost, GENERIC, + "Repeat FSE_CTable has Prob[%u] == 0", s); cost += count[s] * bitCost; } return cost >> kAccuracyLog; @@ -2080,7 +2162,7 @@ static size_t ZSTD_NCountCost(unsigned const* count, unsigned const max, BYTE wksp[FSE_NCOUNTBOUND]; S16 norm[MaxSeq + 1]; const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max); - CHECK_F(FSE_normalizeCount(norm, tableLog, count, nbSeq, max)); + FORWARD_IF_ERROR(FSE_normalizeCount(norm, tableLog, count, nbSeq, max)); return FSE_writeNCount(wksp, sizeof(wksp), norm, max, tableLog); } @@ -2186,15 +2268,15 @@ ZSTD_buildCTable(void* dst, size_t dstCapacity, switch (type) { case set_rle: - CHECK_F(FSE_buildCTable_rle(nextCTable, (BYTE)max)); - if (dstCapacity==0) return ERROR(dstSize_tooSmall); + FORWARD_IF_ERROR(FSE_buildCTable_rle(nextCTable, (BYTE)max)); + RETURN_ERROR_IF(dstCapacity==0, dstSize_tooSmall); *op = codeTable[0]; return 1; case set_repeat: memcpy(nextCTable, prevCTable, prevCTableSize); return 0; case set_basic: - CHECK_F(FSE_buildCTable_wksp(nextCTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize)); /* note : could be pre-calculated */ + FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize)); /* note : could be pre-calculated */ return 0; case set_compressed: { S16 norm[MaxSeq + 1]; @@ -2205,14 +2287,14 @@ ZSTD_buildCTable(void* dst, size_t dstCapacity, nbSeq_1--; } assert(nbSeq_1 > 1); - CHECK_F(FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max)); + FORWARD_IF_ERROR(FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max)); { size_t const NCountSize = FSE_writeNCount(op, oend - op, norm, max, tableLog); /* overflow protected */ - if (FSE_isError(NCountSize)) return NCountSize; - CHECK_F(FSE_buildCTable_wksp(nextCTable, norm, max, tableLog, workspace, workspaceSize)); + FORWARD_IF_ERROR(NCountSize); + FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, norm, max, tableLog, workspace, workspaceSize)); return NCountSize; } } - default: return assert(0), ERROR(GENERIC); + default: assert(0); RETURN_ERROR(GENERIC); } } @@ -2229,7 +2311,9 @@ ZSTD_encodeSequences_body( FSE_CState_t stateOffsetBits; FSE_CState_t stateLitLength; - CHECK_E(BIT_initCStream(&blockStream, dst, dstCapacity), dstSize_tooSmall); /* not enough space remaining */ + RETURN_ERROR_IF( + ERR_isError(BIT_initCStream(&blockStream, dst, dstCapacity)), + dstSize_tooSmall, "not enough space remaining"); DEBUGLOG(6, "available space for bitstream : %i (dstCapacity=%u)", (int)(blockStream.endPtr - blockStream.startPtr), (unsigned)dstCapacity); @@ -2303,7 +2387,7 @@ ZSTD_encodeSequences_body( FSE_flushCState(&blockStream, &stateLitLength); { size_t const streamSize = BIT_closeCStream(&blockStream); - if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */ + RETURN_ERROR_IF(streamSize==0, dstSize_tooSmall, "not enough space"); return streamSize; } } @@ -2368,6 +2452,21 @@ static size_t ZSTD_encodeSequences( sequences, nbSeq, longOffsets); } +static int ZSTD_disableLiteralsCompression(const ZSTD_CCtx_params* cctxParams) +{ + switch (cctxParams->literalCompressionMode) { + case ZSTD_lcm_huffman: + return 0; + case ZSTD_lcm_uncompressed: + return 1; + default: + assert(0 /* impossible: pre-validated */); + /* fall-through */ + case ZSTD_lcm_auto: + return (cctxParams->cParams.strategy == ZSTD_fast) && (cctxParams->cParams.targetLength > 0); + } +} + /* ZSTD_compressSequences_internal(): * actually compresses both literals and sequences */ MEM_STATIC size_t @@ -2403,22 +2502,22 @@ ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, /* Compress literals */ { const BYTE* const literals = seqStorePtr->litStart; size_t const litSize = seqStorePtr->lit - literals; - int const disableLiteralCompression = (cctxParams->cParams.strategy == ZSTD_fast) && (cctxParams->cParams.targetLength > 0); size_t const cSize = ZSTD_compressLiterals( &prevEntropy->huf, &nextEntropy->huf, - cctxParams->cParams.strategy, disableLiteralCompression, + cctxParams->cParams.strategy, + ZSTD_disableLiteralsCompression(cctxParams), op, dstCapacity, literals, litSize, workspace, wkspSize, bmi2); - if (ZSTD_isError(cSize)) - return cSize; + FORWARD_IF_ERROR(cSize); assert(cSize <= dstCapacity); op += cSize; } /* Sequences Header */ - if ((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead*/) return ERROR(dstSize_tooSmall); + RETURN_ERROR_IF((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead*/, + dstSize_tooSmall); if (nbSeq < 0x7F) *op++ = (BYTE)nbSeq; else if (nbSeq < LONGNBSEQ) @@ -2452,7 +2551,7 @@ ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, prevEntropy->fse.litlengthCTable, sizeof(prevEntropy->fse.litlengthCTable), workspace, wkspSize); - if (ZSTD_isError(countSize)) return countSize; + FORWARD_IF_ERROR(countSize); if (LLtype == set_compressed) lastNCount = op; op += countSize; @@ -2474,7 +2573,7 @@ ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, prevEntropy->fse.offcodeCTable, sizeof(prevEntropy->fse.offcodeCTable), workspace, wkspSize); - if (ZSTD_isError(countSize)) return countSize; + FORWARD_IF_ERROR(countSize); if (Offtype == set_compressed) lastNCount = op; op += countSize; @@ -2494,7 +2593,7 @@ ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, prevEntropy->fse.matchlengthCTable, sizeof(prevEntropy->fse.matchlengthCTable), workspace, wkspSize); - if (ZSTD_isError(countSize)) return countSize; + FORWARD_IF_ERROR(countSize); if (MLtype == set_compressed) lastNCount = op; op += countSize; @@ -2509,10 +2608,10 @@ ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, CTable_LitLength, llCodeTable, sequences, nbSeq, longOffsets, bmi2); - if (ZSTD_isError(bitstreamSize)) return bitstreamSize; + FORWARD_IF_ERROR(bitstreamSize); op += bitstreamSize; /* zstd versions <= 1.3.4 mistakenly report corruption when - * FSE_readNCount() recieves a buffer < 4 bytes. + * FSE_readNCount() receives a buffer < 4 bytes. * Fixed by https://github.com/facebook/zstd/pull/1146. * This can happen when the last set_compressed table present is 2 * bytes and the bitstream is only one byte. @@ -2552,7 +2651,7 @@ ZSTD_compressSequences(seqStore_t* seqStorePtr, */ if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity)) return 0; /* block not compressed */ - if (ZSTD_isError(cSize)) return cSize; + FORWARD_IF_ERROR(cSize); /* Check compressibility */ { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, cctxParams->cParams.strategy); @@ -2641,7 +2740,10 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, goto out; /* don't even attempt compression below a certain srcSize */ } ZSTD_resetSeqStore(&(zc->seqStore)); - ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy; /* required for optimal parser to read stats from dictionary */ + /* required for optimal parser to read stats from dictionary */ + ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy; + /* tell the optimal parser how we expect to compress literals */ + ms->opt.literalCompressionMode = zc->appliedParams.literalCompressionMode; /* a gap between an attached dict and the current window is not safe, * they must remain adjacent, @@ -2679,7 +2781,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, ldmSeqStore.seq = zc->ldmSequences; ldmSeqStore.capacity = zc->maxNbLdmSequences; /* Updates ldmSeqStore.size */ - CHECK_F(ZSTD_ldm_generateSequences(&zc->ldmState, &ldmSeqStore, + FORWARD_IF_ERROR(ZSTD_ldm_generateSequences(&zc->ldmState, &ldmSeqStore, &zc->appliedParams.ldmParams, src, srcSize)); /* Updates ldmSeqStore.pos */ @@ -2752,8 +2854,9 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, ZSTD_matchState_t* const ms = &cctx->blockState.matchState; U32 const lastBlock = lastFrameChunk & (blockSize >= remaining); - if (dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE) - return ERROR(dstSize_tooSmall); /* not enough space to store compressed block */ + RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE, + dstSize_tooSmall, + "not enough space to store compressed block"); if (remaining < blockSize) blockSize = remaining; if (ZSTD_window_needOverflowCorrection(ms->window, ip + blockSize)) { @@ -2774,11 +2877,11 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, { size_t cSize = ZSTD_compressBlock_internal(cctx, op+ZSTD_blockHeaderSize, dstCapacity-ZSTD_blockHeaderSize, ip, blockSize); - if (ZSTD_isError(cSize)) return cSize; + FORWARD_IF_ERROR(cSize); if (cSize == 0) { /* block is not compressible */ cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock); - if (ZSTD_isError(cSize)) return cSize; + FORWARD_IF_ERROR(cSize); } else { U32 const cBlockHeader24 = lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3); MEM_writeLE24(op, cBlockHeader24); @@ -2811,11 +2914,11 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, BYTE const windowLogByte = (BYTE)((params.cParams.windowLog - ZSTD_WINDOWLOG_ABSOLUTEMIN) << 3); U32 const fcsCode = params.fParams.contentSizeFlag ? (pledgedSrcSize>=256) + (pledgedSrcSize>=65536+256) + (pledgedSrcSize>=0xFFFFFFFFU) : 0; /* 0-3 */ - BYTE const frameHeaderDecriptionByte = (BYTE)(dictIDSizeCode + (checksumFlag<<2) + (singleSegment<<5) + (fcsCode<<6) ); + BYTE const frameHeaderDescriptionByte = (BYTE)(dictIDSizeCode + (checksumFlag<<2) + (singleSegment<<5) + (fcsCode<<6) ); size_t pos=0; assert(!(params.fParams.contentSizeFlag && pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN)); - if (dstCapacity < ZSTD_FRAMEHEADERSIZE_MAX) return ERROR(dstSize_tooSmall); + RETURN_ERROR_IF(dstCapacity < ZSTD_FRAMEHEADERSIZE_MAX, dstSize_tooSmall); DEBUGLOG(4, "ZSTD_writeFrameHeader : dictIDFlag : %u ; dictID : %u ; dictIDSizeCode : %u", !params.fParams.noDictIDFlag, (unsigned)dictID, (unsigned)dictIDSizeCode); @@ -2823,7 +2926,7 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, MEM_writeLE32(dst, ZSTD_MAGICNUMBER); pos = 4; } - op[pos++] = frameHeaderDecriptionByte; + op[pos++] = frameHeaderDescriptionByte; if (!singleSegment) op[pos++] = windowLogByte; switch(dictIDSizeCode) { @@ -2847,11 +2950,11 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, /* ZSTD_writeLastEmptyBlock() : * output an empty Block with end-of-frame mark to complete a frame * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h)) - * or an error code if `dstCapcity` is too small (<ZSTD_blockHeaderSize) + * or an error code if `dstCapacity` is too small (<ZSTD_blockHeaderSize) */ size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity) { - if (dstCapacity < ZSTD_blockHeaderSize) return ERROR(dstSize_tooSmall); + RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize, dstSize_tooSmall); { U32 const cBlockHeader24 = 1 /*lastBlock*/ + (((U32)bt_raw)<<1); /* 0 size */ MEM_writeLE24(dst, cBlockHeader24); return ZSTD_blockHeaderSize; @@ -2860,10 +2963,9 @@ size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity) size_t ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq) { - if (cctx->stage != ZSTDcs_init) - return ERROR(stage_wrong); - if (cctx->appliedParams.ldmParams.enableLdm) - return ERROR(parameter_unsupported); + RETURN_ERROR_IF(cctx->stage != ZSTDcs_init, stage_wrong); + RETURN_ERROR_IF(cctx->appliedParams.ldmParams.enableLdm, + parameter_unsupported); cctx->externSeqStore.seq = seq; cctx->externSeqStore.size = nbSeq; cctx->externSeqStore.capacity = nbSeq; @@ -2882,12 +2984,13 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u, srcSize: %u", cctx->stage, (unsigned)srcSize); - if (cctx->stage==ZSTDcs_created) return ERROR(stage_wrong); /* missing init (ZSTD_compressBegin) */ + RETURN_ERROR_IF(cctx->stage==ZSTDcs_created, stage_wrong, + "missing init (ZSTD_compressBegin)"); if (frame && (cctx->stage==ZSTDcs_init)) { fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->appliedParams, cctx->pledgedSrcSizePlusOne-1, cctx->dictID); - if (ZSTD_isError(fhSize)) return fhSize; + FORWARD_IF_ERROR(fhSize); dstCapacity -= fhSize; dst = (char*)dst + fhSize; cctx->stage = ZSTDcs_ongoing; @@ -2922,17 +3025,18 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, { size_t const cSize = frame ? ZSTD_compress_frameChunk (cctx, dst, dstCapacity, src, srcSize, lastFrameChunk) : ZSTD_compressBlock_internal (cctx, dst, dstCapacity, src, srcSize); - if (ZSTD_isError(cSize)) return cSize; + FORWARD_IF_ERROR(cSize); cctx->consumedSrcSize += srcSize; cctx->producedCSize += (cSize + fhSize); assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0)); if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */ ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1); - if (cctx->consumedSrcSize+1 > cctx->pledgedSrcSizePlusOne) { - DEBUGLOG(4, "error : pledgedSrcSize = %u, while realSrcSize >= %u", - (unsigned)cctx->pledgedSrcSizePlusOne-1, (unsigned)cctx->consumedSrcSize); - return ERROR(srcSize_wrong); - } + RETURN_ERROR_IF( + cctx->consumedSrcSize+1 > cctx->pledgedSrcSizePlusOne, + srcSize_wrong, + "error : pledgedSrcSize = %u, while realSrcSize >= %u", + (unsigned)cctx->pledgedSrcSizePlusOne-1, + (unsigned)cctx->consumedSrcSize); } return cSize + fhSize; } @@ -2957,7 +3061,7 @@ size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx) size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { size_t const blockSizeMax = ZSTD_getBlockSize(cctx); - if (srcSize > blockSizeMax) return ERROR(srcSize_wrong); + RETURN_ERROR_IF(srcSize > blockSizeMax, srcSize_wrong); return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0 /* frame mode */, 0 /* last chunk */); } @@ -3020,9 +3124,9 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, NOTE: This behavior is not standard and could be improved in the future. */ static size_t ZSTD_checkDictNCount(short* normalizedCounter, unsigned dictMaxSymbolValue, unsigned maxSymbolValue) { U32 s; - if (dictMaxSymbolValue < maxSymbolValue) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(dictMaxSymbolValue < maxSymbolValue, dictionary_corrupted); for (s = 0; s <= maxSymbolValue; ++s) { - if (normalizedCounter[s] == 0) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(normalizedCounter[s] == 0, dictionary_corrupted); } return 0; } @@ -3060,53 +3164,56 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, { unsigned maxSymbolValue = 255; size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.huf.CTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr); - if (HUF_isError(hufHeaderSize)) return ERROR(dictionary_corrupted); - if (maxSymbolValue < 255) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(HUF_isError(hufHeaderSize), dictionary_corrupted); + RETURN_ERROR_IF(maxSymbolValue < 255, dictionary_corrupted); dictPtr += hufHeaderSize; } { unsigned offcodeLog; size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr); - if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted); - if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(FSE_isError(offcodeHeaderSize), dictionary_corrupted); + RETURN_ERROR_IF(offcodeLog > OffFSELog, dictionary_corrupted); /* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */ /* fill all offset symbols to avoid garbage at end of table */ - CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.offcodeCTable, - offcodeNCount, MaxOff, offcodeLog, - workspace, HUF_WORKSPACE_SIZE), - dictionary_corrupted); + RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp( + bs->entropy.fse.offcodeCTable, + offcodeNCount, MaxOff, offcodeLog, + workspace, HUF_WORKSPACE_SIZE)), + dictionary_corrupted); dictPtr += offcodeHeaderSize; } { short matchlengthNCount[MaxML+1]; unsigned matchlengthMaxValue = MaxML, matchlengthLog; size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr); - if (FSE_isError(matchlengthHeaderSize)) return ERROR(dictionary_corrupted); - if (matchlengthLog > MLFSELog) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(FSE_isError(matchlengthHeaderSize), dictionary_corrupted); + RETURN_ERROR_IF(matchlengthLog > MLFSELog, dictionary_corrupted); /* Every match length code must have non-zero probability */ - CHECK_F( ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML)); - CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.matchlengthCTable, - matchlengthNCount, matchlengthMaxValue, matchlengthLog, - workspace, HUF_WORKSPACE_SIZE), - dictionary_corrupted); + FORWARD_IF_ERROR( ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML)); + RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp( + bs->entropy.fse.matchlengthCTable, + matchlengthNCount, matchlengthMaxValue, matchlengthLog, + workspace, HUF_WORKSPACE_SIZE)), + dictionary_corrupted); dictPtr += matchlengthHeaderSize; } { short litlengthNCount[MaxLL+1]; unsigned litlengthMaxValue = MaxLL, litlengthLog; size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr); - if (FSE_isError(litlengthHeaderSize)) return ERROR(dictionary_corrupted); - if (litlengthLog > LLFSELog) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(FSE_isError(litlengthHeaderSize), dictionary_corrupted); + RETURN_ERROR_IF(litlengthLog > LLFSELog, dictionary_corrupted); /* Every literal length code must have non-zero probability */ - CHECK_F( ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL)); - CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.litlengthCTable, - litlengthNCount, litlengthMaxValue, litlengthLog, - workspace, HUF_WORKSPACE_SIZE), - dictionary_corrupted); + FORWARD_IF_ERROR( ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL)); + RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp( + bs->entropy.fse.litlengthCTable, + litlengthNCount, litlengthMaxValue, litlengthLog, + workspace, HUF_WORKSPACE_SIZE)), + dictionary_corrupted); dictPtr += litlengthHeaderSize; } - if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(dictPtr+12 > dictEnd, dictionary_corrupted); bs->rep[0] = MEM_readLE32(dictPtr+0); bs->rep[1] = MEM_readLE32(dictPtr+4); bs->rep[2] = MEM_readLE32(dictPtr+8); @@ -3119,19 +3226,19 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, offcodeMax = ZSTD_highbit32(maxOffset); /* Calculate minimum offset code required to represent maxOffset */ } /* All offset values <= dictContentSize + 128 KB must be representable */ - CHECK_F (ZSTD_checkDictNCount(offcodeNCount, offcodeMaxValue, MIN(offcodeMax, MaxOff))); + FORWARD_IF_ERROR(ZSTD_checkDictNCount(offcodeNCount, offcodeMaxValue, MIN(offcodeMax, MaxOff))); /* All repCodes must be <= dictContentSize and != 0*/ { U32 u; for (u=0; u<3; u++) { - if (bs->rep[u] == 0) return ERROR(dictionary_corrupted); - if (bs->rep[u] > dictContentSize) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(bs->rep[u] == 0, dictionary_corrupted); + RETURN_ERROR_IF(bs->rep[u] > dictContentSize, dictionary_corrupted); } } bs->entropy.huf.repeatMode = HUF_repeat_valid; bs->entropy.fse.offcode_repeatMode = FSE_repeat_valid; bs->entropy.fse.matchlength_repeatMode = FSE_repeat_valid; bs->entropy.fse.litlength_repeatMode = FSE_repeat_valid; - CHECK_F(ZSTD_loadDictionaryContent(ms, params, dictPtr, dictContentSize, dtlm)); + FORWARD_IF_ERROR(ZSTD_loadDictionaryContent(ms, params, dictPtr, dictContentSize, dtlm)); return dictID; } } @@ -3161,8 +3268,7 @@ ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, DEBUGLOG(4, "raw content dictionary detected"); return ZSTD_loadDictionaryContent(ms, params, dict, dictSize, dtlm); } - if (dictContentType == ZSTD_dct_fullDict) - return ERROR(dictionary_wrong); + RETURN_ERROR_IF(dictContentType == ZSTD_dct_fullDict, dictionary_wrong); assert(0); /* impossible */ } @@ -3189,13 +3295,13 @@ static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, return ZSTD_resetCCtx_usingCDict(cctx, cdict, params, pledgedSrcSize, zbuff); } - CHECK_F( ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, + FORWARD_IF_ERROR( ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, ZSTDcrp_continue, zbuff) ); { size_t const dictID = ZSTD_compress_insertDictionary( cctx->blockState.prevCBlock, &cctx->blockState.matchState, ¶ms, dict, dictSize, dictContentType, dtlm, cctx->entropyWorkspace); - if (ZSTD_isError(dictID)) return dictID; + FORWARD_IF_ERROR(dictID); assert(dictID <= (size_t)(U32)-1); cctx->dictID = (U32)dictID; } @@ -3212,7 +3318,7 @@ size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx, { DEBUGLOG(4, "ZSTD_compressBegin_advanced_internal: wlog=%u", params.cParams.windowLog); /* compression parameters verification and optimization */ - CHECK_F( ZSTD_checkCParams(params.cParams) ); + FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) ); return ZSTD_compressBegin_internal(cctx, dict, dictSize, dictContentType, dtlm, cdict, @@ -3260,12 +3366,12 @@ static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) size_t fhSize = 0; DEBUGLOG(4, "ZSTD_writeEpilogue"); - if (cctx->stage == ZSTDcs_created) return ERROR(stage_wrong); /* init missing */ + RETURN_ERROR_IF(cctx->stage == ZSTDcs_created, stage_wrong, "init missing"); /* special case : empty frame */ if (cctx->stage == ZSTDcs_init) { fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->appliedParams, 0, 0); - if (ZSTD_isError(fhSize)) return fhSize; + FORWARD_IF_ERROR(fhSize); dstCapacity -= fhSize; op += fhSize; cctx->stage = ZSTDcs_ongoing; @@ -3274,7 +3380,7 @@ static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) if (cctx->stage != ZSTDcs_ending) { /* write one last empty block, make it the "last" block */ U32 const cBlockHeader24 = 1 /* last block */ + (((U32)bt_raw)<<1) + 0; - if (dstCapacity<4) return ERROR(dstSize_tooSmall); + RETURN_ERROR_IF(dstCapacity<4, dstSize_tooSmall); MEM_writeLE32(op, cBlockHeader24); op += ZSTD_blockHeaderSize; dstCapacity -= ZSTD_blockHeaderSize; @@ -3282,7 +3388,7 @@ static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) if (cctx->appliedParams.fParams.checksumFlag) { U32 const checksum = (U32) XXH64_digest(&cctx->xxhState); - if (dstCapacity<4) return ERROR(dstSize_tooSmall); + RETURN_ERROR_IF(dstCapacity<4, dstSize_tooSmall); DEBUGLOG(4, "ZSTD_writeEpilogue: write checksum : %08X", (unsigned)checksum); MEM_writeLE32(op, checksum); op += 4; @@ -3300,18 +3406,20 @@ size_t ZSTD_compressEnd (ZSTD_CCtx* cctx, size_t const cSize = ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1 /* frame mode */, 1 /* last chunk */); - if (ZSTD_isError(cSize)) return cSize; + FORWARD_IF_ERROR(cSize); endResult = ZSTD_writeEpilogue(cctx, (char*)dst + cSize, dstCapacity-cSize); - if (ZSTD_isError(endResult)) return endResult; + FORWARD_IF_ERROR(endResult); assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0)); if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */ ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1); DEBUGLOG(4, "end of frame : controlling src size"); - if (cctx->pledgedSrcSizePlusOne != cctx->consumedSrcSize+1) { - DEBUGLOG(4, "error : pledgedSrcSize = %u, while realSrcSize = %u", - (unsigned)cctx->pledgedSrcSizePlusOne-1, (unsigned)cctx->consumedSrcSize); - return ERROR(srcSize_wrong); - } } + RETURN_ERROR_IF( + cctx->pledgedSrcSizePlusOne != cctx->consumedSrcSize+1, + srcSize_wrong, + "error : pledgedSrcSize = %u, while realSrcSize = %u", + (unsigned)cctx->pledgedSrcSizePlusOne-1, + (unsigned)cctx->consumedSrcSize); + } return cSize + endResult; } @@ -3339,7 +3447,7 @@ size_t ZSTD_compress_advanced (ZSTD_CCtx* cctx, ZSTD_parameters params) { DEBUGLOG(4, "ZSTD_compress_advanced"); - CHECK_F(ZSTD_checkCParams(params.cParams)); + FORWARD_IF_ERROR(ZSTD_checkCParams(params.cParams)); return ZSTD_compress_internal(cctx, dst, dstCapacity, src, srcSize, @@ -3356,7 +3464,7 @@ size_t ZSTD_compress_advanced_internal( ZSTD_CCtx_params params) { DEBUGLOG(4, "ZSTD_compress_advanced_internal (srcSize:%u)", (unsigned)srcSize); - CHECK_F( ZSTD_compressBegin_internal(cctx, + FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL, params, srcSize, ZSTDb_not_buffered) ); return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); @@ -3440,7 +3548,7 @@ static size_t ZSTD_initCDict_internal( void* const internalBuffer = ZSTD_malloc(dictSize, cdict->customMem); cdict->dictBuffer = internalBuffer; cdict->dictContent = internalBuffer; - if (!internalBuffer) return ERROR(memory_allocation); + RETURN_ERROR_IF(!internalBuffer, memory_allocation); memcpy(internalBuffer, dictBuffer, dictSize); } cdict->dictContentSize = dictSize; @@ -3466,7 +3574,7 @@ static size_t ZSTD_initCDict_internal( &cdict->cBlockState, &cdict->matchState, ¶ms, cdict->dictContent, cdict->dictContentSize, dictContentType, ZSTD_dtlm_full, cdict->workspace); - if (ZSTD_isError(dictID)) return dictID; + FORWARD_IF_ERROR(dictID); assert(dictID <= (size_t)(U32)-1); cdict->dictID = (U32)dictID; } @@ -3596,7 +3704,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced( ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize) { DEBUGLOG(4, "ZSTD_compressBegin_usingCDict_advanced"); - if (cdict==NULL) return ERROR(dictionary_wrong); + RETURN_ERROR_IF(cdict==NULL, dictionary_wrong); { ZSTD_CCtx_params params = cctx->requestedParams; params.cParams = ZSTD_getCParamsFromCDict(cdict); /* Increase window log to fit the entire dictionary and source if the @@ -3632,7 +3740,7 @@ size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx, const void* src, size_t srcSize, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams) { - CHECK_F (ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, srcSize)); /* will check if cdict != NULL */ + FORWARD_IF_ERROR(ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, srcSize)); /* will check if cdict != NULL */ return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); } @@ -3700,7 +3808,7 @@ static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx, assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ - CHECK_F( ZSTD_compressBegin_internal(cctx, + FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx, dict, dictSize, dictContentType, ZSTD_dtlm_fast, cdict, params, pledgedSrcSize, @@ -3718,13 +3826,17 @@ static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx, /* ZSTD_resetCStream(): * pledgedSrcSize == 0 means "unknown" */ -size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize) +size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pss) { - ZSTD_CCtx_params params = zcs->requestedParams; + /* temporary : 0 interpreted as "unknown" during transition period. + * Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN. + * 0 will be interpreted as "empty" in the future. + */ + U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss; DEBUGLOG(4, "ZSTD_resetCStream: pledgedSrcSize = %u", (unsigned)pledgedSrcSize); - if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; - params.fParams.contentSizeFlag = 1; - return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dct_auto, zcs->cdict, params, pledgedSrcSize); + FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) ); + FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) ); + return 0; } /*! ZSTD_initCStream_internal() : @@ -3736,32 +3848,18 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize) { DEBUGLOG(4, "ZSTD_initCStream_internal"); - params.cParams = ZSTD_getCParamsFromCCtxParams(¶ms, pledgedSrcSize, dictSize); + FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) ); + FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) ); assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); + zcs->requestedParams = params; assert(!((dict) && (cdict))); /* either dict or cdict, not both */ - - if (dict && dictSize >= 8) { - DEBUGLOG(4, "loading dictionary of size %u", (unsigned)dictSize); - if (zcs->staticSize) { /* static CCtx : never uses malloc */ - /* incompatible with internal cdict creation */ - return ERROR(memory_allocation); - } - ZSTD_freeCDict(zcs->cdictLocal); - zcs->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize, - ZSTD_dlm_byCopy, ZSTD_dct_auto, - params.cParams, zcs->customMem); - zcs->cdict = zcs->cdictLocal; - if (zcs->cdictLocal == NULL) return ERROR(memory_allocation); + if (dict) { + FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) ); } else { - if (cdict) { - params.cParams = ZSTD_getCParamsFromCDict(cdict); /* cParams are enforced from cdict; it includes windowLog */ - } - ZSTD_freeCDict(zcs->cdictLocal); - zcs->cdictLocal = NULL; - zcs->cdict = cdict; + /* Dictionary is cleared if !cdict */ + FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) ); } - - return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dct_auto, zcs->cdict, params, pledgedSrcSize); + return 0; } /* ZSTD_initCStream_usingCDict_advanced() : @@ -3772,22 +3870,20 @@ size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize) { DEBUGLOG(4, "ZSTD_initCStream_usingCDict_advanced"); - if (!cdict) return ERROR(dictionary_wrong); /* cannot handle NULL cdict (does not know what to do) */ - { ZSTD_CCtx_params params = zcs->requestedParams; - params.cParams = ZSTD_getCParamsFromCDict(cdict); - params.fParams = fParams; - return ZSTD_initCStream_internal(zcs, - NULL, 0, cdict, - params, pledgedSrcSize); - } + FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) ); + FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) ); + zcs->requestedParams.fParams = fParams; + FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) ); + return 0; } /* note : cdict must outlive compression session */ size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict) { - ZSTD_frameParameters const fParams = { 0 /* contentSizeFlag */, 0 /* checksum */, 0 /* hideDictID */ }; DEBUGLOG(4, "ZSTD_initCStream_usingCDict"); - return ZSTD_initCStream_usingCDict_advanced(zcs, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN); /* note : will check that cdict != NULL */ + FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) ); + FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, cdict) ); + return 0; } @@ -3797,33 +3893,53 @@ size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict) * dict is loaded with default parameters ZSTD_dm_auto and ZSTD_dlm_byCopy. */ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, - ZSTD_parameters params, unsigned long long pledgedSrcSize) + ZSTD_parameters params, unsigned long long pss) { - DEBUGLOG(4, "ZSTD_initCStream_advanced: pledgedSrcSize=%u, flag=%u", - (unsigned)pledgedSrcSize, params.fParams.contentSizeFlag); - CHECK_F( ZSTD_checkCParams(params.cParams) ); - if ((pledgedSrcSize==0) && (params.fParams.contentSizeFlag==0)) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* for compatibility with older programs relying on this behavior. Users should now specify ZSTD_CONTENTSIZE_UNKNOWN. This line will be removed in the future. */ + /* for compatibility with older programs relying on this behavior. + * Users should now specify ZSTD_CONTENTSIZE_UNKNOWN. + * This line will be removed in the future. + */ + U64 const pledgedSrcSize = (pss==0 && params.fParams.contentSizeFlag==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss; + DEBUGLOG(4, "ZSTD_initCStream_advanced"); + FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) ); + FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) ); + FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) ); zcs->requestedParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params); - return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL /*cdict*/, zcs->requestedParams, pledgedSrcSize); + FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) ); + return 0; } size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel) { - ZSTD_CCtxParams_init(&zcs->requestedParams, compressionLevel); - return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL, zcs->requestedParams, ZSTD_CONTENTSIZE_UNKNOWN); + DEBUGLOG(4, "ZSTD_initCStream_usingDict"); + FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) ); + FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) ); + FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) ); + return 0; } size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pss) { - U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss; /* temporary : 0 interpreted as "unknown" during transition period. Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN. `0` will be interpreted as "empty" in the future */ - ZSTD_CCtxParams_init(&zcs->requestedParams, compressionLevel); - return ZSTD_initCStream_internal(zcs, NULL, 0, NULL, zcs->requestedParams, pledgedSrcSize); + /* temporary : 0 interpreted as "unknown" during transition period. + * Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN. + * 0 will be interpreted as "empty" in the future. + */ + U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss; + DEBUGLOG(4, "ZSTD_initCStream_srcSize"); + FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) ); + FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, NULL) ); + FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) ); + FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) ); + return 0; } size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel) { DEBUGLOG(4, "ZSTD_initCStream"); - return ZSTD_initCStream_srcSize(zcs, compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN); + FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) ); + FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, NULL) ); + FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) ); + return 0; } /*====== Compression ======*/ @@ -3847,10 +3963,10 @@ static size_t ZSTD_limitCopy(void* dst, size_t dstCapacity, * internal function for all *compressStream*() variants * non-static, because can be called from zstdmt_compress.c * @return : hint size for next input */ -size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, - ZSTD_outBuffer* output, - ZSTD_inBuffer* input, - ZSTD_EndDirective const flushMode) +static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, + ZSTD_outBuffer* output, + ZSTD_inBuffer* input, + ZSTD_EndDirective const flushMode) { const char* const istart = (const char*)input->src; const char* const iend = istart + input->size; @@ -3873,8 +3989,7 @@ size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, switch(zcs->streamStage) { case zcss_init: - /* call ZSTD_initCStream() first ! */ - return ERROR(init_missing); + RETURN_ERROR(init_missing, "call ZSTD_initCStream() first!"); case zcss_load: if ( (flushMode == ZSTD_e_end) @@ -3884,7 +3999,7 @@ size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, size_t const cSize = ZSTD_compressEnd(zcs, op, oend-op, ip, iend-ip); DEBUGLOG(4, "ZSTD_compressEnd : cSize=%u", (unsigned)cSize); - if (ZSTD_isError(cSize)) return cSize; + FORWARD_IF_ERROR(cSize); ip = iend; op += cSize; zcs->frameEnded = 1; @@ -3925,7 +4040,7 @@ size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, zcs->inBuff + zcs->inToCompress, iSize) : ZSTD_compressContinue(zcs, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize); - if (ZSTD_isError(cSize)) return cSize; + FORWARD_IF_ERROR(cSize); zcs->frameEnded = lastBlock; /* prepare next block */ zcs->inBuffTarget = zcs->inBuffPos + zcs->blockSize; @@ -4001,7 +4116,7 @@ static size_t ZSTD_nextInputSizeHint_MTorST(const ZSTD_CCtx* cctx) size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { - CHECK_F( ZSTD_compressStream2(zcs, output, input, ZSTD_e_continue) ); + FORWARD_IF_ERROR( ZSTD_compressStream2(zcs, output, input, ZSTD_e_continue) ); return ZSTD_nextInputSizeHint_MTorST(zcs); } @@ -4013,14 +4128,15 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, { DEBUGLOG(5, "ZSTD_compressStream2, endOp=%u ", (unsigned)endOp); /* check conditions */ - if (output->pos > output->size) return ERROR(GENERIC); - if (input->pos > input->size) return ERROR(GENERIC); + RETURN_ERROR_IF(output->pos > output->size, GENERIC); + RETURN_ERROR_IF(input->pos > input->size, GENERIC); assert(cctx!=NULL); /* transparent initialization stage */ if (cctx->streamStage == zcss_init) { ZSTD_CCtx_params params = cctx->requestedParams; ZSTD_prefixDict const prefixDict = cctx->prefixDict; + FORWARD_IF_ERROR( ZSTD_initLocalDict(cctx) ); /* Init the local dict if present. */ memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict)); /* single usage */ assert(prefixDict.dict==NULL || cctx->cdict==NULL); /* only one can be set */ DEBUGLOG(4, "ZSTD_compressStream2 : transparent init stage"); @@ -4039,11 +4155,11 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, DEBUGLOG(4, "ZSTD_compressStream2: creating new mtctx for nbWorkers=%u", params.nbWorkers); cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbWorkers, cctx->customMem); - if (cctx->mtctx == NULL) return ERROR(memory_allocation); + RETURN_ERROR_IF(cctx->mtctx == NULL, memory_allocation); } /* mt compression */ DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbWorkers=%u", params.nbWorkers); - CHECK_F( ZSTDMT_initCStream_internal( + FORWARD_IF_ERROR( ZSTDMT_initCStream_internal( cctx->mtctx, prefixDict.dict, prefixDict.dictSize, ZSTD_dct_rawContent, cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) ); @@ -4051,7 +4167,7 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, cctx->appliedParams.nbWorkers = params.nbWorkers; } else #endif - { CHECK_F( ZSTD_resetCStream_internal(cctx, + { FORWARD_IF_ERROR( ZSTD_resetCStream_internal(cctx, prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType, cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) ); @@ -4063,20 +4179,30 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, /* compression stage */ #ifdef ZSTD_MULTITHREAD if (cctx->appliedParams.nbWorkers > 0) { + int const forceMaxProgress = (endOp == ZSTD_e_flush || endOp == ZSTD_e_end); + size_t flushMin; + assert(forceMaxProgress || endOp == ZSTD_e_continue /* Protection for a new flush type */); if (cctx->cParamsChanged) { ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams); cctx->cParamsChanged = 0; } - { size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp); + do { + flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp); if ( ZSTD_isError(flushMin) || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */ ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only); } - DEBUGLOG(5, "completed ZSTD_compressStream2 delegating to ZSTDMT_compressStream_generic"); - return flushMin; - } } + FORWARD_IF_ERROR(flushMin); + } while (forceMaxProgress && flushMin != 0 && output->pos < output->size); + DEBUGLOG(5, "completed ZSTD_compressStream2 delegating to ZSTDMT_compressStream_generic"); + /* Either we don't require maximum forward progress, we've finished the + * flush, or we are out of output space. + */ + assert(!forceMaxProgress || flushMin == 0 || output->pos == output->size); + return flushMin; + } #endif - CHECK_F( ZSTD_compressStream_generic(cctx, output, input, endOp) ); + FORWARD_IF_ERROR( ZSTD_compressStream_generic(cctx, output, input, endOp) ); DEBUGLOG(5, "completed ZSTD_compressStream2"); return cctx->outBuffContentSize - cctx->outBuffFlushedSize; /* remaining to flush */ } @@ -4107,10 +4233,10 @@ size_t ZSTD_compress2(ZSTD_CCtx* cctx, dst, dstCapacity, &oPos, src, srcSize, &iPos, ZSTD_e_end); - if (ZSTD_isError(result)) return result; + FORWARD_IF_ERROR(result); if (result != 0) { /* compression not completed, due to lack of output space */ assert(oPos == dstCapacity); - return ERROR(dstSize_tooSmall); + RETURN_ERROR(dstSize_tooSmall); } assert(iPos == srcSize); /* all input is expected consumed */ return oPos; @@ -4132,7 +4258,7 @@ size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output) { ZSTD_inBuffer input = { NULL, 0, 0 }; size_t const remainingToFlush = ZSTD_compressStream2(zcs, output, &input, ZSTD_e_end); - CHECK_F( remainingToFlush ); + FORWARD_IF_ERROR( remainingToFlush ); if (zcs->appliedParams.nbWorkers > 0) return remainingToFlush; /* minimal estimation */ /* single thread mode : attempt to calculate remaining to flush more precisely */ { size_t const lastBlockSize = zcs->frameEnded ? 0 : ZSTD_BLOCKHEADERSIZE; @@ -4151,7 +4277,7 @@ int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; } int ZSTD_minCLevel(void) { return (int)-ZSTD_TARGETLENGTH_MAX; } static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = { -{ /* "default" - guarantees a monotonically increasing memory budget */ +{ /* "default" - for any srcSize > 256 KB */ /* W, C, H, S, L, TL, strat */ { 19, 12, 13, 1, 6, 1, ZSTD_fast }, /* base for negative levels */ { 19, 13, 14, 1, 7, 0, ZSTD_fast }, /* level 1 */ @@ -4258,13 +4384,13 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV }; /*! ZSTD_getCParams() : -* @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize. -* Size values are optional, provide 0 if not known or unused */ + * @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize. + * Size values are optional, provide 0 if not known or unused */ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) { size_t const addedSize = srcSizeHint ? 0 : 500; - U64 const rSize = srcSizeHint+dictSize ? srcSizeHint+dictSize+addedSize : (U64)-1; - U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB); /* intentional underflow for srcSizeHint == 0 */ + U64 const rSize = srcSizeHint+dictSize ? srcSizeHint+dictSize+addedSize : ZSTD_CONTENTSIZE_UNKNOWN; /* intentional overflow for srcSizeHint == ZSTD_CONTENTSIZE_UNKNOWN */ + U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB); int row = compressionLevel; DEBUGLOG(5, "ZSTD_getCParams (cLevel=%i)", compressionLevel); if (compressionLevel == 0) row = ZSTD_CLEVEL_DEFAULT; /* 0 == default */ @@ -4272,13 +4398,14 @@ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long l if (compressionLevel > ZSTD_MAX_CLEVEL) row = ZSTD_MAX_CLEVEL; { ZSTD_compressionParameters cp = ZSTD_defaultCParameters[tableID][row]; if (compressionLevel < 0) cp.targetLength = (unsigned)(-compressionLevel); /* acceleration factor */ - return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize); + return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize); /* refine parameters based on srcSize & dictSize */ } } /*! ZSTD_getParams() : -* same as ZSTD_getCParams(), but @return a `ZSTD_parameters` object (instead of `ZSTD_compressionParameters`). -* All fields of `ZSTD_frameParameters` are set to default (0) */ + * same idea as ZSTD_getCParams() + * @return a `ZSTD_parameters` structure (instead of `ZSTD_compressionParameters`). + * Fields of `ZSTD_frameParameters` are set to default values */ ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) { ZSTD_parameters params; ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, srcSizeHint, dictSize); diff --git a/thirdparty/zstd/compress/zstd_compress_internal.h b/thirdparty/zstd/compress/zstd_compress_internal.h index 29bca59859..cc3cbb9da9 100644 --- a/thirdparty/zstd/compress/zstd_compress_internal.h +++ b/thirdparty/zstd/compress/zstd_compress_internal.h @@ -36,9 +36,9 @@ extern "C" { #define ZSTD_DUBT_UNSORTED_MARK 1 /* For btlazy2 strategy, index 1 now means "unsorted". It could be confused for a real successor at index "1", if sorted as larger than its predecessor. It's not a big deal though : candidate will just be sorted again. - Additionnally, candidate position 1 will be lost. + Additionally, candidate position 1 will be lost. But candidate 1 cannot hide a large tree of candidates, so it's a minimal loss. - The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled after table re-use with a different strategy + The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be mishandled after table re-use with a different strategy Constant required by ZSTD_compressBlock_btlazy2() and ZSTD_reduceTable_internal() */ @@ -55,6 +55,14 @@ typedef struct ZSTD_prefixDict_s { } ZSTD_prefixDict; typedef struct { + void* dictBuffer; + void const* dict; + size_t dictSize; + ZSTD_dictContentType_e dictContentType; + ZSTD_CDict* cdict; +} ZSTD_localDict; + +typedef struct { U32 CTable[HUF_CTABLE_SIZE_U32(255)]; HUF_repeat repeatMode; } ZSTD_hufCTables_t; @@ -107,6 +115,7 @@ typedef struct { U32 offCodeSumBasePrice; /* to compare to log2(offreq) */ ZSTD_OptPrice_e priceType; /* prices can be determined dynamically, or follow a pre-defined cost structure */ const ZSTD_entropyCTables_t* symbolCosts; /* pre-calculated dictionary statistics */ + ZSTD_literalCompressionMode_e literalCompressionMode; } optState_t; typedef struct { @@ -188,6 +197,7 @@ struct ZSTD_CCtx_params_s { * 1<<wLog, even for dictionary */ ZSTD_dictAttachPref_e attachDictPref; + ZSTD_literalCompressionMode_e literalCompressionMode; /* Multithreading: used to pass parameters to mtctx */ int nbWorkers; @@ -243,7 +253,7 @@ struct ZSTD_CCtx_s { U32 frameEnded; /* Dictionary */ - ZSTD_CDict* cdictLocal; + ZSTD_localDict localDict; const ZSTD_CDict* cdict; ZSTD_prefixDict prefixDict; /* single-usage dictionary */ @@ -806,13 +816,6 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, void ZSTD_resetSeqStore(seqStore_t* ssPtr); -/*! ZSTD_compressStream_generic() : - * Private use only. To be called from zstdmt_compress.c in single-thread mode. */ -size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, - ZSTD_outBuffer* output, - ZSTD_inBuffer* input, - ZSTD_EndDirective const flushMode); - /*! ZSTD_getCParamsFromCDict() : * as the name implies */ ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict); @@ -839,7 +842,7 @@ size_t ZSTD_compress_advanced_internal(ZSTD_CCtx* cctx, /* ZSTD_writeLastEmptyBlock() : * output an empty Block with end-of-frame mark to complete a frame * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h)) - * or an error code if `dstCapcity` is too small (<ZSTD_blockHeaderSize) + * or an error code if `dstCapacity` is too small (<ZSTD_blockHeaderSize) */ size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity); diff --git a/thirdparty/zstd/compress/zstd_fast.c b/thirdparty/zstd/compress/zstd_fast.c index 40ba0f73e6..ed997b441c 100644 --- a/thirdparty/zstd/compress/zstd_fast.c +++ b/thirdparty/zstd/compress/zstd_fast.c @@ -45,7 +45,155 @@ FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_fast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, - U32 const mls, ZSTD_dictMode_e const dictMode) + U32 const mls) +{ + const ZSTD_compressionParameters* const cParams = &ms->cParams; + U32* const hashTable = ms->hashTable; + U32 const hlog = cParams->hashLog; + /* support stepSize of 0 */ + size_t const stepSize = cParams->targetLength + !(cParams->targetLength) + 1; + const BYTE* const base = ms->window.base; + const BYTE* const istart = (const BYTE*)src; + /* We check ip0 (ip + 0) and ip1 (ip + 1) each loop */ + const BYTE* ip0 = istart; + const BYTE* ip1; + const BYTE* anchor = istart; + const U32 prefixStartIndex = ms->window.dictLimit; + const BYTE* const prefixStart = base + prefixStartIndex; + const BYTE* const iend = istart + srcSize; + const BYTE* const ilimit = iend - HASH_READ_SIZE; + U32 offset_1=rep[0], offset_2=rep[1]; + U32 offsetSaved = 0; + + /* init */ + ip0 += (ip0 == prefixStart); + ip1 = ip0 + 1; + { + U32 const maxRep = (U32)(ip0 - prefixStart); + if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; + if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; + } + + /* Main Search Loop */ + while (ip1 < ilimit) { /* < instead of <=, because check at ip0+2 */ + size_t mLength; + BYTE const* ip2 = ip0 + 2; + size_t const h0 = ZSTD_hashPtr(ip0, hlog, mls); + U32 const val0 = MEM_read32(ip0); + size_t const h1 = ZSTD_hashPtr(ip1, hlog, mls); + U32 const val1 = MEM_read32(ip1); + U32 const current0 = (U32)(ip0-base); + U32 const current1 = (U32)(ip1-base); + U32 const matchIndex0 = hashTable[h0]; + U32 const matchIndex1 = hashTable[h1]; + BYTE const* repMatch = ip2-offset_1; + const BYTE* match0 = base + matchIndex0; + const BYTE* match1 = base + matchIndex1; + U32 offcode; + hashTable[h0] = current0; /* update hash table */ + hashTable[h1] = current1; /* update hash table */ + + assert(ip0 + 1 == ip1); + + if ((offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip2))) { + mLength = ip2[-1] == repMatch[-1] ? 1 : 0; + ip0 = ip2 - mLength; + match0 = repMatch - mLength; + offcode = 0; + goto _match; + } + if ((matchIndex0 > prefixStartIndex) && MEM_read32(match0) == val0) { + /* found a regular match */ + goto _offset; + } + if ((matchIndex1 > prefixStartIndex) && MEM_read32(match1) == val1) { + /* found a regular match after one literal */ + ip0 = ip1; + match0 = match1; + goto _offset; + } + { + size_t const step = ((ip0-anchor) >> (kSearchStrength - 1)) + stepSize; + assert(step >= 2); + ip0 += step; + ip1 += step; + continue; + } +_offset: /* Requires: ip0, match0 */ + /* Compute the offset code */ + offset_2 = offset_1; + offset_1 = (U32)(ip0-match0); + offcode = offset_1 + ZSTD_REP_MOVE; + mLength = 0; + /* Count the backwards match length */ + while (((ip0>anchor) & (match0>prefixStart)) + && (ip0[-1] == match0[-1])) { ip0--; match0--; mLength++; } /* catch up */ + +_match: /* Requires: ip0, match0, offcode */ + /* Count the forward length */ + mLength += ZSTD_count(ip0+mLength+4, match0+mLength+4, iend) + 4; + ZSTD_storeSeq(seqStore, ip0-anchor, anchor, offcode, mLength-MINMATCH); + /* match found */ + ip0 += mLength; + anchor = ip0; + ip1 = ip0 + 1; + + if (ip0 <= ilimit) { + /* Fill Table */ + assert(base+current0+2 > istart); /* check base overflow */ + hashTable[ZSTD_hashPtr(base+current0+2, hlog, mls)] = current0+2; /* here because current+2 could be > iend-8 */ + hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base); + + while ( (ip0 <= ilimit) + && ( (offset_2>0) + & (MEM_read32(ip0) == MEM_read32(ip0 - offset_2)) )) { + /* store sequence */ + size_t const rLength = ZSTD_count(ip0+4, ip0+4-offset_2, iend) + 4; + U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */ + hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base); + ip0 += rLength; + ip1 = ip0 + 1; + ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); + anchor = ip0; + continue; /* faster when present (confirmed on gcc-8) ... (?) */ + } + } + } + + /* save reps for next block */ + rep[0] = offset_1 ? offset_1 : offsetSaved; + rep[1] = offset_2 ? offset_2 : offsetSaved; + + /* Return the last literals size */ + return iend - anchor; +} + + +size_t ZSTD_compressBlock_fast( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + void const* src, size_t srcSize) +{ + ZSTD_compressionParameters const* cParams = &ms->cParams; + U32 const mls = cParams->minMatch; + assert(ms->dictMatchState == NULL); + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 4); + case 5 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 5); + case 6 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 6); + case 7 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 7); + } +} + +FORCE_INLINE_TEMPLATE +size_t ZSTD_compressBlock_fast_dictMatchState_generic( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + void const* src, size_t srcSize, U32 const mls) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable; @@ -64,46 +212,26 @@ size_t ZSTD_compressBlock_fast_generic( U32 offsetSaved = 0; const ZSTD_matchState_t* const dms = ms->dictMatchState; - const ZSTD_compressionParameters* const dictCParams = - dictMode == ZSTD_dictMatchState ? - &dms->cParams : NULL; - const U32* const dictHashTable = dictMode == ZSTD_dictMatchState ? - dms->hashTable : NULL; - const U32 dictStartIndex = dictMode == ZSTD_dictMatchState ? - dms->window.dictLimit : 0; - const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? - dms->window.base : NULL; - const BYTE* const dictStart = dictMode == ZSTD_dictMatchState ? - dictBase + dictStartIndex : NULL; - const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? - dms->window.nextSrc : NULL; - const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? - prefixStartIndex - (U32)(dictEnd - dictBase) : - 0; + const ZSTD_compressionParameters* const dictCParams = &dms->cParams ; + const U32* const dictHashTable = dms->hashTable; + const U32 dictStartIndex = dms->window.dictLimit; + const BYTE* const dictBase = dms->window.base; + const BYTE* const dictStart = dictBase + dictStartIndex; + const BYTE* const dictEnd = dms->window.nextSrc; + const U32 dictIndexDelta = prefixStartIndex - (U32)(dictEnd - dictBase); const U32 dictAndPrefixLength = (U32)(ip - prefixStart + dictEnd - dictStart); - const U32 dictHLog = dictMode == ZSTD_dictMatchState ? - dictCParams->hashLog : hlog; - - assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); + const U32 dictHLog = dictCParams->hashLog; /* otherwise, we would get index underflow when translating a dict index * into a local index */ - assert(dictMode != ZSTD_dictMatchState - || prefixStartIndex >= (U32)(dictEnd - dictBase)); + assert(prefixStartIndex >= (U32)(dictEnd - dictBase)); /* init */ ip += (dictAndPrefixLength == 0); - if (dictMode == ZSTD_noDict) { - U32 const maxRep = (U32)(ip - prefixStart); - if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; - if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; - } - if (dictMode == ZSTD_dictMatchState) { - /* dictMatchState repCode checks don't currently handle repCode == 0 - * disabling. */ - assert(offset_1 <= dictAndPrefixLength); - assert(offset_2 <= dictAndPrefixLength); - } + /* dictMatchState repCode checks don't currently handle repCode == 0 + * disabling. */ + assert(offset_1 <= dictAndPrefixLength); + assert(offset_2 <= dictAndPrefixLength); /* Main Search Loop */ while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ @@ -113,50 +241,37 @@ size_t ZSTD_compressBlock_fast_generic( U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; const U32 repIndex = current + 1 - offset_1; - const BYTE* repMatch = (dictMode == ZSTD_dictMatchState - && repIndex < prefixStartIndex) ? + const BYTE* repMatch = (repIndex < prefixStartIndex) ? dictBase + (repIndex - dictIndexDelta) : base + repIndex; hashTable[h] = current; /* update hash table */ - if ( (dictMode == ZSTD_dictMatchState) - && ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */ + if ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */ && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else if ( dictMode == ZSTD_noDict - && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { - mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; - ip++; - ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else if ( (matchIndex <= prefixStartIndex) ) { - if (dictMode == ZSTD_dictMatchState) { - size_t const dictHash = ZSTD_hashPtr(ip, dictHLog, mls); - U32 const dictMatchIndex = dictHashTable[dictHash]; - const BYTE* dictMatch = dictBase + dictMatchIndex; - if (dictMatchIndex <= dictStartIndex || - MEM_read32(dictMatch) != MEM_read32(ip)) { - assert(stepSize >= 1); - ip += ((ip-anchor) >> kSearchStrength) + stepSize; - continue; - } else { - /* found a dict match */ - U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); - mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, prefixStart) + 4; - while (((ip>anchor) & (dictMatch>dictStart)) - && (ip[-1] == dictMatch[-1])) { - ip--; dictMatch--; mLength++; - } /* catch up */ - offset_2 = offset_1; - offset_1 = offset; - ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); - } - } else { + size_t const dictHash = ZSTD_hashPtr(ip, dictHLog, mls); + U32 const dictMatchIndex = dictHashTable[dictHash]; + const BYTE* dictMatch = dictBase + dictMatchIndex; + if (dictMatchIndex <= dictStartIndex || + MEM_read32(dictMatch) != MEM_read32(ip)) { assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; continue; + } else { + /* found a dict match */ + U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); + mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, prefixStart) + 4; + while (((ip>anchor) & (dictMatch>dictStart)) + && (ip[-1] == dictMatch[-1])) { + ip--; dictMatch--; mLength++; + } /* catch up */ + offset_2 = offset_1; + offset_1 = offset; + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } } else if (MEM_read32(match) != MEM_read32(ip)) { /* it's not a match, and we're not going to check the dictionary */ @@ -185,41 +300,27 @@ size_t ZSTD_compressBlock_fast_generic( hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ - if (dictMode == ZSTD_dictMatchState) { - while (ip <= ilimit) { - U32 const current2 = (U32)(ip-base); - U32 const repIndex2 = current2 - offset_2; - const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? - dictBase - dictIndexDelta + repIndex2 : - base + repIndex2; - if ( ((U32)((prefixStartIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) - && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; - U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); - hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; - ip += repLength2; - anchor = ip; - continue; - } - break; + while (ip <= ilimit) { + U32 const current2 = (U32)(ip-base); + U32 const repIndex2 = current2 - offset_2; + const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? + dictBase - dictIndexDelta + repIndex2 : + base + repIndex2; + if ( ((U32)((prefixStartIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) + && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { + const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; + size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; + U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ + ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); + hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; + ip += repLength2; + anchor = ip; + continue; } + break; } - - if (dictMode == ZSTD_noDict) { - while ( (ip <= ilimit) - && ( (offset_2>0) - & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { - /* store sequence */ - size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; - U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */ - hashTable[ZSTD_hashPtr(ip, hlog, mls)] = (U32)(ip-base); - ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); - ip += rLength; - anchor = ip; - continue; /* faster when present ... (?) */ - } } } } + } + } /* save reps for next block */ rep[0] = offset_1 ? offset_1 : offsetSaved; @@ -229,28 +330,6 @@ size_t ZSTD_compressBlock_fast_generic( return iend - anchor; } - -size_t ZSTD_compressBlock_fast( - ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - void const* src, size_t srcSize) -{ - ZSTD_compressionParameters const* cParams = &ms->cParams; - U32 const mls = cParams->minMatch; - assert(ms->dictMatchState == NULL); - switch(mls) - { - default: /* includes case 3 */ - case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_noDict); - case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_noDict); - case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_noDict); - case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_noDict); - } -} - size_t ZSTD_compressBlock_fast_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) @@ -262,13 +341,13 @@ size_t ZSTD_compressBlock_fast_dictMatchState( { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_dictMatchState); + return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 4); case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_dictMatchState); + return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 5); case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_dictMatchState); + return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 6); case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_dictMatchState); + return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 7); } } diff --git a/thirdparty/zstd/compress/zstd_lazy.h b/thirdparty/zstd/compress/zstd_lazy.h index ef85a6df9c..bb1763069f 100644 --- a/thirdparty/zstd/compress/zstd_lazy.h +++ b/thirdparty/zstd/compress/zstd_lazy.h @@ -19,7 +19,7 @@ extern "C" { U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip); -void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const reducerValue); /*! used in ZSTD_reduceIndex(). pre-emptively increase value of ZSTD_DUBT_UNSORTED_MARK */ +void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const reducerValue); /*! used in ZSTD_reduceIndex(). preemptively increase value of ZSTD_DUBT_UNSORTED_MARK */ size_t ZSTD_compressBlock_btlazy2( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], diff --git a/thirdparty/zstd/compress/zstd_ldm.c b/thirdparty/zstd/compress/zstd_ldm.c index 58eb2ffe4d..784d20f3ab 100644 --- a/thirdparty/zstd/compress/zstd_ldm.c +++ b/thirdparty/zstd/compress/zstd_ldm.c @@ -429,7 +429,7 @@ size_t ZSTD_ldm_generateSequences( */ assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize); /* The input could be very large (in zstdmt), so it must be broken up into - * chunks to enforce the maximmum distance and handle overflow correction. + * chunks to enforce the maximum distance and handle overflow correction. */ assert(sequences->pos <= sequences->size); assert(sequences->size <= sequences->capacity); diff --git a/thirdparty/zstd/compress/zstd_opt.c b/thirdparty/zstd/compress/zstd_opt.c index 44de6e97fd..efb69d3267 100644 --- a/thirdparty/zstd/compress/zstd_opt.c +++ b/thirdparty/zstd/compress/zstd_opt.c @@ -64,9 +64,15 @@ MEM_STATIC double ZSTD_fCost(U32 price) } #endif +static int ZSTD_compressedLiterals(optState_t const* const optPtr) +{ + return optPtr->literalCompressionMode != ZSTD_lcm_uncompressed; +} + static void ZSTD_setBasePrices(optState_t* optPtr, int optLevel) { - optPtr->litSumBasePrice = WEIGHT(optPtr->litSum, optLevel); + if (ZSTD_compressedLiterals(optPtr)) + optPtr->litSumBasePrice = WEIGHT(optPtr->litSum, optLevel); optPtr->litLengthSumBasePrice = WEIGHT(optPtr->litLengthSum, optLevel); optPtr->matchLengthSumBasePrice = WEIGHT(optPtr->matchLengthSum, optLevel); optPtr->offCodeSumBasePrice = WEIGHT(optPtr->offCodeSum, optLevel); @@ -99,6 +105,7 @@ ZSTD_rescaleFreqs(optState_t* const optPtr, const BYTE* const src, size_t const srcSize, int const optLevel) { + int const compressedLiterals = ZSTD_compressedLiterals(optPtr); DEBUGLOG(5, "ZSTD_rescaleFreqs (srcSize=%u)", (unsigned)srcSize); optPtr->priceType = zop_dynamic; @@ -113,9 +120,10 @@ ZSTD_rescaleFreqs(optState_t* const optPtr, /* huffman table presumed generated by dictionary */ optPtr->priceType = zop_dynamic; - assert(optPtr->litFreq != NULL); - optPtr->litSum = 0; - { unsigned lit; + if (compressedLiterals) { + unsigned lit; + assert(optPtr->litFreq != NULL); + optPtr->litSum = 0; for (lit=0; lit<=MaxLit; lit++) { U32 const scaleLog = 11; /* scale to 2K */ U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->huf.CTable, lit); @@ -163,10 +171,11 @@ ZSTD_rescaleFreqs(optState_t* const optPtr, } else { /* not a dictionary */ assert(optPtr->litFreq != NULL); - { unsigned lit = MaxLit; + if (compressedLiterals) { + unsigned lit = MaxLit; HIST_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */ + optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1); } - optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1); { unsigned ll; for (ll=0; ll<=MaxLL; ll++) @@ -190,7 +199,8 @@ ZSTD_rescaleFreqs(optState_t* const optPtr, } else { /* new block : re-use previous statistics, scaled down */ - optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1); + if (compressedLiterals) + optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1); optPtr->litLengthSum = ZSTD_downscaleStat(optPtr->litLengthFreq, MaxLL, 0); optPtr->matchLengthSum = ZSTD_downscaleStat(optPtr->matchLengthFreq, MaxML, 0); optPtr->offCodeSum = ZSTD_downscaleStat(optPtr->offCodeFreq, MaxOff, 0); @@ -207,6 +217,10 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, int optLevel) { if (litLength == 0) return 0; + + if (!ZSTD_compressedLiterals(optPtr)) + return (litLength << 3) * BITCOST_MULTIPLIER; /* Uncompressed - 8 bytes per literal. */ + if (optPtr->priceType == zop_predef) return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */ @@ -310,7 +324,8 @@ static void ZSTD_updateStats(optState_t* const optPtr, U32 offsetCode, U32 matchLength) { /* literals */ - { U32 u; + if (ZSTD_compressedLiterals(optPtr)) { + U32 u; for (u=0; u < litLength; u++) optPtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD; optPtr->litSum += litLength*ZSTD_LITFREQ_ADD; @@ -870,7 +885,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, /* large match -> immediate encoding */ { U32 const maxML = matches[nbMatches-1].len; U32 const maxOffset = matches[nbMatches-1].off; - DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffCode=%u at cPos=%u => start new serie", + DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffCode=%u at cPos=%u => start new series", nbMatches, maxML, maxOffset, (U32)(ip-prefixStart)); if (maxML > sufficient_len) { @@ -1108,7 +1123,8 @@ static U32 ZSTD_upscaleStat(unsigned* table, U32 lastEltIndex, int bonus) /* used in 2-pass strategy */ MEM_STATIC void ZSTD_upscaleStats(optState_t* optPtr) { - optPtr->litSum = ZSTD_upscaleStat(optPtr->litFreq, MaxLit, 0); + if (ZSTD_compressedLiterals(optPtr)) + optPtr->litSum = ZSTD_upscaleStat(optPtr->litFreq, MaxLit, 0); optPtr->litLengthSum = ZSTD_upscaleStat(optPtr->litLengthFreq, MaxLL, 0); optPtr->matchLengthSum = ZSTD_upscaleStat(optPtr->matchLengthFreq, MaxML, 0); optPtr->offCodeSum = ZSTD_upscaleStat(optPtr->offCodeFreq, MaxOff, 0); @@ -1117,7 +1133,7 @@ MEM_STATIC void ZSTD_upscaleStats(optState_t* optPtr) /* ZSTD_initStats_ultra(): * make a first compression pass, just to seed stats with more accurate starting values. * only works on first block, with no dictionary and no ldm. - * this function cannot error, hence its constract must be respected. + * this function cannot error, hence its contract must be respected. */ static void ZSTD_initStats_ultra(ZSTD_matchState_t* ms, diff --git a/thirdparty/zstd/compress/zstdmt_compress.c b/thirdparty/zstd/compress/zstdmt_compress.c index 2cbd6ffade..38fbb90768 100644 --- a/thirdparty/zstd/compress/zstdmt_compress.c +++ b/thirdparty/zstd/compress/zstdmt_compress.c @@ -22,6 +22,7 @@ /* ====== Dependencies ====== */ #include <string.h> /* memcpy, memset */ #include <limits.h> /* INT_MAX, UINT_MAX */ +#include "mem.h" /* MEM_STATIC */ #include "pool.h" /* threadpool */ #include "threading.h" /* mutex */ #include "zstd_compress_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */ @@ -456,7 +457,7 @@ typedef struct { * Must be acquired after the main mutex when acquiring both. */ ZSTD_pthread_mutex_t ldmWindowMutex; - ZSTD_pthread_cond_t ldmWindowCond; /* Signaled when ldmWindow is udpated */ + ZSTD_pthread_cond_t ldmWindowCond; /* Signaled when ldmWindow is updated */ ZSTD_window_t ldmWindow; /* A thread-safe copy of ldmState.window */ } serialState_t; @@ -647,7 +648,7 @@ static void ZSTDMT_compressionJob(void* jobDescription) buffer_t dstBuff = job->dstBuff; size_t lastCBlockSize = 0; - /* ressources */ + /* resources */ if (cctx==NULL) JOB_ERROR(ERROR(memory_allocation)); if (dstBuff.start == NULL) { /* streaming job : doesn't provide a dstBuffer */ dstBuff = ZSTDMT_getBuffer(job->bufPool); @@ -672,7 +673,7 @@ static void ZSTDMT_compressionJob(void* jobDescription) if (ZSTD_isError(initError)) JOB_ERROR(initError); } else { /* srcStart points at reloaded section */ U64 const pledgedSrcSize = job->firstJob ? job->fullFrameSize : job->src.size; - { size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_c_forceMaxWindow, !job->firstJob); + { size_t const forceWindowError = ZSTD_CCtxParams_setParameter(&jobParams, ZSTD_c_forceMaxWindow, !job->firstJob); if (ZSTD_isError(forceWindowError)) JOB_ERROR(forceWindowError); } { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, @@ -864,14 +865,10 @@ static size_t ZSTDMT_expandJobsTable (ZSTDMT_CCtx* mtctx, U32 nbWorkers) { * Internal use only */ size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers) { - if (nbWorkers > ZSTDMT_NBWORKERS_MAX) nbWorkers = ZSTDMT_NBWORKERS_MAX; - params->nbWorkers = nbWorkers; - params->overlapLog = ZSTDMT_OVERLAPLOG_DEFAULT; - params->jobSize = 0; - return nbWorkers; + return ZSTD_CCtxParams_setParameter(params, ZSTD_c_nbWorkers, (int)nbWorkers); } -ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem) +MEM_STATIC ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced_internal(unsigned nbWorkers, ZSTD_customMem cMem) { ZSTDMT_CCtx* mtctx; U32 nbJobs = nbWorkers + 2; @@ -906,6 +903,17 @@ ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem) return mtctx; } +ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem) +{ +#ifdef ZSTD_MULTITHREAD + return ZSTDMT_createCCtx_advanced_internal(nbWorkers, cMem); +#else + (void)nbWorkers; + (void)cMem; + return NULL; +#endif +} + ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers) { return ZSTDMT_createCCtx_advanced(nbWorkers, ZSTD_defaultCMem); @@ -986,26 +994,13 @@ ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, { case ZSTDMT_p_jobSize : DEBUGLOG(4, "ZSTDMT_CCtxParam_setMTCtxParameter : set jobSize to %i", value); - if ( value != 0 /* default */ - && value < ZSTDMT_JOBSIZE_MIN) - value = ZSTDMT_JOBSIZE_MIN; - assert(value >= 0); - if (value > ZSTDMT_JOBSIZE_MAX) value = ZSTDMT_JOBSIZE_MAX; - params->jobSize = value; - return value; - + return ZSTD_CCtxParams_setParameter(params, ZSTD_c_jobSize, value); case ZSTDMT_p_overlapLog : DEBUGLOG(4, "ZSTDMT_p_overlapLog : %i", value); - if (value < ZSTD_OVERLAPLOG_MIN) value = ZSTD_OVERLAPLOG_MIN; - if (value > ZSTD_OVERLAPLOG_MAX) value = ZSTD_OVERLAPLOG_MAX; - params->overlapLog = value; - return value; - + return ZSTD_CCtxParams_setParameter(params, ZSTD_c_overlapLog, value); case ZSTDMT_p_rsyncable : - value = (value != 0); - params->rsyncable = value; - return value; - + DEBUGLOG(4, "ZSTD_p_rsyncable : %i", value); + return ZSTD_CCtxParams_setParameter(params, ZSTD_c_rsyncable, value); default : return ERROR(parameter_unsupported); } @@ -1021,32 +1016,29 @@ size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, { switch (parameter) { case ZSTDMT_p_jobSize: - assert(mtctx->params.jobSize <= INT_MAX); - *value = (int)(mtctx->params.jobSize); - break; + return ZSTD_CCtxParams_getParameter(&mtctx->params, ZSTD_c_jobSize, value); case ZSTDMT_p_overlapLog: - *value = mtctx->params.overlapLog; - break; + return ZSTD_CCtxParams_getParameter(&mtctx->params, ZSTD_c_overlapLog, value); case ZSTDMT_p_rsyncable: - *value = mtctx->params.rsyncable; - break; + return ZSTD_CCtxParams_getParameter(&mtctx->params, ZSTD_c_rsyncable, value); default: return ERROR(parameter_unsupported); } - return 0; } /* Sets parameters relevant to the compression job, * initializing others to default values. */ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) { - ZSTD_CCtx_params jobParams; - memset(&jobParams, 0, sizeof(jobParams)); - - jobParams.cParams = params.cParams; - jobParams.fParams = params.fParams; - jobParams.compressionLevel = params.compressionLevel; - + ZSTD_CCtx_params jobParams = params; + /* Clear parameters related to multithreading */ + jobParams.forceWindow = 0; + jobParams.nbWorkers = 0; + jobParams.jobSize = 0; + jobParams.overlapLog = 0; + jobParams.rsyncable = 0; + memset(&jobParams.ldmParams, 0, sizeof(ldmParams_t)); + memset(&jobParams.customMem, 0, sizeof(ZSTD_customMem)); return jobParams; } @@ -1056,7 +1048,7 @@ static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params) static size_t ZSTDMT_resize(ZSTDMT_CCtx* mtctx, unsigned nbWorkers) { if (POOL_resize(mtctx->factory, nbWorkers)) return ERROR(memory_allocation); - CHECK_F( ZSTDMT_expandJobsTable(mtctx, nbWorkers) ); + FORWARD_IF_ERROR( ZSTDMT_expandJobsTable(mtctx, nbWorkers) ); mtctx->bufPool = ZSTDMT_expandBufferPool(mtctx->bufPool, nbWorkers); if (mtctx->bufPool == NULL) return ERROR(memory_allocation); mtctx->cctxPool = ZSTDMT_expandCCtxPool(mtctx->cctxPool, nbWorkers); @@ -1263,7 +1255,7 @@ static size_t ZSTDMT_compress_advanced_internal( if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, avgJobSize)) return ERROR(memory_allocation); - CHECK_F( ZSTDMT_expandJobsTable(mtctx, nbJobs) ); /* only expands if necessary */ + FORWARD_IF_ERROR( ZSTDMT_expandJobsTable(mtctx, nbJobs) ); /* only expands if necessary */ { unsigned u; for (u=0; u<nbJobs; u++) { @@ -1396,7 +1388,7 @@ size_t ZSTDMT_initCStream_internal( /* init */ if (params.nbWorkers != mtctx->params.nbWorkers) - CHECK_F( ZSTDMT_resize(mtctx, params.nbWorkers) ); + FORWARD_IF_ERROR( ZSTDMT_resize(mtctx, params.nbWorkers) ); if (params.jobSize != 0 && params.jobSize < ZSTDMT_JOBSIZE_MIN) params.jobSize = ZSTDMT_JOBSIZE_MIN; if (params.jobSize > (size_t)ZSTDMT_JOBSIZE_MAX) params.jobSize = ZSTDMT_JOBSIZE_MAX; @@ -1547,7 +1539,7 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel) { /* ZSTDMT_writeLastEmptyBlock() * Write a single empty block with an end-of-frame to finish a frame. * Job must be created from streaming variant. - * This function is always successfull if expected conditions are fulfilled. + * This function is always successful if expected conditions are fulfilled. */ static void ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job) { @@ -1987,7 +1979,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, assert(input->pos <= input->size); if (mtctx->singleBlockingThread) { /* delegate to single-thread (synchronous) */ - return ZSTD_compressStream_generic(mtctx->cctxPool->cctx[0], output, input, endOp); + return ZSTD_compressStream2(mtctx->cctxPool->cctx[0], output, input, endOp); } if ((mtctx->frameEnded) && (endOp==ZSTD_e_continue)) { @@ -2051,7 +2043,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, || ((endOp == ZSTD_e_end) && (!mtctx->frameEnded)) ) { /* must finish the frame with a zero-size block */ size_t const jobSize = mtctx->inBuff.filled; assert(mtctx->inBuff.filled <= mtctx->targetSectionSize); - CHECK_F( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp) ); + FORWARD_IF_ERROR( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp) ); } /* check for potential compressed data ready to be flushed */ @@ -2065,7 +2057,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { - CHECK_F( ZSTDMT_compressStream_generic(mtctx, output, input, ZSTD_e_continue) ); + FORWARD_IF_ERROR( ZSTDMT_compressStream_generic(mtctx, output, input, ZSTD_e_continue) ); /* recommended next input size : fill current input buffer */ return mtctx->targetSectionSize - mtctx->inBuff.filled; /* note : could be zero when input buffer is fully filled and no more availability to create new job */ @@ -2082,7 +2074,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* ou || ((endFrame==ZSTD_e_end) && !mtctx->frameEnded)) { /* need a last 0-size block to end frame */ DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job (%u bytes, end:%u)", (U32)srcSize, (U32)endFrame); - CHECK_F( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) ); + FORWARD_IF_ERROR( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) ); } /* check if there is any data available to flush */ diff --git a/thirdparty/zstd/compress/zstdmt_compress.h b/thirdparty/zstd/compress/zstdmt_compress.h index ee771681fa..12e6bcb3a3 100644 --- a/thirdparty/zstd/compress/zstdmt_compress.h +++ b/thirdparty/zstd/compress/zstdmt_compress.h @@ -17,10 +17,25 @@ /* Note : This is an internal API. - * Some methods are still exposed (ZSTDLIB_API), + * These APIs used to be exposed with ZSTDLIB_API, * because it used to be the only way to invoke MT compression. - * Now, it's recommended to use ZSTD_compress_generic() instead. - * These methods will stop being exposed in a future version */ + * Now, it's recommended to use ZSTD_compress2 and ZSTD_compressStream2() + * instead. + * + * If you depend on these APIs and can't switch, then define + * ZSTD_LEGACY_MULTITHREADED_API when making the dynamic library. + * However, we may completely remove these functions in a future + * release, so please switch soon. + * + * This API requires ZSTD_MULTITHREAD to be defined during compilation, + * otherwise ZSTDMT_createCCtx*() will fail. + */ + +#ifdef ZSTD_LEGACY_MULTITHREADED_API +# define ZSTDMT_API ZSTDLIB_API +#else +# define ZSTDMT_API +#endif /* === Dependencies === */ #include <stddef.h> /* size_t */ @@ -40,17 +55,19 @@ /* === Memory management === */ typedef struct ZSTDMT_CCtx_s ZSTDMT_CCtx; -ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers); -ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, +/* Requires ZSTD_MULTITHREAD to be defined during compilation, otherwise it will return NULL. */ +ZSTDMT_API ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers); +/* Requires ZSTD_MULTITHREAD to be defined during compilation, otherwise it will return NULL. */ +ZSTDMT_API ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem); -ZSTDLIB_API size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx); +ZSTDMT_API size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx); -ZSTDLIB_API size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx); +ZSTDMT_API size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx); /* === Simple one-pass compression function === */ -ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, +ZSTDMT_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel); @@ -59,31 +76,31 @@ ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, /* === Streaming functions === */ -ZSTDLIB_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel); -ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize); /**< if srcSize is not known at reset time, use ZSTD_CONTENTSIZE_UNKNOWN. Note: for compatibility with older programs, 0 means the same as ZSTD_CONTENTSIZE_UNKNOWN, but it will change in the future to mean "empty" */ +ZSTDMT_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel); +ZSTDMT_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize); /**< if srcSize is not known at reset time, use ZSTD_CONTENTSIZE_UNKNOWN. Note: for compatibility with older programs, 0 means the same as ZSTD_CONTENTSIZE_UNKNOWN, but it will change in the future to mean "empty" */ -ZSTDLIB_API size_t ZSTDMT_nextInputSizeHint(const ZSTDMT_CCtx* mtctx); -ZSTDLIB_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input); +ZSTDMT_API size_t ZSTDMT_nextInputSizeHint(const ZSTDMT_CCtx* mtctx); +ZSTDMT_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input); -ZSTDLIB_API size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ -ZSTDLIB_API size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ +ZSTDMT_API size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ +ZSTDMT_API size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ /* === Advanced functions and parameters === */ -ZSTDLIB_API size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const ZSTD_CDict* cdict, - ZSTD_parameters params, - int overlapLog); +ZSTDMT_API size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const ZSTD_CDict* cdict, + ZSTD_parameters params, + int overlapLog); -ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, +ZSTDMT_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, const void* dict, size_t dictSize, /* dict can be released after init, a local copy is preserved within zcs */ ZSTD_parameters params, unsigned long long pledgedSrcSize); /* pledgedSrcSize is optional and can be zero == unknown */ -ZSTDLIB_API size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, +ZSTDMT_API size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, const ZSTD_CDict* cdict, ZSTD_frameParameters fparams, unsigned long long pledgedSrcSize); /* note : zero means empty */ @@ -92,7 +109,7 @@ ZSTDLIB_API size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, * List of parameters that can be set using ZSTDMT_setMTCtxParameter() */ typedef enum { ZSTDMT_p_jobSize, /* Each job is compressed in parallel. By default, this value is dynamically determined depending on compression parameters. Can be set explicitly here. */ - ZSTDMT_p_overlapLog, /* Each job may reload a part of previous job to enhance compressionr ratio; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window. This is a "sticky" parameter : its value will be re-used on next compression job */ + ZSTDMT_p_overlapLog, /* Each job may reload a part of previous job to enhance compression ratio; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window. This is a "sticky" parameter : its value will be re-used on next compression job */ ZSTDMT_p_rsyncable /* Enables rsyncable mode. */ } ZSTDMT_parameter; @@ -101,12 +118,12 @@ typedef enum { * The function must be called typically after ZSTD_createCCtx() but __before ZSTDMT_init*() !__ * Parameters not explicitly reset by ZSTDMT_init*() remain the same in consecutive compression sessions. * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ -ZSTDLIB_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int value); +ZSTDMT_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int value); /* ZSTDMT_getMTCtxParameter() : * Query the ZSTDMT_CCtx for a parameter value. * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ -ZSTDLIB_API size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int* value); +ZSTDMT_API size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int* value); /*! ZSTDMT_compressStream_generic() : @@ -116,7 +133,7 @@ ZSTDLIB_API size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter * 0 if fully flushed * or an error code * note : needs to be init using any ZSTD_initCStream*() variant */ -ZSTDLIB_API size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, +ZSTDMT_API size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input, ZSTD_EndDirective endOp); diff --git a/thirdparty/zstd/decompress/zstd_ddict.c b/thirdparty/zstd/decompress/zstd_ddict.c index 2ad0440684..0af3d23bfe 100644 --- a/thirdparty/zstd/decompress/zstd_ddict.c +++ b/thirdparty/zstd/decompress/zstd_ddict.c @@ -105,9 +105,9 @@ ZSTD_loadEntropy_intoDDict(ZSTD_DDict* ddict, ddict->dictID = MEM_readLE32((const char*)ddict->dictContent + ZSTD_FRAMEIDSIZE); /* load entropy tables */ - CHECK_E( ZSTD_loadDEntropy(&ddict->entropy, - ddict->dictContent, ddict->dictSize), - dictionary_corrupted ); + RETURN_ERROR_IF(ZSTD_isError(ZSTD_loadDEntropy( + &ddict->entropy, ddict->dictContent, ddict->dictSize)), + dictionary_corrupted); ddict->entropyPresent = 1; return 0; } @@ -133,7 +133,7 @@ static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict, ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ /* parse dictionary content */ - CHECK_F( ZSTD_loadEntropy_intoDDict(ddict, dictContentType) ); + FORWARD_IF_ERROR( ZSTD_loadEntropy_intoDDict(ddict, dictContentType) ); return 0; } diff --git a/thirdparty/zstd/decompress/zstd_decompress.c b/thirdparty/zstd/decompress/zstd_decompress.c index feef1ef67a..675596f5aa 100644 --- a/thirdparty/zstd/decompress/zstd_decompress.c +++ b/thirdparty/zstd/decompress/zstd_decompress.c @@ -106,6 +106,7 @@ static void ZSTD_initDCtx_internal(ZSTD_DCtx* dctx) dctx->ddictLocal = NULL; dctx->dictEnd = NULL; dctx->ddictIsCold = 0; + dctx->dictUses = ZSTD_dont_use; dctx->inBuff = NULL; dctx->inBuffSize = 0; dctx->outBuffSize = 0; @@ -147,13 +148,20 @@ ZSTD_DCtx* ZSTD_createDCtx(void) return ZSTD_createDCtx_advanced(ZSTD_defaultCMem); } +static void ZSTD_clearDict(ZSTD_DCtx* dctx) +{ + ZSTD_freeDDict(dctx->ddictLocal); + dctx->ddictLocal = NULL; + dctx->ddict = NULL; + dctx->dictUses = ZSTD_dont_use; +} + size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx) { if (dctx==NULL) return 0; /* support free on NULL */ - if (dctx->staticSize) return ERROR(memory_allocation); /* not compatible with static DCtx */ + RETURN_ERROR_IF(dctx->staticSize, memory_allocation, "not compatible with static DCtx"); { ZSTD_customMem const cMem = dctx->customMem; - ZSTD_freeDDict(dctx->ddictLocal); - dctx->ddictLocal = NULL; + ZSTD_clearDict(dctx); ZSTD_free(dctx->inBuff, cMem); dctx->inBuff = NULL; #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) @@ -203,7 +211,7 @@ unsigned ZSTD_isFrame(const void* buffer, size_t size) static size_t ZSTD_frameHeaderSize_internal(const void* src, size_t srcSize, ZSTD_format_e format) { size_t const minInputSize = ZSTD_startingInputLength(format); - if (srcSize < minInputSize) return ERROR(srcSize_wrong); + RETURN_ERROR_IF(srcSize < minInputSize, srcSize_wrong); { BYTE const fhd = ((const BYTE*)src)[minInputSize-1]; U32 const dictID= fhd & 3; @@ -238,7 +246,7 @@ size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, s memset(zfhPtr, 0, sizeof(*zfhPtr)); /* not strictly necessary, but static analyzer do not understand that zfhPtr is only going to be read only if return value is zero, since they are 2 different signals */ if (srcSize < minInputSize) return minInputSize; - if (src==NULL) return ERROR(GENERIC); /* invalid parameter */ + RETURN_ERROR_IF(src==NULL, GENERIC, "invalid parameter"); if ( (format != ZSTD_f_zstd1_magicless) && (MEM_readLE32(src) != ZSTD_MAGICNUMBER) ) { @@ -251,7 +259,7 @@ size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, s zfhPtr->frameType = ZSTD_skippableFrame; return 0; } - return ERROR(prefix_unknown); + RETURN_ERROR(prefix_unknown); } /* ensure there is enough `srcSize` to fully read/decode frame header */ @@ -269,14 +277,13 @@ size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, s U64 windowSize = 0; U32 dictID = 0; U64 frameContentSize = ZSTD_CONTENTSIZE_UNKNOWN; - if ((fhdByte & 0x08) != 0) - return ERROR(frameParameter_unsupported); /* reserved bits, must be zero */ + RETURN_ERROR_IF((fhdByte & 0x08) != 0, frameParameter_unsupported, + "reserved bits, must be zero"); if (!singleSegment) { BYTE const wlByte = ip[pos++]; U32 const windowLog = (wlByte >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN; - if (windowLog > ZSTD_WINDOWLOG_MAX) - return ERROR(frameParameter_windowTooLarge); + RETURN_ERROR_IF(windowLog > ZSTD_WINDOWLOG_MAX, frameParameter_windowTooLarge); windowSize = (1ULL << windowLog); windowSize += (windowSize >> 3) * (wlByte&7); } @@ -348,12 +355,11 @@ static size_t readSkippableFrameSize(void const* src, size_t srcSize) size_t const skippableHeaderSize = ZSTD_SKIPPABLEHEADERSIZE; U32 sizeU32; - if (srcSize < ZSTD_SKIPPABLEHEADERSIZE) - return ERROR(srcSize_wrong); + RETURN_ERROR_IF(srcSize < ZSTD_SKIPPABLEHEADERSIZE, srcSize_wrong); sizeU32 = MEM_readLE32((BYTE const*)src + ZSTD_FRAMEIDSIZE); - if ((U32)(sizeU32 + ZSTD_SKIPPABLEHEADERSIZE) < sizeU32) - return ERROR(frameParameter_unsupported); + RETURN_ERROR_IF((U32)(sizeU32 + ZSTD_SKIPPABLEHEADERSIZE) < sizeU32, + frameParameter_unsupported); return skippableHeaderSize + sizeU32; } @@ -428,67 +434,124 @@ static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t he { size_t const result = ZSTD_getFrameHeader_advanced(&(dctx->fParams), src, headerSize, dctx->format); if (ZSTD_isError(result)) return result; /* invalid header */ - if (result>0) return ERROR(srcSize_wrong); /* headerSize too small */ - if (dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID)) - return ERROR(dictionary_wrong); + RETURN_ERROR_IF(result>0, srcSize_wrong, "headerSize too small"); +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* Skip the dictID check in fuzzing mode, because it makes the search + * harder. + */ + RETURN_ERROR_IF(dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID), + dictionary_wrong); +#endif if (dctx->fParams.checksumFlag) XXH64_reset(&dctx->xxhState, 0); return 0; } +static ZSTD_frameSizeInfo ZSTD_errorFrameSizeInfo(size_t ret) +{ + ZSTD_frameSizeInfo frameSizeInfo; + frameSizeInfo.compressedSize = ret; + frameSizeInfo.decompressedBound = ZSTD_CONTENTSIZE_ERROR; + return frameSizeInfo; +} -/** ZSTD_findFrameCompressedSize() : - * compatible with legacy mode - * `src` must point to the start of a ZSTD frame, ZSTD legacy frame, or skippable frame - * `srcSize` must be at least as large as the frame contained - * @return : the compressed size of the frame starting at `src` */ -size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) +static ZSTD_frameSizeInfo ZSTD_findFrameSizeInfo(const void* src, size_t srcSize) { + ZSTD_frameSizeInfo frameSizeInfo; + memset(&frameSizeInfo, 0, sizeof(ZSTD_frameSizeInfo)); + #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) if (ZSTD_isLegacy(src, srcSize)) - return ZSTD_findFrameCompressedSizeLegacy(src, srcSize); + return ZSTD_findFrameSizeInfoLegacy(src, srcSize); #endif - if ( (srcSize >= ZSTD_SKIPPABLEHEADERSIZE) - && (MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START ) { - return readSkippableFrameSize(src, srcSize); + + if ((srcSize >= ZSTD_SKIPPABLEHEADERSIZE) + && (MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { + frameSizeInfo.compressedSize = readSkippableFrameSize(src, srcSize); + return frameSizeInfo; } else { const BYTE* ip = (const BYTE*)src; const BYTE* const ipstart = ip; size_t remainingSize = srcSize; + size_t nbBlocks = 0; ZSTD_frameHeader zfh; /* Extract Frame Header */ { size_t const ret = ZSTD_getFrameHeader(&zfh, src, srcSize); - if (ZSTD_isError(ret)) return ret; - if (ret > 0) return ERROR(srcSize_wrong); + if (ZSTD_isError(ret)) + return ZSTD_errorFrameSizeInfo(ret); + if (ret > 0) + return ZSTD_errorFrameSizeInfo(ERROR(srcSize_wrong)); } ip += zfh.headerSize; remainingSize -= zfh.headerSize; - /* Loop on each block */ + /* Iterate over each block */ while (1) { blockProperties_t blockProperties; size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); - if (ZSTD_isError(cBlockSize)) return cBlockSize; + if (ZSTD_isError(cBlockSize)) + return ZSTD_errorFrameSizeInfo(cBlockSize); if (ZSTD_blockHeaderSize + cBlockSize > remainingSize) - return ERROR(srcSize_wrong); + return ZSTD_errorFrameSizeInfo(ERROR(srcSize_wrong)); ip += ZSTD_blockHeaderSize + cBlockSize; remainingSize -= ZSTD_blockHeaderSize + cBlockSize; + nbBlocks++; if (blockProperties.lastBlock) break; } - if (zfh.checksumFlag) { /* Final frame content checksum */ - if (remainingSize < 4) return ERROR(srcSize_wrong); + /* Final frame content checksum */ + if (zfh.checksumFlag) { + if (remainingSize < 4) + return ZSTD_errorFrameSizeInfo(ERROR(srcSize_wrong)); ip += 4; } - return ip - ipstart; + frameSizeInfo.compressedSize = ip - ipstart; + frameSizeInfo.decompressedBound = (zfh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) + ? zfh.frameContentSize + : nbBlocks * zfh.blockSizeMax; + return frameSizeInfo; } } +/** ZSTD_findFrameCompressedSize() : + * compatible with legacy mode + * `src` must point to the start of a ZSTD frame, ZSTD legacy frame, or skippable frame + * `srcSize` must be at least as large as the frame contained + * @return : the compressed size of the frame starting at `src` */ +size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) +{ + ZSTD_frameSizeInfo const frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize); + return frameSizeInfo.compressedSize; +} + + +/** ZSTD_decompressBound() : + * compatible with legacy mode + * `src` must point to the start of a ZSTD frame or a skippeable frame + * `srcSize` must be at least as large as the frame contained + * @return : the maximum decompressed size of the compressed source + */ +unsigned long long ZSTD_decompressBound(const void* src, size_t srcSize) +{ + unsigned long long bound = 0; + /* Iterate over each frame */ + while (srcSize > 0) { + ZSTD_frameSizeInfo const frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize); + size_t const compressedSize = frameSizeInfo.compressedSize; + unsigned long long const decompressedBound = frameSizeInfo.decompressedBound; + if (ZSTD_isError(compressedSize) || decompressedBound == ZSTD_CONTENTSIZE_ERROR) + return ZSTD_CONTENTSIZE_ERROR; + src = (const BYTE*)src + compressedSize; + srcSize -= compressedSize; + bound += decompressedBound; + } + return bound; +} /*-************************************************************* @@ -522,9 +585,9 @@ static size_t ZSTD_copyRawBlock(void* dst, size_t dstCapacity, DEBUGLOG(5, "ZSTD_copyRawBlock"); if (dst == NULL) { if (srcSize == 0) return 0; - return ERROR(dstBuffer_null); + RETURN_ERROR(dstBuffer_null); } - if (srcSize > dstCapacity) return ERROR(dstSize_tooSmall); + RETURN_ERROR_IF(srcSize > dstCapacity, dstSize_tooSmall); memcpy(dst, src, srcSize); return srcSize; } @@ -535,9 +598,9 @@ static size_t ZSTD_setRleBlock(void* dst, size_t dstCapacity, { if (dst == NULL) { if (regenSize == 0) return 0; - return ERROR(dstBuffer_null); + RETURN_ERROR(dstBuffer_null); } - if (regenSize > dstCapacity) return ERROR(dstSize_tooSmall); + RETURN_ERROR_IF(regenSize > dstCapacity, dstSize_tooSmall); memset(dst, b, regenSize); return regenSize; } @@ -560,15 +623,16 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, DEBUGLOG(4, "ZSTD_decompressFrame (srcSize:%i)", (int)*srcSizePtr); /* check */ - if (remainingSrcSize < ZSTD_FRAMEHEADERSIZE_MIN+ZSTD_blockHeaderSize) - return ERROR(srcSize_wrong); + RETURN_ERROR_IF( + remainingSrcSize < ZSTD_FRAMEHEADERSIZE_MIN+ZSTD_blockHeaderSize, + srcSize_wrong); /* Frame Header */ { size_t const frameHeaderSize = ZSTD_frameHeaderSize(ip, ZSTD_FRAMEHEADERSIZE_PREFIX); if (ZSTD_isError(frameHeaderSize)) return frameHeaderSize; - if (remainingSrcSize < frameHeaderSize+ZSTD_blockHeaderSize) - return ERROR(srcSize_wrong); - CHECK_F( ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize) ); + RETURN_ERROR_IF(remainingSrcSize < frameHeaderSize+ZSTD_blockHeaderSize, + srcSize_wrong); + FORWARD_IF_ERROR( ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize) ); ip += frameHeaderSize; remainingSrcSize -= frameHeaderSize; } @@ -581,7 +645,7 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, ip += ZSTD_blockHeaderSize; remainingSrcSize -= ZSTD_blockHeaderSize; - if (cBlockSize > remainingSrcSize) return ERROR(srcSize_wrong); + RETURN_ERROR_IF(cBlockSize > remainingSrcSize, srcSize_wrong); switch(blockProperties.blockType) { @@ -596,7 +660,7 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, break; case bt_reserved : default: - return ERROR(corruption_detected); + RETURN_ERROR(corruption_detected); } if (ZSTD_isError(decodedSize)) return decodedSize; @@ -609,15 +673,15 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, } if (dctx->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) { - if ((U64)(op-ostart) != dctx->fParams.frameContentSize) { - return ERROR(corruption_detected); - } } + RETURN_ERROR_IF((U64)(op-ostart) != dctx->fParams.frameContentSize, + corruption_detected); + } if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */ U32 const checkCalc = (U32)XXH64_digest(&dctx->xxhState); U32 checkRead; - if (remainingSrcSize<4) return ERROR(checksum_wrong); + RETURN_ERROR_IF(remainingSrcSize<4, checksum_wrong); checkRead = MEM_readLE32(ip); - if (checkRead != checkCalc) return ERROR(checksum_wrong); + RETURN_ERROR_IF(checkRead != checkCalc, checksum_wrong); ip += 4; remainingSrcSize -= 4; } @@ -652,8 +716,8 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, size_t decodedSize; size_t const frameSize = ZSTD_findFrameCompressedSizeLegacy(src, srcSize); if (ZSTD_isError(frameSize)) return frameSize; - /* legacy support is not compatible with static dctx */ - if (dctx->staticSize) return ERROR(memory_allocation); + RETURN_ERROR_IF(dctx->staticSize, memory_allocation, + "legacy support is not compatible with static dctx"); decodedSize = ZSTD_decompressLegacy(dst, dstCapacity, src, frameSize, dict, dictSize); if (ZSTD_isError(decodedSize)) return decodedSize; @@ -676,7 +740,7 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, size_t const skippableSize = readSkippableFrameSize(src, srcSize); if (ZSTD_isError(skippableSize)) return skippableSize; - if (srcSize < skippableSize) return ERROR(srcSize_wrong); + RETURN_ERROR_IF(srcSize < skippableSize, srcSize_wrong); src = (const BYTE *)src + skippableSize; srcSize -= skippableSize; @@ -685,29 +749,29 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, if (ddict) { /* we were called from ZSTD_decompress_usingDDict */ - CHECK_F(ZSTD_decompressBegin_usingDDict(dctx, ddict)); + FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDDict(dctx, ddict)); } else { /* this will initialize correctly with no dict if dict == NULL, so * use this in all cases but ddict */ - CHECK_F(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize)); + FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize)); } ZSTD_checkContinuity(dctx, dst); { const size_t res = ZSTD_decompressFrame(dctx, dst, dstCapacity, &src, &srcSize); - if ( (ZSTD_getErrorCode(res) == ZSTD_error_prefix_unknown) - && (moreThan1Frame==1) ) { - /* at least one frame successfully completed, - * but following bytes are garbage : - * it's more likely to be a srcSize error, - * specifying more bytes than compressed size of frame(s). - * This error message replaces ERROR(prefix_unknown), - * which would be confusing, as the first header is actually correct. - * Note that one could be unlucky, it might be a corruption error instead, - * happening right at the place where we expect zstd magic bytes. - * But this is _much_ less likely than a srcSize field error. */ - return ERROR(srcSize_wrong); - } + RETURN_ERROR_IF( + (ZSTD_getErrorCode(res) == ZSTD_error_prefix_unknown) + && (moreThan1Frame==1), + srcSize_wrong, + "at least one frame successfully completed, but following " + "bytes are garbage: it's more likely to be a srcSize error, " + "specifying more bytes than compressed size of frame(s). This " + "error message replaces ERROR(prefix_unknown), which would be " + "confusing, as the first header is actually correct. Note that " + "one could be unlucky, it might be a corruption error instead, " + "happening right at the place where we expect zstd magic " + "bytes. But this is _much_ less likely than a srcSize field " + "error."); if (ZSTD_isError(res)) return res; assert(res <= dstCapacity); dst = (BYTE*)dst + res; @@ -716,7 +780,7 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, moreThan1Frame = 1; } /* while (srcSize >= ZSTD_frameHeaderSize_prefix) */ - if (srcSize) return ERROR(srcSize_wrong); /* input not entirely consumed */ + RETURN_ERROR_IF(srcSize, srcSize_wrong, "input not entirely consumed"); return (BYTE*)dst - (BYTE*)dststart; } @@ -730,9 +794,26 @@ size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx, } +static ZSTD_DDict const* ZSTD_getDDict(ZSTD_DCtx* dctx) +{ + switch (dctx->dictUses) { + default: + assert(0 /* Impossible */); + /* fall-through */ + case ZSTD_dont_use: + ZSTD_clearDict(dctx); + return NULL; + case ZSTD_use_indefinitely: + return dctx->ddict; + case ZSTD_use_once: + dctx->dictUses = ZSTD_dont_use; + return dctx->ddict; + } +} + size_t ZSTD_decompressDCtx(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { - return ZSTD_decompress_usingDict(dctx, dst, dstCapacity, src, srcSize, NULL, 0); + return ZSTD_decompress_usingDDict(dctx, dst, dstCapacity, src, srcSize, ZSTD_getDDict(dctx)); } @@ -741,7 +822,7 @@ size_t ZSTD_decompress(void* dst, size_t dstCapacity, const void* src, size_t sr #if defined(ZSTD_HEAPMODE) && (ZSTD_HEAPMODE>=1) size_t regenSize; ZSTD_DCtx* const dctx = ZSTD_createDCtx(); - if (dctx==NULL) return ERROR(memory_allocation); + RETURN_ERROR_IF(dctx==NULL, memory_allocation); regenSize = ZSTD_decompressDCtx(dctx, dst, dstCapacity, src, srcSize); ZSTD_freeDCtx(dctx); return regenSize; @@ -791,8 +872,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c { DEBUGLOG(5, "ZSTD_decompressContinue (srcSize:%u)", (unsigned)srcSize); /* Sanity check */ - if (srcSize != dctx->expected) - return ERROR(srcSize_wrong); /* not allowed */ + RETURN_ERROR_IF(srcSize != dctx->expected, srcSize_wrong, "not allowed"); if (dstCapacity) ZSTD_checkContinuity(dctx, dst); switch (dctx->stage) @@ -817,7 +897,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c case ZSTDds_decodeFrameHeader: assert(src != NULL); memcpy(dctx->headerBuffer + (dctx->headerSize - srcSize), src, srcSize); - CHECK_F(ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize)); + FORWARD_IF_ERROR(ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize)); dctx->expected = ZSTD_blockHeaderSize; dctx->stage = ZSTDds_decodeBlockHeader; return 0; @@ -867,7 +947,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c break; case bt_reserved : /* should never happen */ default: - return ERROR(corruption_detected); + RETURN_ERROR(corruption_detected); } if (ZSTD_isError(rSize)) return rSize; DEBUGLOG(5, "ZSTD_decompressContinue: decoded size from block : %u", (unsigned)rSize); @@ -876,10 +956,10 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c if (dctx->stage == ZSTDds_decompressLastBlock) { /* end of frame */ DEBUGLOG(4, "ZSTD_decompressContinue: decoded size from frame : %u", (unsigned)dctx->decodedSize); - if (dctx->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) { - if (dctx->decodedSize != dctx->fParams.frameContentSize) { - return ERROR(corruption_detected); - } } + RETURN_ERROR_IF( + dctx->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN + && dctx->decodedSize != dctx->fParams.frameContentSize, + corruption_detected); if (dctx->fParams.checksumFlag) { /* another round for frame checksum */ dctx->expected = 4; dctx->stage = ZSTDds_checkChecksum; @@ -900,7 +980,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c { U32 const h32 = (U32)XXH64_digest(&dctx->xxhState); U32 const check32 = MEM_readLE32(src); DEBUGLOG(4, "ZSTD_decompressContinue: checksum : calculated %08X :: %08X read", (unsigned)h32, (unsigned)check32); - if (check32 != h32) return ERROR(checksum_wrong); + RETURN_ERROR_IF(check32 != h32, checksum_wrong); dctx->expected = 0; dctx->stage = ZSTDds_getFrameHeaderSize; return 0; @@ -921,7 +1001,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c default: assert(0); /* impossible */ - return ERROR(GENERIC); /* some compiler require default to do something */ + RETURN_ERROR(GENERIC); /* some compiler require default to do something */ } } @@ -945,7 +1025,7 @@ ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy, const BYTE* dictPtr = (const BYTE*)dict; const BYTE* const dictEnd = dictPtr + dictSize; - if (dictSize <= 8) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(dictSize <= 8, dictionary_corrupted); assert(MEM_readLE32(dict) == ZSTD_MAGIC_DICTIONARY); /* dict must be valid */ dictPtr += 8; /* skip header = magic + dictID */ @@ -964,16 +1044,16 @@ ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy, dictPtr, dictEnd - dictPtr, workspace, workspaceSize); #endif - if (HUF_isError(hSize)) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(HUF_isError(hSize), dictionary_corrupted); dictPtr += hSize; } { short offcodeNCount[MaxOff+1]; unsigned offcodeMaxValue = MaxOff, offcodeLog; size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr); - if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted); - if (offcodeMaxValue > MaxOff) return ERROR(dictionary_corrupted); - if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(FSE_isError(offcodeHeaderSize), dictionary_corrupted); + RETURN_ERROR_IF(offcodeMaxValue > MaxOff, dictionary_corrupted); + RETURN_ERROR_IF(offcodeLog > OffFSELog, dictionary_corrupted); ZSTD_buildFSETable( entropy->OFTable, offcodeNCount, offcodeMaxValue, OF_base, OF_bits, @@ -984,9 +1064,9 @@ ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy, { short matchlengthNCount[MaxML+1]; unsigned matchlengthMaxValue = MaxML, matchlengthLog; size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr); - if (FSE_isError(matchlengthHeaderSize)) return ERROR(dictionary_corrupted); - if (matchlengthMaxValue > MaxML) return ERROR(dictionary_corrupted); - if (matchlengthLog > MLFSELog) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(FSE_isError(matchlengthHeaderSize), dictionary_corrupted); + RETURN_ERROR_IF(matchlengthMaxValue > MaxML, dictionary_corrupted); + RETURN_ERROR_IF(matchlengthLog > MLFSELog, dictionary_corrupted); ZSTD_buildFSETable( entropy->MLTable, matchlengthNCount, matchlengthMaxValue, ML_base, ML_bits, @@ -997,9 +1077,9 @@ ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy, { short litlengthNCount[MaxLL+1]; unsigned litlengthMaxValue = MaxLL, litlengthLog; size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr); - if (FSE_isError(litlengthHeaderSize)) return ERROR(dictionary_corrupted); - if (litlengthMaxValue > MaxLL) return ERROR(dictionary_corrupted); - if (litlengthLog > LLFSELog) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(FSE_isError(litlengthHeaderSize), dictionary_corrupted); + RETURN_ERROR_IF(litlengthMaxValue > MaxLL, dictionary_corrupted); + RETURN_ERROR_IF(litlengthLog > LLFSELog, dictionary_corrupted); ZSTD_buildFSETable( entropy->LLTable, litlengthNCount, litlengthMaxValue, LL_base, LL_bits, @@ -1007,12 +1087,13 @@ ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy, dictPtr += litlengthHeaderSize; } - if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(dictPtr+12 > dictEnd, dictionary_corrupted); { int i; size_t const dictContentSize = (size_t)(dictEnd - (dictPtr+12)); for (i=0; i<3; i++) { U32 const rep = MEM_readLE32(dictPtr); dictPtr += 4; - if (rep==0 || rep >= dictContentSize) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(rep==0 || rep >= dictContentSize, + dictionary_corrupted); entropy->rep[i] = rep; } } @@ -1030,7 +1111,7 @@ static size_t ZSTD_decompress_insertDictionary(ZSTD_DCtx* dctx, const void* dict /* load entropy tables */ { size_t const eSize = ZSTD_loadDEntropy(&dctx->entropy, dict, dictSize); - if (ZSTD_isError(eSize)) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(ZSTD_isError(eSize), dictionary_corrupted); dict = (const char*)dict + eSize; dictSize -= eSize; } @@ -1064,9 +1145,11 @@ size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx) size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize) { - CHECK_F( ZSTD_decompressBegin(dctx) ); + FORWARD_IF_ERROR( ZSTD_decompressBegin(dctx) ); if (dict && dictSize) - CHECK_E(ZSTD_decompress_insertDictionary(dctx, dict, dictSize), dictionary_corrupted); + RETURN_ERROR_IF( + ZSTD_isError(ZSTD_decompress_insertDictionary(dctx, dict, dictSize)), + dictionary_corrupted); return 0; } @@ -1085,7 +1168,7 @@ size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) DEBUGLOG(4, "DDict is %s", dctx->ddictIsCold ? "~cold~" : "hot!"); } - CHECK_F( ZSTD_decompressBegin(dctx) ); + FORWARD_IF_ERROR( ZSTD_decompressBegin(dctx) ); if (ddict) { /* NULL ddict is equivalent to no dictionary */ ZSTD_copyDDictParameters(dctx, ddict); } @@ -1104,7 +1187,7 @@ unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize) } /*! ZSTD_getDictID_fromFrame() : - * Provides the dictID required to decompresse frame stored within `src`. + * Provides the dictID required to decompress frame stored within `src`. * If @return == 0, the dictID could not be decoded. * This could for one of the following reasons : * - The frame does not require a dictionary (most common case). @@ -1176,15 +1259,14 @@ size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) { - if (dctx->streamStage != zdss_init) return ERROR(stage_wrong); - ZSTD_freeDDict(dctx->ddictLocal); + RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong); + ZSTD_clearDict(dctx); if (dict && dictSize >= 8) { dctx->ddictLocal = ZSTD_createDDict_advanced(dict, dictSize, dictLoadMethod, dictContentType, dctx->customMem); - if (dctx->ddictLocal == NULL) return ERROR(memory_allocation); - } else { - dctx->ddictLocal = NULL; + RETURN_ERROR_IF(dctx->ddictLocal == NULL, memory_allocation); + dctx->ddict = dctx->ddictLocal; + dctx->dictUses = ZSTD_use_indefinitely; } - dctx->ddict = dctx->ddictLocal; return 0; } @@ -1200,7 +1282,9 @@ size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSi size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType) { - return ZSTD_DCtx_loadDictionary_advanced(dctx, prefix, prefixSize, ZSTD_dlm_byRef, dictContentType); + FORWARD_IF_ERROR(ZSTD_DCtx_loadDictionary_advanced(dctx, prefix, prefixSize, ZSTD_dlm_byRef, dictContentType)); + dctx->dictUses = ZSTD_use_once; + return 0; } size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize) @@ -1215,9 +1299,8 @@ size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSiz size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize) { DEBUGLOG(4, "ZSTD_initDStream_usingDict"); - zds->streamStage = zdss_init; - zds->noForwardProgress = 0; - CHECK_F( ZSTD_DCtx_loadDictionary(zds, dict, dictSize) ); + FORWARD_IF_ERROR( ZSTD_DCtx_reset(zds, ZSTD_reset_session_only) ); + FORWARD_IF_ERROR( ZSTD_DCtx_loadDictionary(zds, dict, dictSize) ); return ZSTD_FRAMEHEADERSIZE_PREFIX; } @@ -1225,7 +1308,7 @@ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t di size_t ZSTD_initDStream(ZSTD_DStream* zds) { DEBUGLOG(4, "ZSTD_initDStream"); - return ZSTD_initDStream_usingDict(zds, NULL, 0); + return ZSTD_initDStream_usingDDict(zds, NULL); } /* ZSTD_initDStream_usingDDict() : @@ -1233,9 +1316,9 @@ size_t ZSTD_initDStream(ZSTD_DStream* zds) * this function cannot fail */ size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict) { - size_t const initResult = ZSTD_initDStream(dctx); - dctx->ddict = ddict; - return initResult; + FORWARD_IF_ERROR( ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only) ); + FORWARD_IF_ERROR( ZSTD_DCtx_refDDict(dctx, ddict) ); + return ZSTD_FRAMEHEADERSIZE_PREFIX; } /* ZSTD_resetDStream() : @@ -1243,19 +1326,19 @@ size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict) * this function cannot fail */ size_t ZSTD_resetDStream(ZSTD_DStream* dctx) { - DEBUGLOG(4, "ZSTD_resetDStream"); - dctx->streamStage = zdss_loadHeader; - dctx->lhSize = dctx->inPos = dctx->outStart = dctx->outEnd = 0; - dctx->legacyVersion = 0; - dctx->hostageByte = 0; + FORWARD_IF_ERROR(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only)); return ZSTD_FRAMEHEADERSIZE_PREFIX; } size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) { - if (dctx->streamStage != zdss_init) return ERROR(stage_wrong); - dctx->ddict = ddict; + RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong); + ZSTD_clearDict(dctx); + if (ddict) { + dctx->ddict = ddict; + dctx->dictUses = ZSTD_use_indefinitely; + } return 0; } @@ -1267,9 +1350,9 @@ size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize) ZSTD_bounds const bounds = ZSTD_dParam_getBounds(ZSTD_d_windowLogMax); size_t const min = (size_t)1 << bounds.lowerBound; size_t const max = (size_t)1 << bounds.upperBound; - if (dctx->streamStage != zdss_init) return ERROR(stage_wrong); - if (maxWindowSize < min) return ERROR(parameter_outOfBound); - if (maxWindowSize > max) return ERROR(parameter_outOfBound); + RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong); + RETURN_ERROR_IF(maxWindowSize < min, parameter_outOfBound); + RETURN_ERROR_IF(maxWindowSize > max, parameter_outOfBound); dctx->maxWindowSize = maxWindowSize; return 0; } @@ -1311,15 +1394,15 @@ static int ZSTD_dParam_withinBounds(ZSTD_dParameter dParam, int value) } #define CHECK_DBOUNDS(p,v) { \ - if (!ZSTD_dParam_withinBounds(p, v)) \ - return ERROR(parameter_outOfBound); \ + RETURN_ERROR_IF(!ZSTD_dParam_withinBounds(p, v), parameter_outOfBound); \ } size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter dParam, int value) { - if (dctx->streamStage != zdss_init) return ERROR(stage_wrong); + RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong); switch(dParam) { case ZSTD_d_windowLogMax: + if (value == 0) value = ZSTD_WINDOWLOG_LIMIT_DEFAULT; CHECK_DBOUNDS(ZSTD_d_windowLogMax, value); dctx->maxWindowSize = ((size_t)1) << value; return 0; @@ -1329,19 +1412,20 @@ size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter dParam, int value return 0; default:; } - return ERROR(parameter_unsupported); + RETURN_ERROR(parameter_unsupported); } size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset) { if ( (reset == ZSTD_reset_session_only) || (reset == ZSTD_reset_session_and_parameters) ) { - (void)ZSTD_initDStream(dctx); + dctx->streamStage = zdss_init; + dctx->noForwardProgress = 0; } if ( (reset == ZSTD_reset_parameters) || (reset == ZSTD_reset_session_and_parameters) ) { - if (dctx->streamStage != zdss_init) - return ERROR(stage_wrong); + RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong); + ZSTD_clearDict(dctx); dctx->format = ZSTD_f_zstd1; dctx->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT; } @@ -1360,7 +1444,8 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long unsigned long long const neededRBSize = windowSize + blockSize + (WILDCOPY_OVERLENGTH * 2); unsigned long long const neededSize = MIN(frameContentSize, neededRBSize); size_t const minRBSize = (size_t) neededSize; - if ((unsigned long long)minRBSize != neededSize) return ERROR(frameParameter_windowTooLarge); + RETURN_ERROR_IF((unsigned long long)minRBSize != neededSize, + frameParameter_windowTooLarge); return minRBSize; } @@ -1378,9 +1463,9 @@ size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize) ZSTD_frameHeader zfh; size_t const err = ZSTD_getFrameHeader(&zfh, src, srcSize); if (ZSTD_isError(err)) return err; - if (err>0) return ERROR(srcSize_wrong); - if (zfh.windowSize > windowSizeMax) - return ERROR(frameParameter_windowTooLarge); + RETURN_ERROR_IF(err>0, srcSize_wrong); + RETURN_ERROR_IF(zfh.windowSize > windowSizeMax, + frameParameter_windowTooLarge); return ZSTD_estimateDStreamSize((size_t)zfh.windowSize); } @@ -1406,16 +1491,16 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB U32 someMoreWork = 1; DEBUGLOG(5, "ZSTD_decompressStream"); - if (input->pos > input->size) { /* forbidden */ - DEBUGLOG(5, "in: pos: %u vs size: %u", - (U32)input->pos, (U32)input->size); - return ERROR(srcSize_wrong); - } - if (output->pos > output->size) { /* forbidden */ - DEBUGLOG(5, "out: pos: %u vs size: %u", - (U32)output->pos, (U32)output->size); - return ERROR(dstSize_tooSmall); - } + RETURN_ERROR_IF( + input->pos > input->size, + srcSize_wrong, + "forbidden. in: pos: %u vs size: %u", + (U32)input->pos, (U32)input->size); + RETURN_ERROR_IF( + output->pos > output->size, + dstSize_tooSmall, + "forbidden. out: pos: %u vs size: %u", + (U32)output->pos, (U32)output->size); DEBUGLOG(5, "input size : %u", (U32)(input->size - input->pos)); while (someMoreWork) { @@ -1423,15 +1508,18 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB { case zdss_init : DEBUGLOG(5, "stage zdss_init => transparent reset "); - ZSTD_resetDStream(zds); /* transparent reset on starting decoding a new frame */ + zds->streamStage = zdss_loadHeader; + zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; + zds->legacyVersion = 0; + zds->hostageByte = 0; /* fall-through */ case zdss_loadHeader : DEBUGLOG(5, "stage zdss_loadHeader (srcSize : %u)", (U32)(iend - ip)); #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) if (zds->legacyVersion) { - /* legacy support is incompatible with static dctx */ - if (zds->staticSize) return ERROR(memory_allocation); + RETURN_ERROR_IF(zds->staticSize, memory_allocation, + "legacy support is incompatible with static dctx"); { size_t const hint = ZSTD_decompressLegacyStream(zds->legacyContext, zds->legacyVersion, output, input); if (hint==0) zds->streamStage = zdss_init; return hint; @@ -1443,12 +1531,13 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) U32 const legacyVersion = ZSTD_isLegacy(istart, iend-istart); if (legacyVersion) { - const void* const dict = zds->ddict ? ZSTD_DDict_dictContent(zds->ddict) : NULL; - size_t const dictSize = zds->ddict ? ZSTD_DDict_dictSize(zds->ddict) : 0; + ZSTD_DDict const* const ddict = ZSTD_getDDict(zds); + const void* const dict = ddict ? ZSTD_DDict_dictContent(ddict) : NULL; + size_t const dictSize = ddict ? ZSTD_DDict_dictSize(ddict) : 0; DEBUGLOG(5, "ZSTD_decompressStream: detected legacy version v0.%u", legacyVersion); - /* legacy support is incompatible with static dctx */ - if (zds->staticSize) return ERROR(memory_allocation); - CHECK_F(ZSTD_initLegacyStream(&zds->legacyContext, + RETURN_ERROR_IF(zds->staticSize, memory_allocation, + "legacy support is incompatible with static dctx"); + FORWARD_IF_ERROR(ZSTD_initLegacyStream(&zds->legacyContext, zds->previousLegacyVersion, legacyVersion, dict, dictSize)); zds->legacyVersion = zds->previousLegacyVersion = legacyVersion; @@ -1482,7 +1571,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend-istart); if (cSize <= (size_t)(iend-istart)) { /* shortcut : using single-pass mode */ - size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, oend-op, istart, cSize, zds->ddict); + size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, oend-op, istart, cSize, ZSTD_getDDict(zds)); if (ZSTD_isError(decompressedSize)) return decompressedSize; DEBUGLOG(4, "shortcut to single-pass ZSTD_decompress_usingDDict()") ip = istart + cSize; @@ -1495,13 +1584,13 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB /* Consume header (see ZSTDds_decodeFrameHeader) */ DEBUGLOG(4, "Consume header"); - CHECK_F(ZSTD_decompressBegin_usingDDict(zds, zds->ddict)); + FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDDict(zds, ZSTD_getDDict(zds))); if ((MEM_readLE32(zds->headerBuffer) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */ zds->expected = MEM_readLE32(zds->headerBuffer + ZSTD_FRAMEIDSIZE); zds->stage = ZSTDds_skipFrame; } else { - CHECK_F(ZSTD_decodeFrameHeader(zds, zds->headerBuffer, zds->lhSize)); + FORWARD_IF_ERROR(ZSTD_decodeFrameHeader(zds, zds->headerBuffer, zds->lhSize)); zds->expected = ZSTD_blockHeaderSize; zds->stage = ZSTDds_decodeBlockHeader; } @@ -1511,7 +1600,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB (U32)(zds->fParams.windowSize >>10), (U32)(zds->maxWindowSize >> 10) ); zds->fParams.windowSize = MAX(zds->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN); - if (zds->fParams.windowSize > zds->maxWindowSize) return ERROR(frameParameter_windowTooLarge); + RETURN_ERROR_IF(zds->fParams.windowSize > zds->maxWindowSize, + frameParameter_windowTooLarge); /* Adapt buffer sizes to frame header instructions */ { size_t const neededInBuffSize = MAX(zds->fParams.blockSizeMax, 4 /* frame checksum */); @@ -1525,14 +1615,15 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB if (zds->staticSize) { /* static DCtx */ DEBUGLOG(4, "staticSize : %u", (U32)zds->staticSize); assert(zds->staticSize >= sizeof(ZSTD_DCtx)); /* controlled at init */ - if (bufferSize > zds->staticSize - sizeof(ZSTD_DCtx)) - return ERROR(memory_allocation); + RETURN_ERROR_IF( + bufferSize > zds->staticSize - sizeof(ZSTD_DCtx), + memory_allocation); } else { ZSTD_free(zds->inBuff, zds->customMem); zds->inBuffSize = 0; zds->outBuffSize = 0; zds->inBuff = (char*)ZSTD_malloc(bufferSize, zds->customMem); - if (zds->inBuff == NULL) return ERROR(memory_allocation); + RETURN_ERROR_IF(zds->inBuff == NULL, memory_allocation); } zds->inBuffSize = neededInBuffSize; zds->outBuff = zds->inBuff + zds->inBuffSize; @@ -1574,7 +1665,9 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB if (isSkipFrame) { loadedSize = MIN(toLoad, (size_t)(iend-ip)); } else { - if (toLoad > zds->inBuffSize - zds->inPos) return ERROR(corruption_detected); /* should never happen */ + RETURN_ERROR_IF(toLoad > zds->inBuffSize - zds->inPos, + corruption_detected, + "should never happen"); loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, iend-ip); } ip += loadedSize; @@ -1615,7 +1708,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB default: assert(0); /* impossible */ - return ERROR(GENERIC); /* some compiler require default to do something */ + RETURN_ERROR(GENERIC); /* some compiler require default to do something */ } } /* result */ @@ -1624,8 +1717,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB if ((ip==istart) && (op==ostart)) { /* no forward progress */ zds->noForwardProgress ++; if (zds->noForwardProgress >= ZSTD_NO_FORWARD_PROGRESS_MAX) { - if (op==oend) return ERROR(dstSize_tooSmall); - if (ip==iend) return ERROR(srcSize_wrong); + RETURN_ERROR_IF(op==oend, dstSize_tooSmall); + RETURN_ERROR_IF(ip==iend, srcSize_wrong); assert(0); } } else { diff --git a/thirdparty/zstd/decompress/zstd_decompress_block.c b/thirdparty/zstd/decompress/zstd_decompress_block.c index 32baad9fbb..a2a7eedcf2 100644 --- a/thirdparty/zstd/decompress/zstd_decompress_block.c +++ b/thirdparty/zstd/decompress/zstd_decompress_block.c @@ -56,14 +56,15 @@ static void ZSTD_copy4(void* dst, const void* src) { memcpy(dst, src, 4); } size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* bpPtr) { - if (srcSize < ZSTD_blockHeaderSize) return ERROR(srcSize_wrong); + RETURN_ERROR_IF(srcSize < ZSTD_blockHeaderSize, srcSize_wrong); + { U32 const cBlockHeader = MEM_readLE24(src); U32 const cSize = cBlockHeader >> 3; bpPtr->lastBlock = cBlockHeader & 1; bpPtr->blockType = (blockType_e)((cBlockHeader >> 1) & 3); bpPtr->origSize = cSize; /* only useful for RLE */ if (bpPtr->blockType == bt_rle) return 1; - if (bpPtr->blockType == bt_reserved) return ERROR(corruption_detected); + RETURN_ERROR_IF(bpPtr->blockType == bt_reserved, corruption_detected); return cSize; } } @@ -78,7 +79,7 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, const void* src, size_t srcSize) /* note : srcSize < BLOCKSIZE */ { - if (srcSize < MIN_CBLOCK_SIZE) return ERROR(corruption_detected); + RETURN_ERROR_IF(srcSize < MIN_CBLOCK_SIZE, corruption_detected); { const BYTE* const istart = (const BYTE*) src; symbolEncodingType_e const litEncType = (symbolEncodingType_e)(istart[0] & 3); @@ -86,11 +87,11 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, switch(litEncType) { case set_repeat: - if (dctx->litEntropy==0) return ERROR(dictionary_corrupted); + RETURN_ERROR_IF(dctx->litEntropy==0, dictionary_corrupted); /* fall-through */ case set_compressed: - if (srcSize < 5) return ERROR(corruption_detected); /* srcSize >= MIN_CBLOCK_SIZE == 3; here we need up to 5 for case 3 */ + RETURN_ERROR_IF(srcSize < 5, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need up to 5 for case 3"); { size_t lhSize, litSize, litCSize; U32 singleStream=0; U32 const lhlCode = (istart[0] >> 2) & 3; @@ -118,8 +119,8 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, litCSize = (lhc >> 22) + (istart[4] << 10); break; } - if (litSize > ZSTD_BLOCKSIZE_MAX) return ERROR(corruption_detected); - if (litCSize + lhSize > srcSize) return ERROR(corruption_detected); + RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected); + RETURN_ERROR_IF(litCSize + lhSize > srcSize, corruption_detected); /* prefetch huffman table if cold */ if (dctx->ddictIsCold && (litSize > 768 /* heuristic */)) { @@ -157,7 +158,7 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, } } - if (HUF_isError(hufSuccess)) return ERROR(corruption_detected); + RETURN_ERROR_IF(HUF_isError(hufSuccess), corruption_detected); dctx->litPtr = dctx->litBuffer; dctx->litSize = litSize; @@ -187,7 +188,7 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, } if (lhSize+litSize+WILDCOPY_OVERLENGTH > srcSize) { /* risk reading beyond src buffer with wildcopy */ - if (litSize+lhSize > srcSize) return ERROR(corruption_detected); + RETURN_ERROR_IF(litSize+lhSize > srcSize, corruption_detected); memcpy(dctx->litBuffer, istart+lhSize, litSize); dctx->litPtr = dctx->litBuffer; dctx->litSize = litSize; @@ -216,17 +217,17 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, case 3: lhSize = 3; litSize = MEM_readLE24(istart) >> 4; - if (srcSize<4) return ERROR(corruption_detected); /* srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4 */ + RETURN_ERROR_IF(srcSize<4, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4"); break; } - if (litSize > ZSTD_BLOCKSIZE_MAX) return ERROR(corruption_detected); + RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected); memset(dctx->litBuffer, istart[lhSize], litSize + WILDCOPY_OVERLENGTH); dctx->litPtr = dctx->litBuffer; dctx->litSize = litSize; return lhSize+1; } default: - return ERROR(corruption_detected); /* impossible */ + RETURN_ERROR(corruption_detected, "impossible"); } } } @@ -436,8 +437,8 @@ static size_t ZSTD_buildSeqTable(ZSTD_seqSymbol* DTableSpace, const ZSTD_seqSymb switch(type) { case set_rle : - if (!srcSize) return ERROR(srcSize_wrong); - if ( (*(const BYTE*)src) > max) return ERROR(corruption_detected); + RETURN_ERROR_IF(!srcSize, srcSize_wrong); + RETURN_ERROR_IF((*(const BYTE*)src) > max, corruption_detected); { U32 const symbol = *(const BYTE*)src; U32 const baseline = baseValue[symbol]; U32 const nbBits = nbAdditionalBits[symbol]; @@ -449,7 +450,7 @@ static size_t ZSTD_buildSeqTable(ZSTD_seqSymbol* DTableSpace, const ZSTD_seqSymb *DTablePtr = defaultTable; return 0; case set_repeat: - if (!flagRepeatTable) return ERROR(corruption_detected); + RETURN_ERROR_IF(!flagRepeatTable, corruption_detected); /* prefetch FSE table if used */ if (ddictIsCold && (nbSeq > 24 /* heuristic */)) { const void* const pStart = *DTablePtr; @@ -461,15 +462,15 @@ static size_t ZSTD_buildSeqTable(ZSTD_seqSymbol* DTableSpace, const ZSTD_seqSymb { unsigned tableLog; S16 norm[MaxSeq+1]; size_t const headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize); - if (FSE_isError(headerSize)) return ERROR(corruption_detected); - if (tableLog > maxLog) return ERROR(corruption_detected); + RETURN_ERROR_IF(FSE_isError(headerSize), corruption_detected); + RETURN_ERROR_IF(tableLog > maxLog, corruption_detected); ZSTD_buildFSETable(DTableSpace, norm, max, baseValue, nbAdditionalBits, tableLog); *DTablePtr = DTableSpace; return headerSize; } - default : /* impossible */ + default : assert(0); - return ERROR(GENERIC); + RETURN_ERROR(GENERIC, "impossible"); } } @@ -483,28 +484,28 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, DEBUGLOG(5, "ZSTD_decodeSeqHeaders"); /* check */ - if (srcSize < MIN_SEQUENCES_SIZE) return ERROR(srcSize_wrong); + RETURN_ERROR_IF(srcSize < MIN_SEQUENCES_SIZE, srcSize_wrong); /* SeqHead */ nbSeq = *ip++; if (!nbSeq) { *nbSeqPtr=0; - if (srcSize != 1) return ERROR(srcSize_wrong); + RETURN_ERROR_IF(srcSize != 1, srcSize_wrong); return 1; } if (nbSeq > 0x7F) { if (nbSeq == 0xFF) { - if (ip+2 > iend) return ERROR(srcSize_wrong); + RETURN_ERROR_IF(ip+2 > iend, srcSize_wrong); nbSeq = MEM_readLE16(ip) + LONGNBSEQ, ip+=2; } else { - if (ip >= iend) return ERROR(srcSize_wrong); + RETURN_ERROR_IF(ip >= iend, srcSize_wrong); nbSeq = ((nbSeq-0x80)<<8) + *ip++; } } *nbSeqPtr = nbSeq; /* FSE table descriptors */ - if (ip+4 > iend) return ERROR(srcSize_wrong); /* minimum possible size */ + RETURN_ERROR_IF(ip+4 > iend, srcSize_wrong); /* minimum possible size */ { symbolEncodingType_e const LLtype = (symbolEncodingType_e)(*ip >> 6); symbolEncodingType_e const OFtype = (symbolEncodingType_e)((*ip >> 4) & 3); symbolEncodingType_e const MLtype = (symbolEncodingType_e)((*ip >> 2) & 3); @@ -517,7 +518,7 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, LL_base, LL_bits, LL_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq); - if (ZSTD_isError(llhSize)) return ERROR(corruption_detected); + RETURN_ERROR_IF(ZSTD_isError(llhSize), corruption_detected); ip += llhSize; } @@ -527,7 +528,7 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, OF_base, OF_bits, OF_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq); - if (ZSTD_isError(ofhSize)) return ERROR(corruption_detected); + RETURN_ERROR_IF(ZSTD_isError(ofhSize), corruption_detected); ip += ofhSize; } @@ -537,7 +538,7 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, ML_base, ML_bits, ML_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq); - if (ZSTD_isError(mlhSize)) return ERROR(corruption_detected); + RETURN_ERROR_IF(ZSTD_isError(mlhSize), corruption_detected); ip += mlhSize; } } @@ -590,8 +591,8 @@ size_t ZSTD_execSequenceLast7(BYTE* op, const BYTE* match = oLitEnd - sequence.offset; /* check */ - if (oMatchEnd>oend) return ERROR(dstSize_tooSmall); /* last match must fit within dstBuffer */ - if (iLitEnd > litLimit) return ERROR(corruption_detected); /* try to read beyond literal buffer */ + RETURN_ERROR_IF(oMatchEnd>oend, dstSize_tooSmall, "last match must fit within dstBuffer"); + RETURN_ERROR_IF(iLitEnd > litLimit, corruption_detected, "try to read beyond literal buffer"); /* copy literals */ while (op < oLitEnd) *op++ = *(*litPtr)++; @@ -599,7 +600,7 @@ size_t ZSTD_execSequenceLast7(BYTE* op, /* copy Match */ if (sequence.offset > (size_t)(oLitEnd - base)) { /* offset beyond prefix */ - if (sequence.offset > (size_t)(oLitEnd - vBase)) return ERROR(corruption_detected); + RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - vBase),corruption_detected); match = dictEnd - (base-match); if (match + sequence.matchLength <= dictEnd) { memmove(oLitEnd, match, sequence.matchLength); @@ -631,8 +632,8 @@ size_t ZSTD_execSequence(BYTE* op, const BYTE* match = oLitEnd - sequence.offset; /* check */ - if (oMatchEnd>oend) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ - if (iLitEnd > litLimit) return ERROR(corruption_detected); /* over-read beyond lit buffer */ + RETURN_ERROR_IF(oMatchEnd>oend, dstSize_tooSmall, "last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend"); + RETURN_ERROR_IF(iLitEnd > litLimit, corruption_detected, "over-read beyond lit buffer"); if (oLitEnd>oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd); /* copy Literals */ @@ -645,8 +646,7 @@ size_t ZSTD_execSequence(BYTE* op, /* copy Match */ if (sequence.offset > (size_t)(oLitEnd - prefixStart)) { /* offset beyond prefix -> go into extDict */ - if (sequence.offset > (size_t)(oLitEnd - virtualStart)) - return ERROR(corruption_detected); + RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - virtualStart), corruption_detected); match = dictEnd + (match - prefixStart); if (match + sequence.matchLength <= dictEnd) { memmove(oLitEnd, match, sequence.matchLength); @@ -712,8 +712,8 @@ size_t ZSTD_execSequenceLong(BYTE* op, const BYTE* match = sequence.match; /* check */ - if (oMatchEnd > oend) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ - if (iLitEnd > litLimit) return ERROR(corruption_detected); /* over-read beyond lit buffer */ + RETURN_ERROR_IF(oMatchEnd > oend, dstSize_tooSmall, "last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend"); + RETURN_ERROR_IF(iLitEnd > litLimit, corruption_detected, "over-read beyond lit buffer"); if (oLitEnd > oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, prefixStart, dictStart, dictEnd); /* copy Literals */ @@ -726,7 +726,7 @@ size_t ZSTD_execSequenceLong(BYTE* op, /* copy Match */ if (sequence.offset > (size_t)(oLitEnd - prefixStart)) { /* offset beyond prefix */ - if (sequence.offset > (size_t)(oLitEnd - dictStart)) return ERROR(corruption_detected); + RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - dictStart), corruption_detected); if (match + sequence.matchLength <= dictEnd) { memmove(oLitEnd, match, sequence.matchLength); return sequenceLength; @@ -801,7 +801,7 @@ ZSTD_updateFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD) /* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1) * bits before reloading. This value is the maximum number of bytes we read - * after reloading when we are decoding long offets. + * after reloading when we are decoding long offsets. */ #define LONG_OFFSETS_MAX_EXTRA_BITS_32 \ (ZSTD_WINDOWLOG_MAX_32 > STREAM_ACCUMULATOR_MIN_32 \ @@ -911,7 +911,9 @@ ZSTD_decompressSequences_body( ZSTD_DCtx* dctx, seqState_t seqState; dctx->fseEntropy = 1; { U32 i; for (i=0; i<ZSTD_REP_NUM; i++) seqState.prevOffset[i] = dctx->entropy.rep[i]; } - CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); + RETURN_ERROR_IF( + ERR_isError(BIT_initDStream(&seqState.DStream, ip, iend-ip)), + corruption_detected); ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); @@ -927,14 +929,14 @@ ZSTD_decompressSequences_body( ZSTD_DCtx* dctx, /* check if reached exact end */ DEBUGLOG(5, "ZSTD_decompressSequences_body: after decode loop, remaining nbSeq : %i", nbSeq); - if (nbSeq) return ERROR(corruption_detected); + RETURN_ERROR_IF(nbSeq, corruption_detected); /* save reps for next block */ { U32 i; for (i=0; i<ZSTD_REP_NUM; i++) dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); } } /* last literal segment */ { size_t const lastLLSize = litEnd - litPtr; - if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall); + RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall); memcpy(op, litPtr, lastLLSize); op += lastLLSize; } @@ -1066,7 +1068,9 @@ ZSTD_decompressSequencesLong_body( seqState.pos = (size_t)(op-prefixStart); seqState.dictEnd = dictEnd; assert(iend >= ip); - CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); + RETURN_ERROR_IF( + ERR_isError(BIT_initDStream(&seqState.DStream, ip, iend-ip)), + corruption_detected); ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); @@ -1076,7 +1080,7 @@ ZSTD_decompressSequencesLong_body( sequences[seqNb] = ZSTD_decodeSequenceLong(&seqState, isLongOffset); PREFETCH_L1(sequences[seqNb].match); PREFETCH_L1(sequences[seqNb].match + sequences[seqNb].matchLength - 1); /* note : it's safe to invoke PREFETCH() on any memory address, including invalid ones */ } - if (seqNb<seqAdvance) return ERROR(corruption_detected); + RETURN_ERROR_IF(seqNb<seqAdvance, corruption_detected); /* decode and decompress */ for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && (seqNb<nbSeq) ; seqNb++) { @@ -1087,7 +1091,7 @@ ZSTD_decompressSequencesLong_body( sequences[seqNb & STORED_SEQS_MASK] = sequence; op += oneSeqSize; } - if (seqNb<nbSeq) return ERROR(corruption_detected); + RETURN_ERROR_IF(seqNb<nbSeq, corruption_detected); /* finish queue */ seqNb -= seqAdvance; @@ -1103,7 +1107,7 @@ ZSTD_decompressSequencesLong_body( /* last literal segment */ { size_t const lastLLSize = litEnd - litPtr; - if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall); + RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall); memcpy(op, litPtr, lastLLSize); op += lastLLSize; } @@ -1176,7 +1180,7 @@ ZSTD_decompressSequences(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, /* ZSTD_decompressSequencesLong() : * decompression function triggered when a minimum share of offsets is considered "long", * aka out of cache. - * note : "long" definition seems overloaded here, sometimes meaning "wider than bitstream register", and sometimes mearning "farther than memory cache distance". + * note : "long" definition seems overloaded here, sometimes meaning "wider than bitstream register", and sometimes meaning "farther than memory cache distance". * This function will try to mitigate main memory latency through the use of prefetching */ static size_t ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx, @@ -1240,7 +1244,7 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, ZSTD_longOffset_e const isLongOffset = (ZSTD_longOffset_e)(MEM_32bits() && (!frame || (dctx->fParams.windowSize > (1ULL << STREAM_ACCUMULATOR_MIN)))); DEBUGLOG(5, "ZSTD_decompressBlock_internal (size : %u)", (U32)srcSize); - if (srcSize >= ZSTD_BLOCKSIZE_MAX) return ERROR(srcSize_wrong); + RETURN_ERROR_IF(srcSize >= ZSTD_BLOCKSIZE_MAX, srcSize_wrong); /* Decode literals section */ { size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize); diff --git a/thirdparty/zstd/decompress/zstd_decompress_internal.h b/thirdparty/zstd/decompress/zstd_decompress_internal.h index abd0030519..ccbdfa090f 100644 --- a/thirdparty/zstd/decompress/zstd_decompress_internal.h +++ b/thirdparty/zstd/decompress/zstd_decompress_internal.h @@ -89,6 +89,12 @@ typedef enum { ZSTDds_getFrameHeaderSize, ZSTDds_decodeFrameHeader, typedef enum { zdss_init=0, zdss_loadHeader, zdss_read, zdss_load, zdss_flush } ZSTD_dStreamStage; +typedef enum { + ZSTD_use_indefinitely = -1, /* Use the dictionary indefinitely */ + ZSTD_dont_use = 0, /* Do not use the dictionary (if one exists free it) */ + ZSTD_use_once = 1 /* Use the dictionary once and set to ZSTD_dont_use */ +} ZSTD_dictUses_e; + struct ZSTD_DCtx_s { const ZSTD_seqSymbol* LLTptr; @@ -123,6 +129,7 @@ struct ZSTD_DCtx_s const ZSTD_DDict* ddict; /* set by ZSTD_initDStream_usingDDict(), or ZSTD_DCtx_refDDict() */ U32 dictID; int ddictIsCold; /* if == 1 : dictionary is "new" for working context, and presumed "cold" (not in cpu cache) */ + ZSTD_dictUses_e dictUses; /* streaming */ ZSTD_dStreamStage streamStage; diff --git a/thirdparty/zstd/zstd.h b/thirdparty/zstd/zstd.h index b18fc8a44b..53470c18f3 100644 --- a/thirdparty/zstd/zstd.h +++ b/thirdparty/zstd/zstd.h @@ -70,8 +70,8 @@ extern "C" { /*------ Version ------*/ #define ZSTD_VERSION_MAJOR 1 -#define ZSTD_VERSION_MINOR 3 -#define ZSTD_VERSION_RELEASE 8 +#define ZSTD_VERSION_MINOR 4 +#define ZSTD_VERSION_RELEASE 0 #define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE) ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< to check runtime library version */ @@ -90,6 +90,21 @@ ZSTDLIB_API const char* ZSTD_versionString(void); /* requires v1.3.0+ */ #endif /*************************************** +* Constants +***************************************/ + +/* All magic numbers are supposed read/written to/from files/memory using little-endian convention */ +#define ZSTD_MAGICNUMBER 0xFD2FB528 /* valid since v0.8.0 */ +#define ZSTD_MAGIC_DICTIONARY 0xEC30A437 /* valid since v0.7.0 */ +#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50 /* all 16 values, from 0x184D2A50 to 0x184D2A5F, signal the beginning of a skippable frame */ +#define ZSTD_MAGIC_SKIPPABLE_MASK 0xFFFFFFF0 + +#define ZSTD_BLOCKSIZELOG_MAX 17 +#define ZSTD_BLOCKSIZE_MAX (1<<ZSTD_BLOCKSIZELOG_MAX) + + + +/*************************************** * Simple API ***************************************/ /*! ZSTD_compress() : @@ -145,12 +160,21 @@ ZSTDLIB_API unsigned long long ZSTD_getFrameContentSize(const void *src, size_t * @return : decompressed size of `src` frame content _if known and not empty_, 0 otherwise. */ ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); +/*! ZSTD_findFrameCompressedSize() : + * `src` should point to the start of a ZSTD frame or skippable frame. + * `srcSize` must be >= first frame size + * @return : the compressed size of the first frame starting at `src`, + * suitable to pass as `srcSize` to `ZSTD_decompress` or similar, + * or an error code if input is invalid */ +ZSTDLIB_API size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize); + /*====== Helper functions ======*/ #define ZSTD_COMPRESSBOUND(srcSize) ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */ ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */ ZSTDLIB_API unsigned ZSTD_isError(size_t code); /*!< tells if a `size_t` function result is an error code */ ZSTDLIB_API const char* ZSTD_getErrorName(size_t code); /*!< provides readable string from an error code */ +ZSTDLIB_API int ZSTD_minCLevel(void); /*!< minimum negative compression level allowed */ ZSTDLIB_API int ZSTD_maxCLevel(void); /*!< maximum compression level available */ @@ -195,279 +219,6 @@ ZSTDLIB_API size_t ZSTD_decompressDCtx(ZSTD_DCtx* dctx, const void* src, size_t srcSize); -/************************** -* Simple dictionary API -***************************/ -/*! ZSTD_compress_usingDict() : - * Compression at an explicit compression level using a Dictionary. - * A dictionary can be any arbitrary data segment (also called a prefix), - * or a buffer with specified information (see dictBuilder/zdict.h). - * Note : This function loads the dictionary, resulting in significant startup delay. - * It's intended for a dictionary used only once. - * Note 2 : When `dict == NULL || dictSize < 8` no dictionary is used. */ -ZSTDLIB_API size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const void* dict,size_t dictSize, - int compressionLevel); - -/*! ZSTD_decompress_usingDict() : - * Decompression using a known Dictionary. - * Dictionary must be identical to the one used during compression. - * Note : This function loads the dictionary, resulting in significant startup delay. - * It's intended for a dictionary used only once. - * Note : When `dict == NULL || dictSize < 8` no dictionary is used. */ -ZSTDLIB_API size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const void* dict,size_t dictSize); - - -/*********************************** - * Bulk processing dictionary API - **********************************/ -typedef struct ZSTD_CDict_s ZSTD_CDict; - -/*! ZSTD_createCDict() : - * When compressing multiple messages / blocks using the same dictionary, it's recommended to load it only once. - * ZSTD_createCDict() will create a digested dictionary, ready to start future compression operations without startup cost. - * ZSTD_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only. - * `dictBuffer` can be released after ZSTD_CDict creation, because its content is copied within CDict. - * Consider experimental function `ZSTD_createCDict_byReference()` if you prefer to not duplicate `dictBuffer` content. - * Note : A ZSTD_CDict can be created from an empty dictBuffer, but it is inefficient when used to compress small data. */ -ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict(const void* dictBuffer, size_t dictSize, - int compressionLevel); - -/*! ZSTD_freeCDict() : - * Function frees memory allocated by ZSTD_createCDict(). */ -ZSTDLIB_API size_t ZSTD_freeCDict(ZSTD_CDict* CDict); - -/*! ZSTD_compress_usingCDict() : - * Compression using a digested Dictionary. - * Recommended when same dictionary is used multiple times. - * Note : compression level is _decided at dictionary creation time_, - * and frame parameters are hardcoded (dictID=yes, contentSize=yes, checksum=no) */ -ZSTDLIB_API size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const ZSTD_CDict* cdict); - - -typedef struct ZSTD_DDict_s ZSTD_DDict; - -/*! ZSTD_createDDict() : - * Create a digested dictionary, ready to start decompression operation without startup delay. - * dictBuffer can be released after DDict creation, as its content is copied inside DDict. */ -ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict(const void* dictBuffer, size_t dictSize); - -/*! ZSTD_freeDDict() : - * Function frees memory allocated with ZSTD_createDDict() */ -ZSTDLIB_API size_t ZSTD_freeDDict(ZSTD_DDict* ddict); - -/*! ZSTD_decompress_usingDDict() : - * Decompression using a digested Dictionary. - * Recommended when same dictionary is used multiple times. */ -ZSTDLIB_API size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const ZSTD_DDict* ddict); - - -/**************************** -* Streaming -****************************/ - -typedef struct ZSTD_inBuffer_s { - const void* src; /**< start of input buffer */ - size_t size; /**< size of input buffer */ - size_t pos; /**< position where reading stopped. Will be updated. Necessarily 0 <= pos <= size */ -} ZSTD_inBuffer; - -typedef struct ZSTD_outBuffer_s { - void* dst; /**< start of output buffer */ - size_t size; /**< size of output buffer */ - size_t pos; /**< position where writing stopped. Will be updated. Necessarily 0 <= pos <= size */ -} ZSTD_outBuffer; - - - -/*-*********************************************************************** -* Streaming compression - HowTo -* -* A ZSTD_CStream object is required to track streaming operation. -* Use ZSTD_createCStream() and ZSTD_freeCStream() to create/release resources. -* ZSTD_CStream objects can be reused multiple times on consecutive compression operations. -* It is recommended to re-use ZSTD_CStream since it will play nicer with system's memory, by re-using already allocated memory. -* -* For parallel execution, use one separate ZSTD_CStream per thread. -* -* note : since v1.3.0, ZSTD_CStream and ZSTD_CCtx are the same thing. -* -* Parameters are sticky : when starting a new compression on the same context, -* it will re-use the same sticky parameters as previous compression session. -* When in doubt, it's recommended to fully initialize the context before usage. -* Use ZSTD_initCStream() to set the parameter to a selected compression level. -* Use advanced API (ZSTD_CCtx_setParameter(), etc.) to set more specific parameters. -* -* Use ZSTD_compressStream() as many times as necessary to consume input stream. -* The function will automatically update both `pos` fields within `input` and `output`. -* Note that the function may not consume the entire input, -* for example, because the output buffer is already full, -* in which case `input.pos < input.size`. -* The caller must check if input has been entirely consumed. -* If not, the caller must make some room to receive more compressed data, -* and then present again remaining input data. -* @return : a size hint, preferred nb of bytes to use as input for next function call -* or an error code, which can be tested using ZSTD_isError(). -* Note 1 : it's just a hint, to help latency a little, any value will work fine. -* Note 2 : size hint is guaranteed to be <= ZSTD_CStreamInSize() -* -* At any moment, it's possible to flush whatever data might remain stuck within internal buffer, -* using ZSTD_flushStream(). `output->pos` will be updated. -* Note that, if `output->size` is too small, a single invocation of ZSTD_flushStream() might not be enough (return code > 0). -* In which case, make some room to receive more compressed data, and call again ZSTD_flushStream(). -* @return : 0 if internal buffers are entirely flushed, -* >0 if some data still present within internal buffer (the value is minimal estimation of remaining size), -* or an error code, which can be tested using ZSTD_isError(). -* -* ZSTD_endStream() instructs to finish a frame. -* It will perform a flush and write frame epilogue. -* The epilogue is required for decoders to consider a frame completed. -* flush() operation is the same, and follows same rules as ZSTD_flushStream(). -* @return : 0 if frame fully completed and fully flushed, -* >0 if some data still present within internal buffer (the value is minimal estimation of remaining size), -* or an error code, which can be tested using ZSTD_isError(). -* -* *******************************************************************/ - -typedef ZSTD_CCtx ZSTD_CStream; /**< CCtx and CStream are now effectively same object (>= v1.3.0) */ - /* Continue to distinguish them for compatibility with older versions <= v1.2.0 */ -/*===== ZSTD_CStream management functions =====*/ -ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream(void); -ZSTDLIB_API size_t ZSTD_freeCStream(ZSTD_CStream* zcs); - -/*===== Streaming compression functions =====*/ -ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel); -ZSTDLIB_API size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); -ZSTDLIB_API size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output); -ZSTDLIB_API size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output); - -ZSTDLIB_API size_t ZSTD_CStreamInSize(void); /**< recommended size for input buffer */ -ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block in all circumstances. */ - - - -/*-*************************************************************************** -* Streaming decompression - HowTo -* -* A ZSTD_DStream object is required to track streaming operations. -* Use ZSTD_createDStream() and ZSTD_freeDStream() to create/release resources. -* ZSTD_DStream objects can be re-used multiple times. -* -* Use ZSTD_initDStream() to start a new decompression operation. -* @return : recommended first input size -* Alternatively, use advanced API to set specific properties. -* -* Use ZSTD_decompressStream() repetitively to consume your input. -* The function will update both `pos` fields. -* If `input.pos < input.size`, some input has not been consumed. -* It's up to the caller to present again remaining data. -* The function tries to flush all data decoded immediately, respecting output buffer size. -* If `output.pos < output.size`, decoder has flushed everything it could. -* But if `output.pos == output.size`, there might be some data left within internal buffers., -* In which case, call ZSTD_decompressStream() again to flush whatever remains in the buffer. -* Note : with no additional input provided, amount of data flushed is necessarily <= ZSTD_BLOCKSIZE_MAX. -* @return : 0 when a frame is completely decoded and fully flushed, -* or an error code, which can be tested using ZSTD_isError(), -* or any other value > 0, which means there is still some decoding or flushing to do to complete current frame : -* the return value is a suggested next input size (just a hint for better latency) -* that will never request more than the remaining frame size. -* *******************************************************************************/ - -typedef ZSTD_DCtx ZSTD_DStream; /**< DCtx and DStream are now effectively same object (>= v1.3.0) */ - /* For compatibility with versions <= v1.2.0, prefer differentiating them. */ -/*===== ZSTD_DStream management functions =====*/ -ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream(void); -ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds); - -/*===== Streaming decompression functions =====*/ -ZSTDLIB_API size_t ZSTD_initDStream(ZSTD_DStream* zds); -ZSTDLIB_API size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input); - -ZSTDLIB_API size_t ZSTD_DStreamInSize(void); /*!< recommended size for input buffer */ -ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output buffer. Guarantee to successfully flush at least one complete block in all circumstances. */ - -#endif /* ZSTD_H_235446 */ - - - - -/**************************************************************************************** - * ADVANCED AND EXPERIMENTAL FUNCTIONS - **************************************************************************************** - * The definitions in the following section are considered experimental. - * They are provided for advanced scenarios. - * They should never be used with a dynamic library, as prototypes may change in the future. - * Use them only in association with static linking. - * ***************************************************************************************/ - -#if defined(ZSTD_STATIC_LINKING_ONLY) && !defined(ZSTD_H_ZSTD_STATIC_LINKING_ONLY) -#define ZSTD_H_ZSTD_STATIC_LINKING_ONLY - - -/**************************************************************************************** - * Candidate API for promotion to stable status - **************************************************************************************** - * The following symbols and constants form the "staging area" : - * they are considered to join "stable API" by v1.4.0. - * The proposal is written so that it can be made stable "as is", - * though it's still possible to suggest improvements. - * Staging is in fact last chance for changes, - * the API is locked once reaching "stable" status. - * ***************************************************************************************/ - - -/* === Constants === */ - -/* all magic numbers are supposed read/written to/from files/memory using little-endian convention */ -#define ZSTD_MAGICNUMBER 0xFD2FB528 /* valid since v0.8.0 */ -#define ZSTD_MAGIC_DICTIONARY 0xEC30A437 /* valid since v0.7.0 */ -#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50 /* all 16 values, from 0x184D2A50 to 0x184D2A5F, signal the beginning of a skippable frame */ -#define ZSTD_MAGIC_SKIPPABLE_MASK 0xFFFFFFF0 - -#define ZSTD_BLOCKSIZELOG_MAX 17 -#define ZSTD_BLOCKSIZE_MAX (1<<ZSTD_BLOCKSIZELOG_MAX) - - -/* === query limits === */ - -ZSTDLIB_API int ZSTD_minCLevel(void); /*!< minimum negative compression level allowed */ - - -/* === frame size === */ - -/*! ZSTD_findFrameCompressedSize() : - * `src` should point to the start of a ZSTD frame or skippable frame. - * `srcSize` must be >= first frame size - * @return : the compressed size of the first frame starting at `src`, - * suitable to pass as `srcSize` to `ZSTD_decompress` or similar, - * or an error code if input is invalid */ -ZSTDLIB_API size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize); - - -/* === Memory management === */ - -/*! ZSTD_sizeof_*() : - * These functions give the _current_ memory usage of selected object. - * Note that object memory usage can evolve (increase or decrease) over time. */ -ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx); -ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx); -ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs); -ZSTDLIB_API size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds); -ZSTDLIB_API size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict); -ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict); - - /*************************************** * Advanced compression API ***************************************/ @@ -503,7 +254,10 @@ typedef enum { ZSTD_fast=1, typedef enum { - /* compression parameters */ + /* compression parameters + * Note: When compressing with a ZSTD_CDict these parameters are superseded + * by the parameters used to construct the ZSTD_CDict. See ZSTD_CCtx_refCDict() + * for more info (superseded-by-cdict). */ ZSTD_c_compressionLevel=100, /* Update all compression parameters according to pre-defined cLevel table * Default level is ZSTD_CLEVEL_DEFAULT==3. * Special: value 0 means default, which is controlled by ZSTD_CLEVEL_DEFAULT. @@ -625,6 +379,7 @@ typedef enum { * ZSTD_c_format * ZSTD_c_forceMaxWindow * ZSTD_c_forceAttachDict + * ZSTD_c_literalCompressionMode * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them. * note : never ever use experimentalParam? names directly; * also, the enums values themselves are unstable and can still change. @@ -632,10 +387,10 @@ typedef enum { ZSTD_c_experimentalParam1=500, ZSTD_c_experimentalParam2=10, ZSTD_c_experimentalParam3=1000, - ZSTD_c_experimentalParam4=1001 + ZSTD_c_experimentalParam4=1001, + ZSTD_c_experimentalParam5=1002, } ZSTD_cParameter; - typedef struct { size_t error; int lowerBound; @@ -677,63 +432,10 @@ ZSTDLIB_API size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param * Note 3 : Whenever all input data is provided and consumed in a single round, * for example with ZSTD_compress2(), * or invoking immediately ZSTD_compressStream2(,,,ZSTD_e_end), - * this value is automatically overriden by srcSize instead. + * this value is automatically overridden by srcSize instead. */ ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize); -/*! ZSTD_CCtx_loadDictionary() : - * Create an internal CDict from `dict` buffer. - * Decompression will have to use same dictionary. - * @result : 0, or an error code (which can be tested with ZSTD_isError()). - * Special: Loading a NULL (or 0-size) dictionary invalidates previous dictionary, - * meaning "return to no-dictionary mode". - * Note 1 : Dictionary is sticky, it will be used for all future compressed frames. - * To return to "no-dictionary" situation, load a NULL dictionary (or reset parameters). - * Note 2 : Loading a dictionary involves building tables. - * It's also a CPU consuming operation, with non-negligible impact on latency. - * Tables are dependent on compression parameters, and for this reason, - * compression parameters can no longer be changed after loading a dictionary. - * Note 3 :`dict` content will be copied internally. - * Use experimental ZSTD_CCtx_loadDictionary_byReference() to reference content instead. - * In such a case, dictionary buffer must outlive its users. - * Note 4 : Use ZSTD_CCtx_loadDictionary_advanced() - * to precisely select how dictionary content must be interpreted. */ -ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); - -/*! ZSTD_CCtx_refCDict() : - * Reference a prepared dictionary, to be used for all next compressed frames. - * Note that compression parameters are enforced from within CDict, - * and supercede any compression parameter previously set within CCtx. - * The dictionary will remain valid for future compressed frames using same CCtx. - * @result : 0, or an error code (which can be tested with ZSTD_isError()). - * Special : Referencing a NULL CDict means "return to no-dictionary mode". - * Note 1 : Currently, only one dictionary can be managed. - * Referencing a new dictionary effectively "discards" any previous one. - * Note 2 : CDict is just referenced, its lifetime must outlive its usage within CCtx. */ -ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); - -/*! ZSTD_CCtx_refPrefix() : - * Reference a prefix (single-usage dictionary) for next compressed frame. - * A prefix is **only used once**. Tables are discarded at end of frame (ZSTD_e_end). - * Decompression will need same prefix to properly regenerate data. - * Compressing with a prefix is similar in outcome as performing a diff and compressing it, - * but performs much faster, especially during decompression (compression speed is tunable with compression level). - * @result : 0, or an error code (which can be tested with ZSTD_isError()). - * Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary - * Note 1 : Prefix buffer is referenced. It **must** outlive compression. - * Its content must remain unmodified during compression. - * Note 2 : If the intention is to diff some large src data blob with some prior version of itself, - * ensure that the window size is large enough to contain the entire source. - * See ZSTD_c_windowLog. - * Note 3 : Referencing a prefix involves building tables, which are dependent on compression parameters. - * It's a CPU consuming operation, with non-negligible impact on latency. - * If there is a need to use the same prefix multiple times, consider loadDictionary instead. - * Note 4 : By default, the prefix is interpreted as raw content (ZSTD_dm_rawContent). - * Use experimental ZSTD_CCtx_refPrefix_advanced() to alter dictionary interpretation. */ -ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, - const void* prefix, size_t prefixSize); - - typedef enum { ZSTD_reset_session_only = 1, ZSTD_reset_parameters = 2, @@ -756,8 +458,6 @@ typedef enum { */ ZSTDLIB_API size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset); - - /*! ZSTD_compress2() : * Behave the same as ZSTD_compressCCtx(), but compression parameters are set using the advanced API. * ZSTD_compress2() always starts a new frame. @@ -772,48 +472,10 @@ ZSTDLIB_API size_t ZSTD_compress2( ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -typedef enum { - ZSTD_e_continue=0, /* collect more data, encoder decides when to output compressed result, for optimal compression ratio */ - ZSTD_e_flush=1, /* flush any data provided so far, - * it creates (at least) one new block, that can be decoded immediately on reception; - * frame will continue: any future data can still reference previously compressed data, improving compression. */ - ZSTD_e_end=2 /* flush any remaining data _and_ close current frame. - * note that frame is only closed after compressed data is fully flushed (return value == 0). - * After that point, any additional data starts a new frame. - * note : each frame is independent (does not reference any content from previous frame). */ -} ZSTD_EndDirective; - -/*! ZSTD_compressStream2() : - * Behaves about the same as ZSTD_compressStream, with additional control on end directive. - * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*() - * - Compression parameters cannot be changed once compression is started (save a list of exceptions in multi-threading mode) - * - outpot->pos must be <= dstCapacity, input->pos must be <= srcSize - * - outpot->pos and input->pos will be updated. They are guaranteed to remain below their respective limit. - * - When nbWorkers==0 (default), function is blocking : it completes its job before returning to caller. - * - When nbWorkers>=1, function is non-blocking : it just acquires a copy of input, and distributes jobs to internal worker threads, flush whatever is available, - * and then immediately returns, just indicating that there is some data remaining to be flushed. - * The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte. - * - Exception : if the first call requests a ZSTD_e_end directive and provides enough dstCapacity, the function delegates to ZSTD_compress2() which is always blocking. - * - @return provides a minimum amount of data remaining to be flushed from internal buffers - * or an error code, which can be tested using ZSTD_isError(). - * if @return != 0, flush is not fully completed, there is still some data left within internal buffers. - * This is useful for ZSTD_e_flush, since in this case more flushes are necessary to empty all buffers. - * For ZSTD_e_end, @return == 0 when internal buffers are fully flushed and frame is completed. - * - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0), - * only ZSTD_e_end or ZSTD_e_flush operations are allowed. - * Before starting a new compression job, or changing compression parameters, - * it is required to fully flush internal buffers. - */ -ZSTDLIB_API size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, - ZSTD_outBuffer* output, - ZSTD_inBuffer* input, - ZSTD_EndDirective endOp); - - -/* ============================== */ -/* Advanced decompression API */ -/* ============================== */ +/*************************************** +* Advanced decompression API +***************************************/ /* The advanced API pushes parameters one by one into an existing DCtx context. * Parameters are sticky, and remain valid for all following frames @@ -823,14 +485,14 @@ ZSTDLIB_API size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, * Therefore, no new decompression function is necessary. */ - typedef enum { ZSTD_d_windowLogMax=100, /* Select a size limit (in power of 2) beyond which * the streaming API will refuse to allocate memory buffer * in order to protect the host from unreasonable memory requirements. * This parameter is only useful in streaming mode, since no internal buffer is allocated in single-pass mode. - * By default, a decompression context accepts window sizes <= (1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT) */ + * By default, a decompression context accepts window sizes <= (1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT). + * Special: value 0 means "use default maximum windowLog". */ /* note : additional experimental parameters are also available * within the experimental section of the API. @@ -843,7 +505,6 @@ typedef enum { } ZSTD_dParameter; - /*! ZSTD_dParam_getBounds() : * All parameters must belong to an interval with lower and upper bounds, * otherwise they will either trigger an error or be automatically clamped. @@ -862,6 +523,389 @@ ZSTDLIB_API ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam); */ ZSTDLIB_API size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int value); +/*! ZSTD_DCtx_reset() : + * Return a DCtx to clean state. + * Session and parameters can be reset jointly or separately. + * Parameters can only be reset when no active frame is being decompressed. + * @return : 0, or an error code, which can be tested with ZSTD_isError() + */ +ZSTDLIB_API size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset); + + +/**************************** +* Streaming +****************************/ + +typedef struct ZSTD_inBuffer_s { + const void* src; /**< start of input buffer */ + size_t size; /**< size of input buffer */ + size_t pos; /**< position where reading stopped. Will be updated. Necessarily 0 <= pos <= size */ +} ZSTD_inBuffer; + +typedef struct ZSTD_outBuffer_s { + void* dst; /**< start of output buffer */ + size_t size; /**< size of output buffer */ + size_t pos; /**< position where writing stopped. Will be updated. Necessarily 0 <= pos <= size */ +} ZSTD_outBuffer; + + + +/*-*********************************************************************** +* Streaming compression - HowTo +* +* A ZSTD_CStream object is required to track streaming operation. +* Use ZSTD_createCStream() and ZSTD_freeCStream() to create/release resources. +* ZSTD_CStream objects can be reused multiple times on consecutive compression operations. +* It is recommended to re-use ZSTD_CStream since it will play nicer with system's memory, by re-using already allocated memory. +* +* For parallel execution, use one separate ZSTD_CStream per thread. +* +* note : since v1.3.0, ZSTD_CStream and ZSTD_CCtx are the same thing. +* +* Parameters are sticky : when starting a new compression on the same context, +* it will re-use the same sticky parameters as previous compression session. +* When in doubt, it's recommended to fully initialize the context before usage. +* Use ZSTD_CCtx_reset() to reset the context and ZSTD_CCtx_setParameter(), +* ZSTD_CCtx_setPledgedSrcSize(), or ZSTD_CCtx_loadDictionary() and friends to +* set more specific parameters, the pledged source size, or load a dictionary. +* +* Use ZSTD_compressStream2() with ZSTD_e_continue as many times as necessary to +* consume input stream. The function will automatically update both `pos` +* fields within `input` and `output`. +* Note that the function may not consume the entire input, for example, because +* the output buffer is already full, in which case `input.pos < input.size`. +* The caller must check if input has been entirely consumed. +* If not, the caller must make some room to receive more compressed data, +* and then present again remaining input data. +* note: ZSTD_e_continue is guaranteed to make some forward progress when called, +* but doesn't guarantee maximal forward progress. This is especially relevant +* when compressing with multiple threads. The call won't block if it can +* consume some input, but if it can't it will wait for some, but not all, +* output to be flushed. +* @return : provides a minimum amount of data remaining to be flushed from internal buffers +* or an error code, which can be tested using ZSTD_isError(). +* +* At any moment, it's possible to flush whatever data might remain stuck within internal buffer, +* using ZSTD_compressStream2() with ZSTD_e_flush. `output->pos` will be updated. +* Note that, if `output->size` is too small, a single invocation with ZSTD_e_flush might not be enough (return code > 0). +* In which case, make some room to receive more compressed data, and call again ZSTD_compressStream2() with ZSTD_e_flush. +* You must continue calling ZSTD_compressStream2() with ZSTD_e_flush until it returns 0, at which point you can change the +* operation. +* note: ZSTD_e_flush will flush as much output as possible, meaning when compressing with multiple threads, it will +* block until the flush is complete or the output buffer is full. +* @return : 0 if internal buffers are entirely flushed, +* >0 if some data still present within internal buffer (the value is minimal estimation of remaining size), +* or an error code, which can be tested using ZSTD_isError(). +* +* Calling ZSTD_compressStream2() with ZSTD_e_end instructs to finish a frame. +* It will perform a flush and write frame epilogue. +* The epilogue is required for decoders to consider a frame completed. +* flush operation is the same, and follows same rules as calling ZSTD_compressStream2() with ZSTD_e_flush. +* You must continue calling ZSTD_compressStream2() with ZSTD_e_end until it returns 0, at which point you are free to +* start a new frame. +* note: ZSTD_e_end will flush as much output as possible, meaning when compressing with multiple threads, it will +* block until the flush is complete or the output buffer is full. +* @return : 0 if frame fully completed and fully flushed, +* >0 if some data still present within internal buffer (the value is minimal estimation of remaining size), +* or an error code, which can be tested using ZSTD_isError(). +* +* *******************************************************************/ + +typedef ZSTD_CCtx ZSTD_CStream; /**< CCtx and CStream are now effectively same object (>= v1.3.0) */ + /* Continue to distinguish them for compatibility with older versions <= v1.2.0 */ +/*===== ZSTD_CStream management functions =====*/ +ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream(void); +ZSTDLIB_API size_t ZSTD_freeCStream(ZSTD_CStream* zcs); + +/*===== Streaming compression functions =====*/ +typedef enum { + ZSTD_e_continue=0, /* collect more data, encoder decides when to output compressed result, for optimal compression ratio */ + ZSTD_e_flush=1, /* flush any data provided so far, + * it creates (at least) one new block, that can be decoded immediately on reception; + * frame will continue: any future data can still reference previously compressed data, improving compression. + * note : multithreaded compression will block to flush as much output as possible. */ + ZSTD_e_end=2 /* flush any remaining data _and_ close current frame. + * note that frame is only closed after compressed data is fully flushed (return value == 0). + * After that point, any additional data starts a new frame. + * note : each frame is independent (does not reference any content from previous frame). + : note : multithreaded compression will block to flush as much output as possible. */ +} ZSTD_EndDirective; + +/*! ZSTD_compressStream2() : + * Behaves about the same as ZSTD_compressStream, with additional control on end directive. + * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*() + * - Compression parameters cannot be changed once compression is started (save a list of exceptions in multi-threading mode) + * - output->pos must be <= dstCapacity, input->pos must be <= srcSize + * - output->pos and input->pos will be updated. They are guaranteed to remain below their respective limit. + * - When nbWorkers==0 (default), function is blocking : it completes its job before returning to caller. + * - When nbWorkers>=1, function is non-blocking : it just acquires a copy of input, and distributes jobs to internal worker threads, flush whatever is available, + * and then immediately returns, just indicating that there is some data remaining to be flushed. + * The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte. + * - Exception : if the first call requests a ZSTD_e_end directive and provides enough dstCapacity, the function delegates to ZSTD_compress2() which is always blocking. + * - @return provides a minimum amount of data remaining to be flushed from internal buffers + * or an error code, which can be tested using ZSTD_isError(). + * if @return != 0, flush is not fully completed, there is still some data left within internal buffers. + * This is useful for ZSTD_e_flush, since in this case more flushes are necessary to empty all buffers. + * For ZSTD_e_end, @return == 0 when internal buffers are fully flushed and frame is completed. + * - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0), + * only ZSTD_e_end or ZSTD_e_flush operations are allowed. + * Before starting a new compression job, or changing compression parameters, + * it is required to fully flush internal buffers. + */ +ZSTDLIB_API size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, + ZSTD_outBuffer* output, + ZSTD_inBuffer* input, + ZSTD_EndDirective endOp); + +ZSTDLIB_API size_t ZSTD_CStreamInSize(void); /**< recommended size for input buffer */ +ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block in all circumstances. */ + +/******************************************************************************* + * This is a legacy streaming API, and can be replaced by ZSTD_CCtx_reset() and + * ZSTD_compressStream2(). It is redundant, but is still fully supported. + * Advanced parameters and dictionary compression can only be used through the + * new API. + ******************************************************************************/ + +/** + * Equivalent to: + * + * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); + * ZSTD_CCtx_refCDict(zcs, NULL); // clear the dictionary (if any) + * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel); + */ +ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel); +/** + * Alternative for ZSTD_compressStream2(zcs, output, input, ZSTD_e_continue). + * NOTE: The return value is different. ZSTD_compressStream() returns a hint for + * the next read size (if non-zero and not an error). ZSTD_compressStream2() + * returns the number of bytes left to flush (if non-zero and not an error). + */ +ZSTDLIB_API size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); +/** Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_flush). */ +ZSTDLIB_API size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output); +/** Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_end). */ +ZSTDLIB_API size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output); + + +/*-*************************************************************************** +* Streaming decompression - HowTo +* +* A ZSTD_DStream object is required to track streaming operations. +* Use ZSTD_createDStream() and ZSTD_freeDStream() to create/release resources. +* ZSTD_DStream objects can be re-used multiple times. +* +* Use ZSTD_initDStream() to start a new decompression operation. +* @return : recommended first input size +* Alternatively, use advanced API to set specific properties. +* +* Use ZSTD_decompressStream() repetitively to consume your input. +* The function will update both `pos` fields. +* If `input.pos < input.size`, some input has not been consumed. +* It's up to the caller to present again remaining data. +* The function tries to flush all data decoded immediately, respecting output buffer size. +* If `output.pos < output.size`, decoder has flushed everything it could. +* But if `output.pos == output.size`, there might be some data left within internal buffers., +* In which case, call ZSTD_decompressStream() again to flush whatever remains in the buffer. +* Note : with no additional input provided, amount of data flushed is necessarily <= ZSTD_BLOCKSIZE_MAX. +* @return : 0 when a frame is completely decoded and fully flushed, +* or an error code, which can be tested using ZSTD_isError(), +* or any other value > 0, which means there is still some decoding or flushing to do to complete current frame : +* the return value is a suggested next input size (just a hint for better latency) +* that will never request more than the remaining frame size. +* *******************************************************************************/ + +typedef ZSTD_DCtx ZSTD_DStream; /**< DCtx and DStream are now effectively same object (>= v1.3.0) */ + /* For compatibility with versions <= v1.2.0, prefer differentiating them. */ +/*===== ZSTD_DStream management functions =====*/ +ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream(void); +ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds); + +/*===== Streaming decompression functions =====*/ + +/* This function is redundant with the advanced API and equivalent to: + * + * ZSTD_DCtx_reset(zds); + * ZSTD_DCtx_refDDict(zds, NULL); + */ +ZSTDLIB_API size_t ZSTD_initDStream(ZSTD_DStream* zds); + +ZSTDLIB_API size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input); + +ZSTDLIB_API size_t ZSTD_DStreamInSize(void); /*!< recommended size for input buffer */ +ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output buffer. Guarantee to successfully flush at least one complete block in all circumstances. */ + + +/************************** +* Simple dictionary API +***************************/ +/*! ZSTD_compress_usingDict() : + * Compression at an explicit compression level using a Dictionary. + * A dictionary can be any arbitrary data segment (also called a prefix), + * or a buffer with specified information (see dictBuilder/zdict.h). + * Note : This function loads the dictionary, resulting in significant startup delay. + * It's intended for a dictionary used only once. + * Note 2 : When `dict == NULL || dictSize < 8` no dictionary is used. */ +ZSTDLIB_API size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const void* dict,size_t dictSize, + int compressionLevel); + +/*! ZSTD_decompress_usingDict() : + * Decompression using a known Dictionary. + * Dictionary must be identical to the one used during compression. + * Note : This function loads the dictionary, resulting in significant startup delay. + * It's intended for a dictionary used only once. + * Note : When `dict == NULL || dictSize < 8` no dictionary is used. */ +ZSTDLIB_API size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const void* dict,size_t dictSize); + + +/*********************************** + * Bulk processing dictionary API + **********************************/ +typedef struct ZSTD_CDict_s ZSTD_CDict; + +/*! ZSTD_createCDict() : + * When compressing multiple messages / blocks using the same dictionary, it's recommended to load it only once. + * ZSTD_createCDict() will create a digested dictionary, ready to start future compression operations without startup cost. + * ZSTD_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only. + * `dictBuffer` can be released after ZSTD_CDict creation, because its content is copied within CDict. + * Consider experimental function `ZSTD_createCDict_byReference()` if you prefer to not duplicate `dictBuffer` content. + * Note : A ZSTD_CDict can be created from an empty dictBuffer, but it is inefficient when used to compress small data. */ +ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict(const void* dictBuffer, size_t dictSize, + int compressionLevel); + +/*! ZSTD_freeCDict() : + * Function frees memory allocated by ZSTD_createCDict(). */ +ZSTDLIB_API size_t ZSTD_freeCDict(ZSTD_CDict* CDict); + +/*! ZSTD_compress_usingCDict() : + * Compression using a digested Dictionary. + * Recommended when same dictionary is used multiple times. + * Note : compression level is _decided at dictionary creation time_, + * and frame parameters are hardcoded (dictID=yes, contentSize=yes, checksum=no) */ +ZSTDLIB_API size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const ZSTD_CDict* cdict); + + +typedef struct ZSTD_DDict_s ZSTD_DDict; + +/*! ZSTD_createDDict() : + * Create a digested dictionary, ready to start decompression operation without startup delay. + * dictBuffer can be released after DDict creation, as its content is copied inside DDict. */ +ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict(const void* dictBuffer, size_t dictSize); + +/*! ZSTD_freeDDict() : + * Function frees memory allocated with ZSTD_createDDict() */ +ZSTDLIB_API size_t ZSTD_freeDDict(ZSTD_DDict* ddict); + +/*! ZSTD_decompress_usingDDict() : + * Decompression using a digested Dictionary. + * Recommended when same dictionary is used multiple times. */ +ZSTDLIB_API size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const ZSTD_DDict* ddict); + + +/******************************** + * Dictionary helper functions + *******************************/ + +/*! ZSTD_getDictID_fromDict() : + * Provides the dictID stored within dictionary. + * if @return == 0, the dictionary is not conformant with Zstandard specification. + * It can still be loaded, but as a content-only dictionary. */ +ZSTDLIB_API unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize); + +/*! ZSTD_getDictID_fromDDict() : + * Provides the dictID of the dictionary loaded into `ddict`. + * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty. + * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */ +ZSTDLIB_API unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict); + +/*! ZSTD_getDictID_fromFrame() : + * Provides the dictID required to decompressed the frame stored within `src`. + * If @return == 0, the dictID could not be decoded. + * This could for one of the following reasons : + * - The frame does not require a dictionary to be decoded (most common case). + * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information. + * Note : this use case also happens when using a non-conformant dictionary. + * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`). + * - This is not a Zstandard frame. + * When identifying the exact failure cause, it's possible to use ZSTD_getFrameHeader(), which will provide a more precise error code. */ +ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); + + +/******************************************************************************* + * Advanced dictionary and prefix API + * + * This API allows dictionaries to be used with ZSTD_compress2(), + * ZSTD_compressStream2(), and ZSTD_decompress(). Dictionaries are sticky, and + * only reset with the context is reset with ZSTD_reset_parameters or + * ZSTD_reset_session_and_parameters. Prefixes are single-use. + ******************************************************************************/ + + +/*! ZSTD_CCtx_loadDictionary() : + * Create an internal CDict from `dict` buffer. + * Decompression will have to use same dictionary. + * @result : 0, or an error code (which can be tested with ZSTD_isError()). + * Special: Loading a NULL (or 0-size) dictionary invalidates previous dictionary, + * meaning "return to no-dictionary mode". + * Note 1 : Dictionary is sticky, it will be used for all future compressed frames. + * To return to "no-dictionary" situation, load a NULL dictionary (or reset parameters). + * Note 2 : Loading a dictionary involves building tables. + * It's also a CPU consuming operation, with non-negligible impact on latency. + * Tables are dependent on compression parameters, and for this reason, + * compression parameters can no longer be changed after loading a dictionary. + * Note 3 :`dict` content will be copied internally. + * Use experimental ZSTD_CCtx_loadDictionary_byReference() to reference content instead. + * In such a case, dictionary buffer must outlive its users. + * Note 4 : Use ZSTD_CCtx_loadDictionary_advanced() + * to precisely select how dictionary content must be interpreted. */ +ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); + +/*! ZSTD_CCtx_refCDict() : + * Reference a prepared dictionary, to be used for all next compressed frames. + * Note that compression parameters are enforced from within CDict, + * and supersede any compression parameter previously set within CCtx. + * The parameters ignored are labled as "superseded-by-cdict" in the ZSTD_cParameter enum docs. + * The ignored parameters will be used again if the CCtx is returned to no-dictionary mode. + * The dictionary will remain valid for future compressed frames using same CCtx. + * @result : 0, or an error code (which can be tested with ZSTD_isError()). + * Special : Referencing a NULL CDict means "return to no-dictionary mode". + * Note 1 : Currently, only one dictionary can be managed. + * Referencing a new dictionary effectively "discards" any previous one. + * Note 2 : CDict is just referenced, its lifetime must outlive its usage within CCtx. */ +ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); + +/*! ZSTD_CCtx_refPrefix() : + * Reference a prefix (single-usage dictionary) for next compressed frame. + * A prefix is **only used once**. Tables are discarded at end of frame (ZSTD_e_end). + * Decompression will need same prefix to properly regenerate data. + * Compressing with a prefix is similar in outcome as performing a diff and compressing it, + * but performs much faster, especially during decompression (compression speed is tunable with compression level). + * @result : 0, or an error code (which can be tested with ZSTD_isError()). + * Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary + * Note 1 : Prefix buffer is referenced. It **must** outlive compression. + * Its content must remain unmodified during compression. + * Note 2 : If the intention is to diff some large src data blob with some prior version of itself, + * ensure that the window size is large enough to contain the entire source. + * See ZSTD_c_windowLog. + * Note 3 : Referencing a prefix involves building tables, which are dependent on compression parameters. + * It's a CPU consuming operation, with non-negligible impact on latency. + * If there is a need to use the same prefix multiple times, consider loadDictionary instead. + * Note 4 : By default, the prefix is interpreted as raw content (ZSTD_dm_rawContent). + * Use experimental ZSTD_CCtx_refPrefix_advanced() to alter dictionary interpretation. */ +ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, + const void* prefix, size_t prefixSize); /*! ZSTD_DCtx_loadDictionary() : * Create an internal DDict from dict buffer, @@ -910,15 +954,32 @@ ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); ZSTDLIB_API size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize); -/*! ZSTD_DCtx_reset() : - * Return a DCtx to clean state. - * Session and parameters can be reset jointly or separately. - * Parameters can only be reset when no active frame is being decompressed. - * @return : 0, or an error code, which can be tested with ZSTD_isError() - */ -ZSTDLIB_API size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset); +/* === Memory management === */ + +/*! ZSTD_sizeof_*() : + * These functions give the _current_ memory usage of selected object. + * Note that object memory usage can evolve (increase or decrease) over time. */ +ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx); +ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx); +ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs); +ZSTDLIB_API size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds); +ZSTDLIB_API size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict); +ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict); + +#endif /* ZSTD_H_235446 */ + +/**************************************************************************************** + * ADVANCED AND EXPERIMENTAL FUNCTIONS + **************************************************************************************** + * The definitions in the following section are considered experimental. + * They are provided for advanced scenarios. + * They should never be used with a dynamic library, as prototypes may change in the future. + * Use them only in association with static linking. + * ***************************************************************************************/ +#if defined(ZSTD_STATIC_LINKING_ONLY) && !defined(ZSTD_H_ZSTD_STATIC_LINKING_ONLY) +#define ZSTD_H_ZSTD_STATIC_LINKING_ONLY /**************************************************************************************** * experimental API (static linking only) @@ -962,7 +1023,7 @@ ZSTDLIB_API size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset); #define ZSTD_WINDOWLOG_LIMIT_DEFAULT 27 /* by default, the streaming decoder will refuse any frame * requiring larger than (1<<ZSTD_WINDOWLOG_LIMIT_DEFAULT) window size, * to preserve host's memory from unreasonable requirements. - * This limit can be overriden using ZSTD_DCtx_setParameter(,ZSTD_d_windowLogMax,). + * This limit can be overridden using ZSTD_DCtx_setParameter(,ZSTD_d_windowLogMax,). * The limit does not apply for one-pass decoders (such as ZSTD_decompress()), since no additional memory is allocated */ @@ -1064,15 +1125,24 @@ typedef enum { ZSTD_dictForceCopy = 2, /* Always copy the dictionary. */ } ZSTD_dictAttachPref_e; +typedef enum { + ZSTD_lcm_auto = 0, /**< Automatically determine the compression mode based on the compression level. + * Negative compression levels will be uncompressed, and positive compression + * levels will be compressed. */ + ZSTD_lcm_huffman = 1, /**< Always attempt Huffman compression. Uncompressed literals will still be + * emitted if Huffman compression is not profitable. */ + ZSTD_lcm_uncompressed = 2, /**< Always emit uncompressed literals. */ +} ZSTD_literalCompressionMode_e; + /*************************************** * Frame size functions ***************************************/ /*! ZSTD_findDecompressedSize() : - * `src` should point the start of a series of ZSTD encoded and/or skippable frames + * `src` should point to the start of a series of ZSTD encoded and/or skippable frames * `srcSize` must be the _exact_ size of this series - * (i.e. there should be a frame boundary exactly at `srcSize` bytes after `src`) + * (i.e. there should be a frame boundary at `src + srcSize`) * @return : - decompressed size of all data in all successive frames * - if the decompressed size cannot be determined: ZSTD_CONTENTSIZE_UNKNOWN * - if an error occurred: ZSTD_CONTENTSIZE_ERROR @@ -1092,6 +1162,21 @@ typedef enum { * however it does mean that all frame data must be present and valid. */ ZSTDLIB_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize); +/** ZSTD_decompressBound() : + * `src` should point to the start of a series of ZSTD encoded and/or skippable frames + * `srcSize` must be the _exact_ size of this series + * (i.e. there should be a frame boundary at `src + srcSize`) + * @return : - upper-bound for the decompressed size of all data in all successive frames + * - if an error occured: ZSTD_CONTENTSIZE_ERROR + * + * note 1 : an error can occur if `src` contains an invalid or incorrectly formatted frame. + * note 2 : the upper-bound is exact when the decompressed size field is available in every ZSTD encoded frame of `src`. + * in this case, `ZSTD_findDecompressedSize` and `ZSTD_decompressBound` return the same value. + * note 3 : when the decompressed size field isn't available, the upper-bound for that frame is calculated by: + * upper-bound = # blocks * min(128 KB, Window_Size) + */ +ZSTDLIB_API unsigned long long ZSTD_decompressBound(const void* src, size_t srcSize); + /*! ZSTD_frameHeaderSize() : * srcSize must be >= ZSTD_FRAMEHEADERSIZE_PREFIX. * @return : size of the Frame Header, @@ -1110,7 +1195,7 @@ ZSTDLIB_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize); * It will also consider src size to be arbitrarily "large", which is worst case. * If srcSize is known to always be small, ZSTD_estimateCCtxSize_usingCParams() can provide a tighter estimation. * ZSTD_estimateCCtxSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel. - * ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_c_nbWorkers is >= 1. + * ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParams_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_c_nbWorkers is >= 1. * Note : CCtx size estimation is only correct for single-threaded compression. */ ZSTDLIB_API size_t ZSTD_estimateCCtxSize(int compressionLevel); ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams); @@ -1122,7 +1207,7 @@ ZSTDLIB_API size_t ZSTD_estimateDCtxSize(void); * It will also consider src size to be arbitrarily "large", which is worst case. * If srcSize is known to always be small, ZSTD_estimateCStreamSize_usingCParams() can provide a tighter estimation. * ZSTD_estimateCStreamSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel. - * ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_c_nbWorkers is >= 1. + * ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParams_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_c_nbWorkers is >= 1. * Note : CStream size estimation is only correct for single-threaded compression. * ZSTD_DStream memory budget depends on window Size. * This information can be passed manually, using ZSTD_estimateDStreamSize, @@ -1226,22 +1311,26 @@ ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictS ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel); /*! ZSTD_getCParams() : -* @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize. -* `estimatedSrcSize` value is optional, select 0 if not known */ + * @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize. + * `estimatedSrcSize` value is optional, select 0 if not known */ ZSTDLIB_API ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize); /*! ZSTD_getParams() : -* same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of sub-component `ZSTD_compressionParameters`. -* All fields of `ZSTD_frameParameters` are set to default : contentSize=1, checksum=0, noDictID=0 */ + * same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of sub-component `ZSTD_compressionParameters`. + * All fields of `ZSTD_frameParameters` are set to default : contentSize=1, checksum=0, noDictID=0 */ ZSTDLIB_API ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize); /*! ZSTD_checkCParams() : -* Ensure param values remain within authorized range */ + * Ensure param values remain within authorized range. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()) */ ZSTDLIB_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params); /*! ZSTD_adjustCParams() : * optimize params for a given `srcSize` and `dictSize`. - * both values are optional, select `0` if unknown. */ + * `srcSize` can be unknown, in which case use ZSTD_CONTENTSIZE_UNKNOWN. + * `dictSize` must be `0` when there is no dictionary. + * cPar can be invalid : all parameters will be clamped within valid range in the @return struct. + * This function never fails (wide contract) */ ZSTDLIB_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize); /*! ZSTD_compress_advanced() : @@ -1314,6 +1403,12 @@ ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* pre * See the comments on that enum for an explanation of the feature. */ #define ZSTD_c_forceAttachDict ZSTD_c_experimentalParam4 +/* Controls how the literals are compressed (default is auto). + * The value must be of type ZSTD_literalCompressionMode_e. + * See ZSTD_literalCompressionMode_t enum definition for details. + */ +#define ZSTD_c_literalCompressionMode ZSTD_c_experimentalParam5 + /*! ZSTD_CCtx_getParameter() : * Get the requested compression parameter value, selected by enum ZSTD_cParameter, * and store it into int* value. @@ -1325,10 +1420,10 @@ ZSTDLIB_API size_t ZSTD_CCtx_getParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param /*! ZSTD_CCtx_params : * Quick howto : * - ZSTD_createCCtxParams() : Create a ZSTD_CCtx_params structure - * - ZSTD_CCtxParam_setParameter() : Push parameters one by one into - * an existing ZSTD_CCtx_params structure. - * This is similar to - * ZSTD_CCtx_setParameter(). + * - ZSTD_CCtxParams_setParameter() : Push parameters one by one into + * an existing ZSTD_CCtx_params structure. + * This is similar to + * ZSTD_CCtx_setParameter(). * - ZSTD_CCtx_setParametersUsingCCtxParams() : Apply parameters to * an existing CCtx. * These parameters will be applied to @@ -1359,20 +1454,20 @@ ZSTDLIB_API size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compre */ ZSTDLIB_API size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params); -/*! ZSTD_CCtxParam_setParameter() : +/*! ZSTD_CCtxParams_setParameter() : * Similar to ZSTD_CCtx_setParameter. * Set one compression parameter, selected by enum ZSTD_cParameter. * Parameters must be applied to a ZSTD_CCtx using ZSTD_CCtx_setParametersUsingCCtxParams(). * @result : 0, or an error code (which can be tested with ZSTD_isError()). */ -ZSTDLIB_API size_t ZSTD_CCtxParam_setParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int value); +ZSTDLIB_API size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int value); -/*! ZSTD_CCtxParam_getParameter() : +/*! ZSTD_CCtxParams_getParameter() : * Similar to ZSTD_CCtx_getParameter. * Get the requested value of one compression parameter, selected by enum ZSTD_cParameter. * @result : 0, or an error code (which can be tested with ZSTD_isError()). */ -ZSTDLIB_API size_t ZSTD_CCtxParam_getParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int* value); +ZSTDLIB_API size_t ZSTD_CCtxParams_getParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int* value); /*! ZSTD_CCtx_setParametersUsingCCtxParams() : * Apply a set of ZSTD_CCtx_params to the compression context. @@ -1415,31 +1510,6 @@ ZSTDLIB_API unsigned ZSTD_isFrame(const void* buffer, size_t size); * it must remain read accessible throughout the lifetime of DDict */ ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize); - -/*! ZSTD_getDictID_fromDict() : - * Provides the dictID stored within dictionary. - * if @return == 0, the dictionary is not conformant with Zstandard specification. - * It can still be loaded, but as a content-only dictionary. */ -ZSTDLIB_API unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize); - -/*! ZSTD_getDictID_fromDDict() : - * Provides the dictID of the dictionary loaded into `ddict`. - * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty. - * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */ -ZSTDLIB_API unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict); - -/*! ZSTD_getDictID_fromFrame() : - * Provides the dictID required to decompressed the frame stored within `src`. - * If @return == 0, the dictID could not be decoded. - * This could for one of the following reasons : - * - The frame does not require a dictionary to be decoded (most common case). - * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information. - * Note : this use case also happens when using a non-conformant dictionary. - * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`). - * - This is not a Zstandard frame. - * When identifying the exact failure cause, it's possible to use ZSTD_getFrameHeader(), which will provide a more precise error code. */ -ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); - /*! ZSTD_DCtx_loadDictionary_byReference() : * Same as ZSTD_DCtx_loadDictionary(), * but references `dict` content instead of copying it into `dctx`. @@ -1501,14 +1571,68 @@ ZSTDLIB_API size_t ZSTD_decompressStream_simpleArgs ( ********************************************************************/ /*===== Advanced Streaming compression functions =====*/ -ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */ -ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/ +/**! ZSTD_initCStream_srcSize() : + * This function is deprecated, and equivalent to: + * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); + * ZSTD_CCtx_refCDict(zcs, NULL); // clear the dictionary (if any) + * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel); + * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); + * + * pledgedSrcSize must be correct. If it is not known at init time, use + * ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, + * "0" also disables frame content size field. It may be enabled in the future. + */ +ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); +/**! ZSTD_initCStream_usingDict() : + * This function is deprecated, and is equivalent to: + * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); + * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel); + * ZSTD_CCtx_loadDictionary(zcs, dict, dictSize); + * + * Creates of an internal CDict (incompatible with static CCtx), except if + * dict == NULL or dictSize < 8, in which case no dict is used. + * Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if + * it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy. + */ +ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); +/**! ZSTD_initCStream_advanced() : + * This function is deprecated, and is approximately equivalent to: + * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); + * ZSTD_CCtx_setZstdParams(zcs, params); // Set the zstd params and leave the rest as-is + * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); + * ZSTD_CCtx_loadDictionary(zcs, dict, dictSize); + * + * pledgedSrcSize must be correct. If srcSize is not known at init time, use + * value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. + */ ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, - ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */ -ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /**< note : cdict will just be referenced, and must outlive compression session */ -ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize); /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters. pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. */ + ZSTD_parameters params, unsigned long long pledgedSrcSize); +/**! ZSTD_initCStream_usingCDict() : + * This function is deprecated, and equivalent to: + * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); + * ZSTD_CCtx_refCDict(zcs, cdict); + * + * note : cdict will just be referenced, and must outlive compression session + */ +ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); +/**! ZSTD_initCStream_usingCDict_advanced() : + * This function is deprecated, and is approximately equivalent to: + * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); + * ZSTD_CCtx_setZstdFrameParams(zcs, fParams); // Set the zstd frame params and leave the rest as-is + * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); + * ZSTD_CCtx_refCDict(zcs, cdict); + * + * same as ZSTD_initCStream_usingCDict(), with control over frame parameters. + * pledgedSrcSize must be correct. If srcSize is not known at init time, use + * value ZSTD_CONTENTSIZE_UNKNOWN. + */ +ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize); /*! ZSTD_resetCStream() : + * This function is deprecated, and is equivalent to: + * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); + * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); + * * start a new frame, using same parameters from previous frame. * This is typically useful to skip dictionary loading stage, since it will re-use it in-place. * Note that zcs must be init at least once before using ZSTD_resetCStream(). @@ -1555,9 +1679,32 @@ ZSTDLIB_API size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx); /*===== Advanced Streaming decompression functions =====*/ -ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: no dictionary will be used if dict == NULL or dictSize < 8 */ -ZSTDLIB_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); /**< note : ddict is referenced, it must outlive decompression session */ -ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); /**< re-use decompression parameters from previous init; saves dictionary loading */ +/** + * This function is deprecated, and is equivalent to: + * + * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only); + * ZSTD_DCtx_loadDictionary(zds, dict, dictSize); + * + * note: no dictionary will be used if dict == NULL or dictSize < 8 + */ +ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); +/** + * This function is deprecated, and is equivalent to: + * + * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only); + * ZSTD_DCtx_refDDict(zds, ddict); + * + * note : ddict is referenced, it must outlive decompression session + */ +ZSTDLIB_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); +/** + * This function is deprecated, and is equivalent to: + * + * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only); + * + * re-use decompression parameters from previous init; saves dictionary loading + */ +ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); /********************************************************************* |