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

pandatool/src/bam/bamToEgg.cxx

Go to the documentation of this file.
00001 // Filename: bamToEgg.cxx
00002 // Created by:  drose (25Jun01)
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 "bamToEgg.h"
00020 
00021 #include "pandaNode.h"
00022 #include "workingNodePath.h"
00023 #include "nodePath.h"
00024 #include "billboardEffect.h"
00025 #include "renderEffects.h"
00026 #include "transformState.h"
00027 #include "colorScaleAttrib.h"
00028 #include "colorAttrib.h"
00029 #include "textureAttrib.h"
00030 #include "cullFaceAttrib.h"
00031 #include "lodNode.h"
00032 #include "geomNode.h"
00033 #include "geom.h"
00034 #include "geomTri.h"
00035 #include "string_utils.h"
00036 #include "bamFile.h"
00037 #include "eggGroup.h"
00038 #include "eggVertexPool.h"
00039 #include "eggVertex.h"
00040 #include "eggPrimitive.h"
00041 #include "eggPolygon.h"
00042 #include "eggTexture.h"
00043 #include "eggMaterial.h"
00044 #include "somethingToEggConverter.h"
00045 #include "dcast.h"
00046 
00047 
00048 ////////////////////////////////////////////////////////////////////
00049 //     Function: BamToEgg::Constructor
00050 //       Access: Public
00051 //  Description:
00052 ////////////////////////////////////////////////////////////////////
00053 BamToEgg::
00054 BamToEgg() :
00055   SomethingToEgg("Bam", ".bam")
00056 {
00057   add_path_replace_options();
00058   add_path_store_options();
00059 
00060   set_program_description
00061     ("This program converts native Panda Bam files to egg.  The conversion "
00062      "is somewhat incomplete; running egg2bam followed by bam2egg should not "
00063      " be expected to yield the same egg file you started with.");
00064 
00065   redescribe_option
00066     ("cs",
00067      "Specify the coordinate system of the input " + _format_name +
00068      " file.  By default, this is taken from the Configrc file, which "
00069      "is currently " + format_string(default_coordinate_system) + ".");
00070 
00071   _coordinate_system = default_coordinate_system;
00072 }
00073 
00074 ////////////////////////////////////////////////////////////////////
00075 //     Function: BamToEgg::run
00076 //       Access: Public
00077 //  Description:
00078 ////////////////////////////////////////////////////////////////////
00079 void BamToEgg::
00080 run() {
00081   BamFile bam_file;
00082 
00083   if (!bam_file.open_read(_input_filename)) {
00084     nout << "Unable to read " << _input_filename << "\n";
00085     exit(1);
00086   }
00087 
00088   nout << _input_filename << " : Bam version "
00089        << bam_file.get_file_major_ver() << "." 
00090        << bam_file.get_file_minor_ver() << "\n";
00091 
00092   typedef pvector<TypedWritable *> Objects;
00093   Objects objects;
00094   TypedWritable *object = bam_file.read_object();
00095   while (object != (TypedWritable *)NULL || !bam_file.is_eof()) {
00096     if (object != (TypedWritable *)NULL) {
00097       objects.push_back(object);
00098     }
00099     object = bam_file.read_object();
00100   }
00101   bam_file.resolve();
00102   bam_file.close();
00103 
00104   _data.set_coordinate_system(_coordinate_system);
00105   _vpool = new EggVertexPool("vpool");
00106   _data.add_child(_vpool);
00107 
00108   if (objects.size() == 1 && 
00109       objects[0]->is_of_type(PandaNode::get_class_type())) {
00110     PandaNode *node = DCAST(PandaNode, objects[0]);
00111     NodePath root(node);
00112     convert_node(WorkingNodePath(root), &_data, false);
00113 
00114   } else {
00115     nout << "File does not contain a scene graph.\n";
00116     exit(1);
00117   }
00118 
00119   // Remove the vertex pool if it has no vertices.
00120   if (_vpool->empty()) {
00121     _data.remove_child(_vpool);
00122   }
00123 
00124   write_egg_file();
00125 }
00126 
00127 ////////////////////////////////////////////////////////////////////
00128 //     Function: BamToEgg::convert_node
00129 //       Access: Private
00130 //  Description: Converts the indicated node to the corresponding Egg
00131 //               constructs, by first determining what kind of node it
00132 //               is.
00133 ////////////////////////////////////////////////////////////////////
00134 void BamToEgg::
00135 convert_node(const WorkingNodePath &node_path, EggGroupNode *egg_parent,
00136              bool has_decal) {
00137   PandaNode *node = node_path.node();
00138   if (node->is_geom_node()) {
00139     convert_geom_node(DCAST(GeomNode, node), node_path, egg_parent, has_decal);
00140 
00141   } else if (node->is_of_type(LODNode::get_class_type())) {
00142     convert_lod_node(DCAST(LODNode, node), node_path, egg_parent, has_decal);
00143 
00144   } else {
00145     // Just a generic node.
00146     EggGroup *egg_group = new EggGroup(node->get_name());
00147     egg_parent->add_child(egg_group);
00148     apply_node_properties(egg_group, node);
00149     
00150     recurse_nodes(node_path, egg_group, has_decal);
00151   }
00152 }
00153 
00154 ////////////////////////////////////////////////////////////////////
00155 //     Function: BamToEgg::convert_lod_node
00156 //       Access: Private
00157 //  Description: Converts the indicated LODNode to the corresponding
00158 //               Egg constructs.
00159 ////////////////////////////////////////////////////////////////////
00160 void BamToEgg::
00161 convert_lod_node(LODNode *node, const WorkingNodePath &node_path,
00162                  EggGroupNode *egg_parent, bool has_decal) {
00163   // An LOD node gets converted to an ordinary EggGroup, but we apply
00164   // the appropriate switch conditions to each of our children.
00165   EggGroup *egg_group = new EggGroup(node->get_name());
00166   egg_parent->add_child(egg_group);
00167   apply_node_properties(egg_group, node);
00168 
00169   int num_children = node->get_num_children();
00170   int num_switches = node->get_num_switches();
00171 
00172   num_children = min(num_children, num_switches);
00173 
00174   for (int i = 0; i < num_children; i++) {
00175     PandaNode *child = node->get_child(i);
00176 
00177     // Convert just this one node to an EggGroup.
00178     PT(EggGroup) next_group = new EggGroup;
00179     convert_node(WorkingNodePath(node_path, child), next_group, has_decal);
00180 
00181     if (next_group->size() == 1) {
00182       // If we have exactly one child, and that child is an EggGroup,
00183       // collapse.
00184       EggNode *child_node = *next_group->begin();
00185       if (child_node->is_of_type(EggGroup::get_class_type())) {
00186         PT(EggGroup) child = DCAST(EggGroup, child_node);
00187         next_group->remove_child(child.p());
00188         next_group = child;
00189       }
00190     }
00191 
00192     // Now set up the switching properties appropriately.
00193     float in = node->get_in(i);
00194     float out = node->get_out(i);
00195     LPoint3f center = node->get_center();
00196     EggSwitchConditionDistance dist(in, out, LCAST(double, center));
00197     next_group->set_lod(dist);
00198     egg_group->add_child(next_group.p());
00199   }
00200 }
00201 
00202 ////////////////////////////////////////////////////////////////////
00203 //     Function: BamToEgg::convert_geom_node
00204 //       Access: Private
00205 //  Description: Converts a GeomNode to the corresponding egg
00206 //               structures.
00207 ////////////////////////////////////////////////////////////////////
00208 void BamToEgg::
00209 convert_geom_node(GeomNode *node, const WorkingNodePath &node_path, 
00210                   EggGroupNode *egg_parent, bool has_decal) {
00211   PT(EggGroup) egg_group = new EggGroup(node->get_name());
00212   bool fancy_attributes = apply_node_properties(egg_group, node);
00213 
00214   if (node->get_effects()->has_decal()) {
00215     has_decal = true;
00216   }
00217 
00218   if (has_decal) {
00219     egg_group->set_decal_flag(true);
00220   }
00221 
00222   if (fancy_attributes || has_decal) {
00223     // If we have any fancy attributes on the node, or if we're making
00224     // decal geometry, we have to make a special node to hold the
00225     // geometry (normally it would just appear within its parent).
00226     egg_parent->add_child(egg_group.p());
00227     egg_parent = egg_group;
00228   }
00229 
00230   NodePath np = node_path.get_node_path();
00231   CPT(RenderState) net_state = np.get_net_state();
00232   CPT(TransformState) net_transform = np.get_net_transform();
00233   LMatrix4f net_mat = net_transform->get_mat();
00234   LMatrix4f inv = LCAST(float, egg_parent->get_vertex_frame_inv());
00235   net_mat = net_mat * inv;
00236 
00237   // Now get out all the various kinds of geometry.
00238   int num_geoms = node->get_num_geoms();
00239   for (int i = 0; i < num_geoms; i++) {
00240     CPT(RenderState) geom_state = net_state->compose(node->get_geom_state(i));
00241 
00242     Geom *geom = node->get_geom(i);
00243     // Explode the Geom before we try to deal with it.  That way, we
00244     // don't have to know about tristrips or whatnot.
00245     PT(Geom) exploded = geom->explode();
00246 
00247     // Now determine what kind of Geom we've got.  Chances are good
00248     // it's triangles.
00249     if (exploded->is_of_type(GeomTri::get_class_type())) {
00250       convert_geom_tri(DCAST(GeomTri, exploded), geom_state, net_mat,
00251                        egg_parent);
00252     }
00253   }
00254   
00255   recurse_nodes(node_path, egg_parent, has_decal);
00256 }
00257 
00258 ////////////////////////////////////////////////////////////////////
00259 //     Function: BamToEgg::convert_geom
00260 //       Access: Private
00261 //  Description: 
00262 ////////////////////////////////////////////////////////////////////
00263 void BamToEgg::
00264 convert_geom_tri(GeomTri *geom, const RenderState *net_state, 
00265                  const LMatrix4f &net_mat, EggGroupNode *egg_parent) {
00266   int nprims = geom->get_num_prims();
00267   Geom::VertexIterator vi = geom->make_vertex_iterator();
00268   Geom::NormalIterator ni = geom->make_normal_iterator();
00269   Geom::TexCoordIterator ti = geom->make_texcoord_iterator();
00270   Geom::ColorIterator ci = geom->make_color_iterator();
00271 
00272   GeomBindType vb = geom->get_binding(G_COORD);
00273   GeomBindType nb = geom->get_binding(G_NORMAL);
00274   GeomBindType tb = geom->get_binding(G_TEXCOORD);
00275   GeomBindType cb = geom->get_binding(G_COLOR);
00276 
00277   // Check for a color scale.
00278   LVecBase4f color_scale(1.0f, 1.0f, 1.0f, 1.0f);
00279   const RenderAttrib *color_scale_attrib = net_state->get_attrib(ColorScaleAttrib::get_class_type());
00280   if (color_scale_attrib != (const RenderAttrib *)NULL) {
00281     const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, color_scale_attrib);
00282     color_scale = csa->get_scale();
00283   }
00284 
00285   // Check for a color override.
00286   bool has_color_override = false;
00287   bool has_color_off = false;
00288   Colorf color_override;
00289   const RenderAttrib *color_attrib = net_state->get_attrib(ColorAttrib::get_class_type());
00290   if (color_attrib != (const RenderAttrib *)NULL) {
00291     const ColorAttrib *ca = DCAST(ColorAttrib, color_attrib);
00292     if (ca->get_color_type() == ColorAttrib::T_flat) {
00293       has_color_override = true;
00294       color_override = ca->get_color();
00295       color_override.set(color_override[0] * color_scale[0],
00296                          color_override[1] * color_scale[1],
00297                          color_override[2] * color_scale[2],
00298                          color_override[3] * color_scale[3]);
00299 
00300     } else if (ca->get_color_type() == ColorAttrib::T_off) {
00301       has_color_off = true;
00302     }
00303   }
00304 
00305   // Check for a texture.
00306   EggTexture *egg_tex = (EggTexture *)NULL;
00307   const RenderAttrib *tex_attrib = net_state->get_attrib(TextureAttrib::get_class_type());
00308   if (tex_attrib != (const RenderAttrib *)NULL) {
00309     const TextureAttrib *ta = DCAST(TextureAttrib, tex_attrib);
00310     egg_tex = get_egg_texture(ta->get_texture());
00311   }
00312 
00313   // Check the backface flag.
00314   bool bface = false;
00315   const RenderAttrib *cf_attrib = net_state->get_attrib(CullFaceAttrib::get_class_type());
00316   if (cf_attrib != (const RenderAttrib *)NULL) {
00317     const CullFaceAttrib *cfa = DCAST(CullFaceAttrib, cf_attrib);
00318     if (cfa->get_effective_mode() == CullFaceAttrib::M_cull_none) {
00319       bface = true;
00320     }
00321   }
00322 
00323   Normalf normal;
00324   Colorf color;
00325 
00326   // Get overall properties.
00327   if (nb == G_OVERALL) {
00328     normal = geom->get_next_normal(ni);
00329   }
00330   if (cb == G_OVERALL) {
00331     color = geom->get_next_color(ci);
00332   }
00333 
00334   for (int i = 0; i < nprims; i++) {
00335     // Get per-prim properties.
00336     if (nb == G_PER_PRIM) {
00337       normal = geom->get_next_normal(ni);
00338     }
00339     if (cb == G_PER_PRIM) {
00340       color = geom->get_next_color(ci);
00341     }
00342 
00343     EggPolygon *egg_poly = new EggPolygon;
00344     egg_parent->add_child(egg_poly);
00345     if (egg_tex != (EggTexture *)NULL) {
00346       egg_poly->set_texture(egg_tex);
00347     }
00348 
00349     if (bface) {
00350       egg_poly->set_bface_flag(true);
00351     }
00352 
00353     for (int j = 0; j < 3; j++) {
00354       EggVertex egg_vert;
00355 
00356       // Get per-vertex properties.
00357       if (vb == G_PER_VERTEX) {
00358         Vertexf vertex = geom->get_next_vertex(vi);
00359         egg_vert.set_pos(LCAST(double, vertex * net_mat));
00360       }
00361       if (nb == G_PER_VERTEX) {
00362         normal = geom->get_next_normal(ni) * net_mat;
00363       }
00364       if (tb == G_PER_VERTEX) {
00365         TexCoordf uv = geom->get_next_texcoord(ti);
00366         egg_vert.set_uv(LCAST(double, uv));
00367       }
00368       if (cb == G_PER_VERTEX) {
00369         color = geom->get_next_color(ci);
00370       }
00371 
00372       if (nb != G_OFF) {
00373         egg_vert.set_normal(LCAST(double, normal * net_mat));
00374       }
00375 
00376       if (has_color_override) {
00377         egg_vert.set_color(color_override);
00378 
00379       } else if (!has_color_off && cb != G_OFF) {
00380         egg_vert.set_color(Colorf(color[0] * color_scale[0],
00381                                   color[1] * color_scale[1],
00382                                   color[2] * color_scale[2],
00383                                   color[3] * color_scale[3]));
00384       }
00385 
00386       EggVertex *new_egg_vert = _vpool->create_unique_vertex(egg_vert);
00387       egg_poly->add_vertex(new_egg_vert);
00388     }
00389   }
00390 }
00391 
00392 ////////////////////////////////////////////////////////////////////
00393 //     Function: BamToEgg::recurse_nodes
00394 //       Access: Private
00395 //  Description: Converts all the children of the indicated node.
00396 ////////////////////////////////////////////////////////////////////
00397 void BamToEgg::
00398 recurse_nodes(const WorkingNodePath &node_path, EggGroupNode *egg_parent,
00399               bool has_decal) {
00400   PandaNode *node = node_path.node();
00401   int num_children = node->get_num_children();
00402   
00403   for (int i = 0; i < num_children; i++) {
00404     PandaNode *child = node->get_child(i);
00405     convert_node(WorkingNodePath(node_path, child), egg_parent, has_decal);
00406   }
00407 }
00408 
00409 ////////////////////////////////////////////////////////////////////
00410 //     Function: BamToEgg::apply_node_properties
00411 //       Access: Public
00412 //  Description: Applies any special properties that might be stored
00413 //               on the node, like billboarding.  Returns true if any
00414 //               were applied, false otherwise.
00415 ////////////////////////////////////////////////////////////////////
00416 bool BamToEgg::
00417 apply_node_properties(EggGroup *egg_group, PandaNode *node) {
00418   bool any_applied = false;
00419 
00420   if (node->get_draw_mask().is_zero()) {
00421     // This node is hidden.  We'll go ahead and convert it, but we'll
00422     // put in the "backstage" flag to mean it's not real geometry.
00423     egg_group->add_object_type("backstage");
00424   }
00425 
00426   const RenderEffects *effects = node->get_effects();
00427   const BillboardEffect *bbe = effects->get_billboard();
00428   if (bbe != (const BillboardEffect *)NULL) {
00429     if (bbe->get_axial_rotate()) {
00430       egg_group->set_billboard_type(EggGroup::BT_axis);
00431       any_applied = true;
00432 
00433     } else if (bbe->get_eye_relative()) {
00434       egg_group->set_billboard_type(EggGroup::BT_point_camera_relative);
00435       any_applied = true;
00436 
00437     } else {
00438       egg_group->set_billboard_type(EggGroup::BT_point_world_relative);
00439       any_applied = true;
00440     }
00441   }
00442 
00443   const TransformState *transform = node->get_transform();
00444   if (!transform->is_identity()) {
00445     if (transform->has_components()) {
00446       // If the transform can be represented componentwise, we prefer
00447       // storing it that way in the egg file.
00448       const LVecBase3f &scale = transform->get_scale();
00449       const LQuaternionf &quat = transform->get_quat();
00450       const LVecBase3f &pos = transform->get_pos();
00451       if (!scale.almost_equal(LVecBase3f(1.0f, 1.0f, 1.0f))) {
00452         egg_group->add_scale(LCAST(double, scale));
00453       }
00454       if (!quat.is_identity()) {
00455         egg_group->add_rotate(LCAST(double, quat));
00456       }
00457       if (!pos.almost_equal(LVecBase3f::zero())) {
00458         egg_group->add_translate(LCAST(double, pos));
00459       }
00460 
00461     } else if (transform->has_mat()) {
00462       // Otherwise, we store the raw matrix.
00463       const LMatrix4f &mat = transform->get_mat();
00464       egg_group->set_transform(LCAST(double, mat));
00465     }
00466     any_applied = true;
00467   }
00468 
00469   return any_applied;
00470 }
00471 
00472 ////////////////////////////////////////////////////////////////////
00473 //     Function: BamToEgg::get_egg_texture
00474 //       Access: Public
00475 //  Description: Returns an EggTexture pointer that corresponds to the
00476 //               indicated Texture.
00477 ////////////////////////////////////////////////////////////////////
00478 EggTexture *BamToEgg::
00479 get_egg_texture(Texture *tex) {
00480   if (tex != (Texture *)NULL) {
00481     if (tex->has_filename()) {
00482       Filename filename = _path_replace->convert_path(tex->get_filename());
00483       EggTexture temp(filename.get_basename_wo_extension(), filename);
00484       if (tex->has_alpha_filename()) {
00485         Filename alpha = _path_replace->convert_path(tex->get_alpha_filename());
00486         temp.set_alpha_filename(alpha);
00487       }
00488 
00489       switch (tex->get_minfilter()) {
00490       case Texture::FT_invalid:
00491         break;
00492       case Texture::FT_nearest:
00493         temp.set_minfilter(EggTexture::FT_nearest);
00494         break;
00495       case Texture::FT_linear:
00496         temp.set_minfilter(EggTexture::FT_linear);
00497         break;
00498       case Texture::FT_nearest_mipmap_nearest:
00499         temp.set_minfilter(EggTexture::FT_nearest_mipmap_nearest);
00500         break;
00501       case Texture::FT_linear_mipmap_nearest:
00502         temp.set_minfilter(EggTexture::FT_linear_mipmap_nearest);
00503         break;
00504       case Texture::FT_nearest_mipmap_linear:
00505         temp.set_minfilter(EggTexture::FT_nearest_mipmap_linear);
00506         break;
00507       case Texture::FT_linear_mipmap_linear:
00508         temp.set_minfilter(EggTexture::FT_linear_mipmap_linear);
00509         break;
00510       }
00511 
00512       switch (tex->get_magfilter()) {
00513       case Texture::FT_nearest:
00514         temp.set_magfilter(EggTexture::FT_nearest);
00515         break;
00516       case Texture::FT_linear:
00517         temp.set_magfilter(EggTexture::FT_linear);
00518         break;
00519       default:
00520         break;
00521       }
00522 
00523       switch (tex->get_wrapu()) {
00524       case Texture::WM_clamp:
00525         temp.set_wrap_u(EggTexture::WM_clamp);
00526         break;
00527       case Texture::WM_repeat:
00528         temp.set_wrap_u(EggTexture::WM_repeat);
00529         break;
00530 
00531       default:
00532         // There are some new wrap options on Texture that aren't yet
00533         // supported in egg.
00534         break;
00535       }
00536 
00537       switch (tex->get_wrapv()) {
00538       case Texture::WM_clamp:
00539         temp.set_wrap_v(EggTexture::WM_clamp);
00540         break;
00541       case Texture::WM_repeat:
00542         temp.set_wrap_v(EggTexture::WM_repeat);
00543         break;
00544 
00545       default:
00546         // There are some new wrap options on Texture that aren't yet
00547         // supported in egg.
00548         break;
00549       }
00550 
00551       PixelBuffer *pbuf = tex->get_ram_image();
00552       if (pbuf != (PixelBuffer *)NULL) {
00553         switch (pbuf->get_format()) {
00554         case PixelBuffer::F_red:
00555           temp.set_format(EggTexture::F_red);
00556           break;
00557         case PixelBuffer::F_green:
00558           temp.set_format(EggTexture::F_green);
00559           break;
00560         case PixelBuffer::F_blue:
00561           temp.set_format(EggTexture::F_blue);
00562           break;
00563         case PixelBuffer::F_alpha:
00564           temp.set_format(EggTexture::F_alpha);
00565           break;
00566         case PixelBuffer::F_rgb:
00567           temp.set_format(EggTexture::F_rgb);
00568           break;
00569         case PixelBuffer::F_rgb5:
00570           temp.set_format(EggTexture::F_rgb5);
00571           break;
00572         case PixelBuffer::F_rgb8:
00573           temp.set_format(EggTexture::F_rgb8);
00574           break;
00575         case PixelBuffer::F_rgb12:
00576           temp.set_format(EggTexture::F_rgb12);
00577           break;
00578         case PixelBuffer::F_rgb332:
00579           temp.set_format(EggTexture::F_rgb332);
00580           break;
00581         case PixelBuffer::F_rgba:
00582           temp.set_format(EggTexture::F_rgba);
00583           break;
00584         case PixelBuffer::F_rgbm:
00585           temp.set_format(EggTexture::F_rgbm);
00586           break;
00587         case PixelBuffer::F_rgba4:
00588           temp.set_format(EggTexture::F_rgba4);
00589           break;
00590         case PixelBuffer::F_rgba5:
00591           temp.set_format(EggTexture::F_rgba5);
00592           break;
00593         case PixelBuffer::F_rgba8:
00594           temp.set_format(EggTexture::F_rgba8);
00595           break;
00596         case PixelBuffer::F_rgba12:
00597           temp.set_format(EggTexture::F_rgba12);
00598           break;
00599         case PixelBuffer::F_luminance:
00600           temp.set_format(EggTexture::F_luminance);
00601           break;
00602         case PixelBuffer::F_luminance_alpha:
00603           temp.set_format(EggTexture::F_luminance_alpha);
00604           break;
00605         case PixelBuffer::F_luminance_alphamask:
00606           temp.set_format(EggTexture::F_luminance_alphamask);
00607           break;
00608         default:
00609           break;
00610         }
00611       }
00612 
00613       return _textures.create_unique_texture(temp, ~EggTexture::E_tref_name);
00614     }
00615   }
00616 
00617   return NULL;
00618 }
00619 
00620 
00621 int main(int argc, char *argv[]) {
00622   BamToEgg prog;
00623   prog.parse_command_line(argc, argv);
00624   prog.run();
00625   return 0;
00626 }

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