/* -*-c++-*- * Copyright (C) 2010 Cedric Pinson */ #include "JSON_Objects" #include #include #include #include #include #include #include #include #include #include #include #include "WriteVisitor" int JSONObjectBase::level = 0; unsigned int JSONObject::uniqueID = 0; std::string JSONObjectBase::indent() { std::string str; for (int i = 0; i < JSONObjectBase::level; ++i) { str += " "; } return str; } void JSONMatrix::write(json_stream& str, WriteVisitor& visitor) { str << "[ "; for (unsigned int i = 0; i < _array.size(); i++) { _array[i]->write(str, visitor); if (i != _array.size() -1) str << ", "; } str << " ]"; } JSONObject::JSONObject(const unsigned int id, const std::string& bufferName) { _bufferName = bufferName; _maps["UniqueID"] = new JSONValue(id); } void JSONObject::addUniqueID() { if(_maps.find("UniqueID") == _maps.end()) { _maps["UniqueID"] = new JSONValue(JSONObject::uniqueID ++); } } unsigned int JSONObject::getUniqueID() const { JSONMap::const_iterator iterator = _maps.find("UniqueID"); if(iterator == _maps.end()) { return 0xffffffff; } const JSONValue* uid = dynamic_cast*>(iterator->second.get()); return uid ? uid->getValue() : 0xffffffff; } void JSONObject::addChild(const std::string& type, JSONObject* child) { if (!getMaps()["Children"]) getMaps()["Children"] = new JSONArray; JSONObject* jsonObject = new JSONObject(); jsonObject->getMaps()[type] = child; getMaps()["Children"]->asArray()->getArray().push_back(jsonObject); } bool JSONObject::isVarintableIntegerBuffer(osg::Array const* array) const { // Return true for buffers representing integer values and that therefore // can be binary encoded compactly using the varint protocol. // Note: as Byte/UByte array are already compact we do not consider // them as compactable bool isInteger = false; switch(static_cast(array->getType())) { case osg::Array::IntArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::ShortArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::UIntArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::UShortArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec2iArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec3iArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec4iArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec2uiArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec3uiArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec4uiArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec2sArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec3sArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec4sArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec2usArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec3usArrayType: isInteger = (dynamic_cast(array) != NULL); break; case osg::Array::Vec4usArrayType: isInteger = (dynamic_cast(array) != NULL); break; default: isInteger = false; break; } return isInteger; } void JSONObject::encodeArrayAsVarintBuffer(osg::Array const* array, std::vector& buffer) const { switch(static_cast(array->getType())) { case osg::Array::IntArrayType: dumpVarintValue(buffer, dynamic_cast(array), false); break; case osg::Array::ShortArrayType: dumpVarintValue(buffer, dynamic_cast(array), false); break; case osg::Array::UIntArrayType: dumpVarintValue(buffer, dynamic_cast(array), true); break; case osg::Array::UShortArrayType: dumpVarintValue(buffer, dynamic_cast(array), true); break; case osg::Array::Vec2iArrayType: dumpVarintVector(buffer, dynamic_cast(array), false); break; case osg::Array::Vec3iArrayType: dumpVarintVector(buffer, dynamic_cast(array), false); break; case osg::Array::Vec4iArrayType: dumpVarintVector(buffer, dynamic_cast(array), false); break; case osg::Array::Vec2sArrayType: dumpVarintVector(buffer, dynamic_cast(array), false); break; case osg::Array::Vec3sArrayType: dumpVarintVector(buffer, dynamic_cast(array), false); break; case osg::Array::Vec4sArrayType: dumpVarintVector(buffer, dynamic_cast(array), false); break; case osg::Array::Vec2uiArrayType: dumpVarintVector(buffer, dynamic_cast(array), true); break; case osg::Array::Vec3uiArrayType: dumpVarintVector(buffer, dynamic_cast(array), true); break; case osg::Array::Vec4uiArrayType: dumpVarintVector(buffer, dynamic_cast(array), true); break; case osg::Array::Vec2usArrayType: dumpVarintVector(buffer, dynamic_cast(array), true); break; case osg::Array::Vec3usArrayType: dumpVarintVector(buffer, dynamic_cast(array), true); break; case osg::Array::Vec4usArrayType: dumpVarintVector(buffer, dynamic_cast(array), true); break; default: break; } } template void JSONObject::dumpVarintVector(std::vector& oss, T const* buffer, bool isUnsigned) const { if (!buffer) return; unsigned int n = buffer->getDataSize(); for(typename T::const_iterator it = buffer->begin() ; it != buffer->end() ; ++ it) { for(unsigned int i = 0 ; i < n ; ++ i) { unsigned int value = isUnsigned ? (*it)[i] : JSONObject::toVarintUnsigned((*it)[i]); std::vector encoding = varintEncoding(value); oss.insert(oss.end(), encoding.begin(), encoding.end()); } } } template void JSONObject::dumpVarintValue(std::vector& oss, T const* buffer, bool isUnsigned) const { if (!buffer) return; for(typename T::const_iterator it = buffer->begin() ; it != buffer->end() ; ++ it) { unsigned int value = isUnsigned ? (*it) : JSONObject::toVarintUnsigned(*it); std::vector encoding = varintEncoding(value); oss.insert(oss.end(), encoding.begin(), encoding.end()); } } // varint encoding adapted from http://stackoverflow.com/questions/19758270/read-varint-from-linux-sockets std::vector JSONObject::varintEncoding(unsigned int value) const { std::vector buffer; do { uint8_t next_byte = value & 0x7F; value >>= 7; if (value) { next_byte |= 0x80; } buffer.push_back(next_byte); } while (value); return buffer; } static void writeEntry(json_stream& str, const std::string& key, JSONObject::JSONMap& map, WriteVisitor& visitor) { if (key.empty()) return; JSONObject::JSONMap::iterator keyValue = map.find(key); if ( keyValue != map.end() && keyValue->second.valid() ) { str << JSONObjectBase::indent() << '"' << key << '"' << ": "; keyValue->second->write(str, visitor); map.erase(keyValue); if (!map.empty()) { str << ",\n"; } } } void JSONObject::writeOrder(json_stream& str, const std::vector& order, WriteVisitor& visitor) { str << "{" << std::endl; JSONObjectBase::level++; for (unsigned int i = 0; i < order.size(); i++) { writeEntry(str, order[i], _maps, visitor); } while(!_maps.empty()) { std::string key = _maps.begin()->first; writeEntry(str, key, _maps, visitor); } JSONObjectBase::level--; str << std::endl << JSONObjectBase::indent() << "}"; } void JSONObject::write(json_stream& str, WriteVisitor& visitor) { OrderList defaultOrder; defaultOrder.push_back("UniqueID"); defaultOrder.push_back("Name"); defaultOrder.push_back("TargetName"); writeOrder(str, defaultOrder, visitor); } std::pair JSONVertexArray::writeMergeData(const osg::Array* array, WriteVisitor &visitor, const std::string& filename, std::string& encoding) { std::ofstream& output = visitor.getBufferFile(filename); unsigned int offset = output.tellp(); if(visitor._varint && isVarintableIntegerBuffer(array)) { std::vector varintByteBuffer; encodeArrayAsVarintBuffer(array, varintByteBuffer); output.write((char*)&varintByteBuffer[0], varintByteBuffer.size() * sizeof(uint8_t)); encoding = std::string("varint"); } else { const char* b = static_cast(array->getDataPointer()); size_t totalDataSize = array->getTotalDataSize(); output.write(b, totalDataSize); } unsigned int fsize = output.tellp(); // pad to 4 bytes unsigned int remainder = fsize % 4; if (remainder) { unsigned int null = 0; output.write((char*) (&null), 4 - remainder); fsize = output.tellp(); } return std::pair(offset, fsize - offset); } void JSONVertexArray::write(json_stream& str, WriteVisitor& visitor) { bool _useExternalBinaryArray = visitor._useExternalBinaryArray; bool _mergeAllBinaryFiles = visitor._mergeAllBinaryFiles; std::string basename = visitor._baseName; addUniqueID(); std::stringstream url; if (visitor._useExternalBinaryArray) { std::string bufferName = getBufferName(); if(bufferName.empty()) bufferName = visitor.getBinaryFilename(); if (visitor._mergeAllBinaryFiles) url << bufferName; else url << basename << "_" << getUniqueID() << ".bin"; } std::string type; osg::ref_ptr array = _arrayData; switch (array->getType()) { case osg::Array::QuatArrayType: { osg::ref_ptr converted = new osg::Vec4Array; converted->reserve(array->getNumElements()); const osg::QuatArray* a = static_cast(array.get()); for (unsigned int i = 0; i < array->getNumElements(); ++i) { converted->push_back(osg::Vec4(static_cast((*a)[i][0]), static_cast((*a)[i][1]), static_cast((*a)[i][2]), static_cast((*a)[i][3]))); } array = converted; type = "Float32Array"; break; } case osg::Array::FloatArrayType: case osg::Array::Vec2ArrayType: case osg::Array::Vec3ArrayType: case osg::Array::Vec4ArrayType: type = "Float32Array"; break; case osg::Array::Vec4ubArrayType: { osg::ref_ptr converted = new osg::Vec4Array; converted->reserve(array->getNumElements()); const osg::Vec4ubArray* a = static_cast(array.get()); for (unsigned int i = 0; i < a->getNumElements(); ++i) { converted->push_back(osg::Vec4( (*a)[i][0]/255.0, (*a)[i][1]/255.0, (*a)[i][2]/255.0, (*a)[i][3]/255.0)); } array = converted; type = "Float32Array"; } break; case osg::Array::UByteArrayType: case osg::Array::Vec2ubArrayType: case osg::Array::Vec3ubArrayType: type = "Uint8Array"; break; case osg::Array::UShortArrayType: case osg::Array::Vec2usArrayType: case osg::Array::Vec3usArrayType: case osg::Array::Vec4usArrayType: type = "Uint16Array"; break; case osg::Array::UIntArrayType: case osg::Array::Vec2uiArrayType: case osg::Array::Vec3uiArrayType: case osg::Array::Vec4uiArrayType: type = "Uint32Array"; break; case osg::Array::ByteArrayType: case osg::Array::Vec2bArrayType: case osg::Array::Vec3bArrayType: case osg::Array::Vec4bArrayType: type = "Int8Array"; break; case osg::Array::ShortArrayType: case osg::Array::Vec2sArrayType: case osg::Array::Vec3sArrayType: case osg::Array::Vec4sArrayType: type = "Int16Array"; break; case osg::Array::IntArrayType: case osg::Array::Vec2iArrayType: case osg::Array::Vec3iArrayType: case osg::Array::Vec4iArrayType: type = "Int32Array"; break; default: osg::notify(osg::WARN) << "Array of type " << array->getType() << " not supported" << std::endl; break; } str << "{ " << std::endl; JSONObjectBase::level++; str << JSONObjectBase::indent() << "\"" << type << "\"" << ": { " << std::endl; JSONObjectBase::level++; if (_useExternalBinaryArray) { str << JSONObjectBase::indent() << "\"File\": \"" << osgDB::getSimpleFileName(url.str()) << "\","<< std::endl; } else { if (array->getNumElements() == 0) { str << JSONObjectBase::indent() << "\"Elements\": [ ],"; } else { switch (array->getType()) { case osg::Array::FloatArrayType: case osg::Array::Vec2ArrayType: case osg::Array::Vec3ArrayType: case osg::Array::Vec4ArrayType: { const float* a = static_cast(array->getDataPointer()); unsigned int size = array->getNumElements() * array->getDataSize(); writeInlineArrayReal(str, size, a); } break; case osg::Array::DoubleArrayType: case osg::Array::Vec2dArrayType: case osg::Array::Vec3dArrayType: case osg::Array::Vec4dArrayType: case osg::Array::QuatArrayType: { const double* a = static_cast(array->getDataPointer()); unsigned int size = array->getNumElements() * array->getDataSize(); writeInlineArrayReal(str, size, a); } break; case osg::Array::ByteArrayType: case osg::Array::Vec2bArrayType: case osg::Array::Vec3bArrayType: case osg::Array::Vec4bArrayType: { const char* a = static_cast(array->getDataPointer()); unsigned int size = array->getNumElements() * array->getDataSize(); writeInlineArray(str, size, a); // using short to write readable numbers and not `char`s } break; case osg::Array::UByteArrayType: case osg::Array::Vec2ubArrayType: case osg::Array::Vec3ubArrayType: case osg::Array::Vec4ubArrayType: { const unsigned char* a = static_cast(array->getDataPointer()); unsigned int size = array->getNumElements() * array->getDataSize(); writeInlineArray(str, size, a); // using short to write readable numbers and not `char`s } break; case osg::Array::ShortArrayType: case osg::Array::Vec2sArrayType: case osg::Array::Vec3sArrayType: case osg::Array::Vec4sArrayType: { const short* a = static_cast(array->getDataPointer()); unsigned int size = array->getNumElements() * array->getDataSize(); writeInlineArray(str, size, a); } break; case osg::Array::UShortArrayType: case osg::Array::Vec2usArrayType: case osg::Array::Vec3usArrayType: case osg::Array::Vec4usArrayType: { const unsigned short* a = static_cast(array->getDataPointer()); unsigned int size = array->getNumElements() * array->getDataSize(); writeInlineArray(str, size, a); } break; case osg::Array::IntArrayType: case osg::Array::Vec2iArrayType: case osg::Array::Vec3iArrayType: case osg::Array::Vec4iArrayType: { const int* a = static_cast(array->getDataPointer()); unsigned int size = array->getNumElements() * array->getDataSize(); writeInlineArray(str, size, a); } break; case osg::Array::UIntArrayType: case osg::Array::Vec2uiArrayType: case osg::Array::Vec3uiArrayType: case osg::Array::Vec4uiArrayType: { const unsigned int* a = static_cast(array->getDataPointer()); unsigned int size = array->getNumElements() * array->getDataSize(); writeInlineArray(str, size, a); } break; case osg::Array::MatrixdArrayType: default: break; } } } str << JSONObjectBase::indent() << "\"Size\": " << array->getNumElements(); if (_useExternalBinaryArray) { str << "," << std::endl; } else { str << std::endl; } if (_useExternalBinaryArray) { unsigned int size; if (_mergeAllBinaryFiles) { std::pair result; std::string encoding; result = writeMergeData(array.get(), visitor, url.str(), encoding); unsigned int offset = result.first; size = result.second; if(!encoding.empty()) { str << JSONObjectBase::indent() << "\"Offset\": " << offset << "," << std::endl; str << JSONObjectBase::indent() << "\"Encoding\": \"" << encoding << "\"" << std::endl; } else { str << JSONObjectBase::indent() << "\"Offset\": " << offset << std::endl; } } else { size = writeData(array.get(), url.str()); str << JSONObjectBase::indent() << "\"Offset\": " << 0 << std::endl; } std::stringstream b; osg::notify(osg::NOTICE) << "TypedArray " << type << " " << url.str() << " "; 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; } } JSONObjectBase::level--; str << JSONObjectBase::indent() << "}" << std::endl; JSONObjectBase::level--; str << JSONObjectBase::indent() << "}"; } JSONVec4Array::JSONVec4Array(const osg::Vec4& v) : JSONVec3Array() { for (int i = 0; i < 4; ++i) { _array.push_back(new JSONValue(v[i])); } } JSONVec2Array::JSONVec2Array(const osg::Vec2& v) : JSONVec3Array() { for (int i = 0; i < 2; ++i) { _array.push_back(new JSONValue(v[i])); } } JSONVec3Array::JSONVec3Array(const osg::Vec3& v) { for (int i = 0; i < 3; ++i) { _array.push_back(new JSONValue(v[i])); } } void JSONVec3Array::write(json_stream& str,WriteVisitor& visitor) { str << "[ "; for (unsigned int i = 0; i < _array.size(); i++) { if (_array[i].valid()) { _array[i]->write(str, visitor); } else { str << "undefined"; } if (i != _array.size()-1) str << ", "; } str << "]"; } void JSONArray::write(json_stream& str,WriteVisitor& visitor) { str << "[ "; for (unsigned int i = 0; i < _array.size(); i++) { if (_array[i].valid()) { _array[i]->write(str, visitor); } else { str << "undefined"; } if (i != _array.size() -1) { str << ","; str << "\n" << JSONObjectBase::indent(); } } //str << std::endl << JSONObjectBase::indent() << "]"; str << " ]"; } JSONObject* getDrawMode(GLenum mode) { JSONObject* result = 0; switch (mode) { case GL_POINTS: result = new JSONValue("POINTS"); break; case GL_LINES: result = new JSONValue("LINES"); break; case GL_LINE_LOOP: result = new JSONValue("LINE_LOOP"); break; case GL_LINE_STRIP: result = new JSONValue("LINE_STRIP"); break; case GL_TRIANGLES: result = new JSONValue("TRIANGLES"); break; case GL_POLYGON: result = new JSONValue("TRIANGLE_FAN"); break; case GL_QUAD_STRIP: case GL_TRIANGLE_STRIP: result = new JSONValue("TRIANGLE_STRIP"); break; case GL_TRIANGLE_FAN: result = new JSONValue("TRIANGLE_FAN"); break; case GL_QUADS: osg::notify(osg::WARN) << "exporting quads will not be able to work on opengl es" << std::endl; break; } return result; }