diff options
420 files changed, 81388 insertions, 1994 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..d28aabd0a6 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 @@ -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 diff --git a/SConstruct b/SConstruct index 1271156a28..7b2d49e36f 100644 --- a/SConstruct +++ b/SConstruct @@ -5,7 +5,6 @@ EnsureSConsVersion(0, 98, 1) # System import glob import os -import string import sys # Local diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 67de156fc3..98d3e77f66 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; @@ -2453,12 +2453,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..f7b446707d 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; diff --git a/core/class_db.h b/core/class_db.h index 321682d76b..f18a7113d7 100644 --- a/core/class_db.h +++ b/core/class_db.h @@ -371,6 +371,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/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index f74f076c65..86382939f1 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -65,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: { @@ -77,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; } @@ -283,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; @@ -333,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/math/audio_frame.h b/core/math/audio_frame.h index f970c510e0..ebe0356c93 100644 --- a/core/math/audio_frame.h +++ b/core/math/audio_frame.h @@ -122,6 +122,12 @@ 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_ AudioFrame() {} }; diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 708054e4ab..05b49f0815 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); 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/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/ustring.h b/core/ustring.h index cb3d87378a..9288c1526e 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); @@ -339,6 +343,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); diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 3812592639..9ef7b7e7e6 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -1927,9 +1927,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/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml index df52f8503a..e3eca57f2d 100644 --- a/doc/classes/@GDScript.xml +++ b/doc/classes/@GDScript.xml @@ -975,6 +975,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/ARVRAnchor.xml b/doc/classes/ARVRAnchor.xml index 5197c1c651..57ca74fdd3 100644 --- a/doc/classes/ARVRAnchor.xml +++ b/doc/classes/ARVRAnchor.xml @@ -27,6 +27,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 +54,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/ARVRController.xml b/doc/classes/ARVRController.xml index 1264f89f64..08e071ea1e 100644 --- a/doc/classes/ARVRController.xml +++ b/doc/classes/ARVRController.xml @@ -50,6 +50,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 +93,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/ARVRPositionalTracker.xml b/doc/classes/ARVRPositionalTracker.xml index c1be8d2356..c2e48b878b 100644 --- a/doc/classes/ARVRPositionalTracker.xml +++ b/doc/classes/ARVRPositionalTracker.xml @@ -27,6 +27,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/EditorSceneImporterAssimp.xml b/doc/classes/EditorSceneImporterAssimp.xml new file mode 100644 index 0000000000..0214f9eba2 --- /dev/null +++ b/doc/classes/EditorSceneImporterAssimp.xml @@ -0,0 +1,38 @@ +<?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> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/File.xml b/doc/classes/File.xml index 533a6d6399..8a785ab263 100644 --- a/doc/classes/File.xml +++ b/doc/classes/File.xml @@ -84,6 +84,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 +103,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 +139,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 +165,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"> @@ -340,6 +344,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 +372,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 +382,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 +401,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/HeightMapShape.xml b/doc/classes/HeightMapShape.xml new file mode 100644 index 0000000000..ec2e18bd7d --- /dev/null +++ b/doc/classes/HeightMapShape.xml @@ -0,0 +1,28 @@ +<?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> + <demos> + </demos> + <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/RandomNumberGenerator.xml b/doc/classes/RandomNumberGenerator.xml index 6fa2d44fd0..07c9f2a74b 100644 --- a/doc/classes/RandomNumberGenerator.xml +++ b/doc/classes/RandomNumberGenerator.xml @@ -28,6 +28,17 @@ Generates pseudo-random float between [code]from[/code] and [code]to[/code]. </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> diff --git a/doc/classes/StyleBoxFlat.xml b/doc/classes/StyleBoxFlat.xml index 448b635886..d74f481f55 100644 --- a/doc/classes/StyleBoxFlat.xml +++ b/doc/classes/StyleBoxFlat.xml @@ -151,6 +151,9 @@ <member name="shadow_size" type="int" setter="set_shadow_size" getter="get_shadow_size"> The shadow size in pixels. </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> </members> <constants> </constants> diff --git a/doc/classes/TextureRect.xml b/doc/classes/TextureRect.xml index 13fd762908..7f26fe18e5 100644 --- a/doc/classes/TextureRect.xml +++ b/doc/classes/TextureRect.xml @@ -22,6 +22,12 @@ <member name="texture" type="Texture" setter="set_texture" getter="get_texture"> The node's [Texture] resource. </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> </members> <constants> <constant name="STRETCH_SCALE_ON_EXPAND" value="0" enum="StretchMode"> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index fe917acaae..3c047487e0 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -165,6 +165,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"> diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py index dc6d270c08..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 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_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 3c8822175d..c996078ba6 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -2681,7 +2681,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 01d5e35e56..b051c7d811 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -4664,6 +4664,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 +4759,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) { 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/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index ec8202d92c..b0f1be47a1 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -2043,7 +2043,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..d1b704e09a 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -3660,10 +3660,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 +4007,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); diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 51e36c1d7e..6169790163 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -4131,7 +4131,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); @@ -6080,7 +6080,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 +6794,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 +7267,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) { 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/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/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/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index c10f59c49b..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); @@ -1228,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); @@ -1272,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_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_node.cpp b/editor/editor_node.cpp index 6c629c1869..91e104667e 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -5438,7 +5438,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"); @@ -5458,10 +5458,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"); @@ -5472,10 +5472,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 @@ -5499,21 +5499,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"); @@ -5529,7 +5529,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); @@ -5567,14 +5567,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_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 c13a4a5a5e..0e8cd955b5 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); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 5c257b86db..b9ed63c1b6 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" diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index c930824b98..335e3fcd29 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -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); @@ -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..b637148f2d 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1429,6 +1429,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 +2027,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(); } @@ -2336,13 +2340,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 +2367,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); 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/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/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 11eec528c7..c3cac582ad 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"))) { @@ -2190,32 +2190,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,9 +2261,6 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { case DRAG_MOVE: c = CURSOR_MOVE; break; - case DRAG_PAN: - c = CURSOR_DRAG; - break; default: break; } @@ -4591,6 +4590,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { drag_to = Vector2(); dragged_guide_pos = Point2(); dragged_guide_index = -1; + panning = false; bone_last_frame = 0; diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 4d8c0282fd..9173c55ae0 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -205,8 +205,7 @@ private: DRAG_V_GUIDE, DRAG_H_GUIDE, DRAG_DOUBLE_GUIDE, - DRAG_KEY_MOVE, - DRAG_PAN + DRAG_KEY_MOVE }; EditorSelection *editor_selection; @@ -262,6 +261,7 @@ private: bool key_pos; bool key_rot; bool key_scale; + bool panning; MenuOption last_option; 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/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 3a61277f36..42aba78e96 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3083,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/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 0a59fc69e3..f741040fa8 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -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)); diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 8a18af7ae8..33e4bb2336 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -481,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(); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 8fc080c819..5bc1067718 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: @@ -818,52 +818,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 +922,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) { @@ -1037,6 +1077,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 +1261,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 +1308,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 +1334,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 +1343,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 +1386,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")); @@ -1529,10 +1583,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 +1595,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"); @@ -2139,8 +2193,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,11 +2471,11 @@ void TileSetEditor::draw_polygon_shapes() { colors.push_back(c_bg); } } - if (polygon.size() == 0) + + if (polygon.size() < 3) continue; - if (polygon.size() > 2) { - workspace->draw_polygon(polygon, colors); - } + + workspace->draw_polygon(polygon, colors); if (coord == edited_shape_coord || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { 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..f71ff84bbe 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; @@ -344,7 +504,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 +622,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 +631,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 +705,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 +832,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) { } } @@ -719,6 +1004,7 @@ void VisualShaderEditor::_duplicate_nodes() { } void VisualShaderEditor::_mode_selected(int p_id) { + _update_options_menu(); _update_graph(); } @@ -756,6 +1042,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); @@ -777,6 +1185,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 +1206,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); @@ -800,8 +1225,13 @@ VisualShaderEditor::VisualShaderEditor() { graph->connect("duplicate_nodes_request", this, "_duplicate_nodes"); 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 +1247,315 @@ 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("BooleanConstant", "Boolean", "Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("BooleanUniform", "Boolean", "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 +1645,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..2709d72931 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; @@ -126,6 +167,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/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/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/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 5bc30dc643..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 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..ee66097ffd --- /dev/null +++ b/modules/assimp/editor_scene_importer_assimp.cpp @@ -0,0 +1,2181 @@ +/*************************************************************************/ +/* 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, true); + 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]); +} + +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); + Spatial *root = memnew(Spatial); + AnimationPlayer *ap = NULL; + if (p_flags & IMPORT_ANIMATION) { + ap = memnew(AnimationPlayer); + root->add_child(ap); + ap->set_owner(root); + ap->set_name(TTR("AnimationPlayer")); + } + Set<String> bone_names; + Set<String> light_names; + Set<String> camera_names; + real_t factor = 1.0f; + String ext = p_path.get_file().get_extension().to_lower(); + if ((ext == "fbx")) { + if (scene->mMetaData != NULL) { + scene->mMetaData->Get("UnitScaleFactor", factor); + factor = factor * 0.01f; + } + } + for (size_t l = 0; l < scene->mNumLights; l++) { + Light *light = NULL; + aiLight *ai_light = scene->mLights[l]; + ERR_CONTINUE(ai_light == NULL); + 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(); + Transform xform; + Quat quat; + quat.set_euler(dir); + Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); + pos = factor * pos; + xform.origin = pos; + light->set_transform(xform); + } 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; + pos = factor * pos; + light->set_transform(xform); + // No idea for energy + light->set_param(Light::PARAM_ATTENUATION, 0.0f); + } else if (ai_light->mType == aiLightSource_SPOT) { + light = memnew(SpotLight); + Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); + pos = factor * pos; + Transform xform; + xform.origin = pos; + Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z); + dir.normalize(); + Quat quat; + quat.set_euler(dir); + xform.basis = quat; + light->set_transform(xform); + // No idea for energy + light->set_param(Light::PARAM_ATTENUATION, 0.0f); + } + ERR_CONTINUE(light == NULL); + light->set_color(Color(ai_light->mColorDiffuse.r, ai_light->mColorDiffuse.g, ai_light->mColorDiffuse.b)); + root->add_child(light); + light->set_name(_ai_string_to_string(ai_light->mName)); + light->set_owner(root); + light_names.insert(_ai_string_to_string(scene->mLights[l]->mName)); + } + for (size_t c = 0; c < scene->mNumCameras; c++) { + aiCamera *ai_camera = scene->mCameras[c]; + 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(); + Quat quat; + quat.set_euler(look_at); + Transform xform; + xform.basis = quat; + xform.set_origin(pos); + root->add_child(camera); + camera->set_transform(xform); + camera->set_name(_ai_string_to_string(ai_camera->mName)); + camera->set_owner(root); + camera_names.insert(_ai_string_to_string(scene->mCameras[c]->mName)); + } + Map<Skeleton *, MeshInstance *> skeletons; + Map<String, Transform> bone_rests; + Vector<MeshInstance *> meshes; + int32_t mesh_count = 0; + Skeleton *s = memnew(Skeleton); + Set<String> removed_bones; + Map<String, Map<uint32_t, String> > path_morph_mesh_names; + _generate_node(p_path, scene, scene->mRootNode, root, root, bone_names, light_names, camera_names, skeletons, bone_rests, meshes, mesh_count, s, p_max_bone_weights, removed_bones, path_morph_mesh_names); + for (Map<Skeleton *, MeshInstance *>::Element *E = skeletons.front(); E; E = E->next()) { + E->key()->localize_rests(); + } + Set<String> removed_nodes; + Set<Node *> keep_nodes; + _keep_node(p_path, root, root, keep_nodes); + _fill_kept_node(keep_nodes); + _filter_node(p_path, root, root, keep_nodes, removed_nodes); + if (p_flags & IMPORT_ANIMATION) { + for (size_t i = 0; i < scene->mNumAnimations; i++) { + _import_animation(p_path, meshes, scene, ap, i, p_bake_fps, skeletons, removed_nodes, removed_bones, path_morph_mesh_names); + } + List<StringName> animation_names; + ap->get_animation_list(&animation_names); + if (animation_names.empty()) { + root->remove_child(ap); + memdelete(ap); + } + } + return root; +} + +void EditorSceneImporterAssimp::_fill_kept_node(Set<Node *> &keep_nodes) { + for (Set<Node *>::Element *E = keep_nodes.front(); E; E = E->next()) { + Node *node = E->get(); + while (node != NULL) { + if (keep_nodes.has(node) == false) { + keep_nodes.insert(node); + } + node = node->get_parent(); + } + } +} + +String EditorSceneImporterAssimp::_find_skeleton_bone_root(Map<Skeleton *, MeshInstance *> &skeletons, Map<MeshInstance *, String> &meshes, Spatial *root) { + for (Map<Skeleton *, MeshInstance *>::Element *E = skeletons.front(); E; E = E->next()) { + if (meshes.has(E->get())) { + String name = meshes[E->get()]; + if (name != "") { + return name; + } + } + } + return ""; +} + +void EditorSceneImporterAssimp::_set_bone_parent(Skeleton *s, Node *p_owner, aiNode *p_node) { + for (int32_t j = 0; j < s->get_bone_count(); j++) { + String bone_name = s->get_bone_name(j); + const aiNode *ai_bone_node = _ai_find_node(p_node, bone_name); + if (ai_bone_node == NULL) { + continue; + } + ai_bone_node = ai_bone_node->mParent; + while (ai_bone_node != NULL) { + int32_t node_parent_index = -1; + String parent_bone_name = _ai_string_to_string(ai_bone_node->mName); + node_parent_index = s->find_bone(parent_bone_name); + if (node_parent_index != -1) { + s->set_bone_parent(j, node_parent_index); + break; + } + ai_bone_node = ai_bone_node->mParent; + } + } +} + +void EditorSceneImporterAssimp::_insert_animation_track(const aiScene *p_scene, const String p_path, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, float length, const Skeleton *sk, const aiNodeAnim *track, String node_name, NodePath node_path) { + + if (track->mNumRotationKeys || track->mNumPositionKeys || track->mNumScalingKeys) { + //make transform track + int track_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_TRANSFORM); + animation->track_set_path(track_idx, node_path); + //first determine animation length + + for (size_t i = 0; i < track->mNumRotationKeys; i++) { + length = MAX(length, track->mRotationKeys[i].mTime / ticks_per_second); + } + for (size_t i = 0; i < track->mNumPositionKeys; i++) { + length = MAX(length, track->mPositionKeys[i].mTime / ticks_per_second); + } + for (size_t i = 0; i < track->mNumScalingKeys; i++) { + length = MAX(length, track->mScalingKeys[i].mTime / ticks_per_second); + } + + float increment = 1.0 / float(p_bake_fps); + float time = 0.0; + + Vector3 base_pos; + Quat base_rot; + Vector3 base_scale = Vector3(1, 1, 1); + + if (track->mNumRotationKeys != 0) { + aiQuatKey key = track->mRotationKeys[0]; + real_t x = key.mValue.x; + real_t y = key.mValue.y; + real_t z = key.mValue.z; + real_t w = key.mValue.w; + Quat q(x, y, z, w); + q = q.normalized(); + base_rot = q; + } + + if (track->mNumPositionKeys != 0) { + aiVectorKey key = track->mPositionKeys[0]; + real_t x = key.mValue.x; + real_t y = key.mValue.y; + real_t z = key.mValue.z; + base_pos = Vector3(x, y, z); + } + + if (track->mNumScalingKeys != 0) { + aiVectorKey key = track->mScalingKeys[0]; + real_t x = key.mValue.x; + real_t y = key.mValue.y; + real_t z = key.mValue.z; + base_scale = Vector3(x, y, z); + } + + bool last = false; + + 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 < track->mNumPositionKeys; p++) { + aiVector3D pos = track->mPositionKeys[p].mValue; + pos_values.push_back(Vector3(pos.x, pos.y, pos.z)); + pos_times.push_back(track->mPositionKeys[p].mTime / ticks_per_second); + } + + for (size_t r = 0; r < track->mNumRotationKeys; r++) { + aiQuaternion quat = track->mRotationKeys[r].mValue; + rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized()); + rot_times.push_back(track->mRotationKeys[r].mTime / ticks_per_second); + } + + for (size_t sc = 0; sc < track->mNumScalingKeys; sc++) { + aiVector3D scale = track->mScalingKeys[sc].mValue; + scale_values.push_back(Vector3(scale.x, scale.y, scale.z)); + scale_times.push_back(track->mScalingKeys[sc].mTime / ticks_per_second); + } + while (true) { + Vector3 pos = base_pos; + Quat rot = base_rot; + Vector3 scale = base_scale; + + 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 (sk != NULL && sk->find_bone(node_name) != -1) { + Transform xform; + xform.basis.set_quat_scale(rot, scale); + xform.origin = pos; + + int bone = sk->find_bone(node_name); + Transform rest_xform = sk->get_bone_rest(bone); + xform = rest_xform.affine_inverse() * xform; + rot = xform.basis.get_rotation_quat(); + scale = xform.basis.get_scale(); + pos = xform.origin; + } + { + Transform xform; + xform.basis.set_quat_scale(rot, scale); + xform.origin = pos; + Transform anim_xform; + String ext = p_path.get_file().get_extension().to_lower(); + if (ext == "fbx") { + real_t factor = 1.0f; + if (p_scene->mMetaData != NULL) { + p_scene->mMetaData->Get("UnitScaleFactor", factor); + } + anim_xform = anim_xform.scaled(Vector3(factor, factor, factor)); + } + xform = anim_xform * 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) { + break; + } + time += increment; + if (time >= length) { + last = true; + time = length; + } + } + } +} + +void EditorSceneImporterAssimp::_import_animation(const String p_path, const Vector<MeshInstance *> p_meshes, const aiScene *p_scene, AnimationPlayer *ap, int32_t p_index, int p_bake_fps, Map<Skeleton *, MeshInstance *> p_skeletons, const Set<String> p_removed_nodes, const Set<String> removed_bones, const Map<String, Map<uint32_t, String> > p_path_morph_mesh_names) { + String name = "Animation"; + aiAnimation const *anim = NULL; + if (p_index != -1) { + anim = p_scene->mAnimations[p_index]; + if (anim->mName.length > 0) { + name = _ai_anim_string_to_string(anim->mName); + } + } + + Ref<Animation> animation; + animation.instance(); + float length = 0.0f; + animation->set_name(name); + float ticks_per_second = p_scene->mAnimations[p_index]->mTicksPerSecond; + + if (p_scene->mMetaData != NULL && Math::is_equal_approx(ticks_per_second, 0.0f)) { + int32_t time_mode = 0; + p_scene->mMetaData->Get("TimeMode", time_mode); + ticks_per_second = _get_fbx_fps(time_mode, p_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; + } + + length = anim->mDuration / ticks_per_second; + if (anim) { + Map<String, Vector<const aiNodeAnim *> > node_tracks; + for (size_t i = 0; i < anim->mNumChannels; i++) { + const aiNodeAnim *track = anim->mChannels[i]; + String node_name = _ai_string_to_string(track->mNodeName); + NodePath node_path = node_name; + bool is_bone = false; + if (node_name.split(ASSIMP_FBX_KEY).size() > 1) { + String p_track_type = node_name.split(ASSIMP_FBX_KEY)[1]; + if (p_track_type == "_Translation" || p_track_type == "_Rotation" || p_track_type == "_Scaling") { + continue; + } + } + for (Map<Skeleton *, MeshInstance *>::Element *E = p_skeletons.front(); E; E = E->next()) { + Skeleton *sk = E->key(); + const String path = ap->get_owner()->get_path_to(sk); + if (path.empty()) { + continue; + } + if (sk->find_bone(node_name) == -1) { + continue; + } + node_path = path + ":" + node_name; + ERR_CONTINUE(ap->get_owner()->has_node(node_path) == false); + _insert_animation_track(p_scene, p_path, p_bake_fps, animation, ticks_per_second, length, sk, track, node_name, node_path); + is_bone = true; + } + if (is_bone) { + continue; + } + Node *node = ap->get_owner()->find_node(node_name); + if (node == NULL) { + continue; + } + if (p_removed_nodes.has(node_name)) { + continue; + } + const String path = ap->get_owner()->get_path_to(node); + if (path.empty()) { + print_verbose("Can't animate path"); + continue; + } + node_path = path; + if (ap->get_owner()->has_node(node_path) == false) { + continue; + } + _insert_animation_track(p_scene, p_path, p_bake_fps, animation, ticks_per_second, length, NULL, track, node_name, node_path); + } + for (size_t i = 0; i < anim->mNumChannels; i++) { + const aiNodeAnim *track = anim->mChannels[i]; + String node_name = _ai_string_to_string(track->mNodeName); + Vector<String> split_name = node_name.split(ASSIMP_FBX_KEY); + String bare_name = split_name[0]; + Node *node = ap->get_owner()->find_node(bare_name); + if (node != NULL && split_name.size() > 1) { + Map<String, Vector<const aiNodeAnim *> >::Element *E = node_tracks.find(bare_name); + Vector<const aiNodeAnim *> ai_tracks; + if (E) { + ai_tracks = E->get(); + ai_tracks.push_back(anim->mChannels[i]); + } else { + ai_tracks.push_back(anim->mChannels[i]); + } + node_tracks.insert(bare_name, ai_tracks); + } + } + for (Map<Skeleton *, MeshInstance *>::Element *E = p_skeletons.front(); E; E = E->next()) { + Skeleton *sk = E->key(); + Map<String, Vector<const aiNodeAnim *> > anim_tracks; + for (int32_t i = 0; i < sk->get_bone_count(); i++) { + String _bone_name = sk->get_bone_name(i); + Vector<const aiNodeAnim *> ai_tracks; + + if (sk->find_bone(_bone_name) == -1) { + continue; + } + for (size_t j = 0; j < anim->mNumChannels; j++) { + if (_ai_string_to_string(anim->mChannels[j]->mNodeName).split(ASSIMP_FBX_KEY).size() == 1) { + continue; + } + String track_name = _ai_string_to_string(anim->mChannels[j]->mNodeName).split(ASSIMP_FBX_KEY)[0]; + if (track_name != _bone_name) { + continue; + } + if (sk->find_bone(_bone_name) == -1) { + continue; + } + ai_tracks.push_back(anim->mChannels[j]); + } + if (ai_tracks.size() == 0) { + continue; + } + anim_tracks.insert(_bone_name, ai_tracks); + } + for (Map<String, Vector<const aiNodeAnim *> >::Element *F = anim_tracks.front(); F; F = F->next()) { + _insert_pivot_anim_track(p_meshes, F->key(), F->get(), ap, sk, length, ticks_per_second, animation, p_bake_fps, p_path, p_scene); + } + } + for (Map<String, Vector<const aiNodeAnim *> >::Element *E = node_tracks.front(); E; E = E->next()) { + if (p_removed_nodes.has(E->key())) { + continue; + } + if (removed_bones.find(E->key())) { + continue; + } + _insert_pivot_anim_track(p_meshes, E->key(), E->get(), ap, NULL, length, ticks_per_second, animation, p_bake_fps, p_path, p_scene); + } + for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) { + const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i]; + const String prop_name = _ai_string_to_string(anim_mesh->mName); + const String mesh_name = prop_name.split("*")[0]; + if (p_removed_nodes.has(mesh_name)) { + continue; + } + ERR_CONTINUE(prop_name.split("*").size() != 2); + const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(ap->get_owner()->find_node(mesh_name)); + ERR_CONTINUE(mesh_instance == NULL); + if (ap->get_owner()->find_node(mesh_instance->get_name()) == NULL) { + print_verbose("Can't find mesh in scene: " + mesh_instance->get_name()); + continue; + } + const String path = ap->get_owner()->get_path_to(mesh_instance); + if (path.empty()) { + print_verbose("Can't find mesh in scene"); + continue; + } + Ref<Mesh> mesh = mesh_instance->get_mesh(); + ERR_CONTINUE(mesh.is_null()); + const Map<String, Map<uint32_t, String> >::Element *E = p_path_morph_mesh_names.find(mesh_name); + ERR_CONTINUE(E == NULL); + for (size_t k = 0; k < anim_mesh->mNumKeys; k++) { + for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) { + const Map<uint32_t, String>::Element *F = E->get().find(anim_mesh->mKeys[k].mValues[j]); + ERR_CONTINUE(F == NULL); + const String prop = "blend_shapes/" + F->get(); + const NodePath node_path = String(path) + ":" + prop; + ERR_CONTINUE(ap->get_owner()->has_node(node_path) == false); + int32_t blend_track_idx = -1; + if (animation->find_track(node_path) == -1) { + blend_track_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_VALUE); + animation->track_set_interpolation_type(blend_track_idx, Animation::INTERPOLATION_LINEAR); + animation->track_set_path(blend_track_idx, node_path); + } else { + blend_track_idx = animation->find_track(node_path); + } + float t = anim_mesh->mKeys[k].mTime / ticks_per_second; + float w = anim_mesh->mKeys[k].mWeights[j]; + animation->track_insert_key(blend_track_idx, t, w); + } + } + } + } + animation->set_length(length); + if (animation->get_track_count()) { + ap->add_animation(name, animation); + } +} + +void EditorSceneImporterAssimp::_insert_pivot_anim_track(const Vector<MeshInstance *> p_meshes, const String p_node_name, Vector<const aiNodeAnim *> F, AnimationPlayer *ap, Skeleton *sk, float &length, float ticks_per_second, Ref<Animation> animation, int p_bake_fps, const String &p_path, const aiScene *p_scene) { + NodePath node_path; + if (sk != NULL) { + const String path = ap->get_owner()->get_path_to(sk); + if (path.empty()) { + return; + } + if (sk->find_bone(p_node_name) == -1) { + return; + } + node_path = path + ":" + p_node_name; + } else { + Node *node = ap->get_owner()->find_node(p_node_name); + if (node == NULL) { + return; + } + const String path = ap->get_owner()->get_path_to(node); + node_path = path; + } + if (node_path.is_empty()) { + return; + } + + Vector<Vector3> pos_values; + Vector<float> pos_times; + Vector<Vector3> scale_values; + Vector<float> scale_times; + Vector<Quat> rot_values; + Vector<float> rot_times; + Vector3 base_pos; + Quat base_rot; + Vector3 base_scale = Vector3(1, 1, 1); + bool is_translation = false; + bool is_rotation = false; + bool is_scaling = false; + for (int32_t k = 0; k < F.size(); k++) { + String p_track_type = _ai_string_to_string(F[k]->mNodeName).split(ASSIMP_FBX_KEY)[1]; + if (p_track_type == "_Translation") { + is_translation = is_translation || true; + } else if (p_track_type == "_Rotation") { + is_rotation = is_rotation || true; + } else if (p_track_type == "_Scaling") { + is_scaling = is_scaling || true; + } else { + continue; + } + ERR_CONTINUE(ap->get_owner()->has_node(node_path) == false); + + if (F[k]->mNumRotationKeys || F[k]->mNumPositionKeys || F[k]->mNumScalingKeys) { + + if (is_rotation) { + for (size_t i = 0; i < F[k]->mNumRotationKeys; i++) { + length = MAX(length, F[k]->mRotationKeys[i].mTime / ticks_per_second); + } + } + if (is_translation) { + for (size_t i = 0; i < F[k]->mNumPositionKeys; i++) { + length = MAX(length, F[k]->mPositionKeys[i].mTime / ticks_per_second); + } + } + if (is_scaling) { + for (size_t i = 0; i < F[k]->mNumScalingKeys; i++) { + length = MAX(length, F[k]->mScalingKeys[i].mTime / ticks_per_second); + } + } + + if (is_rotation == false && is_translation == false && is_scaling == false) { + return; + } + + if (is_rotation) { + if (F[k]->mNumRotationKeys != 0) { + aiQuatKey key = F[k]->mRotationKeys[0]; + real_t x = key.mValue.x; + real_t y = key.mValue.y; + real_t z = key.mValue.z; + real_t w = key.mValue.w; + Quat q(x, y, z, w); + q = q.normalized(); + base_rot = q; + } + } + + if (is_translation) { + if (F[k]->mNumPositionKeys != 0) { + aiVectorKey key = F[k]->mPositionKeys[0]; + real_t x = key.mValue.x; + real_t y = key.mValue.y; + real_t z = key.mValue.z; + base_pos = Vector3(x, y, z); + } + } + + if (is_scaling) { + if (F[k]->mNumScalingKeys != 0) { + aiVectorKey key = F[k]->mScalingKeys[0]; + real_t x = key.mValue.x; + real_t y = key.mValue.y; + real_t z = key.mValue.z; + base_scale = Vector3(x, y, z); + } + } + if (is_translation) { + for (size_t p = 0; p < F[k]->mNumPositionKeys; p++) { + aiVector3D pos = F[k]->mPositionKeys[p].mValue; + pos_values.push_back(Vector3(pos.x, pos.y, pos.z)); + pos_times.push_back(F[k]->mPositionKeys[p].mTime / ticks_per_second); + } + } + + if (is_rotation) { + for (size_t r = 0; r < F[k]->mNumRotationKeys; r++) { + aiQuaternion quat = F[k]->mRotationKeys[r].mValue; + rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized()); + rot_times.push_back(F[k]->mRotationKeys[r].mTime / ticks_per_second); + } + } + + if (is_scaling) { + for (size_t sc = 0; sc < F[k]->mNumScalingKeys; sc++) { + aiVector3D scale = F[k]->mScalingKeys[sc].mValue; + scale_values.push_back(Vector3(scale.x, scale.y, scale.z)); + scale_times.push_back(F[k]->mScalingKeys[sc].mTime / ticks_per_second); + } + } + } + } + int32_t track_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_TRANSFORM); + animation->track_set_path(track_idx, node_path); + float increment = 1.0 / float(p_bake_fps); + float time = 0.0; + bool last = false; + while (true) { + Vector3 pos = Vector3(); + Quat rot = Quat(); + Vector3 scale = Vector3(1.0f, 1.0f, 1.0f); + if (is_translation && pos_values.size()) { + pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR); + Transform anim_xform; + String ext = p_path.get_file().get_extension().to_lower(); + if (ext == "fbx") { + aiNode *ai_node = _ai_find_node(p_scene->mRootNode, p_node_name); + Transform mesh_xform = _get_global_ai_node_transform(p_scene, ai_node); + pos = mesh_xform.origin + pos; + real_t factor = 1.0f; + if (p_scene->mMetaData != NULL) { + p_scene->mMetaData->Get("UnitScaleFactor", factor); + factor = factor * 0.01f; + } + pos = pos * factor; + } + } + if (is_rotation && rot_values.size()) { + rot = _interpolate_track<Quat>(rot_times, rot_values, time, AssetImportAnimation::INTERP_LINEAR).normalized(); + } + if (is_scaling && scale_values.size()) { + scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR); + } + animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR); + animation->transform_track_insert_key(track_idx, time, pos, rot, scale); + + if (last) { + break; + } + time += increment; + if (time >= length) { + last = true; + time = length; + } + } +} + +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_ai_node_transform(const aiScene *p_scene, const aiNode *p_current_node) { + aiNode const *current_node = p_current_node; + Transform xform; + while (current_node != NULL) { + xform = _ai_matrix_transform(current_node->mTransformation) * xform; + current_node = current_node->mParent; + } + return xform; +} + +void EditorSceneImporterAssimp::_generate_node_bone(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const String p_path, const int32_t p_max_bone_weights) { + for (size_t i = 0; i < p_node->mNumMeshes; i++) { + const unsigned int mesh_idx = p_node->mMeshes[i]; + const aiMesh *ai_mesh = p_scene->mMeshes[mesh_idx]; + for (size_t j = 0; j < ai_mesh->mNumBones; j++) { + String bone_name = _ai_string_to_string(ai_mesh->mBones[j]->mName); + if (p_skeleton->find_bone(bone_name) != -1) { + continue; + } + p_mesh_bones.insert(bone_name, true); + p_skeleton->add_bone(bone_name); + int32_t idx = p_skeleton->find_bone(bone_name); + Transform xform = _ai_matrix_transform(ai_mesh->mBones[j]->mOffsetMatrix); + String ext = p_path.get_file().get_extension().to_lower(); + if (ext == "fbx") { + Transform mesh_xform = _get_global_ai_node_transform(p_scene, p_node); + mesh_xform.basis = Basis(); + xform = mesh_xform.affine_inverse() * xform; + } + p_skeleton->set_bone_rest(idx, xform.affine_inverse()); + } + } +} + +void EditorSceneImporterAssimp::_generate_node_bone_parents(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const MeshInstance *p_mi) { + for (size_t i = 0; i < p_node->mNumMeshes; i++) { + const unsigned int mesh_idx = p_node->mMeshes[i]; + const aiMesh *ai_mesh = p_scene->mMeshes[mesh_idx]; + + for (size_t j = 0; j < ai_mesh->mNumBones; j++) { + aiNode *bone_node = p_scene->mRootNode->FindNode(ai_mesh->mBones[j]->mName); + ERR_CONTINUE(bone_node == NULL); + aiNode *bone_node_parent = bone_node->mParent; + while (bone_node_parent != NULL) { + String bone_parent_name = _ai_string_to_string(bone_node_parent->mName); + bone_parent_name = bone_parent_name.split(ASSIMP_FBX_KEY)[0]; + if (bone_parent_name == p_mi->get_name()) { + break; + } + if (p_mi->get_parent() == NULL) { + break; + } + if (bone_parent_name == p_mi->get_parent()->get_name()) { + break; + } + if (bone_node_parent->mParent == p_scene->mRootNode) { + break; + } + if (p_skeleton->find_bone(bone_parent_name) == -1) { + p_mesh_bones.insert(bone_parent_name, true); + } + bone_node_parent = bone_node_parent->mParent; + } + } + } +} +void EditorSceneImporterAssimp::_calculate_skeleton_root(Skeleton *s, const aiScene *p_scene, aiNode *&p_ai_skeleton_root, Map<String, bool> &mesh_bones, const aiNode *p_node) { + if (s->get_bone_count() > 0) { + String bone_name = s->get_bone_name(0); + p_ai_skeleton_root = _ai_find_node(p_scene->mRootNode, bone_name); + for (size_t i = 0; i < p_scene->mRootNode->mNumChildren; i++) { + if (p_ai_skeleton_root == NULL) { + break; + } + aiNode *found = p_scene->mRootNode->mChildren[i]->FindNode(p_ai_skeleton_root->mName); + if (found) { + p_ai_skeleton_root = p_scene->mRootNode->mChildren[i]; + break; + } + } + } + + if (p_ai_skeleton_root == NULL) { + p_ai_skeleton_root = p_scene->mRootNode->FindNode(p_node->mName); + while (p_ai_skeleton_root && p_ai_skeleton_root->mParent && p_ai_skeleton_root->mParent != p_scene->mRootNode) { + p_ai_skeleton_root = p_scene->mRootNode->FindNode(p_ai_skeleton_root->mName)->mParent; + } + } + p_ai_skeleton_root = _ai_find_node(p_scene->mRootNode, _ai_string_to_string(p_ai_skeleton_root->mName).split(ASSIMP_FBX_KEY)[0]); +} + +void EditorSceneImporterAssimp::_fill_skeleton(const aiScene *p_scene, const aiNode *p_node, Spatial *p_current, Node *p_owner, Skeleton *p_skeleton, const Map<String, bool> p_mesh_bones, const Map<String, Transform> &p_bone_rests, Set<String> p_tracks, const String p_path, Set<String> &r_removed_bones) { + String node_name = _ai_string_to_string(p_node->mName); + if (p_mesh_bones.find(node_name) != NULL && p_skeleton->find_bone(node_name) == -1) { + r_removed_bones.insert(node_name); + p_skeleton->add_bone(node_name); + int32_t idx = p_skeleton->find_bone(node_name); + Transform xform = _get_global_ai_node_transform(p_scene, p_node); + xform = _format_rot_xform(p_path, p_scene) * xform; + p_skeleton->set_bone_rest(idx, xform); + } + + for (size_t i = 0; i < p_node->mNumChildren; i++) { + _fill_skeleton(p_scene, p_node->mChildren[i], p_current, p_owner, p_skeleton, p_mesh_bones, p_bone_rests, p_tracks, p_path, r_removed_bones); + } +} + +void EditorSceneImporterAssimp::_keep_node(const String &p_path, Node *p_current, Node *p_owner, Set<Node *> &r_keep_nodes) { + if (p_current == p_owner) { + r_keep_nodes.insert(p_current); + } + + if (p_current->get_class() != Spatial().get_class()) { + r_keep_nodes.insert(p_current); + } + + for (int i = 0; i < p_current->get_child_count(); i++) { + _keep_node(p_path, p_current->get_child(i), p_owner, r_keep_nodes); + } +} + +void EditorSceneImporterAssimp::_filter_node(const String &p_path, Node *p_current, Node *p_owner, const Set<Node *> p_keep_nodes, Set<String> &r_removed_nodes) { + if (p_keep_nodes.has(p_current) == false) { + r_removed_nodes.insert(p_current->get_name()); + p_current->queue_delete(); + } + for (int i = 0; i < p_current->get_child_count(); i++) { + _filter_node(p_path, p_current->get_child(i), p_owner, p_keep_nodes, r_removed_nodes); + } +} + +void EditorSceneImporterAssimp::_generate_node(const String &p_path, const aiScene *p_scene, const aiNode *p_node, Node *p_parent, Node *p_owner, Set<String> &r_bone_name, Set<String> p_light_names, Set<String> p_camera_names, Map<Skeleton *, MeshInstance *> &r_skeletons, const Map<String, Transform> &p_bone_rests, Vector<MeshInstance *> &r_mesh_instances, int32_t &r_mesh_count, Skeleton *p_skeleton, const int32_t p_max_bone_weights, Set<String> &r_removed_bones, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names) { + Spatial *child_node = NULL; + if (p_node == NULL) { + return; + } + String node_name = _ai_string_to_string(p_node->mName); + real_t factor = 1.0f; + String ext = p_path.get_file().get_extension().to_lower(); + if (ext == "fbx") { + if (p_scene->mMetaData != NULL) { + p_scene->mMetaData->Get("UnitScaleFactor", factor); + factor = factor * 0.01f; + } + } + { + Transform xform = _ai_matrix_transform(p_node->mTransformation); + + child_node = memnew(Spatial); + p_parent->add_child(child_node); + child_node->set_owner(p_owner); + if (p_node == p_scene->mRootNode) { + if ((ext == "fbx") && p_node == p_scene->mRootNode) { + xform = xform.scaled(Vector3(factor, factor, factor)); + Transform format_xform = _format_rot_xform(p_path, p_scene); + xform = format_xform * xform; + } + } + child_node->set_transform(xform * child_node->get_transform()); + } + + if (p_node->mNumMeshes > 0) { + MeshInstance *mesh_node = memnew(MeshInstance); + p_parent->add_child(mesh_node); + mesh_node->set_owner(p_owner); + mesh_node->set_transform(child_node->get_transform()); + { + Map<String, bool> mesh_bones; + p_skeleton->set_use_bones_in_world_transform(true); + _generate_node_bone(p_scene, p_node, mesh_bones, p_skeleton, p_path, p_max_bone_weights); + Set<String> tracks; + _get_track_set(p_scene, tracks); + aiNode *skeleton_root = NULL; + _calculate_skeleton_root(p_skeleton, p_scene, skeleton_root, mesh_bones, p_node); + _generate_node_bone_parents(p_scene, p_node, mesh_bones, p_skeleton, mesh_node); + if (p_skeleton->get_bone_count() > 0) { + _fill_skeleton(p_scene, skeleton_root, mesh_node, p_owner, p_skeleton, mesh_bones, p_bone_rests, tracks, p_path, r_removed_bones); + _set_bone_parent(p_skeleton, p_owner, p_scene->mRootNode); + } + MeshInstance *mi = Object::cast_to<MeshInstance>(mesh_node); + if (mi) { + r_mesh_instances.push_back(mi); + } + _add_mesh_to_mesh_instance(p_node, p_scene, p_skeleton, p_path, mesh_node, p_owner, r_bone_name, r_mesh_count, p_max_bone_weights, r_name_morph_mesh_names); + } + if (mesh_node != NULL && p_skeleton->get_bone_count() > 0 && p_owner->find_node(p_skeleton->get_name()) == NULL) { + Node *node = p_owner->find_node(_ai_string_to_string(p_scene->mRootNode->mName)); + ERR_FAIL_COND(node == NULL); + node->add_child(p_skeleton); + p_skeleton->set_owner(p_owner); + if (ext == "fbx") { + Transform mesh_xform = _get_global_ai_node_transform(p_scene, p_node); + mesh_xform.origin = Vector3(); + p_skeleton->set_transform(mesh_xform); + } + r_skeletons.insert(p_skeleton, mesh_node); + } + for (size_t i = 0; i < p_node->mNumMeshes; i++) { + if (p_scene->mMeshes[p_node->mMeshes[i]]->HasBones()) { + mesh_node->set_name(node_name); + // Meshes without skeletons must not have skeletons + mesh_node->set_skeleton_path(String(mesh_node->get_path_to(p_owner)) + "/" + p_owner->get_path_to(p_skeleton)); + } + } + child_node->get_parent()->remove_child(child_node); + memdelete(child_node); + child_node = mesh_node; + } else if (p_light_names.has(node_name)) { + Spatial *light_node = Object::cast_to<Light>(p_owner->find_node(node_name)); + ERR_FAIL_COND(light_node == NULL); + if (!p_parent->has_node(light_node->get_path())) { + p_parent->add_child(light_node); + } + light_node->set_owner(p_owner); + light_node->set_transform(child_node->get_transform().scaled(Vector3(factor, factor, factor)) * + light_node->get_transform().scaled(Vector3(factor, factor, factor))); + child_node->get_parent()->remove_child(child_node); + memdelete(child_node); + child_node = light_node; + } else if (p_camera_names.has(node_name)) { + Spatial *camera_node = Object::cast_to<Camera>(p_owner->find_node(node_name)); + ERR_FAIL_COND(camera_node == NULL); + if (!p_parent->has_node(camera_node->get_path())) { + p_parent->add_child(camera_node); + } + camera_node->set_owner(p_owner); + camera_node->set_transform(child_node->get_transform().scaled(Vector3(factor, factor, factor)) * + camera_node->get_transform().scaled(Vector3(factor, factor, factor))); + camera_node->scale(Vector3(factor, factor, factor)); + child_node->get_parent()->remove_child(child_node); + memdelete(child_node); + child_node = camera_node; + } + child_node->set_name(node_name); + for (size_t i = 0; i < p_node->mNumChildren; i++) { + _generate_node(p_path, p_scene, p_node->mChildren[i], child_node, p_owner, r_bone_name, p_light_names, p_camera_names, r_skeletons, p_bone_rests, r_mesh_instances, r_mesh_count, p_skeleton, p_max_bone_weights, r_removed_bones, r_name_morph_mesh_names); + } +} + +aiNode *EditorSceneImporterAssimp::_ai_find_node(aiNode *ai_child_node, const String bone_name) { + + if (_ai_string_to_string(ai_child_node->mName) == bone_name) { + return ai_child_node; + } + aiNode *target = NULL; + for (size_t i = 0; i < ai_child_node->mNumChildren; i++) { + + target = _ai_find_node(ai_child_node->mChildren[i], bone_name); + if (target != NULL) { + return target; + } + } + return target; +} + +Transform EditorSceneImporterAssimp::_format_rot_xform(const String p_path, const aiScene *p_scene) { + String ext = p_path.get_file().get_extension().to_lower(); + + Transform xform; + int32_t up_axis = 0; + Vector3 up_axis_vec3 = Vector3(); + + int32_t front_axis = 0; + Vector3 front_axis_vec3 = Vector3(); + + if (p_scene->mMetaData != NULL) { + p_scene->mMetaData->Get("UpAxis", up_axis); + if (up_axis == AssetImportFbx::UP_VECTOR_AXIS_X) { + if (p_scene->mMetaData != NULL) { + p_scene->mMetaData->Get("FrontAxis", front_axis); + if (front_axis == AssetImportFbx::FRONT_PARITY_EVEN) { + // y + } else if (front_axis == AssetImportFbx::FRONT_PARITY_ODD) { + // z + //front_axis_vec3 = Vector3(0.0f, Math::deg2rad(-180.f), 0.0f); + } + } + } else if (up_axis == AssetImportFbx::UP_VECTOR_AXIS_Y) { + up_axis_vec3 = Vector3(Math::deg2rad(-90.f), 0.0f, 0.0f); + if (p_scene->mMetaData != NULL) { + p_scene->mMetaData->Get("FrontAxis", front_axis); + if (front_axis == AssetImportFbx::FRONT_PARITY_EVEN) { + // x + } else if (front_axis == AssetImportFbx::FRONT_PARITY_ODD) { + // z + } + } + } else if (up_axis == AssetImportFbx::UP_VECTOR_AXIS_Z) { + up_axis_vec3 = Vector3(0.0f, Math ::deg2rad(90.f), 0.0f); + if (p_scene->mMetaData != NULL) { + p_scene->mMetaData->Get("FrontAxis", front_axis); + if (front_axis == AssetImportFbx::FRONT_PARITY_EVEN) { + // x + } else if (front_axis == AssetImportFbx::FRONT_PARITY_ODD) { + // y + } + } + } + } + + int32_t up_axis_sign = 0; + if (p_scene->mMetaData != NULL) { + p_scene->mMetaData->Get("UpAxisSign", up_axis_sign); + up_axis_vec3 = up_axis_vec3 * up_axis_sign; + } + + int32_t front_axis_sign = 0; + if (p_scene->mMetaData != NULL) { + p_scene->mMetaData->Get("FrontAxisSign", front_axis_sign); + front_axis_vec3 = front_axis_vec3 * front_axis_sign; + } + + int32_t coord_axis = 0; + Vector3 coord_axis_vec3 = Vector3(); + if (p_scene->mMetaData != NULL) { + p_scene->mMetaData->Get("CoordAxis", coord_axis); + if (coord_axis == AssetImportFbx::COORD_LEFT) { + } else if (coord_axis == AssetImportFbx::COORD_RIGHT) { + } + } + + int32_t coord_axis_sign = 0; + if (p_scene->mMetaData != NULL) { + p_scene->mMetaData->Get("CoordAxisSign", coord_axis_sign); + } + + Quat up_quat; + up_quat.set_euler(up_axis_vec3); + + Quat coord_quat; + coord_quat.set_euler(coord_axis_vec3); + + Quat front_quat; + front_quat.set_euler(front_axis_vec3); + + xform.basis.set_quat(up_quat * coord_quat * front_quat); + return xform; +} + +void EditorSceneImporterAssimp::_get_track_set(const aiScene *p_scene, Set<String> &tracks) { + for (size_t i = 0; i < p_scene->mNumAnimations; i++) { + for (size_t j = 0; j < p_scene->mAnimations[i]->mNumChannels; j++) { + aiString ai_name = p_scene->mAnimations[i]->mChannels[j]->mNodeName; + String name = _ai_string_to_string(ai_name); + tracks.insert(name); + } + } +} + +void EditorSceneImporterAssimp::_add_mesh_to_mesh_instance(const aiNode *p_node, const aiScene *p_scene, Skeleton *s, const String &p_path, MeshInstance *p_mesh_instance, Node *p_owner, Set<String> &r_bone_name, int32_t &r_mesh_count, int32_t p_max_bone_weights, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names) { + Ref<ArrayMesh> mesh; + mesh.instance(); + bool has_uvs = false; + for (size_t i = 0; i < p_node->mNumMeshes; i++) { + const unsigned int mesh_idx = p_node->mMeshes[i]; + const aiMesh *ai_mesh = p_scene->mMeshes[mesh_idx]; + + Map<uint32_t, Vector<float> > vertex_weight; + Map<uint32_t, Vector<String> > vertex_bone_name; + for (size_t b = 0; b < ai_mesh->mNumBones; b++) { + aiBone *bone = ai_mesh->mBones[b]; + for (size_t w = 0; w < bone->mNumWeights; w++) { + String name = _ai_string_to_string(bone->mName); + aiVertexWeight ai_weights = bone->mWeights[w]; + uint32_t vertexId = ai_weights.mVertexId; + Map<uint32_t, Vector<float> >::Element *result = vertex_weight.find(vertexId); + Vector<float> weights; + if (result != NULL) { + weights.append_array(result->value()); + } + weights.push_back(ai_weights.mWeight); + if (vertex_weight.has(vertexId)) { + vertex_weight[vertexId] = weights; + } else { + vertex_weight.insert(vertexId, weights); + } + Map<uint32_t, Vector<String> >::Element *bone_result = vertex_bone_name.find(vertexId); + Vector<String> bone_names; + if (bone_result != NULL) { + bone_names.append_array(bone_result->value()); + } + bone_names.push_back(name); + if (vertex_bone_name.has(vertexId)) { + vertex_bone_name[vertexId] = bone_names; + } else { + vertex_bone_name.insert(vertexId, bone_names); + } + } + } + + 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 (s != NULL && s->get_bone_count() > 0) { + Map<uint32_t, Vector<String> >::Element *I = vertex_bone_name.find(j); + Vector<int32_t> bones; + if (I != NULL) { + Vector<String> bone_names; + bone_names.append_array(I->value()); + for (int32_t f = 0; f < bone_names.size(); f++) { + int32_t bone = s->find_bone(bone_names[f]); + ERR_EXPLAIN("Asset Importer: Mesh can't find bone " + bone_names[f]); + ERR_FAIL_COND(bone == -1); + bones.push_back(bone); + } + if (s->get_bone_count()) { + int32_t add = CLAMP(p_max_bone_weights - bones.size(), 0, p_max_bone_weights); + for (int32_t f = 0; f < add; f++) { + bones.push_back(0); + } + } + st->add_bones(bones); + Map<uint32_t, Vector<float> >::Element *E = vertex_weight.find(j); + Vector<float> weights; + if (E != NULL) { + weights = E->value(); + if (weights.size() != p_max_bone_weights) { + int32_t add = CLAMP(p_max_bone_weights - weights.size(), 0, p_max_bone_weights); + for (int32_t f = 0; f < add; f++) { + weights.push_back(0.0f); + } + } + } + ERR_CONTINUE(weights.size() == 0); + 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_FAIL_COND(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(); + } + aiMaterial *ai_material = p_scene->mMaterials[ai_mesh->mMaterialIndex]; + 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 = _ai_string_to_string(ai_mesh->mName); + aiString mat_name; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) { + mat->set_name(_ai_string_to_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 = _ai_raw_string_to_string(ai_filename); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, 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 = _ai_raw_string_to_string(ai_filename); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, 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 = _ai_raw_string_to_string(ai_filename); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, 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 = _ai_raw_string_to_string(ai_filename); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, 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 = _ai_raw_string_to_string(tex_gltf_base_color_path); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, path); + _find_texture_path(p_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 = _ai_raw_string_to_string(tex_fbx_pbs_base_color_path); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, path); + _find_texture_path(p_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 = _ai_raw_string_to_string(tex_fbx_pbs_normal_path); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, path); + _find_texture_path(p_path, path, found); + if (texture != NULL) { + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + } + } + } + } + + aiString cull_mode; + if (p_node->mMetaData) { + p_node->mMetaData->Get("Culling", cull_mode); + } + if (cull_mode.length != 0 && cull_mode == aiString("CullingOff")) { + 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 = _ai_raw_string_to_string(tex_fbx_stingray_normal_path); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, path); + _find_texture_path(p_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 = _ai_raw_string_to_string(tex_fbx_pbs_base_color_path); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, path); + _find_texture_path(p_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 = _ai_raw_string_to_string(tex_fbx_pbs_emissive_path); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, path); + _find_texture_path(p_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 = _ai_raw_string_to_string(tex_gltf_pbr_metallicroughness_path); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, 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 = _ai_raw_string_to_string(tex_fbx_pbs_metallic_path); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, 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 = _ai_raw_string_to_string(tex_fbx_pbs_rough_path); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, 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 = _ai_raw_string_to_string(tex_fbx_pbs_metallic_path); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, 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 = _ai_raw_string_to_string(tex_fbx_pbs_rough_path); + String path = p_path.get_base_dir() + "/" + filename.replace("\\", "/"); + bool found = false; + _find_texture_path(p_path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(p_scene, 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); + } + } + } + + 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++) { + + String ai_anim_mesh_name = _ai_string_to_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); + morph_mesh_idx_names.insert(j, 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; + } + r_name_morph_mesh_names.insert(_ai_string_to_string(p_node->mName), morph_mesh_idx_names); + mesh->add_surface_from_arrays(primitive, array_mesh, morphs); + mesh->surface_set_material(i, mat); + mesh->surface_set_name(i, _ai_string_to_string(ai_mesh->mName)); + r_mesh_count++; + print_line(String("Open Asset Import: Created mesh (including instances) ") + _ai_string_to_string(ai_mesh->mName) + " " + itos(r_mesh_count) + " of " + itos(p_scene->mNumMeshes)); + } + p_mesh_instance->set_mesh(mesh); +} + +Ref<Texture> EditorSceneImporterAssimp::_load_texture(const aiScene *p_scene, 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 >= p_scene->mNumTextures, Ref<Texture>()); + aiTexture *tex = p_scene->mTextures[texture_idx]; + String filename = _ai_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; +} + +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::_ai_string_to_string(const aiString p_string) const { + Vector<char> raw_name; + raw_name.resize(p_string.length); + memcpy(raw_name.ptrw(), p_string.C_Str(), p_string.length); + String name; + name.parse_utf8(raw_name.ptrw(), raw_name.size()); + if (name.find(":") != -1) { + String replaced_name = name.split(":")[1]; + print_verbose("Replacing " + name + " containing : with " + replaced_name); + name = replaced_name; + } + if (name.find(".") != -1) { + String replaced_name = name.replace(".", ""); + print_verbose("Replacing " + name + " containing . with " + replaced_name); + name = replaced_name; + } + return name; +} + +String EditorSceneImporterAssimp::_ai_anim_string_to_string(const aiString p_string) const { + Vector<char> raw_name; + raw_name.resize(p_string.length); + memcpy(raw_name.ptrw(), p_string.C_Str(), p_string.length); + String name; + name.parse_utf8(raw_name.ptrw(), raw_name.size()); + 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::_ai_raw_string_to_string(const aiString p_string) const { + Vector<char> raw_name; + raw_name.resize(p_string.length); + memcpy(raw_name.ptrw(), p_string.C_Str(), p_string.length); + String name; + name.parse_utf8(raw_name.ptrw(), raw_name.size()); + 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::_ai_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.basis.inverse(); + xform.basis.transpose(); + Vector3 scale = xform.basis.get_scale(); + Quat rot = xform.basis.get_rotation_quat(); + xform.basis.set_quat_scale(rot, scale); + 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..8f9ed434ae --- /dev/null +++ b/modules/assimp/editor_scene_importer_assimp.h @@ -0,0 +1,206 @@ +/*************************************************************************/ +/* 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 + }; + }; + 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); + void _fill_kept_node(Set<Node *> &keep_nodes); + String _find_skeleton_bone_root(Map<Skeleton *, MeshInstance *> &skeletons, Map<MeshInstance *, String> &meshes, Spatial *root); + void _set_bone_parent(Skeleton *s, Node *p_owner, aiNode *p_node); + Transform _get_global_ai_node_transform(const aiScene *p_scene, const aiNode *p_current_node); + void _generate_node_bone(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const String p_path, const int32_t p_max_bone_weights); + void _generate_node_bone_parents(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const MeshInstance *p_mi); + void _calculate_skeleton_root(Skeleton *s, const aiScene *p_scene, aiNode *&p_ai_skeleton_root, Map<String, bool> &mesh_bones, const aiNode *p_node); + void _fill_skeleton(const aiScene *p_scene, const aiNode *p_node, Spatial *p_current, Node *p_owner, Skeleton *p_skeleton, const Map<String, bool> p_mesh_bones, const Map<String, Transform> &p_bone_rests, Set<String> p_tracks, const String p_path, Set<String> &r_removed_bones); + void _keep_node(const String &p_path, Node *p_current, Node *p_owner, Set<Node *> &r_keep_nodes); + void _filter_node(const String &p_path, Node *p_current, Node *p_owner, const Set<Node *> p_keep_nodes, Set<String> &r_removed_nodes); + void _generate_node(const String &p_path, const aiScene *p_scene, const aiNode *p_node, Node *p_parent, Node *p_owner, Set<String> &r_bone_name, Set<String> p_light_names, Set<String> p_camera_names, Map<Skeleton *, MeshInstance *> &r_skeletons, const Map<String, Transform> &p_bone_rests, Vector<MeshInstance *> &r_mesh_instances, int32_t &r_mesh_count, Skeleton *p_skeleton, const int32_t p_max_bone_weights, Set<String> &r_removed_bones, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names); + aiNode *_ai_find_node(aiNode *ai_child_node, const String bone_name); + Transform _format_rot_xform(const String p_path, const aiScene *p_scene); + void _get_track_set(const aiScene *p_scene, Set<String> &tracks); + void _insert_animation_track(const aiScene *p_scene, const String p_path, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, float length, const Skeleton *sk, const aiNodeAnim *track, String node_name, NodePath node_path); + void _add_mesh_to_mesh_instance(const aiNode *p_node, const aiScene *p_scene, Skeleton *s, const String &p_path, MeshInstance *p_mesh_instance, Node *p_owner, Set<String> &r_bone_name, int32_t &r_mesh_count, int32_t p_max_bone_weights, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names); + Ref<Texture> _load_texture(const aiScene *p_scene, String p_path); + 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); + String _ai_string_to_string(const aiString p_string) const; + String _ai_anim_string_to_string(const aiString p_string) const; + String _ai_raw_string_to_string(const aiString p_string) const; + void _import_animation(const String p_path, const Vector<MeshInstance *> p_meshes, const aiScene *p_scene, AnimationPlayer *ap, int32_t p_index, int p_bake_fps, Map<Skeleton *, MeshInstance *> p_skeletons, const Set<String> p_removed_nodes, const Set<String> removed_bones, const Map<String, Map<uint32_t, String> > p_path_morph_mesh_names); + void _insert_pivot_anim_track(const Vector<MeshInstance *> p_meshes, const String p_node_name, Vector<const aiNodeAnim *> F, AnimationPlayer *ap, Skeleton *sk, float &length, float ticks_per_second, Ref<Animation> animation, int p_bake_fps, const String &p_path, const aiScene *p_scene); + 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); + const Transform _ai_matrix_transform(const aiMatrix4x4 p_matrix); + 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/drivers/gl_context/context_gl.cpp b/modules/assimp/register_types.h index 759ab6f3ec..f841cd26b2 100644 --- a/drivers/gl_context/context_gl.cpp +++ b/modules/assimp/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_assimp_types(); +void unregister_assimp_types(); 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/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/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/gdscript/gdscript.h b/modules/gdscript/gdscript.h index ded873c7d3..c67e390e32 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -444,6 +444,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_editor.cpp b/modules/gdscript/gdscript_editor.cpp index fafc73b7e6..ee0d5ae055 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 { 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/mono/SCsub b/modules/mono/SCsub index 706949154e..b6ba9b1c23 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -257,7 +257,6 @@ def find_msbuild_windows(): 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])) diff --git a/modules/mono/config.py b/modules/mono/config.py index a81ecfce70..6b88600c56 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -5,7 +5,7 @@ import sys import subprocess from distutils.version import LooseVersion -from SCons.Script import BoolVariable, Dir, Environment, File, SCons, Variables +from SCons.Script import BoolVariable, Dir, Environment, Variables monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index dfdb4a0b3a..5f97069485 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); @@ -2166,7 +2166,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 +2192,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 +2259,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); diff --git a/modules/mono/glue/Managed/Files/Array.cs b/modules/mono/glue/Managed/Files/Array.cs index 1ee64f3b71..2277c7bf18 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,36 +143,6 @@ namespace Godot.Collections } } - public int IndexOf(object item) - { - 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(); - } - [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_Array_Ctor(); @@ -184,7 +162,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 +171,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); @@ -231,6 +209,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 +232,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 +261,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 +332,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 +349,9 @@ 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(); - } } } diff --git a/modules/mono/glue/Managed/Files/Dictionary.cs b/modules/mono/glue/Managed/Files/Dictionary.cs index fb4521065f..af1782b79a 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,97 @@ 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; - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); + public object Value => Entry.Value; + + public bool MoveNext() + { + index++; + return index < count; + } + + public void Reset() + { + index = -1; + } } [MethodImpl(MethodImplOptions.InternalCall)] @@ -250,9 +246,7 @@ namespace Godot.Collections } public class Dictionary<TKey, TValue> : - IDictionary<TKey, TValue>, - ICollection<KeyValuePair<TKey, TValue>>, - IEnumerable<KeyValuePair<TKey, TValue>> + IDictionary<TKey, TValue> { Dictionary objectDict; @@ -269,6 +263,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 +300,13 @@ namespace Godot.Collections return from.objectDict; } + internal IntPtr GetPtr() + { + return objectDict.GetPtr(); + } + + // IDictionary<TKey, TValue> + public TValue this[TKey key] { get @@ -319,6 +337,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 +378,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 +393,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 +417,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 +438,9 @@ 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(); - } } } 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/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..7f367fa095 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) { diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index 1aad1c53bc..84c3f354d8 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) { diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h index 85a2e243a2..69768500d8 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); 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_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..429d4f68d9 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 @@ -712,7 +729,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/mono_reg_utils.py b/modules/mono/mono_reg_utils.py index c8ebb54ded..583708bf07 100644 --- a/modules/mono/mono_reg_utils.py +++ b/modules/mono/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/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/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/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index dd14d60327..104a90059e 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -204,7 +204,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/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index 0d379205e4..c2ce5f0bf5 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); @@ -1379,6 +1396,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 +1451,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/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..48fd076d31 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -606,6 +606,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(); } 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_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..d65cf2f31e 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 { @@ -505,18 +510,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 +528,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 +559,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 +569,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 +599,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 +616,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 +644,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 +723,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 +731,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..3a5404124a 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); @@ -241,7 +200,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/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/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/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/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/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/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..0118b5bae2 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -1,6 +1,5 @@ import methods import os -import sys def is_active(): 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 5cc5ef4ac0..5f7b825f5e 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -1,9 +1,9 @@ import os import platform import sys -from compat import decode_utf8 from methods import get_compiler_version, using_gcc + def is_active(): return True diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index 7a50574be2..932db8f001 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -679,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", "backwards"), &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); 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/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/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/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/main/node.cpp b/scene/main/node.cpp index 96e286602a..128168ca72 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); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 49c9c4c23c..87a15bbeda 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,6 +515,7 @@ 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>(); @@ -591,6 +609,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>(); 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/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/drivers/gl_context/context_gl.h b/scene/resources/height_map_shape.h index 890683b7c5..b062f4e893 100644 --- a/drivers/gl_context/context_gl.h +++ b/scene/resources/height_map_shape.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* context_gl.h */ +/* height_map_shape.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,39 +28,35 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CONTEXT_GL_H -#define CONTEXT_GL_H +#ifndef HEIGHT_MAP_SHAPE_H +#define HEIGHT_MAP_SHAPE_H -#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) +#include "scene/resources/shape.h" -#include "core/typedefs.h" +class HeightMapShape : public Shape { + GDCLASS(HeightMapShape, Shape); -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ + int map_width; + int map_depth; + PoolRealArray map_data; + float min_height; + float max_height; -class ContextGL { +protected: + static void _bind_methods(); + virtual void _update_shape(); - static ContextGL *singleton; + virtual Vector<Vector3> _gen_debug_mesh_lines(); 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(); + 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 - -#endif +#endif /* !HEIGHT_MAP_SHAPE_H */ 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/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..bac10a1cd5 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(" + 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,6 +837,7 @@ 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: {} } @@ -1262,6 +1277,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..dac999bf1d 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 { diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 27b557494a..28ead64fe2 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) 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/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/visual/rasterizer.h b/servers/visual/rasterizer.h index dd54698471..39530bbbee 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; @@ -1101,7 +1102,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/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index af6e3db149..4adf53c183 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -629,7 +629,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) { diff --git a/servers/visual/visual_server_viewport.cpp b/servers/visual/visual_server_viewport.cpp index 79fdefe5c1..9ad3e4a308 100644 --- a/servers/visual/visual_server_viewport.cpp +++ b/servers/visual/visual_server_viewport.cpp @@ -62,6 +62,16 @@ static Transform2D _canvas_get_transform(VisualServerViewport::Viewport *p_viewp 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 */ @@ -89,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) { @@ -201,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; } @@ -237,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; @@ -252,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; @@ -308,12 +300,22 @@ void VisualServerViewport::draw_viewports() { // 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); @@ -324,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/thirdparty/README.md b/thirdparty/README.md index 4fb4786d67..7f9fc28b3e 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -1,5 +1,11 @@ # Third party libraries +## assimp + +- Upstream: http://github.com/assimp/assimp +- Version: git (d2b45377e4b09a1f43be95e45553afcc06b03f4b) +- License: BSD-3-Clause + ## b2d_convexdecomp 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 + |