diff --git a/dtool/src/dtoolutil/panda_getopt_impl.cxx b/dtool/src/dtoolutil/panda_getopt_impl.cxx index 030641a357..2779a7906a 100644 --- a/dtool/src/dtoolutil/panda_getopt_impl.cxx +++ b/dtool/src/dtoolutil/panda_getopt_impl.cxx @@ -491,4 +491,13 @@ getopt_long_only(int argc, char *const argv[], const char *optstring, return pgetopt->process(opterr, longindex, optarg, optind, optopt); } +/** + * Resets the internal PandaGetopt state. + * This is a necessary step to reset getopt state in general. + */ +void +pgetopt_reset() { + pgetopt = nullptr; +} + #endif // defined(HAVE_GETOPT) && defined(HAVE_GETOPT_LONG_ONLY) diff --git a/dtool/src/dtoolutil/panda_getopt_impl.h b/dtool/src/dtoolutil/panda_getopt_impl.h index c1691ae069..46a5864a34 100644 --- a/dtool/src/dtoolutil/panda_getopt_impl.h +++ b/dtool/src/dtoolutil/panda_getopt_impl.h @@ -62,6 +62,8 @@ getopt_long(int argc, char *const argv[], const char *optstring, extern EXPCL_DTOOL_DTOOLUTIL int getopt_long_only(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex); +extern EXPCL_DTOOL_DTOOLUTIL void +pgetopt_reset(); #ifdef __cplusplus } diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index ab4aedcdef..50e92dc858 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -5769,6 +5769,8 @@ if not PkgSkip("PANDATOOL"): # DIRECTORY: pandatool/src/mayaprogs/ # +MAYA_BUILT = False + for VER in MAYAVERSIONS: VNUM = VER[4:] if PkgSkip(VER) or PkgSkip("PANDATOOL") or PkgSkip("EGG"): @@ -5785,6 +5787,8 @@ for VER in MAYAVERSIONS: else: ARCH_OPTS = [] + MAYA_BUILT = True + OPTS=['DIR:pandatool/src/mayaprogs', 'DIR:pandatool/src/maya', 'DIR:pandatool/src/mayaegg', 'BUILDING:MISC', VER] + ARCH_OPTS TargetAdd('mayaeggimport'+VNUM+'_mayaeggimport.obj', opts=OPTS, input='mayaEggImport.cxx') TargetAdd('mayaeggimport'+VNUM+'.mll', input='mayaegg'+VNUM+'_loader.obj') @@ -5822,30 +5826,31 @@ for VER in MAYAVERSIONS: TargetAdd('libmayapview'+VNUM+'.mll', input='libmayaegg'+VNUM+'.lib') TargetAdd('libmayapview'+VNUM+'.mll', input='libmaya'+VNUM+'.lib') TargetAdd('libmayapview'+VNUM+'.mll', input='libp3framework.dll') - if GetTarget() == 'windows': - TargetAdd('libmayapview'+VNUM+'.mll', input=COMMON_EGG2X_LIBS) - else: - TargetAdd('libmayapview'+VNUM+'.mll', input=COMMON_EGG2X_LIBS) + TargetAdd('libmayapview'+VNUM+'.mll', input=COMMON_EGG2X_LIBS) TargetAdd('libmayapview'+VNUM+'.mll', opts=['ADVAPI', VER]+ARCH_OPTS) - TargetAdd('maya2egg'+VNUM+'_mayaToEgg.obj', opts=OPTS, input='mayaToEgg.cxx') - TargetAdd('maya2egg'+VNUM+'_bin.exe', input='maya2egg'+VNUM+'_mayaToEgg.obj') + TargetAdd('mayaprogs'+VNUM+'_eggToMaya.obj', opts=OPTS, input='eggToMaya.cxx') + TargetAdd('mayaprogs'+VNUM+'_mayaToEgg.obj', opts=OPTS, input='mayaToEgg.cxx') + TargetAdd('mayaprogs_mayaConversionServer.obj', opts=OPTS, input='mayaConversionServer.cxx') + + TargetAdd('maya2egg'+VNUM+'_mayaToEggBin.obj', opts=OPTS, input='mayaToEggBin.cxx') + TargetAdd('maya2egg'+VNUM+'_bin.exe', input='mayaprogs'+VNUM+'_eggToMaya.obj') + TargetAdd('maya2egg'+VNUM+'_bin.exe', input='mayaprogs'+VNUM+'_mayaToEgg.obj') + TargetAdd('maya2egg'+VNUM+'_bin.exe', input='mayaprogs_mayaConversionServer.obj') + TargetAdd('maya2egg'+VNUM+'_bin.exe', input='maya2egg'+VNUM+'_mayaToEggBin.obj') TargetAdd('maya2egg'+VNUM+'_bin.exe', input='libmayaegg'+VNUM+'.lib') TargetAdd('maya2egg'+VNUM+'_bin.exe', input='libmaya'+VNUM+'.lib') - if GetTarget() == 'windows': - TargetAdd('maya2egg'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) - else: - TargetAdd('maya2egg'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('maya2egg'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) TargetAdd('maya2egg'+VNUM+'_bin.exe', opts=['ADVAPI', VER]+ARCH_OPTS) - TargetAdd('egg2maya'+VNUM+'_eggToMaya.obj', opts=OPTS, input='eggToMaya.cxx') - TargetAdd('egg2maya'+VNUM+'_bin.exe', input='egg2maya'+VNUM+'_eggToMaya.obj') + TargetAdd('egg2maya'+VNUM+'_eggToMayaBin.obj', opts=OPTS, input='eggToMayaBin.cxx') + TargetAdd('egg2maya'+VNUM+'_bin.exe', input='mayaprogs'+VNUM+'_eggToMaya.obj') + TargetAdd('egg2maya'+VNUM+'_bin.exe', input='mayaprogs'+VNUM+'_mayaToEgg.obj') + TargetAdd('egg2maya'+VNUM+'_bin.exe', input='mayaprogs_mayaConversionServer.obj') + TargetAdd('egg2maya'+VNUM+'_bin.exe', input='egg2maya'+VNUM+'_eggToMayaBin.obj') TargetAdd('egg2maya'+VNUM+'_bin.exe', input='libmayaegg'+VNUM+'.lib') TargetAdd('egg2maya'+VNUM+'_bin.exe', input='libmaya'+VNUM+'.lib') - if GetTarget() == 'windows': - TargetAdd('egg2maya'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) - else: - TargetAdd('egg2maya'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('egg2maya'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS) TargetAdd('egg2maya'+VNUM+'_bin.exe', opts=['ADVAPI', VER]+ARCH_OPTS) TargetAdd('mayasavepview'+VNUM+'_mayaSavePview.obj', opts=OPTS, input='mayaSavePview.cxx') @@ -5864,6 +5869,19 @@ for VER in MAYAVERSIONS: TargetAdd('egg2maya'+VNUM+'.exe', input=COMMON_DTOOL_LIBS) TargetAdd('egg2maya'+VNUM+'.exe', opts=['ADVAPI']+ARCH_OPTS) +if MAYA_BUILT: + TargetAdd('mayaprogs_mayaConversionClient.obj', opts=OPTS, input='mayaConversionClient.cxx') + + TargetAdd('maya2egg_mayaToEggClient.obj', opts=OPTS, input='mayaToEggClient.cxx') + TargetAdd('maya2egg_client.exe', input='mayaprogs_mayaConversionClient.obj') + TargetAdd('maya2egg_client.exe', input='maya2egg_mayaToEggClient.obj') + TargetAdd('maya2egg_client.exe', input=COMMON_EGG2X_LIBS) + + TargetAdd('egg2maya_eggToMayaClient.obj', opts=OPTS, input='eggToMayaClient.cxx') + TargetAdd('egg2maya_client.exe', input='mayaprogs_mayaConversionClient.obj') + TargetAdd('egg2maya_client.exe', input='egg2maya_eggToMayaClient.obj') + TargetAdd('egg2maya_client.exe', input=COMMON_EGG2X_LIBS) + # # DIRECTORY: contrib/src/ai/ # diff --git a/pandatool/src/eggbase/eggReader.cxx b/pandatool/src/eggbase/eggReader.cxx index 16c5e2ad28..4146189af9 100644 --- a/pandatool/src/eggbase/eggReader.cxx +++ b/pandatool/src/eggbase/eggReader.cxx @@ -55,6 +55,7 @@ EggReader() { _got_tex_dirname = false; _got_tex_extension = false; + _exit_on_failure = true; } /** @@ -124,6 +125,16 @@ add_delod_options(double default_delod) { } } +/** + * Sets whether the reader will quit the program upon encountering a fatal error. + * + * If true, the entire program will quit as soon as an egg loading error occurs. + */ +void EggReader:: +set_exit_on_failure(bool exit_on_failure) { + _exit_on_failure = exit_on_failure; +} + /** * Returns this object as an EggReader pointer, if it is in fact an EggReader, * or NULL if it is not. @@ -175,18 +186,27 @@ handle_args(ProgramBase::Args &args) { // Rather than returning false, we simply exit here, so the ProgramBase // won't try to tell the user how to run the program just because we got // a bad egg file. - exit(1); + if (_exit_on_failure) { + exit(1); + } + return false; } } else { if (!file_data.read(std::cin)) { - exit(1); + if (_exit_on_failure) { + exit(1); + } + return false; } } if (_noabs && file_data.original_had_absolute_pathnames()) { nout << filename.get_basename() << " includes absolute pathnames!\n"; - exit(1); + if (_exit_on_failure) { + exit(1); + } + return false; } DSearchPath file_path; @@ -194,7 +214,10 @@ handle_args(ProgramBase::Args &args) { if (_force_complete) { if (!file_data.load_externals(file_path)) { - exit(1); + if (_exit_on_failure) { + exit(1); + } + return false; } } diff --git a/pandatool/src/eggbase/eggReader.h b/pandatool/src/eggbase/eggReader.h index 134799431d..305b58a2ee 100644 --- a/pandatool/src/eggbase/eggReader.h +++ b/pandatool/src/eggbase/eggReader.h @@ -31,6 +31,7 @@ public: void add_texture_options(); void add_delod_options(double default_delod = -1.0); + void set_exit_on_failure(bool exit_on_failure); virtual EggReader *as_reader(); virtual void pre_process_egg_file(); @@ -47,6 +48,7 @@ private: protected: bool _force_complete; + bool _exit_on_failure; private: Filename _tex_dirname; diff --git a/pandatool/src/maya/mayaApi.cxx b/pandatool/src/maya/mayaApi.cxx index d3324279ee..ce24e72d68 100644 --- a/pandatool/src/maya/mayaApi.cxx +++ b/pandatool/src/maya/mayaApi.cxx @@ -31,7 +31,7 @@ using std::string; -MayaApi *MayaApi::_global_api = nullptr; +PT(MayaApi) MayaApi::_global_api = nullptr; // We need this bogus object just to force the application to link with // OpenMayaAnim.lib; otherwise, Maya will complain (when compiled on Windows) @@ -217,6 +217,15 @@ open_api(string program_name, bool view_license, bool revertdir) { return _global_api; } +/** + * Returns true if the global API has been successfully opened and may be used, or + * false if the API has not created yet or if there is some problem. + */ +bool MayaApi:: +is_api_valid() { + return _global_api != nullptr && _global_api->is_valid(); +} + /** * Returns true if the API has been successfully opened and may be used, or * false if there is some problem. diff --git a/pandatool/src/maya/mayaApi.h b/pandatool/src/maya/mayaApi.h index 02cc421955..f4e19e45da 100644 --- a/pandatool/src/maya/mayaApi.h +++ b/pandatool/src/maya/mayaApi.h @@ -37,6 +37,7 @@ public: ~MayaApi(); static PT(MayaApi) open_api(std::string program_name = "", bool view_license = false, bool revertdir = true); + static bool is_api_valid(); bool is_valid() const; bool read(const Filename &filename); @@ -51,7 +52,7 @@ private: bool _is_valid; bool _plug_in; Filename _cwd; - static MayaApi *_global_api; + static PT(MayaApi) _global_api; }; #endif diff --git a/pandatool/src/mayaprogs/eggToMaya.cxx b/pandatool/src/mayaprogs/eggToMaya.cxx index da944e58e6..36be9362db 100644 --- a/pandatool/src/mayaprogs/eggToMaya.cxx +++ b/pandatool/src/mayaprogs/eggToMaya.cxx @@ -14,6 +14,7 @@ #include "eggToMaya.h" #include "mayaEggLoader.h" #include "mayaApi.h" +#include "mayaConversionServer.h" // We must define this to prevent Maya from doubly-declaring its MApiVersion // string in this file as well as in libmayaegg. @@ -56,14 +57,40 @@ EggToMaya() : "respect vertex and polygon normals.", &EggToMaya::dispatch_none, &_respect_normals); + add_option("server", "", 0, + "Runs the Maya model conversion server. This server can be used in tandem " + "with the egg2maya_client and maya2egg_client utilities to batch convert " + "both Maya and Panda3D model files.", + &EggToMaya::dispatch_none, &_run_server); + // Maya files always store centimeters. _output_units = DU_centimeters; } /** - * + * Attempts to create the global Maya API. + * Exits the program if unsuccessful. */ -void EggToMaya:: +PT(MayaApi) EggToMaya:: +open_api() { + if (!MayaApi::is_api_valid()) { + nout << "Initializing Maya...\n"; + } + + PT(MayaApi) api = MayaApi::open_api(_program_name, true, true); + + if (!api || !api->is_valid()) { + nout << "Unable to initialize Maya.\n"; + exit(1); + } + + return api; +} + +/** + * Returns true if the model has been successfully converted. + */ +bool EggToMaya:: run() { if (!_convert_anim && !_convert_model) { _convert_model = true; @@ -73,18 +100,13 @@ run() { // since Maya now has a nasty habit of changing the current directory. _output_filename.make_absolute(); - nout << "Initializing Maya.\n"; - PT(MayaApi) maya = MayaApi::open_api(_program_name); - if (!maya->is_valid()) { - nout << "Unable to initialize Maya.\n"; - exit(1); - } + PT(MayaApi) maya = open_api(); MStatus status; status = MFileIO::newFile(true); if (!status) { status.perror("Could not initialize file"); - exit(1); + return false; } // [gjeon] since maya's internal unit is fixed to cm and when we can't @@ -101,12 +123,12 @@ run() { // Now convert the data. if (!MayaLoadEggData(_data, true, _convert_model, _convert_anim, _respect_normals)) { nout << "Unable to convert egg file.\n"; - exit(1); + return false; } if (!maya->write(_output_filename)) { status.perror("Could not save file"); - exit(1); + return false; } /* @@ -119,14 +141,29 @@ run() { status = MFileIO::saveAs(os_specific.c_str(), file_type); if (!status) { status.perror("Could not save file"); - exit(1); + return false; } */ + + return true; } -int main(int argc, char *argv[]) { - EggToMaya prog; - prog.parse_command_line(argc, argv); - prog.run(); - return 0; +/** + * Processes the arguments parsed by the program. + * + * If the server flag is specified, the Maya conversion server is started + * up rather than the usual conversion utility functionality. + */ +bool EggToMaya:: +handle_args(ProgramBase::Args &args) { + if (_run_server) { + open_api(); + + MayaConversionServer server; + server.listen(); + exit(0); + return true; + } + + return EggToSomething::handle_args(args); } diff --git a/pandatool/src/mayaprogs/eggToMaya.h b/pandatool/src/mayaprogs/eggToMaya.h index 3a103ffd2e..d18dc928d1 100644 --- a/pandatool/src/mayaprogs/eggToMaya.h +++ b/pandatool/src/mayaprogs/eggToMaya.h @@ -17,6 +17,9 @@ #include "pandatoolbase.h" #include "eggToSomething.h" +#include "mayaApi.h" + +#include "programBase.h" /** * A program to read an egg file and write a maya file. @@ -25,12 +28,17 @@ class EggToMaya : public EggToSomething { public: EggToMaya(); - void run(); + bool run(); private: + virtual bool handle_args(ProgramBase::Args &args); + + PT(MayaApi) open_api(); + bool _convert_anim; bool _convert_model; bool _respect_normals; + bool _run_server; }; #endif diff --git a/pandatool/src/mayaprogs/eggToMayaBin.cxx b/pandatool/src/mayaprogs/eggToMayaBin.cxx new file mode 100644 index 0000000000..e388255aac --- /dev/null +++ b/pandatool/src/mayaprogs/eggToMayaBin.cxx @@ -0,0 +1,28 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file eggToMayaBin.cxx + * @author Derzsi Dániel + * @date 2020-10-01 + */ + +#include "eggToMaya.h" + +/** + * Entrypoint for egg2maya. + */ +int main(int argc, char *argv[]) { + EggToMaya prog; + prog.parse_command_line(argc, argv); + + if (!prog.run()) { + return 1; + } + + return 0; +} diff --git a/pandatool/src/mayaprogs/eggToMayaClient.cxx b/pandatool/src/mayaprogs/eggToMayaClient.cxx new file mode 100644 index 0000000000..e3f95155f4 --- /dev/null +++ b/pandatool/src/mayaprogs/eggToMayaClient.cxx @@ -0,0 +1,22 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file eggToMayaClient.cxx + * @author Derzsi Dániel + * @date 2020-10-01 + */ + +#include "mayaConversionClient.h" + +/** + * Entrypoint for egg2maya_client. + */ +int main(int argc, char *argv[]) { + MayaConversionClient client; + return client.main(argc, argv, MayaConversionServer::ConversionType::CT_egg_to_maya); +} diff --git a/pandatool/src/mayaprogs/mayaConversionClient.cxx b/pandatool/src/mayaprogs/mayaConversionClient.cxx new file mode 100644 index 0000000000..900b7ca0d4 --- /dev/null +++ b/pandatool/src/mayaprogs/mayaConversionClient.cxx @@ -0,0 +1,192 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file mayaConversionClient.cxx + * @author Derzsi Dániel + * @date 2020-10-01 + */ + +#include "mayaConversionClient.h" +#include "mayaConversionServer.h" +#include "datagramIterator.h" + +/** + * Initializes the Maya conversion client. + */ +MayaConversionClient:: +MayaConversionClient() +{ + _qManager = new QueuedConnectionManager(); + _qReader = new QueuedConnectionReader(_qManager, 0); + _cWriter = new ConnectionWriter(_qManager, 0); +} + +/** + * Cleans up the Maya conversison client's pending connections. + */ +MayaConversionClient:: +~MayaConversionClient() { + close(); +} + +/** + * Attempts to connect to a Maya conversion server. + * If a connection is already active, it will be removed. + * + * Returns true if the connection was created successfully. + */ +bool MayaConversionClient:: +connect(NetAddress server) { + if (_conn) { + // Remove this connection from the readers list + _qReader->remove_connection(_conn); + _conn = nullptr; + } + + // Attempt to open a connection + _conn = _qManager->open_TCP_client_connection(server, 0); + + if (!_conn || _conn.is_null()) { + // This connection could not be opened + return false; + } + + // Add this connection to the readers list + _qReader->add_connection(_conn); + return true; +} + +/** + * Sends a conversion workload to the Maya conversion server. + * It will be processed as soon as the server's queue is empty. + * + * Specify the working directory in where the models are located, + * the command line arguments, and the conversion type. + * + * Returns true if the workload has been successfully sent. + */ +bool MayaConversionClient:: +queue(Filename working_directory, int argc, char *argv[], MayaConversionServer::ConversionType conversion_type) { + if (!_conn) { + return false; + } + + std::string s_cwd = (std::string) working_directory.to_os_specific(); + NetDatagram datagram; + + // First part of the datagram is the argc + datagram.add_uint8(argc); + + // Add the rest of the arguments as strings to the datagram + for (int i = 0; i < argc; i++) { + datagram.add_string(argv[i]); + } + + // Add the current working dir as a string to the datagram + datagram.add_string(s_cwd); + + // Lastly, add the conversion type + datagram.add_uint8(conversion_type); + + // Send the conversion request + if (!_cWriter->send(datagram, _conn) || !_conn->flush()) { + nout << "Failed to send workload to server process.\n"; + return false; + } + + // Wait for a response + while (_conn->get_socket()->Active() && !_qReader->data_available()) { + _qReader->poll(); + } + + if (!_qReader->data_available()) { + // No response has been given by the server. + nout << "No response has been given by the conversion server.\n"; + return false; + } + + NetDatagram response; + + // Let's read the response now! + if (!_qReader->get_data(response)) { + nout << "The conversion response could not be read.\n"; + return false; + } + + // Iterate through the response. + DatagramIterator response_data(response); + + // Read the first and only argument. + // Did our conversion request succeed? + bool converted = response_data.get_bool(); + + if (!converted) { + nout << "The server reported that the conversion has failed.\n" + << "Please check the server logs for further information.\n"; + return false; + } + + return true; +} + +/** + * Closes the current connection to the Maya conversion server, + * waiting for all currently queued requests to finish. + * + * Does nothing if the connection has not been made yet. + */ +void MayaConversionClient:: +close() { + if (!_conn) { + return; + } + + while (true) { + _qReader->data_available(); + + if (_qManager->reset_connection_available()) { + PT(Connection) connection; + + if (_qManager->get_reset_connection(connection)) { + _qManager->close_connection(_conn); + _conn = nullptr; + return; + } + } + + Thread::sleep(0.1); + } +} + +/** + * The entrypoint to this Maya conversion client. + * + * Connects to the default server at port 4242, queues + * one conversion request and waits for its completion. + */ +int MayaConversionClient:: +main(int argc, char *argv[], MayaConversionServer::ConversionType conversion_type) { + NetAddress server; + + // We assume the server is local and on port 4242 + server.set_host("localhost", 4242); + + if (!connect(server)) { + nout << "Failed to open port to server process.\n" + << "Make sure maya2egg -server or egg2maya -server is running on localhost!\n"; + return 1; + } + + if (!queue(ExecutionEnvironment::get_cwd(), argc, argv, conversion_type)) { + return 1; + } + + nout << "Conversion successful!\n"; + close(); + return 0; +} diff --git a/pandatool/src/mayaprogs/mayaConversionClient.h b/pandatool/src/mayaprogs/mayaConversionClient.h new file mode 100644 index 0000000000..bd524a07eb --- /dev/null +++ b/pandatool/src/mayaprogs/mayaConversionClient.h @@ -0,0 +1,53 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file mayaConversionClient.h + * @author Derzsi Dániel + * @date 2020-10-01 + */ + +#ifndef MAYACONVERSIONCLIENT_H +#define MAYACONVERSIONCLIENT_H + +#include "queuedConnectionManager.h" +#include "queuedConnectionReader.h" +#include "connectionWriter.h" +#include "netAddress.h" +#include "filename.h" +#include "mayaConversionServer.h" + +/** + * The Maya conversion client sends batch conversion + * requests to the Maya conversion server. + * + * These utilities rely on the Maya conversion server. + * Use egg2maya or maya2egg to boot up the Maya conversion server. + * Use egg2maya_client and maya2egg_client as a replacement for + * + * Very useful in case you need to batch convert models. + * The regular utilities can only convert one model at a time. +*/ +class MayaConversionClient { +public: + MayaConversionClient(); + ~MayaConversionClient(); + + bool connect(NetAddress server); + bool queue(Filename working_directory, int argc, char *argv[], MayaConversionServer::ConversionType conversion_type); + void close(); + + int main(int argc, char *argv[], MayaConversionServer::ConversionType conversion_type); + +private: + QueuedConnectionManager *_qManager; + QueuedConnectionReader *_qReader; + ConnectionWriter *_cWriter; + PT(Connection) _conn; +}; + +#endif diff --git a/pandatool/src/mayaprogs/mayaConversionServer.cxx b/pandatool/src/mayaprogs/mayaConversionServer.cxx new file mode 100644 index 0000000000..0d6b7e8056 --- /dev/null +++ b/pandatool/src/mayaprogs/mayaConversionServer.cxx @@ -0,0 +1,234 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file mayaConversionServer.cxx + * @author cbrunner + * @author Derzsi Dániel + * @date 2020-10-01 + */ + +#ifdef _WIN32 +#include // for chdir +#endif + +#include "mayaConversionServer.h" +#include "mayaToEgg.h" +#include "eggToMaya.h" +#include "virtualFileSystem.h" +#include "filename.h" + +/** + * Initializes the Maya conversion server. + */ +MayaConversionServer:: +MayaConversionServer() { + _qManager = new QueuedConnectionManager(); + _qListener = new QueuedConnectionListener(_qManager, 0); + _qReader = new QueuedConnectionReader(_qManager, 0); + _cWriter = new ConnectionWriter(_qManager, 0); +} + +/** + * Cleans up the connection managers for the Maya conversion server. + */ +MayaConversionServer:: +~MayaConversionServer() { + delete _qManager; + delete _qReader; + delete _qListener; + delete _cWriter; +} + +/** + * Checks for any network activity and handles it, if appropriate, and then + * returns. This must be called periodically + */ +void MayaConversionServer:: +poll() { + // Listen for new connections + _qListener->poll(); + + // If we have a new connection from a client create a new connection pointer + // and add it to the reader list + if (_qListener->new_connection_available()) { + PT(Connection) rendezvous; + PT(Connection) connection; + NetAddress address; + + if (_qListener->get_new_connection(rendezvous, address, connection)) { + _qReader->add_connection(connection); + _clients.insert(connection); + } + } + + // Check for reset clients + if (_qManager->reset_connection_available()) { + PT(Connection) connection; + + if (_qManager->get_reset_connection(connection)) { + _clients.erase(connection); + _qManager->close_connection(connection); + } + } + + // Poll the readers (created above) and if they have data process it + _qReader->poll(); + + if (_qReader->data_available()) { + // Grab the incoming data and unpack it + NetDatagram datagram; + + if (_qReader->get_data(datagram)) { + DatagramIterator data(datagram); + + // First data should be the "argc" (argument count) from the client + int argc = data.get_uint8(); + + // Now we have to get clever because the rest of the data comes as + // strings and parse_command_line() expects arguments of the standard + // argc, argv*[] variety. First, we need a string vector to hold all + // the strings from the datagram. We also need a char * array to keep + // track of all the pointers we're gonna malloc. Needed later for + // cleanup. + vector_string vargv; + std::vector buffers; + + // Get the strings from the datagram and put them into the string vector + for (int i = 0; i < argc; i++) { + vargv.push_back(data.get_string().c_str()); + } + + // Last string is the current directory the client was run from. Not + // part of the argument list, but we still need it + std::string cwd = data.get_string(); + + // We allocate some memory to hold the pointers to the pointers we're + // going to pass in to parse_command_line(). + char **cargv = (char**) malloc(sizeof(char**) * argc); + + // Loop through the string arguments we got from the datagram and + // convert them to const char *'s. parse_command_line() expects char + // *'s, so we have to copy these const versions into fresh char *, since + // there is no casting from const char * to char *. + for (int i = 0; i < argc; i++) { + // string to const char * + const char *cptr = vargv[i].c_str(); + // memory allocated for a new char * of size of the string + char *buffer = (char *) malloc(vargv[i].capacity() + 1); + + // Copy the const char * to the char * + strcpy(buffer, cptr); + // put this into the arry we defined above. This is what will + // eventually be passed to parse_command_line() + cargv[i] = buffer; + // keep track of the pointers to the allocated memory for cleanup + // later + buffers.push_back(buffer); + } + + // Change to the client's current dir +#ifdef _WIN64 + _chdir(cwd.c_str()); +#else + chdir(cwd.c_str()); +#endif + + // Change the VirtualFileSystem's current dir as well + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + vfs->chdir(Filename::from_os_specific(cwd)); + + // Next, we'll need to read the conversion type. + // Are we converting from egg to maya or from maya to egg? + int conversion_type = data.get_uint8(); + bool converted = false; + + switch (conversion_type) { + case ConversionType::CT_maya_to_egg: + { + MayaToEgg egg; + + // Pass in the 'new' argc and argv we got from the client + if (egg.parse_command_line(argc, cargv, false) == ProgramBase::ExitCode::EC_not_exited) { + // Actually run the damn thing + converted = egg.run(); + } + + break; + } + case ConversionType::CT_egg_to_maya: + { + EggToMaya maya; + maya.set_exit_on_failure(false); + + // Pass in the 'new' argc and argv we got from the client + if (maya.parse_command_line(argc, cargv, false) == ProgramBase::ExitCode::EC_not_exited) { + // Actually run the damn thing + converted = maya.run(); + } + + break; + } + } + + // Let's send the result back to the client + NetDatagram response; + + // The first and only part of the response is the success value + response.add_bool(converted); + + // Send the response + if (!_cWriter->send(response, datagram.get_connection())) { + // Looks like we couldn't send the response + nout << "Could not send response to the client.\n"; + } + + std::cout.flush(); + + // Cleanup first, release the string vector + vargv.clear(); + // No, iterate through the char * vector and cleanup the malloc'd + // pointers + std::vector::iterator vi; + for ( vi = buffers.begin() ; vi != buffers.end(); vi++) { + free(*vi); + } + // Clean up the malloc'd pointer pointer + free(cargv); + } // qReader->get_data + + Clients::iterator ci; + for (ci = _clients.begin(); ci != _clients.end(); ++ci) { + _qManager->close_connection(*ci); + } + } // qReader->data_available +} // poll + +/** + * Blocks the current thread and listens to conversion requests. + */ +void MayaConversionServer:: +listen() { + // Open a rendezvous port for receiving new connections from the client + PT(Connection) rend = _qManager->open_TCP_server_rendezvous(4242, 100); + + if (rend.is_null()) { + nout << "Port opening failed!\n"; + return; + } + + nout << "Server opened on port 4242, waiting for requests...\n"; + + // Add this connection to the listeners list + _qListener->add_connection(rend); + + // Main loop. Keep polling for connections, but don't eat up all the CPU. + while (true) { + this->poll(); + Thread::force_yield(); + } +} diff --git a/pandatool/src/mayaprogs/mayaConversionServer.h b/pandatool/src/mayaprogs/mayaConversionServer.h new file mode 100644 index 0000000000..10212676a8 --- /dev/null +++ b/pandatool/src/mayaprogs/mayaConversionServer.h @@ -0,0 +1,58 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file mayaConversionServer.h + * @author Derzsi Dániel + * @date 2020-10-01 + */ + +#ifndef MAYACONVERSIONSERVER_H +#define MAYACONVERSIONSERVER_H + +#include "queuedConnectionManager.h" +#include "queuedConnectionListener.h" +#include "queuedConnectionReader.h" +#include "connectionWriter.h" + +/** + * The Maya conversion server listens for incoming requests to + * handle maya-to-egg and egg-to-maya model conversions. + * + * This server listens to port 4242 after being started with + * egg2maya -server or maya2egg -server. + * + * Use egg2maya_client and maya2egg_client as a replacement for + * the egg2maya and maya2egg utilities after starting the server. + * + * Very useful in case you need to batch convert models. + * The regular utilities can only convert one model at a time. +*/ +class MayaConversionServer { +public: + enum ConversionType { + CT_maya_to_egg = 0, + CT_egg_to_maya = 1 + }; + + MayaConversionServer(); + ~MayaConversionServer(); + + void listen(); + void poll(); + +protected: + typedef pset< PT(Connection) > Clients; + Clients _clients; + + QueuedConnectionManager *_qManager; + QueuedConnectionListener *_qListener; + QueuedConnectionReader *_qReader; + ConnectionWriter *_cWriter; +}; + +#endif diff --git a/pandatool/src/mayaprogs/mayaToEgg.cxx b/pandatool/src/mayaprogs/mayaToEgg.cxx index 1219f5389a..10a09d1798 100644 --- a/pandatool/src/mayaprogs/mayaToEgg.cxx +++ b/pandatool/src/mayaprogs/mayaToEgg.cxx @@ -43,6 +43,7 @@ #include "config_mayaegg.h" #include "config_maya.h" // for maya_cat #include "globPattern.h" +#include "mayaConversionServer.h" /** * @@ -124,6 +125,12 @@ MayaToEgg() : "Legacy option. Same as -pc.", &MayaToEgg::dispatch_filename, &_legacy_copytex, &_legacy_copytex_dir); + add_option("server", "", 0, + "Runs the Maya model conversion server. This server can be used in tandem " + "with the egg2maya_client and maya2egg_client utilities to batch convert " + "both Maya and Panda3D model files.", + &MayaToEgg::dispatch_none, &_run_server); + add_option ("trans", "type", 0, "Specifies which transforms in the Maya file should be converted to " @@ -203,7 +210,34 @@ MayaToEgg() : /** * */ -void MayaToEgg:: +MayaToEgg:: +~MayaToEgg() { +} + +/** + * Attempts to create the global Maya API. + * Exits the program if unsuccessful. + */ +PT(MayaApi) MayaToEgg:: +open_api() { + if (!MayaApi::is_api_valid()) { + nout << "Initializing Maya...\n"; + } + + PT(MayaApi) api = MayaApi::open_api(_program_name, true, true); + + if (!api || !api->is_valid()) { + nout << "Unable to initialize Maya.\n"; + exit(1); + } + + return api; +} + +/** + * Returns true if the model has been successfully converted. + */ +bool MayaToEgg:: run() { // Set the verbose level by using Notify. if (_verbose >= 3) { @@ -229,14 +263,8 @@ run() { _path_replace->_path_directory.make_absolute(); } - nout << "Initializing Maya.\n"; + open_api(); MayaToEggConverter converter(_program_name); - // reverting directories is really not needed for maya2egg. It's more - // needed for mayaeggloader and such - if (!converter.open_api(false)) { - nout << "Unable to initialize Maya.\n"; - exit(1); - } // Copy in the command-line parameters. converter._polygon_output = _polygon_output; @@ -299,7 +327,7 @@ run() { if (!converter.convert_file(_input_filename)) { nout << "Errors in conversion.\n"; - exit(1); + return false; } // Use the standard Maya units, if the user didn't specify otherwise. This @@ -310,8 +338,10 @@ run() { _input_units = converter.get_input_units(); } + // Write output file write_egg_file(); - nout << "\n"; + close_output(); + return true; } /** @@ -332,9 +362,22 @@ dispatch_transform_type(const std::string &opt, const std::string &arg, void *va return true; } -int main(int argc, char *argv[]) { - MayaToEgg prog; - prog.parse_command_line(argc, argv); - prog.run(); - return 0; +/** + * Processes the arguments parsed by the program. + * + * If the server flag is specified, the Maya conversion server is started + * up rather than the usual conversion utility functionality. + */ +bool MayaToEgg:: +handle_args(ProgramBase::Args &args) { + if (_run_server) { + open_api(); + + MayaConversionServer server; + server.listen(); + exit(0); + return true; + } + + return SomethingToEgg::handle_args(args); } diff --git a/pandatool/src/mayaprogs/mayaToEgg.h b/pandatool/src/mayaprogs/mayaToEgg.h index a6ec0453f2..237266f04e 100644 --- a/pandatool/src/mayaprogs/mayaToEgg.h +++ b/pandatool/src/mayaprogs/mayaToEgg.h @@ -17,6 +17,9 @@ #include "pandatoolbase.h" #include "somethingToEgg.h" #include "mayaToEggConverter.h" +#include "mayaApi.h" + +#include "programBase.h" /** * @@ -24,11 +27,15 @@ class MayaToEgg : public SomethingToEgg { public: MayaToEgg(); + ~MayaToEgg(); - void run(); + bool run(); protected: static bool dispatch_transform_type(const std::string &opt, const std::string &arg, void *var); + virtual bool handle_args(ProgramBase::Args &args); + + PT(MayaApi) open_api(); int _verbose; bool _polygon_output; @@ -49,6 +56,9 @@ protected: vector_string _excludes; vector_string _ignore_sliders; vector_string _force_joints; + +public: + bool _run_server; }; #endif diff --git a/pandatool/src/mayaprogs/mayaToEggBin.cxx b/pandatool/src/mayaprogs/mayaToEggBin.cxx new file mode 100644 index 0000000000..d65bdcae24 --- /dev/null +++ b/pandatool/src/mayaprogs/mayaToEggBin.cxx @@ -0,0 +1,28 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file mayaToEggBin.cxx + * @author Derzsi Dániel + * @date 2020-10-01 + */ + +#include "mayaToEgg.h" + +/** + * Entrypoint for maya2egg. + */ +int main(int argc, char *argv[]) { + MayaToEgg prog; + prog.parse_command_line(argc, argv); + + if (!prog.run()) { + return 1; + } + + return 0; +} diff --git a/pandatool/src/mayaprogs/mayaToEggClient.cxx b/pandatool/src/mayaprogs/mayaToEggClient.cxx new file mode 100644 index 0000000000..066b41ae31 --- /dev/null +++ b/pandatool/src/mayaprogs/mayaToEggClient.cxx @@ -0,0 +1,22 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file mayaToEggClient.cxx + * @author Derzsi Dániel + * @date 2020-10-01 + */ + +#include "mayaConversionClient.h" + +/** + * Entrypoint for maya2egg_client. + */ +int main(int argc, char *argv[]) { + MayaConversionClient client; + return client.main(argc, argv, MayaConversionServer::ConversionType::CT_maya_to_egg); +} diff --git a/pandatool/src/mayaprogs/mayaToEgg_client.cxx b/pandatool/src/mayaprogs/mayaToEgg_client.cxx deleted file mode 100644 index a92e6195ec..0000000000 --- a/pandatool/src/mayaprogs/mayaToEgg_client.cxx +++ /dev/null @@ -1,73 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file mayaToEgg_client.cxx - * @author cbrunner - * @date 2009-11-09 - */ - -#include "mayaToEgg_client.h" - -/** - * - */ -MayaToEggClient:: -MayaToEggClient() : - SomethingToEgg("Maya", ".mb") -{ - qManager = new QueuedConnectionManager(); - qReader = new QueuedConnectionReader(qManager, 0); - cWriter = new ConnectionWriter(qManager, 0); - // We assume the server is local and on port 4242 - server.set_host("localhost", 4242); -} - -int main(int argc, char *argv[]) { - MayaToEggClient prog; - // Open a connection to the server process - PT(Connection) con = prog.qManager->open_TCP_client_connection(prog.server,0); - if (con.is_null()) { - nout << "Failed to open port to server process.\nMake sure maya2egg_server is running on localhost\n"; - exit(1); - } - - // Add this connection to the readers list - prog.qReader->add_connection(con); - - // Get the current working directory and make sure it's a string - Filename cwd = ExecutionEnvironment::get_cwd(); - std::string s_cwd = (std::string)cwd.to_os_specific(); - NetDatagram datagram; - - // First part of the datagram is the argc - datagram.add_uint8(argc); - - // Add the rest of the arguments as strings to the datagram - int i; - for (i = 0; i < argc; i++) { - datagram.add_string(argv[i]); - } - - // Lastly, add the current working dir as a string to the datagram - datagram.add_string(s_cwd); - - // Send it and close the connection - prog.cWriter->send(datagram, con); - con->flush(); - while (true) { - prog.qReader->data_available(); - if (prog.qManager->reset_connection_available()) { - PT(Connection) connection; - if (prog.qManager->get_reset_connection(connection)) { - prog.qManager->close_connection(con); - return 0; - } - } - Thread::sleep(0.1); - } -} diff --git a/pandatool/src/mayaprogs/mayaToEgg_client.h b/pandatool/src/mayaprogs/mayaToEgg_client.h deleted file mode 100644 index f3c7192467..0000000000 --- a/pandatool/src/mayaprogs/mayaToEgg_client.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file mayaToEgg_client.h - * @author cbrunner - * @date 2009-11-09 - */ - -#ifndef MAYATOEGGCLIENT_H -#define MAYATOEGGCLIENT_H - -#include "somethingToEgg.h" -#include "queuedConnectionManager.h" -#include "queuedConnectionReader.h" -#include "connectionWriter.h" - -/** - * - */ -class MayaToEggClient : public SomethingToEgg { -public: - MayaToEggClient(); - - QueuedConnectionManager *qManager; - QueuedConnectionReader *qReader; - ConnectionWriter *cWriter; - NetAddress server; -}; - -#endif diff --git a/pandatool/src/mayaprogs/mayaToEgg_server.cxx b/pandatool/src/mayaprogs/mayaToEgg_server.cxx deleted file mode 100644 index 11b33a20d4..0000000000 --- a/pandatool/src/mayaprogs/mayaToEgg_server.cxx +++ /dev/null @@ -1,474 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file mayaToEgg_server.cxx - * @author cbrunner - * @date 2009-11-09 - */ - -#ifdef _WIN32 -#include // for chdir -#endif -#include "mayaToEgg_server.h" -#include "mayaToEggConverter.h" -#include "config_mayaegg.h" -#include "config_maya.h" // for maya_cat -#include "globPattern.h" - -/** - * - */ -MayaToEggServer:: -MayaToEggServer() : - SomethingToEgg("Maya", ".mb") -{ - add_path_replace_options(); - add_path_store_options(); - add_animation_options(); - add_units_options(); - add_normals_options(); - add_transform_options(); - - set_program_brief("convert Maya model files to .egg"); - set_program_description - ("This program converts Maya model files to egg. Static and animatable " - "models can be converted, with polygon or NURBS output. Animation tables " - "can also be generated to apply to an animatable model."); - - add_option - ("p", "", 0, - "Generate polygon output only. Tesselate all NURBS surfaces to " - "polygons via the built-in Maya tesselator. The tesselation will " - "be based on the tolerance factor given by -ptol.", - &MayaToEggServer::dispatch_none, &_polygon_output); - - add_option - ("ptol", "tolerance", 0, - "Specify the fit tolerance for Maya polygon tesselation. The smaller " - "the number, the more polygons will be generated. The default is " - "0.01.", - &MayaToEggServer::dispatch_double, nullptr, &_polygon_tolerance); - - add_option - ("bface", "", 0, - "Respect the Maya \"double sided\" rendering flag to indicate whether " - "polygons should be double-sided or single-sided. Since this flag " - "is set to double-sided by default in Maya, it is often better to " - "ignore this flag (unless your modelers are diligent in turning it " - "off where it is not desired). If this flag is not specified, the " - "default is to treat all polygons as single-sided, unless an " - "egg object type of \"double-sided\" is set.", - &MayaToEggServer::dispatch_none, &_respect_maya_double_sided); - - add_option - ("suppress-vcolor", "", 0, - "Ignore vertex color for geometry that has a texture applied. " - "(This is the way Maya normally renders internally.) The egg flag " - "'vertex-color' may be applied to a particular model to override " - "this setting locally.", - &MayaToEggServer::dispatch_none, &_suppress_vertex_color); - - add_option - ("keep-uvs", "", 0, - "Convert all UV sets on all vertices, even those that do not appear " - "to be referenced by any textures.", - &MayaToEggServer::dispatch_none, &_keep_all_uvsets); - - add_option - ("round-uvs", "", 0, - "round up uv coordinates to the nearest 1/100th. i.e. -0.001 becomes" - "0.0; 0.444 becomes 0.44; 0.778 becomes 0.78.", - &MayaToEggServer::dispatch_none, &_round_uvs); - - add_option - ("trans", "type", 0, - "Specifies which transforms in the Maya file should be converted to " - "transforms in the egg file. The option may be one of all, model, " - "dcs, or none. The default is model, which means only transforms on " - "nodes that have the model flag or the dcs flag are preserved.", - &MayaToEggServer::dispatch_transform_type, nullptr, &_transform_type); - - add_option - ("subroot", "name", 0, - "Specifies that only a subroot of the geometry in the Maya file should " - "be converted; specifically, the geometry under the node or nodes whose " - "name matches the parameter (which may include globbing characters " - "like * or ?). This parameter may be repeated multiple times to name " - "multiple roots. If it is omitted altogether, the entire file is " - "converted.", - &MayaToEggServer::dispatch_vector_string, nullptr, &_subroots); - - add_option - ("subset", "name", 0, - "Specifies that only a subset of the geometry in the Maya file should " - "be converted; specifically, the geometry under the node or nodes whose " - "name matches the parameter (which may include globbing characters " - "like * or ?). This parameter may be repeated multiple times to name " - "multiple roots. If it is omitted altogether, the entire file is " - "converted.", - &MayaToEggServer::dispatch_vector_string, nullptr, &_subsets); - - add_option - ("exclude", "name", 0, - "Specifies that a subset of the geometry in the Maya file should " - "not be converted; specifically, the geometry under the node or nodes whose " - "name matches the parameter (which may include globbing characters " - "like * or ?). This parameter may be repeated multiple times to name " - "multiple roots.", - &MayaToEggServer::dispatch_vector_string, nullptr, &_excludes); - - add_option - ("ignore-slider", "name", 0, - "Specifies the name of a slider (blend shape deformer) that maya2egg " - "should not process. The slider will not be touched during conversion " - "and it will not become a part of the animation. This " - "parameter may including globbing characters, and it may be repeated " - "as needed.", - &MayaToEggServer::dispatch_vector_string, nullptr, &_ignore_sliders); - - add_option - ("force-joint", "name", 0, - "Specifies the name of a DAG node that maya2egg " - "should treat as a joint, even if it does not appear to be a Maya joint " - "and does not appear to be animated.", - &MayaToEggServer::dispatch_vector_string, nullptr, &_force_joints); - - add_option - ("v", "", 0, - "Increase verbosity. More v's means more verbose.", - &MayaToEggServer::dispatch_count, nullptr, &_verbose); - - add_option - ("legacy-shaders", "", 0, - "Use this flag to turn off modern (Phong) shader generation" - "and treat all shaders as if they were Lamberts (legacy).", - &MayaToEggServer::dispatch_none, &_legacy_shader); - - // Unfortunately, the Maya API doesn't allow us to differentiate between - // relative and absolute pathnames--everything comes out as an absolute - // pathname, even if it is stored in the Maya file as a relative path. So - // we can't support -noabs. - remove_option("noabs"); - - _verbose = 0; - _polygon_tolerance = 0.01; - _transform_type = MayaToEggConverter::TT_model; - _got_tbnauto = true; - qManager = new QueuedConnectionManager(); - qListener = new QueuedConnectionListener(qManager, 0); - qReader = new QueuedConnectionReader(qManager, 0); - cWriter = new ConnectionWriter(qManager, 0); - dummy = new MayaToEggConverter(); - - nout << "Initializing Maya...\n"; - if (!dummy->open_api()) { - nout << "Unable to initialize Maya.\n"; - exit(1); - } -} -/** - * - */ -MayaToEggServer:: -~MayaToEggServer() { - delete qManager; - delete qReader; - delete qListener; - delete cWriter; - delete dummy; -} - -/** - * - */ -void MayaToEggServer:: -run() { - // Make sure we have good clean data to start with - _data = new EggData(); - - // Set the verbose level by using Notify. - if (_verbose >= 3) { - maya_cat->set_severity(NS_spam); - mayaegg_cat->set_severity(NS_spam); - } else if (_verbose >= 2) { - maya_cat->set_severity(NS_debug); - mayaegg_cat->set_severity(NS_debug); - } else if (_verbose >= 1) { - maya_cat->set_severity(NS_info); - mayaegg_cat->set_severity(NS_info); - } - - // Let's convert the output file to a full path before we initialize Maya, - // since Maya now has a nasty habit of changing the current directory. - if (_got_output_filename) { - _output_filename.make_absolute(); - _path_replace->_path_directory.make_absolute(); - } - - MayaToEggConverter converter(_program_name); - - // Copy in the command-line parameters. - converter._polygon_output = _polygon_output; - converter._polygon_tolerance = _polygon_tolerance; - converter._respect_maya_double_sided = _respect_maya_double_sided; - converter._always_show_vertex_color = !_suppress_vertex_color; - converter._keep_all_uvsets = _keep_all_uvsets; - converter._round_uvs = _round_uvs; - converter._transform_type = _transform_type; - converter._legacy_shader = _legacy_shader; - - vector_string::const_iterator si; - if (!_subroots.empty()) { - converter.clear_subroots(); - for (si = _subroots.begin(); si != _subroots.end(); ++si) { - converter.add_subroot(GlobPattern(*si)); - } - } - - if (!_subsets.empty()) { - converter.clear_subsets(); - for (si = _subsets.begin(); si != _subsets.end(); ++si) { - converter.add_subset(GlobPattern(*si)); - } - } - - if (!_excludes.empty()) { - converter.clear_excludes(); - for (si = _excludes.begin(); si != _excludes.end(); ++si) { - converter.add_exclude(GlobPattern(*si)); - } - } - - if (!_ignore_sliders.empty()) { - converter.clear_ignore_sliders(); - for (si = _ignore_sliders.begin(); si != _ignore_sliders.end(); ++si) { - converter.add_ignore_slider(GlobPattern(*si)); - } - } - - if (!_force_joints.empty()) { - converter.clear_force_joints(); - for (si = _force_joints.begin(); si != _force_joints.end(); ++si) { - converter.add_force_joint(GlobPattern(*si)); - } - } - - // Copy in the path and animation parameters. - apply_parameters(converter); - - // Set the coordinate system to match Maya's. - if (!_got_coordinate_system) { - _coordinate_system = converter._maya->get_coordinate_system(); - } - _data->set_coordinate_system(_coordinate_system); - - converter.set_egg_data(_data); - - if (!converter.convert_file(_input_filename)) { - nout << "Errors in conversion.\n"; - exit(1); - } - - // Use the standard Maya units, if the user didn't specify otherwise. This - // always returns centimeters, which is the way all Maya files are stored - // internally (and is the units returned by all of the API functions called - // here). - if (_input_units == DU_invalid) { - _input_units = converter.get_input_units(); - } - - // Add the command line comment at the top of the egg file - append_command_comment(_data); - - write_egg_file(); - - // Clean and out - close_output(); - _verbose = 0; - _polygon_tolerance = 0.01; - _polygon_output = 0; - _transform_type = MayaToEggConverter::TT_model; - _subsets.clear(); - _subroots.clear(); - _input_units = DU_invalid; - _output_units = DU_invalid; - _excludes.clear(); - _ignore_sliders.clear(); - _force_joints.clear(); - _got_transform = false; - _transform = LMatrix4d::ident_mat(); - _normals_mode = NM_preserve; - _normals_threshold = 0.0; - _got_start_frame = false; - _got_end_frame = false; - _got_frame_inc = false; - _got_neutral_frame = false; - _got_input_frame_rate = false; - _got_output_frame_rate = false; - _got_output_filename = false; - _merge_externals = false; - _got_tbnall = false; - _got_tbnauto = false; - _got_transform = false; - _coordinate_system = CS_yup_right; - _noabs = false; - _program_args.clear(); - _data->clear(); - _animation_convert = AC_none; - _character_name = ""; - dummy->clear(); -} - -/** - * Dispatches a parameter that expects a MayaToEggConverter::TransformType - * option. - */ -bool MayaToEggServer:: -dispatch_transform_type(const std::string &opt, const std::string &arg, void *var) { - MayaToEggConverter::TransformType *ip = (MayaToEggConverter::TransformType *)var; - (*ip) = MayaToEggConverter::string_transform_type(arg); - - if ((*ip) == MayaToEggConverter::TT_invalid) { - nout << "Invalid type for -" << opt << ": " << arg << "\n" - << "Valid types are all, model, dcs, and none.\n"; - return false; - } - - return true; -} - -/** - * Checks for any network activity and handles it, if appropriate, and then - * returns. This must be called periodically - */ -void MayaToEggServer:: -poll() { - // Listen for new connections - qListener->poll(); - - // If we have a new connection from a client create a new connection pointer - // and add it to the reader list - if (qListener->new_connection_available()) { - PT(Connection) con; - PT(Connection) rv; - NetAddress address; - if (qListener->get_new_connection(rv, address, con)) { - qReader->add_connection(con); - _clients.insert(con); - } - } - - // Check for reset clients - if (qManager->reset_connection_available()) { - PT(Connection) connection; - if (qManager->get_reset_connection(connection)) { - _clients.erase(connection); - qManager->close_connection(connection); - } - } - - // Poll the readers (created above) and if they have data process it - qReader->poll(); - if (qReader->data_available()) { - // Grab the incomming data and unpack it - NetDatagram datagram; - if (qReader->get_data(datagram)) { - DatagramIterator data(datagram); - // First data should be the "argc" (argument count) from the client - int argc = data.get_uint8(); - - // Now we have to get clever because the rest of the data comes as - // strings and parse_command_line() expects arguments of the standard - // argc, argv*[] variety. First, we need a string vector to hold all - // the strings from the datagram. We also need a char * array to keep - // track of all the pointers we're gonna malloc. Needed later for - // cleanup. - vector_string vargv; - std::vector buffers; - - // Get the strings from the datagram and put them into the string vector - int i; - for ( i = 0; i < argc; i++ ) { - vargv.push_back(data.get_string()); - } - - // Last string is the current directory the client was run from. Not - // part of the argument list, but we still need it - std::string cwd = data.get_string(); - - // We allocate some memory to hold the pointers to the pointers we're - // going to pass in to parse_command_line(). - char ** cargv = (char**) malloc(sizeof(char**) * argc); - - // Loop through the string arguments we got from the datagram and - // convert them to const char *'s. parse_command_line() expects char - // *'s, so we have to copy these const versions into fresh char *, since - // there is no casting from const char * to char *. - for ( i = 0; i < argc; i++) { - // string to const char * - const char * cptr = vargv[i].c_str(); - // memory allocated for a new char * of size of the string - char * buffer = (char*) malloc(vargv[i].capacity()+1); - // Copy the const char * to the char * - strcpy(buffer, cptr); - // put this into the arry we defined above. This is what will - // eventually be passed to parse_command_line() - cargv[i] = buffer; - // keep track of the pointers to the allocated memory for cleanup - // later - buffers.push_back(buffer); - } - // Change to the client's current dir -#ifdef _WIN64 - _chdir(cwd.c_str()); -#else - chdir(cwd.c_str()); -#endif - - // Pass in the 'new' argc and argv we got from the client - this->parse_command_line(argc, cargv); - // Actually run the damn thing - this->run(); - - // Cleanup First, release the string vector - vargv.clear(); - // No, iterate through the char * vector and cleanup the malloc'd - // pointers - std::vector::iterator vi; - for ( vi = buffers.begin() ; vi != buffers.end(); vi++) { - free(*vi); - } - // Clean up the malloc'd pointer pointer - free(cargv); - } // qReader->get_data - - Clients::iterator ci; - for (ci = _clients.begin(); ci != _clients.end(); ++ci) { - qManager->close_connection(*ci); - } - } // qReader->data_available -} // poll - -int main(int argc, char *argv[]) { - MayaToEggServer prog; - // Open a rendezvous port for receiving new connections from the client - PT(Connection) rend = prog.qManager->open_TCP_server_rendezvous(4242, 50); - if (rend.is_null()) { - nout << "port opened fail"; - } - - // Add this connection to the listeners list - prog.qListener->add_connection(rend); - - // Main loop. Keep polling for connections, but don't eat up all the CPU. - while (true) { - prog.poll(); - Thread::force_yield(); - } - return 0; -} diff --git a/pandatool/src/mayaprogs/mayaToEgg_server.h b/pandatool/src/mayaprogs/mayaToEgg_server.h deleted file mode 100644 index ba014f4846..0000000000 --- a/pandatool/src/mayaprogs/mayaToEgg_server.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file mayaToEgg_server.h - * @author cbrunner - * @date 2009-11-09 - */ - -#ifndef MAYATOEGGSERVER_H -#define MAYATOEGGSERVER_H - -#include "pandatoolbase.h" -#include "somethingToEgg.h" -#include "mayaToEggConverter.h" -#include "queuedConnectionManager.h" -#include "queuedConnectionListener.h" -#include "queuedConnectionReader.h" -#include "connectionWriter.h" - -/** - * - */ -class MayaToEggServer : public SomethingToEgg { -public: - MayaToEggServer(); - ~MayaToEggServer(); - - void run(); - void poll(); - - void add_reader(Connection *connection, QueuedConnectionReader *reader); - void remove_reader(Connection *connection, QueuedConnectionReader *reader); - QueuedConnectionManager *qManager; - QueuedConnectionListener *qListener; - QueuedConnectionReader *qReader; - ConnectionWriter *cWriter; - MayaToEggConverter *dummy; - - -protected: - static bool dispatch_transform_type(const std::string &opt, const std::string &arg, void *var); - typedef pset< PT(Connection) > Clients; - Clients _clients; - - int _verbose; - bool _polygon_output; - double _polygon_tolerance; - bool _respect_maya_double_sided; - bool _suppress_vertex_color; - bool _keep_all_uvsets; - bool _round_uvs; - bool _legacy_shader; - - MayaToEggConverter::TransformType _transform_type; - vector_string _subroots; - vector_string _subsets; - vector_string _excludes; - vector_string _ignore_sliders; - vector_string _force_joints; -}; - -#endif diff --git a/pandatool/src/progbase/programBase.cxx b/pandatool/src/progbase/programBase.cxx index a509e93edf..fb6a4458ba 100644 --- a/pandatool/src/progbase/programBase.cxx +++ b/pandatool/src/progbase/programBase.cxx @@ -268,10 +268,16 @@ write_man_page(std::ostream &out) { /** * Dispatches on each of the options on the command line, and passes the * remaining parameters to handle_args(). If an error on the command line is - * detected, will automatically call show_usage() and exit(1). + * detected, will automatically call show_usage() and exit(1). If exit_on_complete + * is not set, will return the program's exit code instead of exiting. */ -void ProgramBase:: -parse_command_line(int argc, char **argv) { +ProgramBase::ExitCode ProgramBase:: +parse_command_line(int argc, char **argv, bool exit_on_complete) { + if (!exit_on_complete) { + // The help option should not be available in programmatic environments. + remove_option("h"); + } + preprocess_argv(argc, argv); // Setting this variable to zero reinitializes the options parser This is @@ -280,6 +286,11 @@ parse_command_line(int argc, char **argv) { extern int optind; optind = 0; +#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_LONG_ONLY) + // We're using a Panda implementation of getopt. Let's reset that as well. + pgetopt_reset(); +#endif + _program_name = Filename::from_os_specific(argv[0]); int i; for (i = 1; i < argc; i++) { @@ -310,9 +321,16 @@ parse_command_line(int argc, char **argv) { } } else { cerr << "Invalid number of options for -write-man!\n"; - exit(1); + if (exit_on_complete) { + exit(1); + } + return ExitCode::EC_failure; } - exit(0); + + if (exit_on_complete) { + exit(0); + } + return ExitCode::EC_clean_exit; } // Build up the long options list and the short options string for @@ -395,7 +413,10 @@ parse_command_line(int argc, char **argv) { case '?': // Invalid option or parameter. show_usage(); - exit(1); + if (exit_on_complete) { + exit(1); + } + return ExitCode::EC_failure; case '\x1': // A special return value from getopt() indicating a non-option @@ -427,7 +448,10 @@ parse_command_line(int argc, char **argv) { if (!okflag) { show_usage(); - exit(1); + if (exit_on_complete) { + exit(1); + } + return ExitCode::EC_failure; } } } @@ -438,13 +462,21 @@ parse_command_line(int argc, char **argv) { if (!handle_args(remaining_args)) { show_usage(); - exit(1); + if (exit_on_complete) { + exit(1); + } + return ExitCode::EC_failure; } if (!post_command_line()) { show_usage(); - exit(1); + if (exit_on_complete) { + exit(1); + } + return ExitCode::EC_failure; } + + return ExitCode::EC_not_exited; } /** diff --git a/pandatool/src/progbase/programBase.h b/pandatool/src/progbase/programBase.h index d3d06eb67a..a245e99951 100644 --- a/pandatool/src/progbase/programBase.h +++ b/pandatool/src/progbase/programBase.h @@ -33,6 +33,12 @@ */ class ProgramBase { public: + enum ExitCode { + EC_not_exited = -1, + EC_clean_exit = 0, + EC_failure = 1 + }; + ProgramBase(const std::string &name = std::string()); virtual ~ProgramBase(); @@ -45,7 +51,7 @@ public: void write_man_page(std::ostream &out); - virtual void parse_command_line(int argc, char **argv); + virtual ExitCode parse_command_line(int argc, char **argv, bool exit_on_complete = true); std::string get_exec_command() const;