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