GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/blender-geom-writer.cc Lines: 2 158 1.3 %
Date: 2020-05-14 11:23:33 Branches: 3 780 0.4 %

Line Branch Exec Source
1
// Copyright (c) 2016, Joseph Mirabel
2
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
3
//
4
// This file is part of gepetto-viewer.
5
// gepetto-viewer is free software: you can redistribute it
6
// and/or modify it under the terms of the GNU Lesser General Public
7
// License as published by the Free Software Foundation, either version
8
// 3 of the License, or (at your option) any later version.
9
//
10
// gepetto-viewer is distributed in the hope that it will be
11
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
12
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
// General Lesser Public License for more details.  You should have
14
// received a copy of the GNU Lesser General Public License along with
15
// gepetto-viewer. If not, see <http://www.gnu.org/licenses/>.
16
17
#include <gepetto/viewer/blender-geom-writer.h>
18
19
#include <algorithm>
20
#include <sys/stat.h>
21
22
#include <osgDB/WriteFile>
23
24
#include <gepetto/viewer/node.h>
25
#include <gepetto/viewer/group-node.h>
26
#include <gepetto/viewer/leaf-node-arrow.h>
27
#include <gepetto/viewer/leaf-node-box.h>
28
#include <gepetto/viewer/leaf-node-capsule.h>
29
#include <gepetto/viewer/leaf-node-collada.h>
30
#include <gepetto/viewer/leaf-node-cone.h>
31
#include <gepetto/viewer/leaf-node-cylinder.h>
32
#include <gepetto/viewer/leaf-node-face.h>
33
#include <gepetto/viewer/leaf-node-ground.h>
34
#include <gepetto/viewer/leaf-node-light.h>
35
#include <gepetto/viewer/leaf-node-line.h>
36
#include <gepetto/viewer/leaf-node-sphere.h>
37
#include <gepetto/viewer/leaf-node-xyzaxis.h>
38
//#include <gepetto/viewer/node-rod.h>
39
40
namespace gepetto {
41
namespace viewer {
42
  namespace {
43
    const char indent[] = "  ";
44
    const char end = '\n';
45
    const char group[] = "##";
46
    const char node[] = "#";
47
    const char varMat[] = "material";
48
    const char warning[] = "# Warning: ";
49
1
    const std::string varShape = "currentShape";
50
51
    template <typename Vector>
52
      std::ostream& writeVectorAsList (std::ostream& os, const Vector& v)
53
    {
54
      os << "( ";
55
      for (int i = 0; i < Vector::num_components; ++i)
56
        os << v[i] << ", ";
57
      os << ")";
58
      return os;
59
    }
60
61
    template <>
62
      std::ostream& writeVectorAsList<osg::Quat> (std::ostream& os, const osg::Quat& q)
63
    {
64
      os << "( " << q.w() << ", " << q.x() << ", " << q.y() << ", " << q.z() << ", )";
65
      return os;
66
    }
67
68
    std::ostream& writeRGB(std::ostream& os, const osgVector4& rgba)
69
    {
70
      return os << "( " << rgba.r() << ", " << rgba.g() << ", " << rgba.b() << ", )";
71
    }
72
73
    void writeMaterial(std::ostream& os, const std::string& name, const osg::Material* mat,
74
        bool diffuse = true, bool ambient = false, bool specular = false) {
75
      const osg::Material::Face face = osg::Material::FRONT;
76
      os << varMat << " = bpy.data.materials.new(\"" << name << "\")" << end;
77
      if (diffuse)
78
        writeRGB (os << varMat << ".diffuse_color = ", mat->getDiffuse(face)) << end
79
          << varMat << ".diffuse_intensity = " << mat->getDiffuse(face).a() << end;
80
      if (ambient)
81
        writeRGB (os << varMat << ".ambient_color = ", mat->getAmbient(face)) << end
82
          << varMat << ".ambient_intensity = " << mat->getAmbient(face).a() << end;
83
      if (specular)
84
        writeRGB (os << varMat << ".specular_color = ", mat->getSpecular(face)) << end
85
          << varMat << ".specular_intensity = " << mat->getSpecular(face).a() << end;
86
    }
87
88
    void writeMaterial(std::ostream& os, const std::string& name, const osgVector4& rgbaDiffuse)
89
    {
90
      os << varMat << " = bpy.data.materials.new(\"" << name << "\")" << end;
91
      writeRGB (os << varMat << ".diffuse_color = ", rgbaDiffuse) << end
92
        << varMat << ".diffuse_intensity = " << rgbaDiffuse.a() << end;
93
    }
94
95
    template <typename NodeType> void setColor(std::ostream& os, NodeType& node, const std::string& var) {
96
      writeMaterial(os, node.getID() + "__mat", node.getColor());
97
      os << var << ".materials.append(" << varMat << ')' << end;
98
    }
99
100
    void setColor(std::ostream& os, LeafNodeCollada& node) {
101
      osg::Material* mat_ptr = dynamic_cast<osg::Material*> (
102
          node.getOsgNode()->getStateSet()->getAttribute(osg::StateAttribute::MATERIAL));
103
      if (mat_ptr != NULL) { // Color was set by URDF
104
        writeMaterial(os, node.getID() + "__mat", mat_ptr, true, false, false);
105
        os << "setMaterial (imported_objects, " << varMat << ')' << end;
106
      }
107
    }
108
109
110
    void loadMeshFile(std::ostream& os, const std::string& nodeName, const std::string fn)
111
    {
112
      os << "tagObjects()" << end;
113
114
      std::string ext = fn.substr(fn.find_last_of(".")+1,fn.size());
115
      std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
116
      if(ext == "obj") {
117
        os << "bpy.ops.import_scene.obj (filepath=\"" << fn << "\", axis_forward='Y', axis_up='Z')" << end;
118
      } else if (ext == "dae") {
119
        os << "bpy.ops.wm.collada_import (filepath=\"" << fn << "\")" << end;
120
      } else if (ext == "stl") {
121
        os << "bpy.ops.import_mesh.stl (filepath=\"" << fn << "\")" << end;
122
      } else {
123
        std::cout << "Extension of file " << fn << " with name " << nodeName
124
          << " is not known." << end
125
          << "You can load the file manually and add it to the empty object called " << nodeName << end
126
          << "To fix the issue, upate" __FILE__ ":" << __LINE__ << std::endl;
127
        os << "# Here goes the content of file " << fn << end;
128
        os << "# To fix it, upate" __FILE__ ":" << __LINE__ << end;
129
      }
130
131
      os
132
        << "imported_objects = getNonTaggedObjects ()" << end
133
        << "print(imported_objects)" << end
134
        << varShape << " = makeEmpty(\"" << nodeName << "__shape\")" << end
135
        << "setLocQuatSca(" << varShape << ')' << end
136
        << "setParent (imported_objects, " << varShape << ')' << end;
137
    }
138
  }
139
140
  BlenderGeomWriterVisitor::BlenderGeomWriterVisitor (const std::string& filename)
141
    : NodeVisitor(false)
142
    , filename_ (filename)
143
    , nodeCount_ (0)
144
    , groupDepth_ (0)
145
  {
146
    bool isNew = !openFile();
147
    const std::string comment = (isNew ? "" : "# ");
148
    out() << comment << "import bpy" << end << end
149
      << comment << "## Start convenient functions" << end
150
      << comment << "def checkConf():" << end
151
      << comment << "  if bpy.app.version[0:2] != (2, 75):" << end
152
      << comment << "    print(\"Using blender version \" + str(bpy.app.version))" << end
153
      << comment << "    print(\"Developed under version 2.75.0.\")" << end
154
      << comment << "    return False" << end
155
      << comment << "  if bpy.context.scene.render.engine != 'CYCLES':" << end
156
      << comment << "    print(\"Cycles renderer is prefered\")" << end
157
      << comment << "    return False" << end
158
      << comment << "  return True" << end
159
      << comment << end
160
      << comment << "taggedObjects = list()" << end
161
      << comment << "def tagObjects ():" << end
162
      << comment << "  global taggedObjects" << end
163
      << comment << "  taggedObjects = list ()" << end
164
      << comment << "  for obj in bpy.data.objects:" << end
165
      << comment << "    taggedObjects.append (obj.name)" << end
166
      << comment << "" << end
167
      << comment << "def getNonTaggedObjects ():" << end
168
      << comment << "  global taggedObjects" << end
169
      << comment << "  return [obj for obj in bpy.data.objects if obj.name not in taggedObjects]" << end
170
      << comment << "" << end
171
      << comment << "def setParent (children, parent):" << end
172
      << comment << "  for child in children:" << end
173
      << comment << "    child.parent = parent" << end << end
174
      << comment << "" << end
175
      << comment << "def setMaterial (children, mat):" << end
176
      << comment << "  for child in children:" << end
177
      << comment << "    child.data.materials.append(mat)" << end << end
178
      << comment << "" << end
179
      << comment << "def makeEmpty(objname):" << end
180
      << comment << "  bpy.ops.object.empty_add()" << end
181
      << comment << "  bpy.context.object.name = objname" << end
182
      << comment << "  return bpy.context.object" << end
183
      << comment << "" << end
184
      << comment << "def setLocQuatSca(obj, loc = None, quat=None, sca=None):" << end
185
      << comment << "  if loc is not None: obj.location = loc" << end
186
      << comment << "  if quat is not None: " << end
187
      << comment << "    obj.rotation_mode = 'QUATERNION'" << end
188
      << comment << "    obj.rotation_quaternion = quat" << end
189
      << comment << "  if sca is not None: obj.scale = sca" << end
190
      << comment << "" << end
191
      << comment << "def makePolyLine(objname, curvename, cList):" << end
192
      << comment << "  curvedata = bpy.data.curves.new(name=curvename, type='CURVE')" << end
193
      << comment << "  curvedata.dimensions = '3D'" << end
194
      << comment << "  curvedata.fill_mode = 'FULL'" << end
195
      << comment << "  curvedata.bevel_depth = 0.01" << end
196
      << comment << "  curvedata.bevel_resolution = 10" << end
197
      << comment << "" << end
198
      << comment << "  objectdata = bpy.data.objects.new(objname, curvedata)" << end
199
      << comment << "  objectdata.location = (0,0,0) #object origin" << end
200
      << comment << "  bpy.context.scene.objects.link(objectdata)" << end
201
      << comment << "  w = 1.0" << end
202
      << comment << "  polyline = curvedata.splines.new('POLY')" << end
203
      << comment << "  polyline.points.add(len(cList)-1)" << end
204
      << comment << "  for num in range(len(cList)):" << end
205
      << comment << "    x, y, z = cList[num]" << end
206
      << comment << "    polyline.points[num].co = (x, y, z, w)" << end
207
      << comment << "  return objectdata, curvedata" << end
208
      << comment << "" << end << end
209
      << comment << "## End of convenient functions" << end
210
      << comment << "checkConf()" << end << end
211
      << comment << "bpy.ops.view3d.snap_cursor_to_center()" << end << end
212
      << comment << "obj_stack = []" << end;
213
  }
214
215
  bool BlenderGeomWriterVisitor::openFile ()
216
  {
217
    struct stat buffer;
218
    bool ret = (stat (filename_.c_str(), &buffer) == 0);
219
    file_.open (filename_.c_str(),
220
        std::ofstream::out | std::ofstream::app);
221
    if (!file_.is_open ())
222
      throw std::ios_base::failure ("Unable to open file " + filename_);
223
    return ret;
224
  }
225
226
  void BlenderGeomWriterVisitor::apply (Node& node)
227
  {
228
    std::cerr << "This function should not be called. Type of node " << node.getID() << " may be unknown" << std::endl;
229
    standardApply(node);
230
  }
231
  void BlenderGeomWriterVisitor::apply (GroupNode& node)
232
  {
233
    for (std::size_t i = 0; i < groupDepth_ + 1; ++i)
234
      out() << group;
235
    out() << " Group " << node.getID() << end;
236
237
    out() << "bpy.ops.object.empty_add()" << end
238
      << "bpy.context.object.name = \"" << node.getID() << '"' << end;
239
    // Set parent of this group
240
    out() << "if obj_stack: bpy.context.object.parent = obj_stack[-1]" << end;
241
242
    ++groupDepth_;
243
    out() << "obj_stack.append(bpy.context.object)" << end;
244
    NodeVisitor::apply(static_cast<Node&>(node));
245
    --groupDepth_;
246
    out() << "obj_stack.pop()" << end;
247
248
    for (std::size_t i = 0; i < groupDepth_ + 1; ++i)
249
      out() << group;
250
    out() << end;
251
  }
252
  void BlenderGeomWriterVisitor::apply (LeafNodeArrow& node)
253
  {
254
    unimplemented("LeafNodeArrow", node);
255
  }
256
  void BlenderGeomWriterVisitor::apply (LeafNodeBox& node)
257
  {
258
    out()
259
      << "bpy.ops.mesh.primitive_cube_add ()" << end
260
      << varShape << " = bpy.context.object" << end;
261
262
    setColor(out(), node, varShape + ".data");
263
264
    // scale should use getHalfAxis() * getScale()
265
    standardApply(node, node.getHalfAxis());
266
  }
267
  void BlenderGeomWriterVisitor::apply (LeafNodeCapsule& node)
268
  {
269
    unimplemented("LeafNodeCapsule", node);
270
  }
271
  void BlenderGeomWriterVisitor::apply (LeafNodeCollada& node)
272
  {
273
    loadMeshFile (out(), node.getID(), node.meshFilePath());
274
275
    setColor(out(), node);
276
277
    standardApply(node);
278
  }
279
  void BlenderGeomWriterVisitor::apply (LeafNodeCone& node)
280
  {
281
    out() << "bpy.ops.mesh.primitive_cone_add (radius1="
282
      << node.getRadius() << ", depth=" << node.getHeight() << ')' << end
283
      << varShape << " = bpy.context.object" << end;
284
    setColor(out(), node, varShape + ".data");
285
    standardApply(node);
286
  }
287
  void BlenderGeomWriterVisitor::apply (LeafNodeCylinder& node)
288
  {
289
    out() << "bpy.ops.mesh.primitive_cylinder_add (radius="
290
      << node.getRadius() << ", depth=" << node.getHeight() << ')' << end
291
      << varShape << " = bpy.context.object" << end;
292
    setColor(out(), node, varShape + ".data");
293
    standardApply(node);
294
  }
295
  void BlenderGeomWriterVisitor::apply (LeafNodeFace& node)
296
  {
297
    unimplemented("LeafNodeFace", node);
298
  }
299
  void BlenderGeomWriterVisitor::apply (LeafNodeGround& node)
300
  {
301
    unimplemented("LeafNodeGround", node);
302
  }
303
  void BlenderGeomWriterVisitor::apply (LeafNodeLight& node)
304
  {
305
    unimplemented("LeafNodeLight", node);
306
  }
307
  void BlenderGeomWriterVisitor::apply (LeafNodeLine& node)
308
  {
309
    if (node.getMode() != GL_LINE_STRIP) {
310
      const char varCurve[] = "currentCurve";
311
312
      ::osg::Vec3ArrayRefPtr points = node.getPoints();
313
      out() << "points = [";
314
      for (std::size_t i = 0; i < points->size(); ++i)
315
        writeVectorAsList(out(), points->at(i)) << ',';
316
      out() << ']' << end;
317
318
      out()
319
        << varShape << ", " << varCurve
320
        << " = makePolyLine(\"" << node.getID() <<  "__shape\", \"" << node.getID() <<  "__curve\", points)" << end;
321
322
      setColor(out(), node, varCurve);
323
    } else {
324
      std::stringstream ss; ss << filename_ << "_curve" << nodeCount_ << ".dae";
325
      std::string fn = ss.str();
326
      osg::ref_ptr <osgDB::Options> os = new osgDB::Options;
327
      os->setOptionString ("NoExtras");
328
      osgDB::writeNodeFile (*node.asGroup(), fn, os.get());
329
330
      loadMeshFile (out(), node.getID(), fn);
331
      //setColor(out(), node);
332
    }
333
    standardApply(node);
334
  }
335
  void BlenderGeomWriterVisitor::apply (LeafNodeSphere& node)
336
  {
337
    out () << "bpy.ops.mesh.primitive_ico_sphere_add (size="
338
      << node.getRadius() << ")" << end
339
      << varShape << " = bpy.context.object" << end;
340
    setColor(out(), node, varShape + ".data");
341
    standardApply(node);
342
  }
343
  void BlenderGeomWriterVisitor::apply (LeafNodeXYZAxis& node)
344
  {
345
    unimplemented("LeafNodeXYZAxis", node);
346
  }
347
348
  void BlenderGeomWriterVisitor::standardApply (Node& node, osgVector3 scale)
349
  {
350
    const osgVector3 s = node.getScale();
351
    scale[0] *= s[0]; scale[1] *= s[1]; scale[2] *= s[2];
352
    const std::string& id = node.getID ();
353
    // Set name
354
    out()
355
      << varShape << ".name = \"" << id << "__shape\"" << end
356
      << "setLocQuatSca(" << varShape << ", ";
357
    writeVectorAsList(out() << "loc = " , node.getStaticPosition()) << ", ";
358
    writeVectorAsList(out() << "quat = ", node.getStaticRotation()) << ", ";
359
    writeVectorAsList(out() << "sca = " , scale) << ')' << end;
360
    out()
361
      << "obj = makeEmpty(\"" << id << "\")" << end
362
      << "setLocQuatSca(obj)" << end
363
      << varShape << ".parent = obj" << end;
364
    // Set parent group
365
    out() << "if obj_stack: obj.parent = obj_stack[-1]" << end;
366
367
    ++nodeCount_;
368
    NodeVisitor::apply (node);
369
  }
370
371
  void BlenderGeomWriterVisitor::unimplemented(const char* type, Node& node)
372
  {
373
    std::cout << type << " is not implemented. " << node.getID()
374
      << " not added" << std::endl;
375
    out() << varShape << " = makeEmpty(\"" << node.getID() << "\")" << end;
376
    standardApply(node);
377
  }
378
} // namespace viewer
379

3
} // namespace gepetto
380