1 #include <iostream>
2 #include <sstream>
3 #include "bwm_world.h"
4 #include "bwm_observer_mgr.h"
5 #include "algo/bwm_shape_file.h"
6 #include "algo/bwm_algo.h"
7 #include "algo/bwm_utils.h"
8 
9 #ifdef _MSC_VER
10 #  include "vcl_msvc_warnings.h"
11 #endif
12 
13 #include "vul/vul_file.h"
14 #include "vgl/vgl_vector_3d.h"
15 #include "vgui/vgui_dialog.h"
16 
17 #include "bwm_observable.h"
18 #include "bwm_observable_textured_mesh.h"
19 
20 bwm_world* bwm_world::instance_ = nullptr;
21 
instance()22 bwm_world* bwm_world::instance()
23 {
24   if (!instance_)
25     instance_ = new bwm_world();
26   return bwm_world::instance_;
27 }
28 
29 
set_world_pt(vgl_point_3d<double> const & pt)30 void  bwm_world::set_world_pt(vgl_point_3d<double> const& pt)
31 {
32   world_pt_ = pt;
33   vgl_vector_3d<double> normal(0, 0, 1);//z axis
34   world_plane_ = vgl_plane_3d<double>(normal, pt);
35   world_pt_valid_ = true;
36 }
37 // adds an observable to the world
add(bwm_observable_sptr obj)38 bool bwm_world::add(bwm_observable_sptr obj)
39 {
40   // find the object
41   std::vector<bwm_observable_sptr>::iterator it = objects_.begin();
42   while (it != objects_.end()) {
43     if (*it == obj)
44       return false;
45     ++it;
46   }
47   objects_.push_back(obj);
48   return true;
49 }
50 // removes an observable from the world
remove(bwm_observable_sptr obj)51 bool bwm_world::remove(bwm_observable_sptr obj)
52 {
53   // find the object
54   std::vector<bwm_observable_sptr>::iterator it = objects_.begin();
55   while (it != objects_.end()) {
56     if (*it == obj) {
57       objects_.erase(it, it+1);
58       return true;
59     }
60     it++;
61   }
62   return false; // object was not in the world
63 }
64 
set_lvcs(double lat,double lon,double elev)65 void bwm_world::set_lvcs(double lat, double lon, double elev)
66 {
67   // clean up the olde one, if any
68   lvcs_ = vpgl_lvcs(lat, lon, elev);
69   lvcs_valid_ = true;
70 }
71 
get_lvcs(vpgl_lvcs & lvcs)72 bool bwm_world::get_lvcs(vpgl_lvcs &lvcs)
73 {
74   // if lvcs is set get that
75   if (lvcs_valid_) {
76     lvcs = lvcs_;
77     return true;
78   }
79 #if 0 // commented out
80   // else, create from the world point
81   else if (world_pt_valid_) {
82     lvcs = vpgl_lvcs(world_pt_.x(), world_pt_.y(), world_pt_.z());
83     return true;
84   }
85 
86   // get from the tableau mgr, as the avg of the camera centers
87   else if (bwm_observer_mgr::instance()->comp_avg_camera_center(center)) {
88     lvcs = vpgl_lvcs(center.x(), center.y(), center.z());
89     return true;
90   }
91 #endif // 0
92 
93   // else, request from user
94   double lat, lon, elev;
95   vgui_dialog lvcs_dialog("Set LVCS");
96   lvcs_dialog.message("Please Enter and LVCS origin:");
97   lvcs_dialog.field("Latitude:", lat);
98   lvcs_dialog.field("Longitude:", lon);
99   lvcs_dialog.field("Elevation:", elev);
100   if (!lvcs_dialog.ask())
101     return false;
102   lvcs_ = vpgl_lvcs(lat, lon, elev);
103   lvcs_valid_ = true;
104   return true;
105 }
106 
load_shape_file()107 void bwm_world::load_shape_file()
108 {
109   std::string file = bwm_utils::select_file();
110   bwm_shape_file sfile;
111   if (sfile.load(file)) {
112     // "#define SHPT_POLYGONZ 15" in shapefil.h
113     if (sfile.shape_type() == 15) {
114       std::vector<std::vector<vsol_point_3d_sptr> > polys = sfile.vertices();
115       for (unsigned i=0; i<polys.size(); i++) {
116         bwm_observable_mesh_sptr mesh = new bwm_observable_mesh();
117         bwm_observer_mgr::instance()->attach(mesh);
118         vsol_polygon_3d_sptr poly3d = bwm_algo::move_points_to_plane(polys[i]);
119         mesh->set_object(poly3d);
120       }
121     }
122   }
123 }
124 
125 #if 0
126 void bwm_world::save_all()
127 {
128   if (objects_.size() == 0) {
129     vgui_dialog error ("Error");
130     error.message ("There is no object to save..." );
131     error.ask();
132     return;
133   }
134 
135   vgui_dialog_extensions save("Save 3D objects");
136   std::vector<std::string> file_types;
137   file_types.push_back("ply");
138   file_types.push_back("gml");
139   file_types.push_back("kml");
140   file_types.push_back("kml collada");
141   file_types.push_back("x3d");
142 
143   std::string ext, file_path;
144   unsigned t = 0;
145   save.dir("Path:", ext, file_path);
146   save.choice("File Type", file_types, t);
147   //save.line_break();
148 
149   if (!save.ask())
150     return;
151 
152   // what if they choose a dir instead of a file name
153   if (vul_file::is_directory(file_path)) {
154    // for (unsigned i=0; i<objects_.size(); i++) {
155     //  std::string path = file_path + "\\objects";
156 
157       if (file_types[t].compare("ply") == 0)
158         save_ply(path); // HOW ABOUT LVCS, do we use world point to create one
159 
160       else if (file_types[t].compare("gml") == 0)
161         save_gml(path);
162 
163       else if (file_types[t].compare("kml") == 0)
164         bwm_file_io::save_kml(objects_[i], path+".kml");
165 
166       else if (file_types[t].compare("kml collada") == 0)
167         bwm_file_io::save_kml_collada(objects_[i], path+".kml");  // ??what is the extension of kml collada??
168 
169       else if (file_types[t].compare("x3d") == 0)
170         bwm_file_io::save_x3d(objects_[i], path+".x3d");
171     }
172   }
173 }
174 #endif // 0
175 
save_ply()176 void bwm_world::save_ply()  // how about use lvcs??
177 {
178   vgui_dialog params("File Save");
179   std::string ext, list_name, empty="";
180   bool use_lvcs = false;
181 
182   params.file ("Filename...", ext, list_name);
183   params.checkbox("Use LVCS", use_lvcs);
184 
185   if (!params.ask())
186     return;
187 
188   if (list_name == "") {
189     vgui_dialog error ("Error");
190     error.message ("Please specify a filename." );
191     error.ask();
192     return;
193   }
194 
195   std::string directory_name = vul_file::dirname(list_name);
196 
197   std::ofstream list_out(list_name.data());
198   if (!list_out.good()) {
199     std::cerr << "error opening file "<< list_name <<std::endl;
200     return;
201   }
202 
203   for (unsigned idx=0; idx<objects_.size(); idx++) {
204     std::ostringstream objname;
205     std::ostringstream fullpath;
206     objname << "obj" << idx <<".ply";
207     fullpath << directory_name << '/' << objname.str();
208 
209     list_out << objname.str() << std::endl;
210     bwm_observable_sptr o = objects_[idx];
211     // figure out the lvcs here
212 
213     o->save(fullpath.str().data(),&lvcs_);
214   }
215 }
216 
save_gml()217 void bwm_world::save_gml()
218 {
219 #if 0
220   if (!lvcs_) {
221     std::cerr << "Error: lvcs not defined.\n";
222     return;
223   }
224 #endif // 0
225 
226   vgui_dialog params("File Save (.gml) ");
227   std::string ext, gml_filename, empty="";
228   std::string model_name;
229 
230   params.field("model name", model_name);
231 
232   params.file("Save...", ext, gml_filename);
233   if (!params.ask())
234     return;
235 
236   if (gml_filename == "") {
237     std::cerr << "Error: no filename selected.\n";
238     return;
239   }
240 
241   std::ofstream os(gml_filename.c_str());
242 
243   os << "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n"
244      << "<CityModel xmlns=\"http://www.citygml.org/citygml/1/0/0\" xmlns:gml=\"http://www.opengis.net/gml\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.citygml.org/citygml/1/0/0 http://www.citygml.org/citygml/1/0/0/CityGML.xsd\">\n"
245      << "<gml:description>" << model_name.c_str() << "</gml:description>\n"
246      << "<gml:name>" << model_name.c_str() << "</gml:name>\n";
247 
248   int obj_count = 0;
249   for (unsigned idx=0; idx<objects_.size(); idx++) {
250     bwm_observable_sptr obj = objects_[idx];
251     if (obj->type_name().compare("bwm_observable_textured_mesh") == 0) {
252       bwm_observable_textured_mesh* tm_obj = static_cast<bwm_observable_textured_mesh*>(obj.as_pointer());
253       tm_obj->save_gml(os, obj_count, &lvcs_);
254       os << "   </Building>"
255          << "  </cityObjectMember>";
256     }
257   }
258   os << " </CityModel>";
259   os.close();
260 }
261 
save_kml()262 void bwm_world::save_kml()
263 {
264   vpgl_lvcs lvcs;
265   if (!get_lvcs(lvcs)) {
266     std::cerr << "Error: lvcs not defined.\n";
267     return;
268   }
269 
270   vgui_dialog params("File Save");
271   std::string ext, kml_filename, empty="";
272   std::string model_name;
273   double ground_height = 0.0;
274   double x_offset = 0.0;
275   double y_offset = 0.0;
276 
277   params.field("model name",model_name);
278   params.field("ground height",ground_height);
279   params.field("x offset",x_offset);
280   params.field("y offset",y_offset);
281 
282   params.file("Save...",ext,kml_filename);
283   if (!params.ask())
284     return;
285 
286   if (kml_filename == "") {
287     std::cerr << "Error: no filename selected.\n";
288     return;
289   }
290 
291   std::ofstream os(kml_filename.c_str());
292 
293   os << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
294      << "<kml xmlns=\"http://earth.google.com/kml/2.1\">\n"
295      << "<Document>\n"
296      << "  <name>" << vul_file::strip_directory(kml_filename.c_str()) << "</name>\n"
297      << "  <open>1</open>\n"
298      << "  <Placemark>\n"
299      << "    <name>" << model_name.c_str() << "</name>\n"
300      << "    <visibility>1</visibility>\n"
301      << "    <MultiGeometry>\n";
302 
303   for (unsigned idx=0; idx<objects_.size(); idx++) {
304     bwm_observable_sptr obj = objects_[idx];
305     if (obj->type_name().compare("bwm_observable_textured_mesh") == 0) {
306       bwm_observable_textured_mesh* tm_obj = static_cast<bwm_observable_textured_mesh*>(obj.as_pointer());
307       tm_obj->save_kml(os, idx, &lvcs, ground_height, x_offset, y_offset);
308     }
309   }
310 
311   os << "    </MultiGeometry>\n"
312      << "  </Placemark>\n"
313      << "</Document>\n"
314      << "</kml>\n";
315 
316   os.close();
317 }
318 
save_x3d()319 void bwm_world::save_x3d()
320 {
321   vpgl_lvcs lvcs;
322   if (!get_lvcs(lvcs)) {
323     std::cerr << "Error: lvcs not defined.\n";
324     return;
325   }
326 
327   vgui_dialog params("File Save");
328   std::string ext, x3d_filename, empty="";
329 
330   params.file("Save...",ext,x3d_filename);
331   if (!params.ask())
332     return;
333 
334   if (x3d_filename == "") {
335     std::cerr << "Error: no filename selected.\n";
336     return;
337   }
338 
339   std::ofstream os(x3d_filename.c_str());
340 
341   os << "#VRML V2.0 utf8\n"
342      << "PROFILE Immersive\n\n";
343 
344   for (unsigned idx=0; idx<objects_.size(); idx++) {
345     bwm_observable_sptr obj = objects_[idx];
346     if (obj->type_name().compare("bwm_observable_textured_mesh") == 0) {
347       bwm_observable_textured_mesh* tm_obj = static_cast<bwm_observable_textured_mesh*> (obj.as_pointer());
348       tm_obj->save_x3d(os, &lvcs);
349       os << "      ]\n\n"
350          << "      solid TRUE\n"
351          << "      convex FALSE\n"
352          << "      creaseAngle 0\n"
353          << "    }\n"
354          << "  }\n"
355          << "}\n\n\n";
356     }
357   }
358 
359   os.close();
360   return;
361 }
362 
save_kml_collada()363 void bwm_world::save_kml_collada()
364 {
365   vpgl_lvcs lvcs;
366   if (!get_lvcs(lvcs)) {
367     std::cerr << "Error: lvcs not defined.\n";
368     return;
369   }
370 
371   double origin_lat = 0,origin_lon = 0, origin_elev = 0;
372   lvcs_.get_origin(origin_lat,origin_lon,origin_elev);
373 
374   vgui_dialog params("File Save");
375   std::string ext, kmz_dir, empty="";
376 
377   // guess at ground height = lowest vertex
378   double minz = 1e6;
379   for (unsigned idx=0; idx<objects_.size(); idx++) {
380     bwm_observable_sptr obj = objects_[idx];
381     obj->global_to_local(&lvcs, minz);
382   }
383 
384   double ground_height = minz;
385   double lat_offset = 0.0;
386   double lon_offset = 0.0;
387   std::string model_name;
388 
389   params.field("model name",model_name);
390   params.field("ground height",ground_height);
391   params.field("latitude offset",lat_offset);
392   params.field("longitude offset",lon_offset);
393 
394   params.file("Save...",ext,kmz_dir);
395   if (!params.ask())
396     return;
397 
398   if (kmz_dir == "") {
399     std::cerr << "Error: no filename selected.\n";
400     return;
401   }
402 
403   if (!vul_file::is_directory(kmz_dir)) {
404     std::cerr << "Error: Select a directory name.\n";
405     return;
406   }
407 
408   std::ostringstream dae_fname;
409   dae_fname << kmz_dir << "/models/mesh.dae";
410 
411   std::ofstream os (dae_fname.str().data());
412 
413   std::vector<std::string> image_names;
414   std::vector<std::string> image_fnames;
415   std::vector<std::string> material_ids;
416   std::vector<std::string> material_names;
417   std::vector<std::string> effect_ids;
418   std::vector<std::string> surface_ids;
419   std::vector<std::string> image_sampler_ids;
420   std::vector<std::string> geometry_ids;
421   std::vector<std::string> geometry_position_ids;
422   std::vector<std::string> geometry_position_array_ids;
423   std::vector<std::string> geometry_uv_ids;
424   std::vector<std::string> geometry_uv_array_ids;
425   std::vector<std::string> geometry_vertex_ids;
426   std::vector<std::string> mesh_ids;
427 
428   int nobjects = 0;
429   unsigned min_faces = 3;
430 
431   for (unsigned idx=0; idx<objects_.size(); idx++) {
432     bwm_observable_sptr obj = objects_[idx];
433     if ( obj->num_faces() <  min_faces)
434       // single mesh face is probably ground plane, which we do not want to render
435       continue;
436 
437     // check if object is texture mapped
438     if (obj->type_name().compare("bwm_observable_textured_mesh") == 0)
439     {
440       bwm_observable_textured_mesh* mesh = static_cast<bwm_observable_textured_mesh*>(obj.as_pointer());
441       std::string image_fname = vul_file::strip_directory(mesh->tex_map_uri()); // assume all faces have same texmap img
442       std::string image_name = vul_file::strip_extension(image_fname);
443 
444       std::ostringstream image_path;
445       image_path << "../images/" << image_fname;
446 
447       std::ostringstream objname;
448       objname << "object_"<<nobjects;
449 
450       std::ostringstream material_id;
451       material_id << objname.str() <<"_materialID";
452 
453       std::ostringstream material_name;
454       material_name << objname.str() <<"_material";
455 
456       std::ostringstream effect_id;
457       effect_id << objname.str() << "_effect";
458 
459       std::ostringstream surface_id;
460       surface_id << objname.str() << "_surface";
461 
462       std::ostringstream image_sampler_id;
463       image_sampler_id << objname.str() << "_sampler";
464 
465       std::ostringstream geometry_id;
466       geometry_id << objname.str() << "_geometry";
467 
468       std::ostringstream geometry_position_id;
469       geometry_position_id << objname.str() << "_geometry_position";
470 
471       std::ostringstream geometry_position_array_id;
472       geometry_position_array_id << objname.str() <<"_geometry_position_array";
473 
474       std::ostringstream geometry_uv_id;
475       geometry_uv_id << objname.str() << "_geometry_uv";
476 
477       std::ostringstream geometry_uv_array_id;
478       geometry_uv_array_id << objname.str() << "_geometry_uv_array";
479 
480       std::stringstream geometry_vertex_id;
481       geometry_vertex_id << objname.str() << "_geometry_vertex";
482 
483       mesh_ids.push_back(objname.str());
484       image_names.push_back(image_name);
485       image_fnames.push_back(image_path.str());
486       material_names.push_back(material_name.str());
487       material_ids.push_back(material_id.str());
488       effect_ids.push_back(effect_id.str());
489       surface_ids.push_back(surface_id.str());
490       image_sampler_ids.push_back(image_sampler_id.str());
491       geometry_ids.push_back(geometry_id.str());
492       geometry_position_ids.push_back(geometry_position_id.str());
493       geometry_position_array_ids.push_back(geometry_position_array_id.str());
494       geometry_uv_ids.push_back(geometry_uv_id.str());
495       geometry_uv_array_ids.push_back(geometry_uv_array_id.str());
496       geometry_vertex_ids.push_back(geometry_vertex_id.str());
497 
498       nobjects++;
499     }
500   }
501 
502   os <<"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
503      << "<COLLADA xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.4.1\">\n"
504 
505      << "  <asset>\n"
506      << "    <contributor>\n"
507      << "      <authoring_tool> Brown University World Modeler </authoring_tool>\n"
508      << "    </contributor>\n"
509      << "    <unit name=\"meters\" meter=\"1\"/>\n"
510      << "    <up_axis>Z_UP</up_axis>\n"
511      << "  </asset>\n"
512      << "  <library_images>\n";
513 
514   for (int i=0; i<nobjects; i++) {
515     os << "    <image id=\"" << image_names[i].c_str() << "\" name=\"" << image_names[i].c_str() << "\">\n"
516        << "      <init_from>" << image_fnames[i].c_str() << "</init_from>\n"
517        << "    </image>\n";
518   }
519   os << "  </library_images>\n";
520 
521   os << "  <library_materials>\n";
522   for (int i=0; i<nobjects; i++) {
523     os << "    <material id=\"" << material_ids[i].c_str() << "\" name=\"" << material_names[i].c_str() << "\">\n"
524        << "      <instance_effect url=\"#" << effect_ids[i].c_str() << "\"/>\n"
525        << "    </material>\n";
526   }
527   os << "  </library_materials>\n";
528 
529   os << "  <library_effects>\n";
530   for (int i=0; i<nobjects; i++)
531   {
532     os << "    <effect id=\"" << effect_ids[i].c_str() << "\" name=\"" << effect_ids[i].c_str() << "\">\n"
533        << "      <profile_COMMON>\n"
534        << "        <newparam sid=\"" << surface_ids[i].c_str() << "\">\n"
535        << "          <surface type=\"2D\">\n"
536        << "            <init_from>" << image_names[i].c_str() << "</init_from>\n"
537        << "          </surface>\n"
538        << "        </newparam>\n"
539        << "        <newparam sid=\"" << image_sampler_ids[i].c_str() << "\">\n"
540        << "          <sampler2D>\n"
541        << "            <source>" << surface_ids[i].c_str() << "</source>\n"
542        << "          </sampler2D>\n"
543        << "        </newparam>\n"
544        << "        <technique sid=\"COMMON\">\n"
545        << "          <phong>\n"
546        << "            <emission>\n"
547        << "              <color>0.0 0.0 0.0 1</color>\n"
548        << "            </emission>\n"
549        << "            <ambient>\n"
550        << "              <color>0.0 0.0 0.0 1</color>\n"
551        << "            </ambient>\n"
552        << "            <diffuse>\n"
553        << "              <texture texture=\"" << image_sampler_ids[i].c_str() << "\" texcoord=\"UVSET0\"/>\n"
554        << "            </diffuse>\n"
555        << "            <specular>\n"
556        << "              <color>0.33 0.33 0.33 1</color>\n"
557        << "            </specular>\n"
558        << "            <shininess>\n"
559        << "              <float>20.0</float>\n"
560        << "            </shininess>\n"
561        << "            <reflectivity>\n"
562        << "              <float>0.1</float>\n"
563        << "            </reflectivity>\n"
564        << "            <transparent>\n"
565        << "              <color>1 1 1 1</color>\n"
566        << "            </transparent>\n"
567        << "            <transparency>\n"
568        << "              <float>0.0</float>\n"
569        << "            </transparency>\n"
570        << "          </phong>\n"
571        << "        </technique>\n"
572        << "      </profile_COMMON>\n"
573        << "    </effect>\n";
574   }
575   os << "  </library_effects>\n";
576 
577   os << "  <library_geometries>\n";
578 
579   for (unsigned idx=0; idx<objects_.size(); idx++) {
580     bwm_observable_sptr obj = objects_[idx];
581     // assume object is texture mapped
582     //dbmsh3d_textured_mesh_mc* mesh = (dbmsh3d_textured_mesh_mc*)obj->get_object();
583     if (obj->num_faces() < min_faces) {
584       // single mesh face is probably ground plane, which we do not want to render
585       continue;
586     }
587 
588     if (obj->type_name().compare("bwm_observable_textured_mesh") == 0) {
589       bwm_observable_textured_mesh* tm_obj = static_cast<bwm_observable_textured_mesh*>(obj.as_pointer());
590       tm_obj->save_kml_collada(os, &lvcs, geometry_ids[idx],
591                                geometry_position_ids[idx],
592                                geometry_position_array_ids[idx],
593                                geometry_uv_ids[idx],
594                                geometry_uv_array_ids[idx],
595                                geometry_vertex_ids[idx],
596                                material_names[idx]);
597     }
598 
599     os << "</p>\n"
600        << "      </triangles>\n"
601        << "    </mesh>\n"
602        << "  </geometry>\n";
603     idx++;
604   }
605   os << "</library_geometries>\n";
606 
607   os << "<library_nodes>\n";
608   for (int i=0; i<nobjects; i++) {
609     os << "  <node id=\"Component_" << i << "\" name=\"Component_" << i << "\">\n"
610        << "    <node id=\"" << mesh_ids[i].c_str() << "\" name=\"" << mesh_ids[i].c_str() << "\">\n"
611        << "      <instance_geometry url=\"#" << geometry_ids[i].c_str() << "\">\n"
612        << "        <bind_material>\n"
613        << "          <technique_common>\n"
614        << "            <instance_material symbol=\"" <<material_names[i].c_str() << "\" target=\"#" << material_ids[i].c_str()<< "\">\n"
615        << "              <bind_vertex_input semantic=\"UVSET0\" input_semantic=\"TEXCOORD\" input_set=\"0\"/>\n"
616        << "            </instance_material>\n"
617        << "          </technique_common>\n"
618        << "        </bind_material>\n"
619        << "      </instance_geometry>\n"
620        << "    </node>\n"
621        << "  </node>\n";
622   }
623   os << "</library_nodes>\n";
624 
625   os << "<library_visual_scenes>\n"
626      << "  <visual_scene id=\"WorldModelerScene\" name=\"WorldModelerScene\">\n"
627      << "    <node id=\"Model\" name=\"Model\">\n";
628   for (int i=0; i<nobjects; i++) {
629     os << "      <node id=\"Component_" << i << "_1\" name=\"Component_" << i << "_1\">\n"
630        << "        <instance_node url=\"#Component_" << i << "\"/>\n"
631        << "      </node>\n";
632   }
633   os << "    </node>\n"
634      << "  </visual_scene>\n"
635      << "</library_visual_scenes>\n";
636 
637   os << "<scene>\n"
638      << "  <instance_visual_scene url=\"#WorldModelerScene\"/>\n"
639      << "</scene>\n"
640      << "</COLLADA>\n";
641 
642   os.close();
643 
644   std::ostringstream textures_fname;
645   textures_fname << kmz_dir << "/textures.txt";
646 
647   std::ofstream ost(textures_fname.str().data());
648 
649   for (int i=0; i<nobjects; i++) {
650     ost << '<' << image_fnames[i].c_str() << "> <" << image_fnames[i].c_str() << ">\n";
651   }
652 
653   std::ostringstream kml_fname;
654   kml_fname << kmz_dir << "/doc.kml";
655 
656   std::ofstream oskml(kml_fname.str().data());
657 
658   oskml << "<?xml version='1.0' encoding='UTF-8'?>\n"
659         << "<kml xmlns='http://earth.google.com/kml/2.1'>\n"
660         << "<Folder>\n"
661         << "  <name>" << model_name.c_str() << "</name>\n"
662         << "  <description><![CDATA[Created with <a href=\"http://www.lems.brown.edu\">CrossCut</a>]]></description>\n"
663         << "  <DocumentSource>CrossCut 1.0</DocumentSource>\n"
664         << "  <visibility>1</visibility>\n"
665         << "  <LookAt>\n"
666         << "    <heading>0</heading>\n"
667         << "    <tilt>45</tilt>\n"
668         << "    <longitude>" << origin_lon << "</longitude>\n"
669         << "    <latitude>" << origin_lat << "</latitude>\n"
670         << "    <range>200</range>\n"
671         << "    <altitude>" << 0.0f << "</altitude>\n"
672         << "  </LookAt>\n"
673         << "  <Folder>\n"
674         << "    <name>Tour</name>\n"
675         << "    <Placemark>\n"
676         << "      <name>Camera</name>\n"
677         << "      <visibility>1</visibility>\n"
678         << "    </Placemark>\n"
679         << "  </Folder>\n"
680         << "  <Placemark>\n"
681         << "    <name>Model</name>\n"
682         << "    <description><![CDATA[]]></description>\n"
683         << "    <Style id='default'>\n"
684         << "    </Style>\n"
685         << "    <Model>\n"
686         << "      <altitudeMode>relativeToGround</altitudeMode>\n"
687         << "      <Location>\n"
688         << "        <longitude>" << origin_lon + lon_offset << "</longitude>\n"
689         << "        <latitude>" << origin_lat + lat_offset << "</latitude>\n"
690         << "        <altitude>" << origin_elev - ground_height << "</altitude>\n"
691         << "      </Location>\n"
692         << "      <Orientation>\n"
693         << "        <heading>0</heading>\n"
694         << "        <tilt>0</tilt>\n"
695         << "        <roll>0</roll>\n"
696         << "      </Orientation>\n"
697         << "      <Scale>\n"
698         << "        <x>1.0</x>\n"
699         << "        <y>1.0</y>\n"
700         << "        <z>1.0</z>\n"
701         << "      </Scale>\n"
702         << "      <Link>\n"
703         << "        <href>models/mesh.dae</href>\n"
704         << "      </Link>\n"
705         << "    </Model>\n"
706         << "  </Placemark>\n"
707         << "</Folder>\n"
708         << "</kml>\n";
709 }
710 
clear()711 void bwm_world::clear()
712 {
713   world_pt_valid_ = false;
714   lvcs_valid_ = false;
715   objects_.clear();
716 }
717