GCC Code Coverage Report


Directory: ./
File: src/leaf-node-collada.cpp
Date: 2025-04-14 11:18:00
Exec Total Coverage
Lines: 0 204 0.0%
Branches: 0 398 0.0%

Line Branch Exec Source
1 //
2 // leaf-node-collada.cpp
3 // gepetto-viewer
4 //
5 // Created by Anthony Couret, Mathieu Geisert in November 2014.
6 // Copyright (c) 2014 LAAS-CNRS. All rights reserved.
7 //
8
9 #include <gepetto/viewer/leaf-node-collada.h>
10 #include <sys/stat.h>
11
12 #include <clocale>
13 #include <fstream>
14 #include <ios>
15 #include <osg/LightModel>
16 #include <osg/Texture2D>
17 #include <osg/Version>
18 #include <osgDB/FileNameUtils>
19 #include <osgUtil/Optimizer>
20
21 #include "log.hh"
22
23 namespace gepetto {
24 namespace viewer {
25 ✗ inline bool fileExists(const char* fn) {
26 struct stat buffer;
27 ✗ return (stat(fn, &buffer) == 0);
28 }
29
30 ✗ std::string getCachedFileName(const std::string& meshfile) {
31 ✗ static const std::string exts[3] = {".osgb", ".osg2", ".osg"};
32 ✗ for (int i = 0; i < 3; ++i) {
33 ✗ std::string cached = meshfile + exts[i];
34 ✗ if (fileExists(cached.c_str())) return cached;
35 }
36 ✗ return std::string();
37 }
38
39 class LightSourceRemoving : public osg::NodeVisitor {
40 public:
41 ✗ LightSourceRemoving() : NodeVisitor(TRAVERSE_ALL_CHILDREN) {}
42
43 ✗ void apply(osg::LightSource& node) {
44 ✗ osg::Group* group = new osg::Group(node);
45 ✗ for (unsigned int i = 0; i < node.getNumParents(); ++i)
46 ✗ node.getParent(i)->replaceChild(&node, group);
47 }
48 };
49
50 #if OSG_VERSION_LESS_THAN(3, 3, 3)
51 struct ObjectCache {
52 typedef std::map<std::string, osg::NodeRefPtr> Map_t;
53 Map_t map_;
54 bool get(const std::string& name, osg::NodeRefPtr& node) const {
55 Map_t::const_iterator it = map_.find(name);
56 if (it != map_.end()) {
57 node = it->second;
58 return true;
59 }
60 return false;
61 }
62 void add(const std::string& name, osg::NodeRefPtr& node) {
63 map_.insert(std::make_pair(name, node));
64 }
65 void erase(const std::string& name) { map_.erase(name); }
66 };
67
68 ObjectCache object_cache;
69 #else
70 osg::ref_ptr<osgDB::ObjectCache> object_cache(new osgDB::ObjectCache);
71 #endif
72
73 /* Declaration of private function members */
74
75 ✗ void LeafNodeCollada::init() {
76 ✗ group_ptr_ = new osg::Group;
77 ✗ group_ptr_->setName("groupForMaterial");
78
79 #if OSG_VERSION_LESS_THAN(3, 3, 3)
80 if (!collada_ptr_) object_cache.get(collada_file_path_, collada_ptr_);
81 #endif
82
83 ✗ if (!collada_ptr_) {
84 // Setup cache
85 ✗ options_ = new osgDB::Options();
86 #if OSG_VERSION_GREATER_OR_EQUAL(3, 3, 3)
87 ✗ options_->setObjectCache(object_cache);
88 #endif
89 ✗ options_->setObjectCacheHint(osgDB::Options::CACHE_ALL);
90
91 ✗ if (!fileExists(collada_file_path_.c_str()))
92 ✗ throw std::invalid_argument(std::string("File ") + collada_file_path_ +
93 ✗ std::string(" not found."));
94
95 ✗ std::string osgname = getCachedFileName(collada_file_path_);
96 ✗ if (!osgname.empty()) {
97 ✗ log() << "Using " << osgname << std::endl;
98 ✗ collada_ptr_ = osgDB::readNodeFile(osgname, options_);
99 } else {
100 // get the extension of the meshs file
101 ✗ std::string ext = osgDB::getLowerCaseFileExtension(collada_file_path_);
102 ✗ if (ext == "obj") {
103 ✗ options_->setOptionString("noRotation");
104 ✗ collada_ptr_ = osgDB::readNodeFile(collada_file_path_, options_);
105 ✗ } else if (ext == "dae") {
106 ✗ float scale = 1.f;
107 ✗ options_->setPluginData("DAE-AssetUnitMeter", &scale);
108 ✗ if (*localeconv()->decimal_point != '.') {
109 std::cerr
110 << "Warning: your locale convention uses '"
111 ✗ << localeconv()->decimal_point
112 << "' as decimal separator while DAE "
113 "expects '.'.\nSet LC_NUMERIC to a locale convetion using '.' "
114 "as "
115 ✗ "decimal separator (e.g. export LC_NUMERIC=\"en_US.utf-8\")."
116 ✗ << std::endl;
117 }
118
119 ✗ collada_ptr_ = osgDB::readNodeFile(collada_file_path_, options_);
120
121 // FIXME: Fixes https://github.com/Gepetto/gepetto-viewer/issues/95
122 // The bug: Assimp seems to ignore the DAE up_axis tag. Because this
123 // cannot be fixed in assimp without a huge impact, we make GV
124 // compatible with assimp.
125 //
126 // The fix: OSG DAE plugin rotates the actual model with a root
127 // PositionAttitudeTransform, when the DAE up axis is not Z.
128 // We simply reset the attitude.
129 osg::PositionAttitudeTransform* pat =
130 ✗ dynamic_cast<osg::PositionAttitudeTransform*>(collada_ptr_.get());
131 ✗ if (pat != NULL) {
132 ✗ log() << "Reset up_axis to Z_UP." << std::endl;
133 ✗ pat->setAttitude(osgQuat());
134 }
135
136 ✗ bool error = false;
137 ✗ if (!collada_ptr_) {
138 ✗ log() << "File: " << collada_file_path_ << " could not be loaded\n";
139 ✗ error = true;
140 ✗ } else if (strncasecmp(collada_ptr_->getName().c_str(), "empty", 5) ==
141 0) {
142 ✗ log() << "File: " << collada_file_path_ << " could not be loaded:\n"
143 ✗ << collada_ptr_->getName() << '\n';
144 ✗ error = true;
145 }
146 ✗ if (error) {
147 ✗ log()
148 << "You may try to convert the file with the following command:\n"
149 "osgconv "
150 ✗ << collada_file_path_ << ' ' << collada_file_path_ << ".osgb"
151 ✗ << std::endl;
152 }
153 // Apply scale
154 ✗ if (scale != 1.) {
155 ✗ osg::ref_ptr<osg::MatrixTransform> xform = new osg::MatrixTransform;
156 ✗ xform->setDataVariance(osg::Object::STATIC);
157 ✗ xform->setMatrix(osg::Matrix::scale(scale, scale, scale));
158 ✗ xform->addChild(collada_ptr_);
159 ✗ collada_ptr_ = xform;
160 }
161 } else
162 ✗ collada_ptr_ = osgDB::readNodeFile(collada_file_path_, options_);
163 }
164 ✗ if (!collada_ptr_)
165 ✗ throw std::invalid_argument(
166 ✗ std::string("File ") + collada_file_path_ +
167 ✗ std::string(
168 ✗ " found but could not be opened. Check that a plugin exist."));
169 #if OSG_VERSION_LESS_THAN(3, 3, 3)
170 object_cache.add(collada_file_path_, collada_ptr_);
171 #endif
172
173 /* Allow transparency */
174 ✗ collada_ptr_->getOrCreateStateSet()->setMode(GL_BLEND,
175 ::osg::StateAttribute::ON);
176 ✗ collada_ptr_->setDataVariance(osg::Object::STATIC);
177
178 ✗ osgUtil::Optimizer optimizer;
179 ✗ optimizer.optimize(collada_ptr_, osgUtil::Optimizer::DEFAULT_OPTIMIZATIONS);
180 }
181
182 ✗ collada_ptr_->setName("meshfile");
183 ✗ backfaceDrawing_.stateSet(collada_ptr_->getOrCreateStateSet());
184 ✗ backfaceDrawing_.set(false);
185
186 /* Create PositionAttitudeTransform */
187 ✗ group_ptr_->addChild(collada_ptr_);
188 ✗ this->asQueue()->addChild(group_ptr_);
189
190 ✗ addProperty(StringProperty::create("Mesh file",
191 ✗ StringProperty::getterFromMemberFunction(
192 this, &LeafNodeCollada::meshFilePath),
193 ✗ StringProperty::Setter_t()));
194 ✗ addProperty(&backfaceDrawing_);
195 ✗ addProperty(
196 ✗ StringProperty::create("Texture file",
197 ✗ StringProperty::getterFromMemberFunction(
198 this, &LeafNodeCollada::textureFilePath),
199 ✗ StringProperty::Setter_t()));
200 ✗ addProperty(Vector4Property::create("Color",
201 ✗ Vector4Property::getterFromMemberFunction(
202 this, &LeafNodeCollada::getColor),
203 ✗ Vector4Property::setterFromMemberFunction(
204 this, &LeafNodeCollada::setColor)));
205 ✗ addProperty(VoidProperty::create(
206 ✗ "RemoveLightSources", VoidProperty::memberFunction(
207 this, &LeafNodeCollada::removeLightSources)));
208 ✗ addProperty(VoidProperty::create(
209 "ApplyScale",
210 ✗ VoidProperty::memberFunction(this, &LeafNodeCollada::applyScale)));
211 }
212
213 ✗ LeafNodeCollada::LeafNodeCollada(const std::string& name,
214 ✗ const std::string& collada_file_path)
215 ✗ : Node(name), collada_file_path_(collada_file_path), collada_ptr_() {
216 ✗ init();
217 }
218
219 ✗ LeafNodeCollada::LeafNodeCollada(const std::string& name,
220 const std::string& collada_file_path,
221 ✗ const osgVector4& color)
222 ✗ : Node(name), collada_file_path_(collada_file_path), collada_ptr_() {
223 ✗ init();
224 ✗ setColor(color);
225 }
226
227 ✗ LeafNodeCollada::LeafNodeCollada(const std::string& name,
228 const ::osg::NodeRefPtr& node,
229 ✗ const std::string& collada_file_path)
230 ✗ : Node(name), collada_file_path_(collada_file_path), collada_ptr_(node) {
231 ✗ init();
232 }
233
234 ✗ LeafNodeCollada::LeafNodeCollada(const LeafNodeCollada& other)
235 ✗ : Node(other.getID()), collada_file_path_(other.collada_file_path_) {
236 ✗ init();
237 }
238
239 ✗ void LeafNodeCollada::initWeakPtr(LeafNodeColladaWeakPtr other_weak_ptr) {
240 ✗ weak_ptr_ = other_weak_ptr;
241 }
242
243 /* End of declaration of private function members */
244
245 /* Declaration of protected function members */
246
247 ✗ LeafNodeColladaPtr_t LeafNodeCollada::create(
248 const std::string& name, const std::string& collada_file_path) {
249 ✗ std::ifstream infile(collada_file_path.c_str());
250 ✗ if (!infile.good()) {
251 ✗ throw std::ios_base::failure(collada_file_path +
252 ✗ std::string(" does not exist."));
253 }
254 ✗ LeafNodeColladaPtr_t shared_ptr(new LeafNodeCollada(name, collada_file_path));
255
256 // Add reference to itself
257 ✗ shared_ptr->initWeakPtr(shared_ptr);
258
259 ✗ return shared_ptr;
260 }
261
262 ✗ LeafNodeColladaPtr_t LeafNodeCollada::create(
263 const std::string& name, ::osg::NodeRefPtr mesh,
264 const std::string& collada_file_path) {
265 LeafNodeColladaPtr_t shared_ptr(
266 ✗ new LeafNodeCollada(name, mesh, collada_file_path));
267
268 // Add reference to itself
269 ✗ shared_ptr->initWeakPtr(shared_ptr);
270
271 ✗ return shared_ptr;
272 }
273
274 ✗ LeafNodeColladaPtr_t LeafNodeCollada::create(
275 const std::string& name, const std::string& collada_file_path,
276 const osgVector4& color) {
277 ✗ std::ifstream infile(collada_file_path.c_str());
278 ✗ if (!infile.good()) {
279 ✗ throw std::ios_base::failure(collada_file_path +
280 ✗ std::string(" does not exist."));
281 }
282 LeafNodeColladaPtr_t shared_ptr(
283 ✗ new LeafNodeCollada(name, collada_file_path, color));
284
285 // Add reference to itself
286 ✗ shared_ptr->initWeakPtr(shared_ptr);
287
288 ✗ return shared_ptr;
289 }
290
291 ✗ LeafNodeColladaPtr_t LeafNodeCollada::createCopy(LeafNodeColladaPtr_t other) {
292 ✗ LeafNodeColladaPtr_t shared_ptr(new LeafNodeCollada(*other));
293
294 // Add reference to itself
295 ✗ shared_ptr->initWeakPtr(shared_ptr);
296
297 ✗ return shared_ptr;
298 }
299
300 ✗ ::osg::NodeRefPtr LeafNodeCollada::getColladaPtr() { return collada_ptr_; }
301
302 /* End of declaration of protected function members */
303
304 /* Declaration of public function members */
305
306 ✗ LeafNodeColladaPtr_t LeafNodeCollada::clone(void) const {
307 ✗ return LeafNodeCollada::createCopy(weak_ptr_.lock());
308 }
309
310 ✗ LeafNodeColladaPtr_t LeafNodeCollada::self(void) const {
311 ✗ return weak_ptr_.lock();
312 }
313
314 ✗ void LeafNodeCollada::setColor(const osgVector4& color) {
315 ✗ osg::ref_ptr<osg::Material> mat_ptr(new osg::Material);
316 ✗ osgVector4 ambient(color.r() * 0.5f, color.g() * 0.5f, color.b() * 0.5f,
317 ✗ color.a());
318
319 ✗ mat_ptr->setDiffuse(osg::Material::FRONT_AND_BACK, color);
320 ✗ mat_ptr->setAmbient(osg::Material::FRONT_AND_BACK, ambient);
321
322 ✗ osg::StateSet* ss = group_ptr_->getOrCreateStateSet();
323 ✗ ss->setAttribute(mat_ptr.get());
324 ✗ setTransparentRenderingBin(color[3] < Node::TransparencyRenderingBinThreshold,
325 ss);
326 ✗ setDirty();
327 }
328
329 ✗ osgVector4 LeafNodeCollada::getColor() const {
330 ✗ osg::StateSet* ss = group_ptr_->getStateSet();
331 ✗ if (ss) {
332 ✗ osg::Material* mat = dynamic_cast<osg::Material*>(
333 ✗ ss->getAttribute(osg::StateAttribute::MATERIAL));
334 ✗ if (mat) return mat->getDiffuse(osg::Material::FRONT_AND_BACK);
335 }
336 ✗ return osgVector4(1., 1., 1., 1.);
337 }
338
339 ✗ void LeafNodeCollada::setAlpha(const float& alpha) {
340 // TODO this overload is probably not necessary.
341 ✗ osg::StateSet* ss = group_ptr_->getOrCreateStateSet();
342
343 ✗ alpha_ = alpha;
344 osg::Material* mat;
345 ✗ if (ss->getAttribute(osg::StateAttribute::MATERIAL))
346 ✗ mat = dynamic_cast<osg::Material*>(
347 ✗ ss->getAttribute(osg::StateAttribute::MATERIAL));
348 else {
349 ✗ mat = new osg::Material;
350 ✗ ss->setAttribute(mat);
351 }
352 ✗ mat->setAlpha(osg::Material::FRONT_AND_BACK, alpha);
353 ✗ setTransparentRenderingBin(alpha < Node::TransparencyRenderingBinThreshold,
354 ss);
355 ✗ setDirty();
356 }
357
358 ✗ void LeafNodeCollada::setTexture(const std::string& image_path) {
359 ✗ texture_file_path_ = image_path;
360 ✗ osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
361 ✗ texture->setDataVariance(osg::Object::STATIC);
362 ✗ osg::ref_ptr<osg::Image> image = osgDB::readImageFile(image_path);
363 ✗ if (!image) {
364 ✗ log() << " couldn't find texture, quiting." << std::endl;
365 ✗ return;
366 }
367 ✗ texture->setImage(image);
368 ✗ collada_ptr_->getOrCreateStateSet()->setTextureAttributeAndModes(
369 0, texture, osg::StateAttribute::ON);
370 ✗ setDirty();
371 }
372
373 ✗ const std::string& LeafNodeCollada::meshFilePath() const {
374 ✗ return collada_file_path_;
375 }
376
377 ✗ const std::string& LeafNodeCollada::textureFilePath() const {
378 ✗ return texture_file_path_;
379 }
380
381 /*void LeafNodeCollada::setColor(osg::NodeRefPtr osgNode_ptr,const osgVector4&
382 color)
383 {
384 osg::Vec4ArrayRefPtr colorArray = new osg::Vec4Array();
385 colorArray->push_back(color);
386 osg::GeodeRefPtr geode_ptr = osgNode_ptr->asGeode();
387 if (geode_ptr) {
388 for (unsigned int i = 0 ; i < geode_ptr->getNumDrawables() ; i++) {
389 osg::GeometryRefPtr geom_ptr = geode_ptr->getDrawable(i)->asGeometry();
390 if (geom_ptr) {
391 geom_ptr->setColorArray(colorArray.get());
392 geom_ptr->setColorBinding(osg::Geometry::BIND_OVERALL);
393 }
394 }
395 }
396 else {
397 osg::GroupRefPtr group_ptr = osgNode_ptr->asGroup();
398 if (group_ptr) {
399 for (unsigned int i = 0 ; i < group_ptr->getNumChildren() ; i++) {
400 setColor(group_ptr->getChild(i),color);
401 }
402 }
403 }
404 }*/
405
406 ✗ osg::ref_ptr<osg::Node> LeafNodeCollada::getOsgNode() const {
407 ✗ return collada_ptr_;
408 }
409
410 ✗ void LeafNodeCollada::removeLightSources() {
411 ✗ LightSourceRemoving lsRemoving;
412 ✗ collada_ptr_->traverse(lsRemoving);
413 }
414
415 ✗ void LeafNodeCollada::applyScale() {
416 ✗ osgVector3 scale(getScale());
417 ✗ setScale(1.);
418
419 // Do not remove this brackets. See
420 // https://github.com/openscenegraph/OpenSceneGraph/issues/1020
421 {
422 ✗ osg::ref_ptr<osg::MatrixTransform> xform = new osg::MatrixTransform;
423 ✗ xform->setMatrix(osg::Matrix::scale(scale));
424 ✗ xform->setDataVariance(osg::Object::STATIC);
425
426 ✗ group_ptr_->removeChild(collada_ptr_);
427 ✗ xform->addChild(collada_ptr_);
428 ✗ collada_ptr_ = xform;
429 }
430
431 ✗ osgUtil::Optimizer optimizer;
432 ✗ optimizer.optimize(collada_ptr_,
433 osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS);
434
435 ✗ group_ptr_->addChild(collada_ptr_);
436 }
437
438 ✗ void LeafNodeCollada::removeFromCache() {
439 #if OSG_VERSION_LESS_THAN(3, 3, 3)
440 object_cache.erase(collada_file_path_);
441 #else
442 ✗ object_cache->removeFromObjectCache(collada_file_path_, options_);
443 #endif
444 }
445
446 ✗ LeafNodeCollada::~LeafNodeCollada() {
447 /* Proper deletion of all tree scene */
448 ✗ group_ptr_->removeChild(collada_ptr_);
449
450 ✗ collada_ptr_ = NULL;
451
452 ✗ weak_ptr_.reset();
453 }
454
455 /* End of declaration of public function members */
456
457 } /* namespace viewer */
458
459 } /* namespace gepetto */
460