GCC Code Coverage Report


Directory: ./
File: src/blender-geom-writer.cc
Date: 2024-10-14 11:04:13
Exec Total Coverage
Lines: 0 164 0.0%
Branches: 0 752 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
354