/* -*-c++-*- OpenSceneGraph - Copyright (C) 2012 Cedric Pinson */ #ifndef WRITE_VISITOR_H #define WRITE_VISITOR_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "JSON_Objects" #include "Animation" #include "json_stream" #define WRITER_VERSION 9 class WriteVisitor; osg::Array* getTangentSpaceArray(osg::Geometry& geometry); void getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value); template bool getStringifiedUserValue(osg::Object* o, std::string& name, std::string& value); class WriteVisitor : public osg::NodeVisitor { public: typedef std::vector > StateSetStack; typedef std::pair KeyValue; typedef std::map, osg::ref_ptr > OsgObjectToJSONObject; OsgObjectToJSONObject _maps; std::vector > _parents; osg::ref_ptr _root; StateSetStack _stateset; std::string _baseName; std::string _baseLodURL; bool _useExternalBinaryArray; bool _mergeAllBinaryFiles; bool _inlineImages; int _maxTextureDimension; bool _varint; std::map _specificBuffers; std::map _buffers; JSONObject* getJSON(osg::Object* object) const { OsgObjectToJSONObject::const_iterator lookup = _maps.find(object); if(lookup != _maps.end()) { return lookup->second->getShadowObject(); } return 0; } void setJSON(osg::Object* object, JSONObject* json) { if(json) { _maps[object] = json; } } std::ofstream& getBufferFile(const std::string& name) { if(_buffers.find(name) == _buffers.end()) { _buffers[name] = new std::ofstream(name.c_str(), std::ios::binary); } return *_buffers[name]; } WriteVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _useExternalBinaryArray(false), _mergeAllBinaryFiles(false), _inlineImages(false), _maxTextureDimension(0), _varint(false) {} ~WriteVisitor() { for(std::map::iterator buffer = _buffers.begin() ; buffer != _buffers.end() ; ++ buffer) { delete buffer->second; } } std::string getBinaryFilename(KeyValue flag=KeyValue()) const { std::string suffix, prefix(_baseName); std::map::const_iterator it_buffer = _specificBuffers.find(flag); if(it_buffer != _specificBuffers.end()) { if(osgDB::isAbsolutePath(it_buffer->second)) { // if output is explicit, do not change it return it_buffer->second; } else { suffix = "_" + it_buffer->second; } } return prefix + suffix + ".bin"; } void closeBuffers() { for(std::map::iterator buffer = _buffers.begin() ; buffer != _buffers.end() ; ++ buffer) { buffer->second->close(); } } unsigned int getBuffersSize() const { unsigned int size = 0; for(std::map::const_iterator buffer = _buffers.begin() ; buffer != _buffers.end() ; ++ buffer) { size += buffer->second->tellp(); } return size; } void write(json_stream& str) { osg::ref_ptr o = new JSONObject(); o->getMaps()["Version"] = new JSONValue(WRITER_VERSION); o->getMaps()["Generator"] = new JSONValue("OpenSceneGraph " + std::string(osgGetVersion()) ); o->getMaps()["osg.Node"] = _root.get(); o->write(str, *this); if (_mergeAllBinaryFiles) { closeBuffers(); unsigned int size = getBuffersSize(); osg::notify(osg::NOTICE) << "Use a merged binary file "; if (size/1024.0 < 1.0) { osg::notify(osg::NOTICE) << size << " bytes" << std::endl; } else if (size/(1024.0*1024.0) < 1.0) { osg::notify(osg::NOTICE) << size/1024.0 << " kb" << std::endl; } else { osg::notify(osg::NOTICE) << size/(1024.0*1024.0) << " mb" << std::endl; } } } void error() { throw "Error occur"; } void setBufferName(JSONObject *json, osg::Object* parent=0, osg::Object* object=0) const { if(!_mergeAllBinaryFiles || _specificBuffers.empty()) return; // try to fetch buffer name for object std::string bufferName = getBufferName(object); std::string defaultBufferName = getBinaryFilename(); std::string jsonBufferName = json->getBufferName(); if(bufferName == defaultBufferName) { // in case none is set, fallback to parent buffer name bufferName = getBufferName(parent); } // if the buffer is shared we will always favor dumping it in the default // buffer and otherwise we keep the first buffer name set. if(!jsonBufferName.empty()) { if(jsonBufferName != defaultBufferName && bufferName == defaultBufferName) { json->setBufferName(defaultBufferName); } } else { json->setBufferName(bufferName); } } std::string getBufferName(osg::Object* object) const { KeyValue flag; if(object && object->getUserDataContainer() && object->getUserDataContainer()->getNumUserObjects()) { for(std::map::const_iterator it_flag = _specificBuffers.begin() ; it_flag != _specificBuffers.end() ; ++ it_flag) { std::string key = it_flag->first.first, value = it_flag->first.second; // only looking for existence of user value if(value.empty()) { if(object->getUserDataContainer()->getUserObject(key)) { flag = it_flag->first; break; } } else { std::set uservalues; bool boolValue; int numberValue; unsigned int uNumberValue; std::string stringValue; if(object->getUserValue(key, boolValue)) { std::ostringstream oss; if(boolValue) { uservalues.insert("1"); uservalues.insert("true"); } else { uservalues.insert("0"); uservalues.insert("false"); } } if(object->getUserValue(key, numberValue)) { std::ostringstream oss; oss << numberValue; uservalues.insert(oss.str()); } if(object->getUserValue(key, uNumberValue)) { std::ostringstream oss; oss << uNumberValue; uservalues.insert(oss.str()); } if(object->getUserValue(key, stringValue)) { uservalues.insert(stringValue); } if(uservalues.find(value) != uservalues.end()) { flag = it_flag->first; break; } } } } return getBinaryFilename(flag); } void translateObject(JSONObject* json, osg::Object* osg); JSONObject* createJSONOsgSimUserData(osgSim::ShapeAttributeList*); JSONObject* createJSONUserDataContainer(osg::UserDataContainer*); JSONObject* createJSONPagedLOD(osg::PagedLOD* plod); JSONObject* createJSONStateSet(osg::StateSet* ss); JSONObject* createJSONTexture(osg::Texture* sa); JSONObject* createJSONText(osgText::Text* text); JSONObject* createJSONMaterial(osg::Material* sa); JSONObject* createJSONLight(osg::Light* sa); JSONObject* createJSONCullFace(osg::CullFace* sa); JSONObject* createJSONBlendColor(osg::BlendColor* sa); JSONObject* createJSONBlendFunc(osg::BlendFunc* sa); JSONObject* createJSONBufferArray(osg::Array* array, osg::Object* parent = 0); JSONObject* createJSONDrawElements(osg::DrawArrays* drawArray, osg::Object* parent = 0); JSONObject* createJSONDrawElementsUInt(osg::DrawElementsUInt* de, osg::Object* parent = 0); JSONObject* createJSONDrawElementsUShort(osg::DrawElementsUShort* de, osg::Object* parent = 0); JSONObject* createJSONDrawElementsUByte(osg::DrawElementsUByte* de, osg::Object* parent = 0); JSONObject* createJSONDrawArray(osg::DrawArrays* drawArray, osg::Object* parent = 0); JSONObject* createJSONDrawArrayLengths(osg::DrawArrayLengths* drawArray, osg::Object* parent = 0); JSONObject* createJSONGeometry(osg::Geometry* geometry, osg::Object* parent=0); JSONObject* createJSONRigGeometry(osgAnimation::RigGeometry* rigGeometry); JSONObject* createJSONMorphGeometry(osgAnimation::MorphGeometry* morphGeom, osg::Object* parent=0); JSONObject* getParent() { if (_parents.empty()) { _root = new JSONObject; _parents.push_back(_root.get()); } return _parents.back().get(); } void initJsonObjectFromNode(osg::Node& node, JSONObject& json) { translateObject(&json, &node); } void createJSONStateSet(JSONObject* json, osg::StateSet* ss) { JSONObject* json_stateset = createJSONStateSet(ss); if (json_stateset) { JSONObject* obj = new JSONObject; obj->getMaps()["osg.StateSet"] = json_stateset; json->getMaps()["StateSet"] = obj; } } void createJSONStateSet(osg::Node& node, JSONObject* json) { if (node.getStateSet()) { createJSONStateSet(json, node.getStateSet()); } } void applyCallback(osg::Node& node, JSONObject* in_json) { osg::ref_ptr updateCallbacks = new JSONArray; osg::ref_ptr nc = node.getUpdateCallback(); while (nc) { if(osgAnimation::BasicAnimationManager* am = dynamic_cast(nc.get())) { osg::ref_ptr array = new JSONArray; osg::ref_ptr bam = new JSONObject; translateObject(bam.get(), am); bam->getMaps()["Animations"] = array; osg::ref_ptr nodeCallbackObject = new JSONObject; nodeCallbackObject->getMaps()["osgAnimation.BasicAnimationManager"] = bam; updateCallbacks->getArray().push_back(nodeCallbackObject); for ( unsigned int i = 0; i < am->getAnimationList().size(); i++) { osgAnimation::Animation* animation = am->getAnimationList()[i].get(); osg::ref_ptr jsonAnim = createJSONAnimation(animation, this); if (jsonAnim) { translateObject(jsonAnim.get(), animation); osg::ref_ptr obj = new JSONObject; obj->getMaps()["osgAnimation.Animation"] = jsonAnim; array->getArray().push_back(obj); } } } else if(osgAnimation::UpdateBone* upBone = dynamic_cast(nc.get())) { osg::ref_ptr jsonCallback = createJSONUpdateMatrixTransform(*upBone, this); if (jsonCallback.valid()) { osg::ref_ptr jsonObject = new JSONObject; jsonObject->getMaps()["osgAnimation.UpdateBone"] = jsonCallback; updateCallbacks->getArray().push_back(jsonObject); } } else if(osgAnimation::UpdateMatrixTransform* updateMT = dynamic_cast(nc.get())) { osg::ref_ptr jsonCallback = createJSONUpdateMatrixTransform(*updateMT, this); if (jsonCallback.valid()) { osg::ref_ptr jsonObject = new JSONObject; jsonObject->getMaps()["osgAnimation.UpdateMatrixTransform"] = jsonCallback; updateCallbacks->getArray().push_back(jsonObject); } } else if(dynamic_cast(nc.get())) { osg::ref_ptr json = new JSONNode; osg::ref_ptr jsonObject = new JSONObject; _maps[&node] = json; jsonObject->getMaps()["osgAnimation.UpdateSkeleton"] = json; updateCallbacks->getArray().push_back(jsonObject); } else if(osgAnimation::UpdateMorph* updateMorph = dynamic_cast(nc.get())) { osg::ref_ptr json = new JSONNode; osg::ref_ptr jsonObject = new JSONObject; osg::ref_ptr jsonTargetName = new JSONObject; json->getMaps()["Name"] = new JSONValue(updateMorph->getName()); unsigned int numTarget = updateMorph->getNumTarget(); for(unsigned int i = 0; i < numTarget; ++i) { std::ostringstream oss; oss << i; jsonTargetName->getMaps()[ oss.str() ] = new JSONValue(updateMorph->getTargetName(i)); } json->getMaps()["TargetMap"] = jsonTargetName; json->getMaps()["TargetMap"] = jsonTargetName; _maps[&node] = json; jsonObject->getMaps()["osgAnimation.UpdateMorph"] = json; updateCallbacks->getArray().push_back(jsonObject); } nc = nc->getNestedCallback(); } if (!updateCallbacks->getArray().empty()) { in_json->getMaps()["UpdateCallbacks"] = updateCallbacks; } } void apply(osg::Drawable& drawable) { if(osgAnimation::RigGeometry * rigGeometry = dynamic_cast(&drawable)) { JSONObject* json = createJSONRigGeometry(rigGeometry); translateObject(json, rigGeometry); JSONObject* parent = getParent(); parent->addChild("osgAnimation.RigGeometry", json); } else if(osgAnimation::MorphGeometry * morphGeometry = dynamic_cast(&drawable)) { JSONObject* json = createJSONMorphGeometry(morphGeometry); JSONObject* parent = getParent(); parent->addChild("osgAnimation.MorphGeometry", json); } else if(osg::Geometry* geometry = dynamic_cast(&drawable)) { JSONObject* json = createJSONGeometry(geometry); JSONObject* parent = getParent(); parent->addChild("osg.Geometry", json); } else if(osgText::Text* text = dynamic_cast(&drawable)) { JSONObject* json = createJSONText(text); JSONObject* parent = getParent(); parent->addChild("osgText.Text", json); } } void apply(osg::Geode& node) { JSONObject* parent = getParent(); if (_maps.find(&node) != _maps.end()) { parent->addChild("osg.Node", _maps[&node]->getShadowObject()); return; } osg::ref_ptr json = new JSONNode; _maps[&node] = json; applyCallback(node, json.get()); createJSONStateSet(node, json.get()); parent->addChild("osg.Node", json.get()); initJsonObjectFromNode(node, *json); _parents.push_back(json); for (unsigned int i = 0; i < node.getNumDrawables(); ++i) { if (node.getDrawable(i)) apply(*node.getDrawable(i)); } _parents.pop_back(); } void apply(osg::Group& node) { JSONObject* parent = getParent(); if (_maps.find(&node) != _maps.end()) { parent->addChild("osg.Node", _maps[&node]->getShadowObject()); return; } osg::ref_ptr json = new JSONNode; _maps[&node] = json; parent->addChild("osg.Node", json.get()); applyCallback(node, json.get()); createJSONStateSet(node, json.get()); initJsonObjectFromNode(node, *json); _parents.push_back(json); traverse(node); _parents.pop_back(); } void apply(osg::PagedLOD& node) { JSONObject* parent = getParent(); if (_maps.find(&node) != _maps.end()) { parent->addChild("osg.PagedLOD", _maps[&node]->getShadowObject()); return; } osg::ref_ptr json = createJSONPagedLOD(&node); json->addUniqueID(); _maps[&node] = json; parent->addChild("osg.PagedLOD", json.get()); applyCallback(node, json.get()); createJSONStateSet(node, json.get()); initJsonObjectFromNode(node, *json); _parents.push_back(json); traverse(node); _parents.pop_back(); } void apply(osg::LightSource& node) { JSONObject* parent = getParent(); if (_maps.find(&node) != _maps.end()) { parent->addChild("osg.LightSource", _maps[&node]->getShadowObject()); return; } osg::ref_ptr json = new JSONNode; _maps[&node] = json; applyCallback(node, json.get()); createJSONStateSet(node, json.get()); parent->addChild("osg.LightSource", json.get()); initJsonObjectFromNode(node, *json); if (node.getLight()) { JSONObject* obj = new JSONObject; obj->getMaps()["osg.Light"] = createJSONLight(node.getLight()); json->getMaps()["Light"] = obj; } _parents.push_back(json); traverse(node); _parents.pop_back(); } void apply(osg::Projection& node) { JSONObject* parent = getParent(); if (_maps.find(&node) != _maps.end()) { parent->addChild("osg.Projection", _maps[&node]->getShadowObject()); return; } osg::ref_ptr json = new JSONNode; _maps[&node] = json; applyCallback(node, json.get()); createJSONStateSet(node, json.get()); parent->addChild("osg.Projection", json.get()); initJsonObjectFromNode(node, *json); json->getMaps()["Matrix"] = new JSONMatrix(node.getMatrix()); _parents.push_back(json); traverse(node); _parents.pop_back(); } void applyCommonMatrixTransform(const char * jsClassName, osg::ref_ptr &json, osg::MatrixTransform &node, JSONObject* parent) { json->addUniqueID(); _maps[&node] = json; applyCallback(node, json.get()); createJSONStateSet(node, json.get()); parent->addChild(jsClassName, json.get()); initJsonObjectFromNode(node, *json); json->getMaps()["Matrix"] = new JSONMatrix(node.getMatrix()); } void apply(osgText::Text& node) { JSONObject* parent = getParent(); if (_maps.find(&node) != _maps.end()) { parent->addChild("osgText.Text", _maps[&node]->getShadowObject()); return; } osg::ref_ptr json = createJSONText(&node); json->addUniqueID(); _maps[&node] = json.get(); parent->addChild("osgText.Text", json.get()); applyCallback(node, json.get()); createJSONStateSet(node, json.get()); initJsonObjectFromNode(node, *json); _parents.push_back(json); traverse(node); _parents.pop_back(); } void apply(osg::MatrixTransform& node) { if (dynamic_cast(&node)) { apply(static_cast(node)); return; } if (dynamic_cast(&node)) { apply(static_cast(node)); return; } JSONObject* parent = getParent(); if (_maps.find(&node) != _maps.end()) { parent->addChild("osg.MatrixTransform", _maps[&node]->getShadowObject()); return; } osg::ref_ptr json = new JSONNode; _maps[&node] = json; applyCallback(node, json.get()); createJSONStateSet(node, json.get()); parent->addChild("osg.MatrixTransform", json.get()); initJsonObjectFromNode(node, *json); json->getMaps()["Matrix"] = new JSONMatrix(node.getMatrix()); _parents.push_back(json); traverse(node); _parents.pop_back(); } void apply(osgAnimation::Skeleton& node) { JSONObject* parent = getParent(); if (_maps.find(&node) != _maps.end()) { parent->addChild("osgAnimation.Skeleton", _maps[&node]->getShadowObject()); return; } osg::ref_ptr json = new JSONNode; applyCommonMatrixTransform("osgAnimation.Skeleton", json, node, parent); _parents.push_back(json); traverse(node); _parents.pop_back(); } void apply(osgAnimation::Bone &node) { JSONObject* parent = getParent(); if (_maps.find(&node) != _maps.end()) { parent->addChild("osgAnimation.Bone", _maps[&node]->getShadowObject()); return; } osg::ref_ptr json = new JSONNode; osg::Vec3 min(0,0,0), max(0,0,0); osg::ref_ptr bboxData = new JSONObject; bool hasBoundindBox = ( node.getUserValue("AABBonBone_min", min) && node.getUserValue("AABBonBone_max", max) ); if (hasBoundindBox) { unsigned int idxmin = node.getUserDataContainer()->getUserObjectIndex("AABBonBone_min"); node.getUserDataContainer()->removeUserObject(idxmin); unsigned int idxmax = node.getUserDataContainer()->getUserObjectIndex("AABBonBone_max"); node.getUserDataContainer()->removeUserObject(idxmax); if(node.getUserDataContainer() && node.getUserDataContainer()->getNumUserObjects() == 0) node.setUserDataContainer(NULL); bboxData->getMaps()["min"] = new JSONVec3Array(min); bboxData->getMaps()["max"] = new JSONVec3Array(max); json->getMaps()["BoundingBox"] = bboxData; } json->getMaps()["InvBindMatrixInSkeletonSpace"] = new JSONMatrix(node.getInvBindMatrixInSkeletonSpace()); applyCommonMatrixTransform("osgAnimation.Bone", json, node, parent); _parents.push_back(json); traverse(node); _parents.pop_back(); } void apply(osg::PositionAttitudeTransform& node) { JSONObject* parent = getParent(); if (_maps.find(&node) != _maps.end()) { parent->addChild("osg.MatrixTransform", _maps[&node]->getShadowObject()); return; } osg::ref_ptr json = new JSONNode; _maps[&node] = json; applyCallback(node, json.get()); createJSONStateSet(node, json.get()); parent->addChild("osg.MatrixTransform", json.get()); initJsonObjectFromNode(node, *json); osg::Matrix matrix = osg::Matrix::identity(); node.computeLocalToWorldMatrix(matrix,0); json->getMaps()["Matrix"] = new JSONMatrix(matrix); _parents.push_back(json); traverse(node); _parents.pop_back(); } std::string getBaseName() const { return _baseName; } bool getInlineImages() const { return _inlineImages; } int getMaxTextureDimension() const { return _maxTextureDimension; } void setBaseName(const std::string& basename) { _baseName = basename; } void useExternalBinaryArray(bool use) { _useExternalBinaryArray = use; } void mergeAllBinaryFiles(bool use) { _mergeAllBinaryFiles = use; } void setInlineImages(bool use) { _inlineImages = use; } void setVarint(bool use) { _varint = use; } void setMaxTextureDimension(int use) { _maxTextureDimension = use; } void addSpecificBuffer(const std::string& bufferFlag) { if(bufferFlag.empty()) { return; } std::string key, value, buffer; size_t equal = bufferFlag.find("="), colon = bufferFlag.find(":"); key = bufferFlag.substr(0, std::min(equal, colon)); if(equal != std::string::npos) { if(colon == std::string::npos) { value = bufferFlag.substr(equal + 1, std::string::npos); } else { value = bufferFlag.substr(equal + 1, colon - equal - 1); } } if(colon != std::string::npos) { buffer = bufferFlag.substr(colon + 1, std::string::npos); } else { buffer = key; } // buffer name should be lowercase std::transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower); _specificBuffers[KeyValue(key, value)] = buffer; } void setBaseLodURL(const std::string& baseLodURL) { _baseLodURL = baseLodURL; } }; #endif