Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

pandatool/src/xfile/xFileMaker.cxx

Go to the documentation of this file.
00001 // Filename: xFileMaker.cxx
00002 // Created by:  drose (19Jun01)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "xFileMaker.h"
00020 #include "xFileMesh.h"
00021 #include "xFileMaterial.h"
00022 #include "xFileTemplates.h"
00023 #include "config_xfile.h"
00024 
00025 #include "notify.h"
00026 #include "eggGroupNode.h"
00027 #include "eggGroup.h"
00028 #include "eggBin.h"
00029 #include "eggPolysetMaker.h"
00030 #include "eggVertexPool.h"
00031 #include "eggVertex.h"
00032 #include "eggPolygon.h"
00033 #include "eggData.h"
00034 #include "pvector.h"
00035 #include "vector_int.h"
00036 #include "string_utils.h"
00037 #include "datagram.h"
00038 
00039 ////////////////////////////////////////////////////////////////////
00040 //     Function: XFileMaker::Constructor
00041 //       Access: Public
00042 //  Description:
00043 ////////////////////////////////////////////////////////////////////
00044 XFileMaker::
00045 XFileMaker() {
00046   _dx_file = NULL;
00047   _dx_file_save = NULL;
00048   _mesh_index = 0;
00049 }
00050 
00051 ////////////////////////////////////////////////////////////////////
00052 //     Function: XFileMaker::Destructor
00053 //       Access: Public
00054 //  Description:
00055 ////////////////////////////////////////////////////////////////////
00056 XFileMaker::
00057 ~XFileMaker() {
00058   close();
00059 }
00060 
00061 ////////////////////////////////////////////////////////////////////
00062 //     Function: XFileMaker::open
00063 //       Access: Public
00064 //  Description: Opens the indicated filename for writing, and writes
00065 //               the .x header information; returns true on success,
00066 //               false otherwise.
00067 ////////////////////////////////////////////////////////////////////
00068 bool XFileMaker::
00069 open(const Filename &filename) {
00070   HRESULT hr;
00071 
00072   close();
00073   hr = DirectXFileCreate(&_dx_file);
00074   if (hr != DXFILE_OK) {
00075     nout << "Unable to create X interface.\n";
00076     return false;
00077   }
00078 
00079   // Register our templates.
00080   hr = _dx_file->RegisterTemplates(D3DRM_XTEMPLATES, d3drm_xtemplates_length);
00081   if (hr != DXFILE_OK) {
00082     nout << "Unable to register templates.\n";
00083     return false;
00084   }
00085 
00086   string os_file = filename.to_os_specific();
00087   hr = _dx_file->CreateSaveObject(os_file.c_str(), DXFILEFORMAT_TEXT,
00088                                   &_dx_file_save);
00089   if (hr != DXFILE_OK) {
00090     nout << "Unable to open X file: " << os_file << "\n";
00091     return false;
00092   }
00093 
00094   // Save the templates we will use.
00095   static const GUID *temps[] = {
00096     &mydef_TID_D3DRMHeader,
00097     &TID_D3DRMCoords2d,
00098     &TID_D3DRMVector,
00099     &TID_D3DRMColorRGBA,
00100     &TID_D3DRMColorRGB,
00101     &TID_D3DRMIndexedColor,
00102     &TID_D3DRMTextureFilename,
00103     &TID_D3DRMMatrix4x4,
00104     &TID_D3DRMMaterial,
00105     &TID_D3DRMMeshFace,
00106     &TID_D3DRMMesh,
00107     &TID_D3DRMMeshNormals,
00108     &TID_D3DRMMeshTextureCoords,
00109     &TID_D3DRMMeshMaterialList,
00110     &TID_D3DRMFrameTransformMatrix,
00111     &TID_D3DRMFrame,
00112   };
00113   static const int num_temps = sizeof(temps) / sizeof(temps[0]);
00114   hr = _dx_file_save->SaveTemplates(num_temps, temps);
00115   if (hr != DXFILE_OK) {
00116     nout << "Unable to save templates.\n";
00117     return false;
00118   }
00119 
00120   return true;
00121 }
00122 
00123 ////////////////////////////////////////////////////////////////////
00124 //     Function: XFileMaker::close
00125 //       Access: Public
00126 //  Description: Finalizes and closes the file previously opened via
00127 //               open().
00128 ////////////////////////////////////////////////////////////////////
00129 void XFileMaker::
00130 close() {
00131   if (_dx_file != NULL) {
00132     if (_dx_file_save != NULL) {
00133       _dx_file_save->Release();
00134       _dx_file_save = NULL;
00135     }
00136     _dx_file->Release();
00137     _dx_file = NULL;
00138   }
00139 }
00140 
00141 ////////////////////////////////////////////////////////////////////
00142 //     Function: XFileMaker::add_tree
00143 //       Access: Public
00144 //  Description: Adds the egg tree rooted at the indicated node to the
00145 //               DX structure.  This may be somewhat destructive of
00146 //               the egg tree.  Returns true on success, false on
00147 //               failure.
00148 ////////////////////////////////////////////////////////////////////
00149 bool XFileMaker::
00150 add_tree(EggData &egg_data) {
00151   _meshes.clear();
00152 
00153   // Now collect all the polygons together into polysets.
00154   EggPolysetMaker pmaker;
00155   int num_bins = pmaker.make_bins(&egg_data);
00156 
00157   // And now we're ready to traverse the egg hierarchy.
00158   if (!recurse_nodes(&egg_data, NULL)) {
00159     return false;
00160   }
00161 
00162   // Make sure we finalize any meshes in the root.
00163   if (!finalize_mesh(NULL)) {
00164     return false;
00165   }
00166 
00167   nassertr(_meshes.empty(), false);
00168   return true;
00169 }
00170 
00171 ////////////////////////////////////////////////////////////////////
00172 //     Function: XFileMaker::add_node
00173 //       Access: Private
00174 //  Description: Adds the node to the DX structure, in whatever form
00175 //               it is supported.
00176 ////////////////////////////////////////////////////////////////////
00177 bool XFileMaker::
00178 add_node(EggNode *egg_node, LPDIRECTXFILEDATA dx_parent) {
00179   if (egg_node->is_of_type(EggBin::get_class_type())) {
00180     return add_bin(DCAST(EggBin, egg_node), dx_parent);
00181 
00182   } else if (egg_node->is_of_type(EggGroup::get_class_type())) {
00183     return add_group(DCAST(EggGroup, egg_node), dx_parent);
00184 
00185   } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
00186     // A grouping node of some kind.
00187     EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node);
00188     LPDIRECTXFILEDATA obj;
00189 
00190     if (xfile_one_mesh) {
00191       // Don't create any additional frames representing the egg
00192       // hierarchy.
00193       if (!recurse_nodes(egg_group, dx_parent)) {
00194         return false;
00195       }
00196 
00197     } else {
00198       // Create a frame for each EggGroup.
00199       if (!create_frame(obj, egg_group->get_name())) {
00200         return false;
00201       }
00202     
00203       if (!recurse_nodes(egg_group, obj)) {
00204         obj->Release();
00205         return false;
00206       }
00207       
00208       if (!attach_and_release(obj, dx_parent)) {
00209         return false;
00210       }
00211     }
00212     
00213     return true;
00214   }
00215 
00216   // Some unsupported node type.  Ignore it.
00217   return true;
00218 }
00219 
00220 ////////////////////////////////////////////////////////////////////
00221 //     Function: XFileMaker::add_group
00222 //       Access: Private
00223 //  Description: Adds a frame for the indicated group node.
00224 ////////////////////////////////////////////////////////////////////
00225 bool XFileMaker::
00226 add_group(EggGroup *egg_group, LPDIRECTXFILEDATA dx_parent) {
00227   if (xfile_one_mesh) {
00228     // Don't create any additional frames representing the egg
00229     // hierarchy.
00230     if (!recurse_nodes(egg_group, dx_parent)) {
00231       return false;
00232     }
00233 
00234   } else {
00235     // Create a frame for each EggGroup.
00236     LPDIRECTXFILEDATA obj;
00237     if (!create_frame(obj, egg_group->get_name())) {
00238       return false;
00239     }
00240 
00241     // Set the transform on the frame, if we have one.
00242     if (egg_group->has_transform()) {
00243       add_frame_transform(obj, LCAST(float, egg_group->get_transform()));
00244     }
00245     
00246     if (!recurse_nodes(egg_group, obj)) {
00247       obj->Release();
00248       return false;
00249     }
00250     
00251     if (!attach_and_release(obj, dx_parent)) {
00252       return false;
00253     }
00254   }
00255 
00256   return true;
00257 }
00258 
00259 ////////////////////////////////////////////////////////////////////
00260 //     Function: XFileMaker::add_bin
00261 //       Access: Private
00262 //  Description: Determines what kind of object needs to be added for
00263 //               the indicated bin node.
00264 ////////////////////////////////////////////////////////////////////
00265 bool XFileMaker::
00266 add_bin(EggBin *egg_bin, LPDIRECTXFILEDATA dx_parent) {
00267   switch (egg_bin->get_bin_number()) {
00268   case EggPolysetMaker::BN_polyset:
00269     return add_polyset(egg_bin, dx_parent);
00270   }
00271 
00272   xfile_cat.error()
00273     << "Unexpected bin type " << egg_bin->get_bin_number() << "\n";
00274   return false;
00275 }
00276 
00277 ////////////////////////////////////////////////////////////////////
00278 //     Function: XFileMaker::add_polyset
00279 //       Access: Private
00280 //  Description: Adds a mesh object corresponding to the collection of
00281 //               polygons within the indicated bin.
00282 ////////////////////////////////////////////////////////////////////
00283 bool XFileMaker::
00284 add_polyset(EggBin *egg_bin, LPDIRECTXFILEDATA dx_parent) {
00285   // Make sure that all our polygons are reasonable.
00286   egg_bin->remove_invalid_primitives();
00287 
00288   XFileMesh *mesh = get_mesh(dx_parent);
00289 
00290   EggGroupNode::iterator ci;
00291   for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) {
00292     EggPolygon *poly;
00293     DCAST_INTO_R(poly, *ci, false);
00294 
00295     mesh->add_polygon(poly);
00296   }
00297 
00298   return true;
00299 }
00300 
00301   
00302 ////////////////////////////////////////////////////////////////////
00303 //     Function: XFileMaker::recurse_nodes
00304 //       Access: Private
00305 //  Description: Adds each child of the indicated Node as a child of
00306 //               the indicated DX object.
00307 ////////////////////////////////////////////////////////////////////
00308 bool XFileMaker::
00309 recurse_nodes(EggGroupNode *egg_node, LPDIRECTXFILEDATA dx_parent) {
00310   EggGroupNode::iterator ci;
00311   for (ci = egg_node->begin(); ci != egg_node->end(); ++ci) {
00312     EggNode *node = (*ci);
00313     if (!add_node(node, dx_parent)) {
00314       return false;
00315     }
00316   }
00317 
00318   return true;
00319 }
00320 
00321 ////////////////////////////////////////////////////////////////////
00322 //     Function: XFileMaker::create_object
00323 //       Access: Private
00324 //  Description: Creates a DX data object.
00325 ////////////////////////////////////////////////////////////////////
00326 bool XFileMaker::
00327 create_object(LPDIRECTXFILEDATA &obj, REFGUID template_id,
00328               const string &name, const Datagram &dg) {
00329   HRESULT hr;
00330 
00331   string nice_name = make_nice_name(name);
00332 
00333   int data_size = dg.get_length();
00334   void *data_pointer = (void *)dg.get_data();
00335 
00336   if (data_size == 0) {
00337     data_pointer = (void *)NULL;
00338   }
00339 
00340   hr = _dx_file_save->CreateDataObject
00341     (template_id, nice_name.c_str(), NULL, 
00342      data_size, data_pointer, &obj);
00343 
00344   if (hr != DXFILE_OK) {
00345     nout << "Unable to create data object for " << name << "\n";
00346     return false;
00347   }
00348   return true;
00349 }
00350 
00351 ////////////////////////////////////////////////////////////////////
00352 //     Function: XFileMaker::create_frame
00353 //       Access: Private
00354 //  Description: Creates a "frame" object with the indicated name.
00355 ////////////////////////////////////////////////////////////////////
00356 bool XFileMaker::
00357 create_frame(LPDIRECTXFILEDATA &obj, const string &name) {
00358   return create_object(obj, TID_D3DRMFrame, name, Datagram());
00359 }
00360 
00361 ////////////////////////////////////////////////////////////////////
00362 //     Function: XFileMaker::add_frame_transform
00363 //       Access: Private
00364 //  Description: Adds a transformation matrix to the indicated frame.
00365 ////////////////////////////////////////////////////////////////////
00366 bool XFileMaker::
00367 add_frame_transform(LPDIRECTXFILEDATA obj, const LMatrix4f &mat) {
00368   Datagram raw_data;
00369   mat.write_datagram(raw_data);
00370 
00371   LPDIRECTXFILEDATA xtransform;
00372   if (!create_object(xtransform, TID_D3DRMFrameTransformMatrix, 
00373                      "transform", raw_data)) {
00374     return false;
00375   }
00376   if (!attach_and_release(xtransform, obj)) {
00377     return false;
00378   }
00379 
00380   return true;
00381 }
00382 
00383 ////////////////////////////////////////////////////////////////////
00384 //     Function: XFileMaker::attach_and_release
00385 //       Access: Private
00386 //  Description: Assigns the indicated X data object to the indicated
00387 //               parent, and releases the pointer.
00388 ////////////////////////////////////////////////////////////////////
00389 bool XFileMaker::
00390 attach_and_release(LPDIRECTXFILEDATA obj, LPDIRECTXFILEDATA dx_parent) {
00391   HRESULT hr;
00392 
00393   // First, make sure we don't have an outstanding mesh for this
00394   // object.
00395   if (!finalize_mesh(obj)) {
00396     return false;
00397   }
00398 
00399   if (dx_parent == NULL) {
00400     // No parent; it's a toplevel object.
00401     hr = _dx_file_save->SaveData(obj);
00402     if (hr != DXFILE_OK) {
00403       nout << "Unable to save data object\n";
00404       obj->Release();
00405       return false;
00406     }
00407   } else {
00408     // Got a parent; it's a child of the indicated object.
00409     hr = dx_parent->AddDataObject(obj);
00410     if (hr != DXFILE_OK) {
00411       nout << "Unable to save data object\n";
00412       obj->Release();
00413       return false;
00414     }
00415   }
00416 
00417   obj->Release();
00418   return true;
00419 }
00420 
00421 ////////////////////////////////////////////////////////////////////
00422 //     Function: XFileMaker::make_nice_name
00423 //       Access: Private, Static
00424 //  Description: Transforms the indicated egg name to a name that is
00425 //               acceptable to the DirectX format.
00426 ////////////////////////////////////////////////////////////////////
00427 string XFileMaker::
00428 make_nice_name(const string &str) {
00429   string result;
00430 
00431   string::const_iterator si;
00432   for (si = str.begin(); si != str.end(); ++si) {
00433     if (isalnum(*si)) {
00434       result += *si;
00435     } else {
00436       result += "_";
00437     }
00438   }
00439 
00440   if (!str.empty() && isdigit(str[0])) {
00441     // If the name begins with a digit, we must make it begin with
00442     // something else, like for instance an underscore.
00443     result = '_' + result;
00444   }
00445 
00446   return result;
00447 }
00448 
00449 ////////////////////////////////////////////////////////////////////
00450 //     Function: XFileMaker::get_mesh
00451 //       Access: Private
00452 //  Description: Returns a suitable XFileMesh object for creating
00453 //               meshes within the indicated dx_parent object.
00454 ////////////////////////////////////////////////////////////////////
00455 XFileMesh *XFileMaker::
00456 get_mesh(LPDIRECTXFILEDATA dx_parent) {
00457   Meshes::iterator mi = _meshes.find(dx_parent);
00458   if (mi != _meshes.end()) {
00459     // We've already started working on this dx_parent before; use the
00460     // same mesh object.
00461     return (*mi).second;
00462   }
00463 
00464   // We haven't seen this dx_parent before; create a new mesh object.
00465   XFileMesh *mesh = new XFileMesh;
00466   _meshes.insert(Meshes::value_type(dx_parent, mesh));
00467   return mesh;
00468 }
00469 
00470 
00471 ////////////////////////////////////////////////////////////////////
00472 //     Function: XFileMaker::finalize_mesh
00473 //       Access: Private
00474 //  Description: Creates the actual DX mesh object corresponding to
00475 //               the indicated dx_parent object.
00476 ////////////////////////////////////////////////////////////////////
00477 bool XFileMaker::
00478 finalize_mesh(LPDIRECTXFILEDATA dx_parent) {
00479   Meshes::iterator mi = _meshes.find(dx_parent);
00480   if (mi == _meshes.end()) {
00481     // We haven't got a mesh for this object; do nothing.
00482     return true;
00483   }
00484 
00485   XFileMesh *mesh = (*mi).second;
00486   _meshes.erase(mi);
00487 
00488   // Get a unique number for each mesh.
00489   _mesh_index++;
00490   string mesh_index = format_string(_mesh_index);
00491 
00492   // Finally, create the Mesh object.
00493   Datagram raw_data;
00494   mesh->make_mesh_data(raw_data);
00495 
00496   LPDIRECTXFILEDATA xobj;
00497   if (!create_object(xobj, TID_D3DRMMesh, "mesh" + mesh_index, raw_data)) {
00498     return false;
00499   }
00500 
00501   if (mesh->has_normals()) {
00502     // Tack on normals.
00503     LPDIRECTXFILEDATA xnormals;
00504     mesh->make_normal_data(raw_data);
00505     if (!create_object(xnormals, TID_D3DRMMeshNormals, "norms" + mesh_index,
00506                        raw_data)) {
00507       return false;
00508     }
00509     if (!attach_and_release(xnormals, xobj)) {
00510       return false;
00511     }
00512   }
00513 
00514   if (mesh->has_colors()) {
00515     // Tack on colors.
00516     LPDIRECTXFILEDATA xcolors;
00517     mesh->make_color_data(raw_data);
00518     if (!create_object(xcolors, TID_D3DRMMeshVertexColors, 
00519                        "colors" + mesh_index, raw_data)) {
00520       return false;
00521     }
00522     if (!attach_and_release(xcolors, xobj)) {
00523       return false;
00524     }
00525   }
00526 
00527   if (mesh->has_uvs()) {
00528     // Tack on texture coordinates.
00529     LPDIRECTXFILEDATA xuvs;
00530     mesh->make_uv_data(raw_data);
00531     if (!create_object(xuvs, TID_D3DRMMeshTextureCoords, 
00532                        "uvs" + mesh_index, raw_data)) {
00533       return false;
00534     }
00535     if (!attach_and_release(xuvs, xobj)) {
00536       return false;
00537     }
00538   }
00539 
00540   if (mesh->has_materials()) {
00541     // Tack on material definitions.
00542     LPDIRECTXFILEDATA xmaterial_list;
00543     mesh->make_material_list_data(raw_data);
00544     if (!create_object(xmaterial_list, TID_D3DRMMeshMaterialList, 
00545                        "materials" + mesh_index, raw_data)) {
00546       return false;
00547     }
00548 
00549     // Now we need to iterate through the list of Materials
00550     // themselves, and add *these* as children of the material list.
00551     int num_materials = mesh->get_num_materials();
00552     for (int i = 0; i < num_materials; i++) {
00553       XFileMaterial *material = mesh->get_material(i);
00554       LPDIRECTXFILEDATA xmaterial;
00555       material->make_material_data(raw_data);
00556       if (!create_object(xmaterial, TID_D3DRMMaterial, 
00557                          "material" + mesh_index + "_" + format_string(i),
00558                          raw_data)) {
00559         return false;
00560       }
00561 
00562       // Also, if the Material has a texture map, we must add *this*
00563       // as a child of the material.  What a weird system.
00564       if (material->has_texture()) {
00565         LPDIRECTXFILEDATA xtexture;
00566         material->make_texture_data(raw_data);
00567         if (!create_object(xtexture, TID_D3DRMTextureFilename, 
00568                            "texture" + mesh_index + "_" + format_string(i),
00569                            raw_data)) {
00570           return false;
00571         }
00572 
00573         if (!attach_and_release(xtexture, xmaterial)) {
00574           return false;
00575         }
00576       }
00577 
00578       if (!attach_and_release(xmaterial, xmaterial_list)) {
00579         return false;
00580       }
00581     }
00582 
00583     if (!attach_and_release(xmaterial_list, xobj)) {
00584       return false;
00585     }
00586   }
00587 
00588   if (!attach_and_release(xobj, dx_parent)) {
00589     return false;
00590   }
00591   return true;
00592 }

Generated on Fri May 2 03:21:52 2003 for Panda-Tool by doxygen1.3