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 |
|
|
|