Panda3D
 All Classes Functions Variables Enumerations
mayaPview.cxx
1 // Filename: mayaPview.cxx
2 // Created by: drose (10Mar03)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #ifdef __MACH__
16 #define __OPENTRANSPORTPROVIDERS__
17 #endif
18 
19 #include "mayaPview.h"
20 #include "mayaToEggConverter.h"
21 #include "eggData.h"
22 #include "load_egg_file.h"
23 #include "config_util.h"
24 #include "config_chan.h"
25 #include "config_gobj.h"
26 #include "textNode.h"
27 #include "multiplexStream.h"
28 #include "distanceUnit.h"
29 #include "configVariableEnum.h"
30 
31 // We must define this to prevent Maya from doubly-declaring its
32 // MApiVersion string in this file as well as in libmayaegg.
33 #define _MApiVersion
34 
35 #include "pre_maya_include.h"
36 #include <maya/MString.h>
37 #include <maya/MFnPlugin.h>
38 #include <maya/MFileIO.h>
39 #include <maya/MArgParser.h>
40 #include <maya/MArgList.h>
41 #include <maya/MSyntax.h>
42 #include <maya/MProgressWindow.h>
43 #include "post_maya_include.h"
44 
45 // On Windows, we have code to fork pview as a separate process, which
46 // seems to be better for Maya.
47 #ifdef WIN32_VC
48 #include <windows.h>
49 #include <process.h>
50 #define SEPARATE_PVIEW 1
51 #endif // WIN32_VC
52 
53 ////////////////////////////////////////////////////////////////////
54 // Function: MayaPview::Constructor
55 // Access: Public
56 // Description:
57 ////////////////////////////////////////////////////////////////////
58 MayaPview::
59 MayaPview() {
60 }
61 
62 ////////////////////////////////////////////////////////////////////
63 // Function: MayaPview::doIt
64 // Access: Public, Virtual
65 // Description: Called when the plugin command is invoked.
66 ////////////////////////////////////////////////////////////////////
67 MStatus MayaPview::
68 doIt(const MArgList &args) {
69  MStatus result;
70 
71  // First, parse the plugin arguments.
72  MSyntax syntax;
73  syntax.addFlag("a", "animate");
74 
75  MArgParser parser(syntax, args, &result);
76  if (!result) {
77  result.perror("arguments");
78  return result;
79  }
80 
81  bool animate = parser.isFlagSet("a", &result);
82  if (!result) {
83  result.perror("isFlagSet");
84  return result;
85  }
86 
87  if (!MProgressWindow::reserve()) {
88  nout << "Couldn't reserve progress window.\n";
89  return MS::kFailure;
90  }
91  MProgressWindow::setTitle("Sending to pview");
92  MProgressWindow::setInterruptable(false);
93  MProgressWindow::setProgressRange(0, 3);
94  MProgressWindow::setProgressStatus("Converting scene");
95  MProgressWindow::startProgress();
96 
97 #ifdef SEPARATE_PVIEW
98  // We'll write the bam file to a temporary file first.
99  Filename bam_filename = Filename::temporary("", "pview");
100  bam_filename.set_extension("bam");
101 
102  // Since we're just writing to a bam file in this process, and
103  // running pview in a separate process, we don't actually need to
104  // load textures at this point. Disable the loading of textures.
105  textures_header_only = true;
106 
107  NodePath root("root");
108  if (!convert(root, animate)) {
109  nout << "failure in conversion.\n";
110  MProgressWindow::endProgress();
111  return MS::kFailure;
112  }
113 
114  MProgressWindow::setProgressStatus("Writing bam file");
115  MProgressWindow::advanceProgress(1);
116 
117  if (!root.write_bam_file(bam_filename)) {
118  nout << "Couldn't write to " << bam_filename << ".\n";
119  MProgressWindow::endProgress();
120  return MS::kFailure;
121  }
122 
123  MProgressWindow::setProgressStatus("Spawning pview");
124  MProgressWindow::advanceProgress(1);
125 
126  // Now spawn a pview instance to view this temporary file.
127  string pview_args = "-clD";
128  if (animate) {
129  pview_args = "-clDa";
130  }
131 
132  // On Windows, we use the spawn function to run pview
133  // asynchronously.
134  string quoted = string("\"") + bam_filename.get_fullpath() + string("\"");
135  nout << "pview " << pview_args << " " << quoted << "\n";
136  int retval = _spawnlp(_P_DETACH, "pview",
137  "pview", pview_args.c_str(), quoted.c_str(), NULL);
138  if (retval == -1) {
139  bam_filename.unlink();
140  MProgressWindow::endProgress();
141  return MS::kFailure;
142  }
143 
144  nout << "pview running.\n";
145  MProgressWindow::endProgress();
146 
147 #else // SEPARATE_PVIEW
148  // We'll run PandaFramework directly within this process.
149 
150  // Maya seems to run each invocation of the plugin in a separate
151  // thread. To minimize conflict in our
152  // not-yet-completely-thread-safe Panda, we'll create a separate
153  // PandaFramework for each invocation, even though in principle we
154  // could be sharing one framework for all of them.
155  int argc = 0;
156  char **argv = NULL;
157  PandaFramework framework;
158  framework.open_framework(argc, argv);
159  framework.set_window_title("Panda Viewer");
160  framework.enable_default_keys();
161 
162  PT(WindowFramework) window;
163  window = framework.open_window();
164  if (window == (WindowFramework *)NULL) {
165  // Couldn't open a window.
166  nout << "Couldn't open a window!\n";
167  MProgressWindow::endProgress();
168  return MS::kFailure;
169  }
170 
171  // We've successfully opened a window.
172 
173  // Put up a "loading" message for the user's benefit.
174  NodePath aspect_2d = window->get_aspect_2d();
175  PT(TextNode) loading = new TextNode("loading");
176  NodePath loading_np = aspect_2d.attach_new_node(loading);
177  loading_np.set_scale(0.125f);
178  loading->set_text_color(1.0f, 1.0f, 1.0f, 1.0f);
179  loading->set_shadow_color(0.0f, 0.0f, 0.0f, 1.0f);
180  loading->set_shadow(0.04, 0.04);
181  loading->set_align(TextNode::A_center);
182  loading->set_text("Loading...");
183 
184  // Allow a couple of frames to go by so the window will be fully
185  // created and the text will be visible.
188 
189  window->enable_keyboard();
190  window->setup_trackball();
191  framework.get_models().instance_to(window->get_render());
192 
193  if (!convert(framework.get_models(), animate)) {
194  nout << "failure in conversion.\n";
195  MProgressWindow::endProgress();
196  return MS::kFailure;
197  }
198 
199  nout << "successfully converted.\n";
200 
201  loading_np.remove_node();
202  window->center_trackball(framework.get_models());
203  window->loop_animations();
204 
205  if (animate) {
206  window->set_anim_controls(true);
207  }
208 
209  MProgressWindow::endProgress();
210  framework.main_loop();
211 #endif // SEPARATE_PVIEW
212 
213  return MS::kSuccess;
214 }
215 
216 ////////////////////////////////////////////////////////////////////
217 // Function: MayaPview::creator
218 // Access: Public, Static
219 // Description: This is used to create a new instance of the plugin.
220 ////////////////////////////////////////////////////////////////////
221 void *MayaPview::
223  return new MayaPview;
224 }
225 
226 ////////////////////////////////////////////////////////////////////
227 // Function: MayaPview::convert
228 // Access: Private
229 // Description: Actually converts the Maya selection to Panda
230 // geometry, and parents it to the indicated NodePath.
231 ////////////////////////////////////////////////////////////////////
232 bool MayaPview::
233 convert(const NodePath &parent, bool animate) {
234  // Now make a converter to get all the Maya structures.
235  MayaToEggConverter converter("plug-in");
236 
237  // We always want polygon output since we want to be able to see the
238  // results.
239  converter._polygon_output = true;
240  converter._polygon_tolerance = 0.01;
241 
242  if (animate) {
243  // We also want to get the animation if there is any.
244  converter.set_animation_convert(AC_both);
245 
246  // Don't compress animation channels; that can introduce confusing
247  // artifacts.
248  compress_channels = false;
249  }
250 
251  PathReplace *path_replace = converter.get_path_replace();
252 
253  // Accept relative pathnames in the Maya file.
254  Filename source_file =
255  Filename::from_os_specific(MFileIO::currentFile().asChar());
256  string source_dir = source_file.get_dirname();
257  if (!source_dir.empty()) {
258  path_replace->_path.append_directory(source_dir);
259  }
260 
261  // Also search along the model path.
262  path_replace->_path.append_path(get_model_path());
263 
264  PT(EggData) egg_data = new EggData;
265  converter.set_egg_data(egg_data);
266  converter.set_from_selection(true);
267  converter.set_neutral_frame(-1);
268 
269  if (!converter.convert_maya()) {
270  nout << "Errors in conversion.\n";
271  return false;
272  }
273 
274  MProgressWindow::setProgressStatus("Converting to bam");
275  MProgressWindow::advanceProgress(1);
276 
277  // Now the converter has filled up our egg structure with data, so
278  // convert this egg data to Panda data for immediate viewing.
279  DistanceUnit input_units = converter.get_input_units();
280  ConfigVariableEnum<DistanceUnit> ptloader_units("ptloader-units", DU_invalid);
281  if (input_units != DU_invalid && ptloader_units != DU_invalid &&
282  input_units != ptloader_units) {
283  // Convert the file to the units specified by the ptloader-units
284  // Configrc variable.
285  nout
286  << "Converting from " << format_long_unit(input_units)
287  << " to " << format_long_unit(ptloader_units) << "\n";
288  double scale = convert_units(input_units, ptloader_units);
289  egg_data->transform(LMatrix4d::scale_mat(scale));
290  }
291 
292  egg_data->set_coordinate_system(CS_default);
293  PT(PandaNode) result = load_egg_data(egg_data);
294 
295  if (result == (PandaNode *)NULL) {
296  nout << "Unable to load converted egg data.\n";
297  return false;
298  }
299 
300  parent.attach_new_node(result);
301  return true;
302 }
303 
304 
305 
306 
307 ////////////////////////////////////////////////////////////////////
308 // Function: initializePlugin
309 // Description: Called by Maya when the plugin is loaded.
310 ////////////////////////////////////////////////////////////////////
311 EXPCL_MISC MStatus
312 initializePlugin(MObject obj) {
313  // This code is just for debugging, to cause Notify to write its
314  // output to a log file we can inspect, so we can see the error
315  // messages output by DX7 or DX8 just before it does a panic exit
316  // (and thereby shuts down Maya and its output window).
317  /*
318  MultiplexStream *local_nout = new MultiplexStream();
319  Notify::ptr()->set_ostream_ptr(local_nout, 0);
320  local_nout->add_file(Filename::expand_from("$TEMP/libmayapview.log"));
321  local_nout->add_standard_output();
322  */
323 
324  MFnPlugin plugin(obj, "VR Studio", "1.0");
325  MStatus status;
326  status = plugin.registerCommand("pview", MayaPview::creator);
327  if (!status) {
328  status.perror("registerCommand");
329  }
330 
331  return status;
332 }
333 
334 ////////////////////////////////////////////////////////////////////
335 // Function: uninitializePlugin
336 // Description: Called by Maya when the plugin is unloaded.
337 ////////////////////////////////////////////////////////////////////
338 EXPCL_MISC MStatus
339 uninitializePlugin(MObject obj) {
340  MFnPlugin plugin(obj);
341  MStatus status;
342  status = plugin.deregisterCommand("pview");
343 
344  if (!status) {
345  status.perror("deregisterCommand");
346  }
347  return status;
348 }
string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:398
void set_extension(const string &s)
Replaces the file extension.
Definition: filename.cxx:837
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
bool unlink() const
Permanently deletes the file associated with the filename, if possible.
Definition: filename.cxx:2554
void enable_default_keys()
Sets callbacks on the event handler to handle all of the normal viewer keys, like t to toggle texture...
NodePath & get_models()
Returns the root of the scene graph normally reserved for parenting models and such.
void set_scale(PN_stdfloat scale)
Sets the scale component of the transform, leaving translation and rotation untouched.
Definition: nodePath.I:848
This encapsulates the data that is normally associated with a single window, or with a single display...
bool write_bam_file(const Filename &filename) const
Writes the contents of this node and below out to a bam file with the indicated filename.
Definition: nodePath.cxx:6408
static LMatrix4d scale_mat(const LVecBase3d &scale)
Returns a matrix that applies the indicated scale in each of the three axes.
Definition: lmatrix.h:6721
string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:424
void append_directory(const Filename &directory)
Adds a new directory to the end of the search list.
NodePath instance_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Adds the referenced node of the NodePath as a child of the referenced node of the indicated other Nod...
Definition: nodePath.cxx:618
This is the primary interface into all the egg data, and the root of the egg file structure...
Definition: eggData.h:41
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
void append_path(const string &path, const string &separator=string())
Adds all of the directories listed in the search path to the end of the search list.
static Filename temporary(const string &dirname, const string &prefix, const string &suffix=string(), Type type=T_general)
Generates a temporary filename within the indicated directory, using the indicated prefix...
Definition: filename.cxx:439
This class serves as a plug-in to Maya to allow viewing the current Maya selection as it will be conv...
Definition: mayaPview.h:33
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
void main_loop()
Called to yield control to the panda framework.
This class specializes ConfigVariable as an enumerated type.
WindowFramework * open_window()
Opens a window on the default graphics pipe.
This class supervises the construction of an EggData structure from a single Maya file...
static void * creator()
This is used to create a new instance of the plugin.
Definition: mayaPview.cxx:222
void open_framework(int &argc, char **&argv)
Should be called once at the beginning of the application to initialize Panda (and the framework) for...
void remove_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from the scene graph.
Definition: nodePath.cxx:757
This encapsulates the user&#39;s command-line request to replace existing, incorrect pathnames to models ...
Definition: pathReplace.h:40
The primary interface to this module.
Definition: textNode.h:52
void set_window_title(const string &title)
Specifies the title that is set for all subsequently created windows.
This class serves to provide a high-level framework for basic applications that use Panda in simple w...
virtual MStatus doIt(const MArgList &args)
Called when the plugin command is invoked.
Definition: mayaPview.cxx:68
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
Definition: nodePath.cxx:723
virtual bool do_frame(Thread *current_thread)
Renders one frame and performs all associated processing.
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).
Definition: filename.cxx:332