GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/blender-geom-writer.cc Lines: 0 163 0.0 %
Date: 2024-04-14 11:13:22 Branches: 0 754 0.0 %

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