Panda3D
|
00001 // Filename: mayaPview.cxx 00002 // Created by: drose (10Mar03) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #ifdef __MACH__ 00016 #define __OPENTRANSPORTPROVIDERS__ 00017 #endif 00018 00019 #include "mayaPview.h" 00020 #include "mayaToEggConverter.h" 00021 #include "eggData.h" 00022 #include "load_egg_file.h" 00023 #include "config_util.h" 00024 #include "config_chan.h" 00025 #include "config_gobj.h" 00026 #include "textNode.h" 00027 #include "multiplexStream.h" 00028 #include "distanceUnit.h" 00029 #include "configVariableEnum.h" 00030 00031 // We must define this to prevent Maya from doubly-declaring its 00032 // MApiVersion string in this file as well as in libmayaegg. 00033 #define _MApiVersion 00034 00035 #include "pre_maya_include.h" 00036 #include <maya/MString.h> 00037 #include <maya/MFnPlugin.h> 00038 #include <maya/MFileIO.h> 00039 #include <maya/MArgParser.h> 00040 #include <maya/MArgList.h> 00041 #include <maya/MSyntax.h> 00042 #include <maya/MProgressWindow.h> 00043 #include "post_maya_include.h" 00044 00045 // On Windows, we have code to fork pview as a separate process, which 00046 // seems to be better for Maya. 00047 #ifdef WIN32_VC 00048 #include <windows.h> 00049 #include <process.h> 00050 #define SEPARATE_PVIEW 1 00051 #endif // WIN32_VC 00052 00053 //////////////////////////////////////////////////////////////////// 00054 // Function: MayaPview::Constructor 00055 // Access: Public 00056 // Description: 00057 //////////////////////////////////////////////////////////////////// 00058 MayaPview:: 00059 MayaPview() { 00060 } 00061 00062 //////////////////////////////////////////////////////////////////// 00063 // Function: MayaPview::doIt 00064 // Access: Public, Virtual 00065 // Description: Called when the plugin command is invoked. 00066 //////////////////////////////////////////////////////////////////// 00067 MStatus MayaPview:: 00068 doIt(const MArgList &args) { 00069 MStatus result; 00070 00071 // First, parse the plugin arguments. 00072 MSyntax syntax; 00073 syntax.addFlag("a", "animate"); 00074 00075 MArgParser parser(syntax, args, &result); 00076 if (!result) { 00077 result.perror("arguments"); 00078 return result; 00079 } 00080 00081 bool animate = parser.isFlagSet("a", &result); 00082 if (!result) { 00083 result.perror("isFlagSet"); 00084 return result; 00085 } 00086 00087 if (!MProgressWindow::reserve()) { 00088 nout << "Couldn't reserve progress window.\n"; 00089 return MS::kFailure; 00090 } 00091 MProgressWindow::setTitle("Sending to pview"); 00092 MProgressWindow::setInterruptable(false); 00093 MProgressWindow::setProgressRange(0, 3); 00094 MProgressWindow::setProgressStatus("Converting scene"); 00095 MProgressWindow::startProgress(); 00096 00097 #ifdef SEPARATE_PVIEW 00098 // We'll write the bam file to a temporary file first. 00099 Filename bam_filename = Filename::temporary("", "pview"); 00100 bam_filename.set_extension("bam"); 00101 00102 // Since we're just writing to a bam file in this process, and 00103 // running pview in a separate process, we don't actually need to 00104 // load textures at this point. Disable the loading of textures. 00105 textures_header_only = true; 00106 00107 NodePath root("root"); 00108 if (!convert(root, animate)) { 00109 nout << "failure in conversion.\n"; 00110 MProgressWindow::endProgress(); 00111 return MS::kFailure; 00112 } 00113 00114 MProgressWindow::setProgressStatus("Writing bam file"); 00115 MProgressWindow::advanceProgress(1); 00116 00117 if (!root.write_bam_file(bam_filename)) { 00118 nout << "Couldn't write to " << bam_filename << ".\n"; 00119 MProgressWindow::endProgress(); 00120 return MS::kFailure; 00121 } 00122 00123 MProgressWindow::setProgressStatus("Spawning pview"); 00124 MProgressWindow::advanceProgress(1); 00125 00126 // Now spawn a pview instance to view this temporary file. 00127 string pview_args = "-clD"; 00128 if (animate) { 00129 pview_args = "-clDa"; 00130 } 00131 00132 // On Windows, we use the spawn function to run pview 00133 // asynchronously. 00134 string quoted = string("\"") + bam_filename.get_fullpath() + string("\""); 00135 nout << "pview " << pview_args << " " << quoted << "\n"; 00136 int retval = _spawnlp(_P_DETACH, "pview", 00137 "pview", pview_args.c_str(), quoted.c_str(), NULL); 00138 if (retval == -1) { 00139 bam_filename.unlink(); 00140 MProgressWindow::endProgress(); 00141 return MS::kFailure; 00142 } 00143 00144 nout << "pview running.\n"; 00145 MProgressWindow::endProgress(); 00146 00147 #else // SEPARATE_PVIEW 00148 // We'll run PandaFramework directly within this process. 00149 00150 // Maya seems to run each invocation of the plugin in a separate 00151 // thread. To minimize conflict in our 00152 // not-yet-completely-thread-safe Panda, we'll create a separate 00153 // PandaFramework for each invocation, even though in principle we 00154 // could be sharing one framework for all of them. 00155 int argc = 0; 00156 char **argv = NULL; 00157 PandaFramework framework; 00158 framework.open_framework(argc, argv); 00159 framework.set_window_title("Panda Viewer"); 00160 framework.enable_default_keys(); 00161 00162 PT(WindowFramework) window; 00163 window = framework.open_window(); 00164 if (window == (WindowFramework *)NULL) { 00165 // Couldn't open a window. 00166 nout << "Couldn't open a window!\n"; 00167 MProgressWindow::endProgress(); 00168 return MS::kFailure; 00169 } 00170 00171 // We've successfully opened a window. 00172 00173 // Put up a "loading" message for the user's benefit. 00174 NodePath aspect_2d = window->get_aspect_2d(); 00175 PT(TextNode) loading = new TextNode("loading"); 00176 NodePath loading_np = aspect_2d.attach_new_node(loading); 00177 loading_np.set_scale(0.125f); 00178 loading->set_text_color(1.0f, 1.0f, 1.0f, 1.0f); 00179 loading->set_shadow_color(0.0f, 0.0f, 0.0f, 1.0f); 00180 loading->set_shadow(0.04, 0.04); 00181 loading->set_align(TextNode::A_center); 00182 loading->set_text("Loading..."); 00183 00184 // Allow a couple of frames to go by so the window will be fully 00185 // created and the text will be visible. 00186 framework.do_frame(Thread::get_current_thread()); 00187 framework.do_frame(Thread::get_current_thread()); 00188 00189 window->enable_keyboard(); 00190 window->setup_trackball(); 00191 framework.get_models().instance_to(window->get_render()); 00192 00193 if (!convert(framework.get_models(), animate)) { 00194 nout << "failure in conversion.\n"; 00195 MProgressWindow::endProgress(); 00196 return MS::kFailure; 00197 } 00198 00199 nout << "successfully converted.\n"; 00200 00201 loading_np.remove_node(); 00202 window->center_trackball(framework.get_models()); 00203 window->loop_animations(); 00204 00205 if (animate) { 00206 window->set_anim_controls(true); 00207 } 00208 00209 MProgressWindow::endProgress(); 00210 framework.main_loop(); 00211 #endif // SEPARATE_PVIEW 00212 00213 return MS::kSuccess; 00214 } 00215 00216 //////////////////////////////////////////////////////////////////// 00217 // Function: MayaPview::creator 00218 // Access: Public, Static 00219 // Description: This is used to create a new instance of the plugin. 00220 //////////////////////////////////////////////////////////////////// 00221 void *MayaPview:: 00222 creator() { 00223 return new MayaPview; 00224 } 00225 00226 //////////////////////////////////////////////////////////////////// 00227 // Function: MayaPview::convert 00228 // Access: Private 00229 // Description: Actually converts the Maya selection to Panda 00230 // geometry, and parents it to the indicated NodePath. 00231 //////////////////////////////////////////////////////////////////// 00232 bool MayaPview:: 00233 convert(const NodePath &parent, bool animate) { 00234 // Now make a converter to get all the Maya structures. 00235 MayaToEggConverter converter("plug-in"); 00236 00237 // We always want polygon output since we want to be able to see the 00238 // results. 00239 converter._polygon_output = true; 00240 converter._polygon_tolerance = 0.01; 00241 00242 if (animate) { 00243 // We also want to get the animation if there is any. 00244 converter.set_animation_convert(AC_both); 00245 00246 // Don't compress animation channels; that can introduce confusing 00247 // artifacts. 00248 compress_channels = false; 00249 } 00250 00251 PathReplace *path_replace = converter.get_path_replace(); 00252 00253 // Accept relative pathnames in the Maya file. 00254 Filename source_file = 00255 Filename::from_os_specific(MFileIO::currentFile().asChar()); 00256 string source_dir = source_file.get_dirname(); 00257 if (!source_dir.empty()) { 00258 path_replace->_path.append_directory(source_dir); 00259 } 00260 00261 // Also search along the model path. 00262 path_replace->_path.append_path(get_model_path()); 00263 00264 PT(EggData) egg_data = new EggData; 00265 converter.set_egg_data(egg_data); 00266 converter.set_from_selection(true); 00267 converter.set_neutral_frame(-1); 00268 00269 if (!converter.convert_maya()) { 00270 nout << "Errors in conversion.\n"; 00271 return false; 00272 } 00273 00274 MProgressWindow::setProgressStatus("Converting to bam"); 00275 MProgressWindow::advanceProgress(1); 00276 00277 // Now the converter has filled up our egg structure with data, so 00278 // convert this egg data to Panda data for immediate viewing. 00279 DistanceUnit input_units = converter.get_input_units(); 00280 ConfigVariableEnum<DistanceUnit> ptloader_units("ptloader-units", DU_invalid); 00281 if (input_units != DU_invalid && ptloader_units != DU_invalid && 00282 input_units != ptloader_units) { 00283 // Convert the file to the units specified by the ptloader-units 00284 // Configrc variable. 00285 nout 00286 << "Converting from " << format_long_unit(input_units) 00287 << " to " << format_long_unit(ptloader_units) << "\n"; 00288 double scale = convert_units(input_units, ptloader_units); 00289 egg_data->transform(LMatrix4d::scale_mat(scale)); 00290 } 00291 00292 egg_data->set_coordinate_system(CS_default); 00293 PT(PandaNode) result = load_egg_data(egg_data); 00294 00295 if (result == (PandaNode *)NULL) { 00296 nout << "Unable to load converted egg data.\n"; 00297 return false; 00298 } 00299 00300 parent.attach_new_node(result); 00301 return true; 00302 } 00303 00304 00305 00306 00307 //////////////////////////////////////////////////////////////////// 00308 // Function: initializePlugin 00309 // Description: Called by Maya when the plugin is loaded. 00310 //////////////////////////////////////////////////////////////////// 00311 EXPCL_MISC MStatus 00312 initializePlugin(MObject obj) { 00313 // This code is just for debugging, to cause Notify to write its 00314 // output to a log file we can inspect, so we can see the error 00315 // messages output by DX7 or DX8 just before it does a panic exit 00316 // (and thereby shuts down Maya and its output window). 00317 /* 00318 MultiplexStream *local_nout = new MultiplexStream(); 00319 Notify::ptr()->set_ostream_ptr(local_nout, 0); 00320 local_nout->add_file(Filename::expand_from("$TEMP/libmayapview.log")); 00321 local_nout->add_standard_output(); 00322 */ 00323 00324 MFnPlugin plugin(obj, "VR Studio", "1.0"); 00325 MStatus status; 00326 status = plugin.registerCommand("pview", MayaPview::creator); 00327 if (!status) { 00328 status.perror("registerCommand"); 00329 } 00330 00331 return status; 00332 } 00333 00334 //////////////////////////////////////////////////////////////////// 00335 // Function: uninitializePlugin 00336 // Description: Called by Maya when the plugin is unloaded. 00337 //////////////////////////////////////////////////////////////////// 00338 EXPCL_MISC MStatus 00339 uninitializePlugin(MObject obj) { 00340 MFnPlugin plugin(obj); 00341 MStatus status; 00342 status = plugin.deregisterCommand("pview"); 00343 00344 if (!status) { 00345 status.perror("deregisterCommand"); 00346 } 00347 return status; 00348 }