Files
OpenSceneGraph/src/osgPlugins/osgjs/JSON_Objects.cpp
2016-07-05 16:48:53 +02:00

681 lines
23 KiB
C++

/* -*-c++-*-
* Copyright (C) 2010 Cedric Pinson <cedric.pinson@plopbyte.net>
*/
#include "JSON_Objects"
#include <osgDB/WriteFile>
#include <osgDB/FileNameUtils>
#include <osg/Material>
#include <osg/BlendFunc>
#include <osg/BlendColor>
#include <osg/Texture>
#include <osg/CullFace>
#include <osg/Texture2D>
#include <osg/Texture1D>
#include <osg/Image>
#include <sstream>
#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<unsigned int>(id);
}
void JSONObject::addUniqueID()
{
if(_maps.find("UniqueID") == _maps.end()) {
_maps["UniqueID"] = new JSONValue<unsigned int>(JSONObject::uniqueID ++);
}
}
unsigned int JSONObject::getUniqueID() const
{
JSONMap::const_iterator iterator = _maps.find("UniqueID");
if(iterator == _maps.end()) {
return 0xffffffff;
}
const JSONValue<unsigned int>* uid = dynamic_cast<JSONValue<unsigned int>*>(iterator->second.get());
return uid->getValue();
}
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<int>(array->getType()))
{
case osg::Array::IntArrayType:
isInteger = (dynamic_cast<osg::IntArray const*>(array) != NULL);
break;
case osg::Array::ShortArrayType:
isInteger = (dynamic_cast<osg::ShortArray const*>(array) != NULL);
break;
case osg::Array::UIntArrayType:
isInteger = (dynamic_cast<osg::UIntArray const*>(array) != NULL);
break;
case osg::Array::UShortArrayType:
isInteger = (dynamic_cast<osg::UShortArray const*>(array) != NULL);
break;
case osg::Array::Vec2iArrayType:
isInteger = (dynamic_cast<osg::Vec2iArray const*>(array) != NULL);
break;
case osg::Array::Vec3iArrayType:
isInteger = (dynamic_cast<osg::Vec3iArray const*>(array) != NULL);
break;
case osg::Array::Vec4iArrayType:
isInteger = (dynamic_cast<osg::Vec4iArray const*>(array) != NULL);
break;
case osg::Array::Vec2uiArrayType:
isInteger = (dynamic_cast<osg::Vec2uiArray const*>(array) != NULL);
break;
case osg::Array::Vec3uiArrayType:
isInteger = (dynamic_cast<osg::Vec3uiArray const*>(array) != NULL);
break;
case osg::Array::Vec4uiArrayType:
isInteger = (dynamic_cast<osg::Vec4uiArray const*>(array) != NULL);
break;
case osg::Array::Vec2sArrayType:
isInteger = (dynamic_cast<osg::Vec2sArray const*>(array) != NULL);
break;
case osg::Array::Vec3sArrayType:
isInteger = (dynamic_cast<osg::Vec3sArray const*>(array) != NULL);
break;
case osg::Array::Vec4sArrayType:
isInteger = (dynamic_cast<osg::Vec4sArray const*>(array) != NULL);
break;
case osg::Array::Vec2usArrayType:
isInteger = (dynamic_cast<osg::Vec2usArray const*>(array) != NULL);
break;
case osg::Array::Vec3usArrayType:
isInteger = (dynamic_cast<osg::Vec3usArray const*>(array) != NULL);
break;
case osg::Array::Vec4usArrayType:
isInteger = (dynamic_cast<osg::Vec4usArray const*>(array) != NULL);
break;
default:
isInteger = false;
break;
}
return isInteger;
}
void JSONObject::encodeArrayAsVarintBuffer(osg::Array const* array, std::vector<uint8_t>& buffer) const
{
switch(static_cast<int>(array->getType()))
{
case osg::Array::IntArrayType:
dumpVarintValue<osg::IntArray>(buffer, dynamic_cast<osg::IntArray const*>(array), false);
break;
case osg::Array::ShortArrayType:
dumpVarintValue<osg::ShortArray>(buffer, dynamic_cast<osg::ShortArray const*>(array), false);
break;
case osg::Array::UIntArrayType:
dumpVarintValue<osg::UIntArray>(buffer, dynamic_cast<osg::UIntArray const*>(array), true);
break;
case osg::Array::UShortArrayType:
dumpVarintValue<osg::UShortArray>(buffer, dynamic_cast<osg::UShortArray const*>(array), true);
break;
case osg::Array::Vec2iArrayType:
dumpVarintVector<osg::Vec2iArray>(buffer, dynamic_cast<osg::Vec2iArray const*>(array), false);
break;
case osg::Array::Vec3iArrayType:
dumpVarintVector<osg::Vec3iArray>(buffer, dynamic_cast<osg::Vec3iArray const*>(array), false);
break;
case osg::Array::Vec4iArrayType:
dumpVarintVector<osg::Vec4iArray>(buffer, dynamic_cast<osg::Vec4iArray const*>(array), false);
break;
case osg::Array::Vec2sArrayType:
dumpVarintVector<osg::Vec2sArray>(buffer, dynamic_cast<osg::Vec2sArray const*>(array), false);
break;
case osg::Array::Vec3sArrayType:
dumpVarintVector<osg::Vec3sArray>(buffer, dynamic_cast<osg::Vec3sArray const*>(array), false);
break;
case osg::Array::Vec4sArrayType:
dumpVarintVector<osg::Vec4sArray>(buffer, dynamic_cast<osg::Vec4sArray const*>(array), false);
break;
case osg::Array::Vec2uiArrayType:
dumpVarintVector<osg::Vec2uiArray>(buffer, dynamic_cast<osg::Vec2uiArray const*>(array), true);
break;
case osg::Array::Vec3uiArrayType:
dumpVarintVector<osg::Vec3uiArray>(buffer, dynamic_cast<osg::Vec3uiArray const*>(array), true);
break;
case osg::Array::Vec4uiArrayType:
dumpVarintVector<osg::Vec4uiArray>(buffer, dynamic_cast<osg::Vec4uiArray const*>(array), true);
break;
case osg::Array::Vec2usArrayType:
dumpVarintVector<osg::Vec2usArray>(buffer, dynamic_cast<osg::Vec2usArray const*>(array), true);
break;
case osg::Array::Vec3usArrayType:
dumpVarintVector<osg::Vec3usArray>(buffer, dynamic_cast<osg::Vec3usArray const*>(array), true);
break;
case osg::Array::Vec4usArrayType:
dumpVarintVector<osg::Vec4usArray>(buffer, dynamic_cast<osg::Vec4usArray const*>(array), true);
break;
default:
break;
}
}
template<typename T>
void JSONObject::dumpVarintVector(std::vector<uint8_t>& 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<uint8_t> encoding = varintEncoding(value);
oss.insert(oss.end(), encoding.begin(), encoding.end());
}
}
}
template<typename T>
void JSONObject::dumpVarintValue(std::vector<uint8_t>& 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<uint8_t> 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<uint8_t> JSONObject::varintEncoding(unsigned int value) const
{
std::vector<uint8_t> 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<std::string>& 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<unsigned int,unsigned int> 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<uint8_t> varintByteBuffer;
encodeArrayAsVarintBuffer(array, varintByteBuffer);
output.write((char*)&varintByteBuffer[0], varintByteBuffer.size() * sizeof(uint8_t));
encoding = std::string("varint");
}
else
{
const char* b = static_cast<const char*>(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<unsigned int, unsigned int>(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<const osg::Array> array = _arrayData;
switch (array->getType()) {
case osg::Array::QuatArrayType:
{
osg::ref_ptr<osg::Vec4Array> converted = new osg::Vec4Array;
converted->reserve(array->getNumElements());
const osg::QuatArray* a = dynamic_cast<const osg::QuatArray*>(array.get());
for (unsigned int i = 0; i < array->getNumElements(); ++i) {
converted->push_back(osg::Vec4(static_cast<float>((*a)[i][0]),
static_cast<float>((*a)[i][1]),
static_cast<float>((*a)[i][2]),
static_cast<float>((*a)[i][3])));
}
array = converted;
type = "Float32Array";
}
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<osg::Vec4Array> converted = new osg::Vec4Array;
converted->reserve(array->getNumElements());
const osg::Vec4ubArray* a = dynamic_cast<const osg::Vec4ubArray*>(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<const float*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArrayReal<float>(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<const double*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArrayReal<double>(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<const char*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<char, short>(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<const unsigned char*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<unsigned char, unsigned short>(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<const short*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<short>(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<const unsigned short*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<unsigned short>(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<const int*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<int>(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<const unsigned int*>(array->getDataPointer());
unsigned int size = array->getNumElements() * array->getDataSize();
writeInlineArray<unsigned int>(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<unsigned int, unsigned int> 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<float>(v[i]));
}
}
JSONVec2Array::JSONVec2Array(const osg::Vec2& v) : JSONVec3Array()
{
for (int i = 0; i < 2; ++i) {
_array.push_back(new JSONValue<float>(v[i]));
}
}
JSONVec3Array::JSONVec3Array(const osg::Vec3& v)
{
for (int i = 0; i < 3; ++i) {
_array.push_back(new JSONValue<float>(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<std::string>("POINTS");
break;
case GL_LINES:
result = new JSONValue<std::string>("LINES");
break;
case GL_LINE_LOOP:
result = new JSONValue<std::string>("LINE_LOOP");
break;
case GL_LINE_STRIP:
result = new JSONValue<std::string>("LINE_STRIP");
break;
case GL_TRIANGLES:
result = new JSONValue<std::string>("TRIANGLES");
break;
case GL_POLYGON:
result = new JSONValue<std::string>("TRIANGLE_FAN");
break;
case GL_QUAD_STRIP:
case GL_TRIANGLE_STRIP:
result = new JSONValue<std::string>("TRIANGLE_STRIP");
break;
case GL_TRIANGLE_FAN:
result = new JSONValue<std::string>("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;
}