summaryrefslogtreecommitdiffstats
path: root/o3d/serializer
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/serializer')
-rw-r--r--o3d/serializer/build.scons65
-rw-r--r--o3d/serializer/cross/serializer.cc787
-rw-r--r--o3d/serializer/cross/serializer.h200
-rw-r--r--o3d/serializer/cross/serializer_binary.cc266
-rw-r--r--o3d/serializer/cross/serializer_binary.h56
-rw-r--r--o3d/serializer/cross/serializer_test.cc1067
-rw-r--r--o3d/serializer/cross/version.h45
7 files changed, 2486 insertions, 0 deletions
diff --git a/o3d/serializer/build.scons b/o3d/serializer/build.scons
new file mode 100644
index 0000000..0f025a9
--- /dev/null
+++ b/o3d/serializer/build.scons
@@ -0,0 +1,65 @@
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+Import('env')
+
+env.Append(
+ LIBS = [
+ 'o3dCore',
+ 'o3dUtils',
+ 'o3d_base',
+ ] + env['ICU_LIBS'],
+)
+
+# add renderer specific libraries and includes to the linker environment
+env.Append(CPPPATH = env['RENDERER_INCLUDE_PATH'],
+ LIBPATH = env['RENDERER_LIB_PATH'],
+ LIBS = env['RENDERER_LIBS'])
+
+if env.Bit('windows'):
+ env.Append(
+ CCFLAGS=['/Wp64', '/D_CRT_SECURE_NO_WARNINGS'],
+ LINKFLAGS=['/SUBSYSTEM:CONSOLE'],
+ )
+
+if env.Bit('mac'):
+ env.Append(
+ FRAMEWORKS = [
+ 'Foundation',
+ ],
+ )
+
+inputs = [
+ 'cross/serializer.cc',
+ 'cross/serializer_binary.cc',
+]
+
+# Create library.
+lib = env.ComponentLibrary('o3dSerializer', inputs)
diff --git a/o3d/serializer/cross/serializer.cc b/o3d/serializer/cross/serializer.cc
new file mode 100644
index 0000000..f9266af
--- /dev/null
+++ b/o3d/serializer/cross/serializer.cc
@@ -0,0 +1,787 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// This file contains the definitions of class Serializer.
+
+#include <vector>
+
+#include "serializer/cross/serializer.h"
+#include "serializer/cross/serializer_binary.h"
+
+#include "core/cross/buffer.h"
+#include "core/cross/curve.h"
+#include "core/cross/iclass_manager.h"
+#include "core/cross/pack.h"
+#include "core/cross/param_array.h"
+#include "core/cross/primitive.h"
+#include "core/cross/shape.h"
+#include "core/cross/skin.h"
+#include "core/cross/texture.h"
+#include "core/cross/transform.h"
+#include "import/cross/iarchive_generator.h"
+#include "import/cross/memory_buffer.h"
+#include "import/cross/memory_stream.h"
+#include "serializer/cross/version.h"
+#include "utils/cross/structured_writer.h"
+
+using std::string;
+
+namespace o3d {
+
+const char* Serializer::ROOT_PREFIX = "o3d_rootObject_";
+
+void Serialize(StructuredWriter* writer, ObjectBase* value) {
+ if (value == NULL) {
+ writer->WriteNull();
+ } else {
+ writer->BeginCompacting();
+ writer->OpenObject();
+ writer->WritePropertyName("ref");
+ writer->WriteInt(GetObjectId(value));
+ writer->CloseObject();
+ writer->EndCompacting();
+ }
+}
+
+void Serialize(StructuredWriter* writer, float value) {
+ writer->WriteFloat(value);
+}
+
+void Serialize(StructuredWriter* writer, const Float2& value) {
+ writer->BeginCompacting();
+ writer->OpenArray();
+ for (int i = 0; i < 2; ++i)
+ writer->WriteFloat(value[i]);
+ writer->CloseArray();
+ writer->EndCompacting();
+}
+
+void Serialize(StructuredWriter* writer, const Float3& value) {
+ writer->BeginCompacting();
+ writer->OpenArray();
+ for (int i = 0; i < 3; ++i)
+ writer->WriteFloat(value[i]);
+ writer->CloseArray();
+ writer->EndCompacting();
+}
+
+void Serialize(StructuredWriter* writer, const Float4& value) {
+ writer->BeginCompacting();
+ writer->OpenArray();
+ for (int i = 0; i < 4; ++i)
+ writer->WriteFloat(value[i]);
+ writer->CloseArray();
+ writer->EndCompacting();
+}
+
+void Serialize(StructuredWriter* writer, int value) {
+ writer->WriteInt(value);
+}
+
+void Serialize(StructuredWriter* writer, unsigned int value) {
+ writer->WriteUnsignedInt(value);
+}
+
+void Serialize(StructuredWriter* writer, bool value) {
+ writer->WriteBool(value);
+}
+
+void Serialize(StructuredWriter* writer, const String& value) {
+ writer->WriteString(value);
+}
+
+void Serialize(StructuredWriter* writer, const Matrix4& value) {
+ writer->BeginCompacting();
+ writer->OpenArray();
+ for (int i = 0; i < 4; ++i) {
+ writer->OpenArray();
+ for (int j = 0; j < 4; ++j)
+ writer->WriteFloat(value[i][j]);
+ writer->CloseArray();
+ }
+ writer->CloseArray();
+ writer->EndCompacting();
+}
+
+void Serialize(StructuredWriter* writer, const BoundingBox& value) {
+ writer->BeginCompacting();
+ writer->OpenArray();
+
+ if (value.valid()) {
+ writer->OpenArray();
+ for (int i = 0; i < 3; ++i)
+ writer->WriteFloat(value.min_extent()[i]);
+ writer->CloseArray();
+ writer->OpenArray();
+ for (int i = 0; i < 3; ++i)
+ writer->WriteFloat(value.max_extent()[i]);
+ writer->CloseArray();
+ }
+
+ writer->CloseArray();
+ writer->EndCompacting();
+}
+
+void Serialize(StructuredWriter* writer, const Stream& stream) {
+ writer->OpenObject();
+
+ writer->WritePropertyName("field");
+ Serialize(writer, GetObjectId(&stream.field()));
+
+ writer->WritePropertyName("startIndex");
+ Serialize(writer, stream.start_index());
+
+ writer->WritePropertyName("semantic");
+ Serialize(writer, stream.semantic());
+
+ writer->WritePropertyName("semanticIndex");
+ Serialize(writer, stream.semantic_index());
+
+ writer->CloseObject();
+}
+
+void BinaryArchiveManager::WriteObjectBinary(ObjectBase* object,
+ const string& file_name,
+ const uint8* data,
+ size_t numBytes) {
+ FileContent& content = file_map_[file_name];
+ ObjectBinaryRangeMap::iterator it = object_binary_range_map_.find(object);
+ BinaryRange range;
+ if (it != object_binary_range_map_.end()) {
+ // It is okay to call WriteObjectBinary multiple times for a single
+ // object but they must not be interleaved with those for other objects.
+ range = it->second;
+ DCHECK(range.end_offset_ == content.size());
+ } else {
+ range.begin_offset_ = content.size();
+ }
+ content.insert(content.end(), data, data + numBytes);
+ range.end_offset_ = content.size();
+ object_binary_range_map_[object] = range;
+}
+
+BinaryRange BinaryArchiveManager::GetObjectRange(ObjectBase* object) {
+ return object_binary_range_map_[object];
+}
+
+void BinaryArchiveManager::WriteArchive(IArchiveGenerator* archive_generator) {
+ for (FileMap::const_iterator it = file_map_.begin();
+ it != file_map_.end();
+ ++it) {
+ const string& file_name = it->first;
+ const FileContent& content = it->second;
+ archive_generator->AddFile(file_name,
+ content.size());
+ MemoryReadStream stream(&content[0],
+ content.size());
+ archive_generator->AddFileBytes(&stream, content.size());
+ }
+}
+
+namespace {
+bool ParamIsSerialized(Param* param) {
+ return param->output_connections().size() != 0 ||
+ (!param->dynamic() && !param->read_only());
+}
+
+class PropertiesVisitor : public VisitorBase<PropertiesVisitor> {
+ public:
+ explicit PropertiesVisitor(StructuredWriter* writer)
+ : writer_(writer) {
+ Enable<Curve>(&PropertiesVisitor::Visit);
+ Enable<Element>(&PropertiesVisitor::Visit);
+ Enable<NamedObject>(&PropertiesVisitor::Visit);
+ Enable<Pack>(&PropertiesVisitor::Visit);
+ Enable<Primitive>(&PropertiesVisitor::Visit);
+ Enable<Shape>(&PropertiesVisitor::Visit);
+ Enable<Skin>(&PropertiesVisitor::Visit);
+ Enable<Transform>(&PropertiesVisitor::Visit);
+ }
+
+ private:
+ void Visit(Curve* curve) {
+ Visit(static_cast<NamedObject*>(curve));
+
+ writer_->WritePropertyName("preInfinity");
+ Serialize(writer_, curve->pre_infinity());
+
+ writer_->WritePropertyName("postInfinity");
+ Serialize(writer_, curve->post_infinity());
+
+ writer_->WritePropertyName("useCache");
+ Serialize(writer_, curve->use_cache());
+
+ writer_->WritePropertyName("sampleRate");
+ Serialize(writer_, curve->sample_rate());
+ }
+
+ void Visit(Element* element) {
+ Visit(static_cast<NamedObject*>(element));
+
+ writer_->WritePropertyName("owner");
+ Serialize(writer_, element->owner());
+ }
+
+ void Visit(NamedObject* object) {
+ Visit(static_cast<ObjectBase*>(object));
+
+ if (object->name().length() > 0) {
+ writer_->WritePropertyName("name");
+ Serialize(writer_, object->name());
+ }
+ }
+
+ void Visit(ObjectBase* object) {
+ }
+
+ void Visit(Pack* pack) {
+ Visit(static_cast<NamedObject*>(pack));
+
+ writer_->WritePropertyName("root");
+ Serialize(writer_, pack->root());
+ }
+
+ void Visit(Primitive* primitive) {
+ Visit(static_cast<Element*>(primitive));
+
+ writer_->WritePropertyName("numberVertices");
+ Serialize(writer_, primitive->number_vertices());
+
+ writer_->WritePropertyName("numberPrimitives");
+ Serialize(writer_, primitive->number_primitives());
+
+ writer_->WritePropertyName("primitiveType");
+ Serialize(writer_, primitive->primitive_type());
+
+ writer_->WritePropertyName("indexBuffer");
+ Serialize(writer_, primitive->index_buffer());
+
+ writer_->WritePropertyName("startIndex");
+ Serialize(writer_, primitive->start_index());
+ }
+
+ void Visit(Shape* shape) {
+ Visit(static_cast<ParamObject*>(shape));
+
+ writer_->WritePropertyName("elements");
+ writer_->BeginCompacting();
+ writer_->OpenArray();
+ const ElementRefArray& elements = shape->GetElementRefs();
+ for (int i = 0; i != elements.size(); ++i) {
+ Serialize(writer_, elements[i].Get());
+ }
+ writer_->CloseArray();
+ writer_->EndCompacting();
+ }
+
+ void Visit(Skin* skin) {
+ Visit(static_cast<NamedObject*>(skin));
+
+ writer_->WritePropertyName("inverseBindPoseMatrices");
+ writer_->BeginCompacting();
+ writer_->OpenArray();
+ const Skin::MatrixArray& inverse_bind_pose_matrices =
+ skin->inverse_bind_pose_matrices();
+ for (int i = 0; i != inverse_bind_pose_matrices.size(); ++i) {
+ const Matrix4& matrix = inverse_bind_pose_matrices[i];
+ Serialize(writer_, matrix);
+ }
+ writer_->CloseArray();
+ writer_->EndCompacting();
+ }
+
+ void Visit(Transform* transform) {
+ Visit(static_cast<ParamObject*>(transform));
+
+ writer_->WritePropertyName("shapes");
+ writer_->BeginCompacting();
+ writer_->OpenArray();
+ const ShapeRefArray& shape_array = transform->GetShapeRefs();
+ for (int i = 0; i != shape_array.size(); ++i) {
+ Serialize(writer_, shape_array[i]);
+ }
+ writer_->CloseArray();
+ writer_->EndCompacting();
+
+ writer_->WritePropertyName("parent");
+ Serialize(writer_, transform->parent());
+ }
+
+ StructuredWriter* writer_;
+};
+
+class CustomVisitor : public VisitorBase<CustomVisitor> {
+ public:
+ explicit CustomVisitor(StructuredWriter* writer,
+ BinaryArchiveManager* binary_archive_manager)
+ : writer_(writer),
+ binary_archive_manager_(binary_archive_manager) {
+ Enable<Buffer>(&CustomVisitor::Visit);
+ Enable<Curve>(&CustomVisitor::Visit);
+ Enable<Primitive>(&CustomVisitor::Visit);
+ Enable<Skin>(&CustomVisitor::Visit);
+ Enable<SkinEval>(&CustomVisitor::Visit);
+ Enable<StreamBank>(&CustomVisitor::Visit);
+ Enable<Texture2D>(&CustomVisitor::Visit);
+ Enable<TextureCUBE>(&CustomVisitor::Visit);
+ }
+
+ private:
+ void Visit(Buffer* buffer) {
+ Visit(static_cast<NamedObject*>(buffer));
+
+ writer_->WritePropertyName("fields");
+ writer_->OpenArray();
+ const FieldRefArray& fields = buffer->fields();
+ for (size_t ii = 0; ii < fields.size(); ++ii) {
+ Field* field = fields[ii].Get();
+ Serialize(writer_, field->id());
+ }
+ writer_->CloseArray();
+
+ WriteObjectBinaryRange(buffer);
+ }
+
+ void Visit(Curve* curve) {
+ Visit(static_cast<NamedObject*>(curve));
+ WriteObjectBinaryRange(curve);
+ }
+
+ void Visit(ObjectBase* object) {
+ }
+
+ void Visit(Primitive* primitive) {
+ Visit(static_cast<Element*>(primitive));
+
+ if (primitive->index_buffer()) {
+ writer_->WritePropertyName("indexBuffer");
+ Serialize(writer_, GetObjectId(primitive->index_buffer()));
+ }
+ }
+
+ void Visit(Skin* skin) {
+ Visit(static_cast<NamedObject*>(skin));
+ WriteObjectBinaryRange(skin);
+ }
+
+ void Visit(SkinEval* skin_eval) {
+ Visit(static_cast<VertexSource*>(skin_eval));
+
+ writer_->WritePropertyName("vertexStreams");
+ writer_->OpenArray();
+ const StreamParamVector& vertex_stream_params =
+ skin_eval->vertex_stream_params();
+ for (int i = 0; i != vertex_stream_params.size(); ++i) {
+ const Stream& stream = vertex_stream_params[i]->stream();
+ writer_->OpenObject();
+ writer_->WritePropertyName("stream");
+ Serialize(writer_, stream);
+
+ if (vertex_stream_params[i]->input_connection() != NULL) {
+ writer_->WritePropertyName("bind");
+ Serialize(writer_,
+ GetObjectId(
+ vertex_stream_params[i]->input_connection()->owner()));
+ }
+
+ writer_->CloseObject();
+ }
+ writer_->CloseArray();
+ }
+
+ void Visit(StreamBank* stream_bank) {
+ Visit(static_cast<NamedObject*>(stream_bank));
+
+ writer_->WritePropertyName("vertexStreams");
+ writer_->OpenArray();
+ const StreamParamVector& vertex_stream_params =
+ stream_bank->vertex_stream_params();
+ for (int i = 0; i != vertex_stream_params.size(); ++i) {
+ const Stream& stream = vertex_stream_params[i]->stream();
+ writer_->OpenObject();
+ writer_->WritePropertyName("stream");
+ Serialize(writer_, stream);
+
+ if (vertex_stream_params[i]->input_connection() != NULL) {
+ writer_->WritePropertyName("bind");
+ Serialize(writer_,
+ GetObjectId(
+ vertex_stream_params[i]->input_connection()->owner()));
+ }
+
+ writer_->CloseObject();
+ }
+ writer_->CloseArray();
+ }
+
+ void Visit(Texture2D* texture) {
+ Visit(static_cast<Texture*>(texture));
+
+ writer_->WritePropertyName("width");
+ Serialize(writer_, texture->width());
+ writer_->WritePropertyName("height");
+ Serialize(writer_, texture->height());
+ writer_->WritePropertyName("format");
+ Serialize(writer_, texture->format());
+ writer_->WritePropertyName("levels");
+ Serialize(writer_, texture->levels());
+ writer_->WritePropertyName("renderSurfacesEnabled");
+ Serialize(writer_, texture->render_surfaces_enabled());
+ }
+
+ void Visit(TextureCUBE* texture) {
+ Visit(static_cast<Texture*>(texture));
+
+ writer_->WritePropertyName("edgeLength");
+ Serialize(writer_, texture->edge_length());
+ writer_->WritePropertyName("format");
+ Serialize(writer_, texture->format());
+ writer_->WritePropertyName("levels");
+ Serialize(writer_, texture->levels());
+ writer_->WritePropertyName("renderSurfacesEnabled");
+ Serialize(writer_, texture->render_surfaces_enabled());
+ }
+
+ void WriteObjectBinaryRange(ObjectBase* object) {
+ writer_->WritePropertyName("binaryRange");
+ writer_->BeginCompacting();
+ writer_->OpenArray();
+ BinaryRange range = binary_archive_manager_->GetObjectRange(object);
+ writer_->WriteUnsignedInt(static_cast<unsigned int>(range.begin_offset_));
+ writer_->WriteUnsignedInt(static_cast<unsigned int>(range.end_offset_));
+ writer_->CloseArray();
+ writer_->EndCompacting();
+ }
+
+ StructuredWriter* writer_;
+ BinaryArchiveManager* binary_archive_manager_;
+};
+
+class ParamVisitor : public VisitorBase<ParamVisitor> {
+ public:
+ explicit ParamVisitor(StructuredWriter* writer)
+ : writer_(writer) {
+ Enable<ParamObject>(&ParamVisitor::Visit);
+ Enable<ParamArray>(&ParamVisitor::Visit);
+ Enable<ParamBoolean>(&ParamVisitor::Visit);
+ Enable<ParamBoundingBox>(&ParamVisitor::Visit);
+ Enable<ParamFloat>(&ParamVisitor::Visit);
+ Enable<ParamFloat2>(&ParamVisitor::Visit);
+ Enable<ParamFloat3>(&ParamVisitor::Visit);
+ Enable<ParamFloat4>(&ParamVisitor::Visit);
+ Enable<ParamInteger>(&ParamVisitor::Visit);
+ Enable<ParamMatrix4>(&ParamVisitor::Visit);
+ Enable<ParamString>(&ParamVisitor::Visit);
+ Enable<RefParamBase>(&ParamVisitor::Visit);
+ Enable<Material>(&ParamVisitor::Visit);
+ }
+
+ private:
+ void Visit(Material* object) {
+ Visit(static_cast<ParamObject*>(object));
+ }
+
+ void Visit(ParamObject* object) {
+ const NamedParamRefMap& params = object->params();
+
+ int numWrittenParams = 0;
+ for (NamedParamRefMap::const_iterator it = params.begin();
+ it != params.end(); ++it) {
+ String param_name = it->first;
+ Param::Ref param = it->second;
+ if (ParamIsSerialized(param.Get())) {
+ ++numWrittenParams;
+ }
+ }
+
+ if (numWrittenParams > 0) {
+ writer_->WritePropertyName("params");
+ writer_->OpenObject();
+ for (NamedParamRefMap::const_iterator it = params.begin();
+ it != params.end(); ++it) {
+ String param_name = it->first;
+ Param::Ref param = it->second;
+ if (ParamIsSerialized(param.Get())) {
+ writer_->WritePropertyName(param_name);
+ Accept(param.Get());
+ }
+ }
+ writer_->CloseObject();
+ }
+ }
+
+ void Visit(ParamArray* param_array) {
+ writer_->WritePropertyName("params");
+ writer_->OpenArray();
+ const ParamArray::ParamRefVector& params = param_array->params();
+ for (unsigned i = 0; i < params.size(); ++i) {
+ Param::Ref param = params[i];
+ Accept(param.Get());
+ }
+ writer_->CloseArray();
+ }
+
+ template <typename ParamType>
+ void Visit(ParamType* param) {
+ writer_->BeginCompacting();
+ writer_->OpenObject();
+
+ if (param->owner() == NULL || param->owner()->IsAddedParam(param)) {
+ writer_->WritePropertyName("class");
+ writer_->WriteString(param->GetClassName());
+ }
+
+ if (param->output_connections().size() != 0) {
+ writer_->WritePropertyName("id");
+ writer_->WriteInt(param->id());
+ }
+ if (param->input_connection() != NULL) {
+ writer_->WritePropertyName("bind");
+ writer_->WriteInt(param->input_connection()->id());
+ } else if (!param->dynamic()) {
+ writer_->WritePropertyName("value");
+ Serialize(writer_, param->value());
+ }
+
+ writer_->CloseObject();
+ writer_->EndCompacting();
+ }
+
+ StructuredWriter* writer_;
+};
+
+class BinaryVisitor : public VisitorBase<BinaryVisitor> {
+ public:
+ explicit BinaryVisitor(BinaryArchiveManager* binary_archive_manager)
+ : binary_archive_manager_(binary_archive_manager) {
+ Enable<Curve>(&BinaryVisitor::Visit);
+ Enable<IndexBuffer>(&BinaryVisitor::Visit);
+ Enable<VertexBufferBase>(&BinaryVisitor::Visit);
+ Enable<Skin>(&BinaryVisitor::Visit);
+ }
+
+ private:
+ void Visit(ObjectBase* object) {
+ }
+
+ // TODO: Replace this when we have code to serialize to the
+ // final binary format. This is just placeholder.
+ void Visit(Curve* curve) {
+ Visit(static_cast<NamedObject*>(curve));
+
+ MemoryBuffer<uint8> serialized_data;
+ SerializeCurve(*curve, &serialized_data);
+
+ binary_archive_manager_->WriteObjectBinary(
+ curve,
+ "curve-keys.bin",
+ serialized_data,
+ serialized_data.GetLength());
+ }
+
+ void Visit(IndexBuffer* buffer) {
+ Visit(static_cast<Buffer*>(buffer));
+
+ MemoryBuffer<uint8> serialized_data;
+ SerializeBuffer(*buffer, &serialized_data);
+
+ binary_archive_manager_->WriteObjectBinary(
+ buffer,
+ "index-buffers.bin",
+ serialized_data,
+ serialized_data.GetLength());
+ }
+
+ void Visit(VertexBufferBase* buffer) {
+ Visit(static_cast<Buffer*>(buffer));
+
+ MemoryBuffer<uint8> serialized_data;
+ SerializeBuffer(*buffer, &serialized_data);
+
+ binary_archive_manager_->WriteObjectBinary(
+ buffer,
+ "vertex-buffers.bin",
+ serialized_data,
+ serialized_data.GetLength());
+ }
+
+ void Visit(Skin* skin) {
+ Visit(static_cast<NamedObject*>(skin));
+
+ MemoryBuffer<uint8> serialized_data;
+ SerializeSkin(*skin, &serialized_data);
+
+ binary_archive_manager_->WriteObjectBinary(
+ skin,
+ "skins.bin",
+ serialized_data,
+ serialized_data.GetLength());
+ }
+
+ BinaryArchiveManager* binary_archive_manager_;
+};
+
+// Checks object has a name that starts with prefix.
+// Parameters:
+// object: ObjectBase object to check.
+// prefix: prefix to check for
+// name: optional, pointer to String to store name.
+bool NameStartsWithPrefix(const ObjectBase* object,
+ const String& prefix,
+ String* name) {
+ String object_name;
+ if (object->IsA(NamedObject::GetApparentClass())) {
+ object_name = down_cast<const NamedObject*>(object)->name();
+ } else if (object->IsA(Param::GetApparentClass())) {
+ object_name = down_cast<const Param*>(object)->name();
+ }
+ if (prefix.size() <= object_name.size() &&
+ object_name.compare(0, prefix.size(), prefix) == 0) {
+ if (name) {
+ *name = object_name;
+ }
+ return true;
+ }
+ return false;
+}
+
+} // namespace anonymous
+
+Serializer::Serializer(ServiceLocator* service_locator,
+ StructuredWriter* writer,
+ IArchiveGenerator* archive_generator)
+ : class_manager_(service_locator),
+ writer_(writer),
+ archive_generator_(archive_generator) {
+ sections_[PROPERTIES_SECTION].name_ = "properties";
+ sections_[PROPERTIES_SECTION].visitor_ = new PropertiesVisitor(writer_);
+ sections_[CUSTOM_SECTION].name_ = "custom";
+ sections_[CUSTOM_SECTION].visitor_ = new CustomVisitor(
+ writer_, &binary_archive_manager_);
+ param_visitor_ = new ParamVisitor(writer_);
+ binary_visitor_ = new BinaryVisitor(&binary_archive_manager_);
+}
+
+Serializer::~Serializer() {
+ for (int i = 0; i != NUM_SECTIONS; ++i) {
+ delete sections_[i].visitor_;
+ }
+ delete param_visitor_;
+ delete binary_visitor_;
+}
+
+void Serializer::SerializePack(Pack* pack) {
+ SerializePackBinary(pack);
+
+ writer_->OpenObject();
+ writer_->WritePropertyName("version");
+ writer_->WriteInt(kSerializerVersion);
+
+ // write out properties for all objects starting with ROOT_PREFIX
+ ObjectBaseArray owned_objects = pack->GetByClass<ObjectBase>();
+ for (ObjectBaseArray::const_iterator it = owned_objects.begin();
+ it != owned_objects.end(); ++it) {
+ String name;
+ if (NameStartsWithPrefix(*it, ROOT_PREFIX, &name)) {
+ writer_->WritePropertyName(name);
+ Serialize(writer_, GetObjectId(*it));
+ }
+ }
+
+
+ writer_->WritePropertyName("objects");
+ writer_->OpenObject();
+
+ std::vector<const ObjectBase::Class*> classes =
+ class_manager_->GetAllClasses();
+
+ for (int i = 0; i != classes.size(); ++i) {
+ const ObjectBase::Class* current_class = classes[i];
+ if (!ObjectBase::ClassIsA(current_class, Param::GetApparentClass())) {
+ std::vector<ObjectBase*> objects_of_class;
+ for (ObjectBaseArray::const_iterator it = owned_objects.begin();
+ it != owned_objects.end(); ++it) {
+ if ((*it)->GetClassName() == current_class->name() &&
+ !NameStartsWithPrefix(*it, ROOT_PREFIX, NULL)) {
+ objects_of_class.push_back(*it);
+ }
+ }
+ if (objects_of_class.size() != 0) {
+ writer_->WritePropertyName(current_class->name());
+ writer_->OpenArray();
+ for (int j = 0; j != objects_of_class.size(); ++j) {
+ writer_->OpenObject();
+ SerializeObject(objects_of_class[j]);
+ writer_->CloseObject();
+ }
+ writer_->CloseArray();
+ }
+ }
+ }
+
+ writer_->CloseObject();
+ writer_->CloseObject();
+
+ binary_archive_manager_.WriteArchive(archive_generator_);
+}
+
+void Serializer::SerializePackBinary(Pack* pack) {
+ std::vector<ObjectBase*> objects = pack->GetByClass<ObjectBase>();
+ for (int i = 0; i < objects.size(); ++i) {
+ binary_visitor_->Accept(objects[i]);
+ }
+}
+
+void Serializer::SerializeObject(ObjectBase* object) {
+ writer_->WritePropertyName("id");
+ writer_->WriteInt(object->id());
+ for (int i = 0; i != NUM_SECTIONS; ++i) {
+ if (sections_[i].visitor_->IsHandled(object->GetClass())) {
+ writer_->WritePropertyName(sections_[i].name_);
+ writer_->OpenObject();
+ SerializeSection(object, static_cast<Section>(i));
+ writer_->CloseObject();
+ }
+ }
+
+ param_visitor_->Accept(object);
+}
+
+void Serializer::SerializeSection(ObjectBase* object, Section section) {
+ DCHECK(section >= 0 && section < NUM_SECTIONS);
+ sections_[section].visitor_->Accept(object);
+}
+
+void Serializer::SerializeParam(Param* param) {
+ param_visitor_->Accept(param);
+}
+} // namespace o3d
diff --git a/o3d/serializer/cross/serializer.h b/o3d/serializer/cross/serializer.h
new file mode 100644
index 0000000..dbcaa8b
--- /dev/null
+++ b/o3d/serializer/cross/serializer.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// This file contains the declaration of class Serializer.
+
+#ifndef O3D_SERIALIZER_CROSS_SERIALIZER_H_
+#define O3D_SERIALIZER_CROSS_SERIALIZER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "core/cross/bounding_box.h"
+#include "core/cross/float_n.h"
+#include "core/cross/object_base.h"
+#include "core/cross/stream.h"
+#include "core/cross/types.h"
+#include "core/cross/visitor_base.h"
+#include "core/cross/service_dependency.h"
+#include "import/cross/iarchive_generator.h"
+#include "utils/cross/structured_writer.h"
+
+namespace o3d {
+
+class IClassManager;
+class Pack;
+class StructuredWriter;
+class Transform;
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, ObjectBase* value);
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, float value);
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, const Float2& value);
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, const Float3& value);
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, const Float4& value);
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, int value);
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, unsigned int value);
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, bool value);
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, const String& value);
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, const Matrix4& value);
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, const BoundingBox& value);
+
+// Serialize value to a StructuredWriter.
+void Serialize(StructuredWriter* writer, const Stream& value);
+
+// A range of bytes within a binary file.
+struct BinaryRange {
+ BinaryRange() : begin_offset_(0), end_offset_(0) {}
+ BinaryRange(size_t begin_offset, size_t end_offset)
+ : begin_offset_(begin_offset),
+ end_offset_(end_offset) {
+ }
+ size_t begin_offset_;
+ size_t end_offset_;
+};
+
+// A class that tracks the contents of binary files in an archive and the ranges
+// corresponding to each object.
+class BinaryArchiveManager {
+ typedef std::vector<uint8> FileContent;
+ typedef std::map<std::string, FileContent > FileMap;
+ typedef std::map<ObjectBase*, BinaryRange> ObjectBinaryRangeMap;
+
+ public:
+ BinaryArchiveManager() {
+ }
+
+ // Write the binary content for an object. Multiple consecutive calls
+ // can be made for a single object. Calls for different objects may not
+ // be interleaved. All binary content for a particular object must be
+ // written to a single file. This function does not write to the archive.
+ // That is deferred until WriteArchive is called.
+ void WriteObjectBinary(ObjectBase* object,
+ const std::string& file_name,
+ const uint8* begin,
+ size_t numBytes);
+
+ // Gets the byte range of the file corresponding to a particular
+ // object.
+ BinaryRange GetObjectRange(ObjectBase* object);
+
+ // Writes all the collected binary data to the archive.
+ void WriteArchive(IArchiveGenerator* archive_generator);
+
+ private:
+ FileMap file_map_;
+ ObjectBinaryRangeMap object_binary_range_map_;
+ DISALLOW_COPY_AND_ASSIGN(BinaryArchiveManager);
+};
+
+// Serializes whole packs, individual objects, individual sections
+// of objects or individual Params to a StructuedWriter.
+class Serializer {
+ public:
+ // Enumeration of all sections that may optionally be included in
+ // an object.
+ enum Section {
+ PROPERTIES_SECTION,
+ CUSTOM_SECTION,
+ NUM_SECTIONS
+ };
+
+ // Any object that starts with this prefix will not be seralized
+ // but a reference to it will be put at the top of the json object.
+ static const char* ROOT_PREFIX;
+
+ // Construct a new Serializer that writes future output to the
+ // given StructuredWriter and IArchiveGenerator.
+ explicit Serializer(ServiceLocator* service_locator,
+ StructuredWriter* writer,
+ IArchiveGenerator* archive_generator);
+ ~Serializer();
+
+ // Serialize a Pack and all the objects contained by the pack
+ // to the StructuredWriter.
+ void SerializePack(Pack* pack);
+
+ // Serialize all the binary files in a pack.
+ void SerializePackBinary(Pack* pack);
+
+ // Serialize a single object to the StructuredWriter.
+ void SerializeObject(ObjectBase* object);
+
+ // Serialize one of the sections of an object to the StructuredWriter.
+ void SerializeSection(ObjectBase* object, Section section);
+
+ // Serialize a single Param of an object to the StructuredWriter.
+ void SerializeParam(Param* param);
+
+ private:
+ ServiceDependency<IClassManager> class_manager_;
+ StructuredWriter* writer_;
+ IArchiveGenerator* archive_generator_;
+
+ struct SectionConfig {
+ const char* name_;
+ IVisitor* visitor_;
+ };
+ SectionConfig sections_[NUM_SECTIONS];
+
+ IVisitor* param_visitor_;
+ IVisitor* binary_visitor_;
+
+ BinaryArchiveManager binary_archive_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(Serializer);
+};
+} // namespace o3d
+
+#endif // O3D_SERIALIZER_CROSS_SERIALIZER_H_
diff --git a/o3d/serializer/cross/serializer_binary.cc b/o3d/serializer/cross/serializer_binary.cc
new file mode 100644
index 0000000..1af3e2c
--- /dev/null
+++ b/o3d/serializer/cross/serializer_binary.cc
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// This file contains the serializer code for binary objects:
+// Buffer, Curve, Skin...
+
+#include <vector>
+
+#include "core/cross/buffer.h"
+#include "core/cross/curve.h"
+#include "core/cross/error.h"
+#include "core/cross/skin.h"
+#include "import/cross/memory_buffer.h"
+#include "import/cross/memory_stream.h"
+
+const size_t kSerializationIDSize = 4;
+const size_t kVersionSize = sizeof(int32);
+
+namespace o3d {
+
+// |output| will be filled with the serialized data
+void SerializeBuffer(const Buffer &buffer, MemoryBuffer<uint8> *output) {
+ // Determine the total size for the serialization data
+ const unsigned num_elements = buffer.num_elements();
+ const size_t num_fields = buffer.fields().size();
+ const size_t kNumFieldsSize = sizeof(int32);
+ const size_t kSingleFieldInfoSize = 2 * sizeof(uint8); // id / num_components
+ const size_t all_field_infos_size = num_fields * kSingleFieldInfoSize;
+ const size_t kNumElementsSize = sizeof(int32);
+
+ // Add up all the parts comprising the header
+ const size_t header_size = kSerializationIDSize +
+ kVersionSize +
+ kNumFieldsSize +
+ all_field_infos_size +
+ kNumElementsSize;
+
+ const size_t data_size = buffer.GetSizeInBytes();
+ const size_t total_size = header_size + data_size;
+ output->Resize(total_size);
+
+ MemoryWriteStream stream(*output, total_size);
+
+ // write out serialization ID for buffer
+ stream.Write(Buffer::kSerializationID, 4);
+
+ // write out version
+ stream.WriteLittleEndianInt32(1);
+
+ // write out number of fields
+ stream.WriteLittleEndianInt32(static_cast<int32>(num_fields));
+
+ // Write out the specification for the fields
+ for (int i = 0; i < num_fields; ++i) {
+ const Field &field = *buffer.fields()[i];
+
+ // Determine the FIELDID code we need to write out
+ // Start out "unknown" until we determine the class
+ uint8 field_id = Field::FIELDID_UNKNOWN;
+
+ if (field.IsA(FloatField::GetApparentClass())) {
+ field_id = Field::FIELDID_FLOAT32;
+ } else if (field.IsA(UInt32Field::GetApparentClass())) {
+ field_id = Field::FIELDID_UINT32;
+ } else if (field.IsA(UByteNField::GetApparentClass())) {
+ field_id = Field::FIELDID_BYTE;
+ } else {
+ O3D_ERROR(buffer.service_locator()) << "illegal buffer field";
+ return;
+ }
+
+ stream.WriteByte(field_id);
+ stream.WriteByte(field.num_components());
+ }
+
+ // Write out the number of elements
+ stream.WriteLittleEndianUInt32(num_elements);
+
+ // Make note of stream position at end of header
+ size_t data_start_position = stream.GetStreamPosition();
+
+ // Write out the data for each field
+ // Write out the specification for the fields
+ for (int i = 0; i < num_fields; ++i) {
+ const Field &field = *buffer.fields()[i];
+
+ MemoryBuffer<uint8> field_data(field.size() * num_elements);
+ uint8 *destination = field_data;
+
+ // Figure out what type of field it is, and get the data
+ // appropriately
+ size_t nitems = num_elements * field.num_components();
+
+ if (field.IsA(FloatField::GetApparentClass())) {
+ float *float_destination = reinterpret_cast<float*>(destination);
+ field.GetAsFloats(0,
+ float_destination,
+ field.num_components(),
+ num_elements);
+ // Write out as little endian float32
+ for (int i = 0; i < nitems; ++i) {
+ stream.WriteLittleEndianFloat32(float_destination[i]);
+ }
+ } else if (field.IsA(UInt32Field::GetApparentClass())) {
+ const UInt32Field &int_field = static_cast<const UInt32Field&>(field);
+ uint32 *int_destination = reinterpret_cast<uint32*>(destination);
+ int_field.GetAsUInt32s(0,
+ int_destination,
+ field.num_components(),
+ num_elements);
+ // Write out as little endian int32
+ for (int i = 0; i < nitems; ++i) {
+ stream.WriteLittleEndianInt32(int_destination[i]);
+ }
+ } else if (field.IsA(UByteNField::GetApparentClass())) {
+ const UByteNField &byte_field = static_cast<const UByteNField&>(field);
+ uint8 *byte_destination = reinterpret_cast<uint8*>(destination);
+ byte_field.GetAsBytes(0,
+ byte_destination,
+ field.num_components(),
+ num_elements);
+ // Write out the bytes
+ stream.Write(byte_destination, nitems);
+ }
+ }
+
+ if (stream.GetStreamPosition() != total_size) {
+ O3D_ERROR(buffer.service_locator()) << "error in serializing buffer";
+ return;
+ }
+}
+
+// |output| will be filled with the serialized data
+void SerializeCurve(const Curve &curve, MemoryBuffer<uint8> *output) {
+ const size_t num_keys = const_cast<Curve&>(curve).keys().size();
+
+ // Bezier entry size is biggest, so compute kKeyEntryMaxSize based on it
+ const size_t kFloat2Size = 2 * sizeof(float);
+ const size_t kKeyEntryMaxSize =
+ sizeof(uint8) + 2 * sizeof(float) + 2 * kFloat2Size;
+
+ const size_t key_data_max_size = num_keys * kKeyEntryMaxSize;
+ const size_t max_total_size =
+ kSerializationIDSize + kVersionSize + key_data_max_size;
+
+ // Allocate a buffer which is large enough to hold the serialized data
+ // It may be larger than the actual size required. It will be resized
+ // to the exact size at the end
+ output->Resize(max_total_size);
+
+ MemoryWriteStream stream(*output, max_total_size);
+
+ // write out serialization ID for curve
+ stream.Write(Curve::kSerializationID, 4);
+
+ // write out version
+ stream.WriteLittleEndianInt32(1);
+
+
+ for (int i = 0; i < num_keys; ++i) {
+ const CurveKey &key = *curve.GetKey(i);
+
+ // determine the KeyType based on the key's class
+ if (key.IsA(StepCurveKey::GetApparentClass())) {
+ stream.WriteByte(CurveKey::TYPE_STEP);
+ stream.WriteLittleEndianFloat32(key.input());
+ stream.WriteLittleEndianFloat32(key.output());
+ } else if (key.IsA(LinearCurveKey::GetApparentClass())) {
+ stream.WriteByte(CurveKey::TYPE_LINEAR);
+ stream.WriteLittleEndianFloat32(key.input());
+ stream.WriteLittleEndianFloat32(key.output());
+ } else if (key.IsA(BezierCurveKey::GetApparentClass())) {
+ const BezierCurveKey &bezier_key =
+ static_cast<const BezierCurveKey&>(key);
+ stream.WriteByte(CurveKey::TYPE_BEZIER);
+ stream.WriteLittleEndianFloat32(bezier_key.input());
+ stream.WriteLittleEndianFloat32(bezier_key.output());
+ stream.WriteLittleEndianFloat32(bezier_key.in_tangent().getX());
+ stream.WriteLittleEndianFloat32(bezier_key.in_tangent().getY());
+ stream.WriteLittleEndianFloat32(bezier_key.out_tangent().getX());
+ stream.WriteLittleEndianFloat32(bezier_key.out_tangent().getY());
+ } else {
+ O3D_ERROR(curve.service_locator()) << "error in serializing curve";
+ return;
+ }
+ }
+
+ // Make note of total amount of data written and set the buffer to this
+ // exact size
+ size_t total_size = stream.GetStreamPosition();
+ output->Resize(total_size);
+}
+
+// |output| will be filled with the serialized data
+void SerializeSkin(const Skin &skin, MemoryBuffer<uint8> *output) {
+ const Skin::InfluencesArray &influences_array = skin.influences();
+ const size_t influences_array_size = influences_array.size();
+
+ // Count up total number of individual influences
+ size_t total_influence_count = 0;
+ for (int i = 0; i < influences_array_size; ++i) {
+ total_influence_count += influences_array[i].size();
+ }
+
+ const size_t kInfluenceSize = sizeof(uint32) + sizeof(float);
+ const size_t total_size = kSerializationIDSize +
+ kVersionSize +
+ influences_array_size * sizeof(uint32) +
+ total_influence_count * kInfluenceSize;
+
+
+ // Allocate a buffer to hold the serialized data
+ output->Resize(total_size);
+ MemoryWriteStream stream(*output, total_size);
+
+ // write out serialization ID for skin
+ stream.Write(Skin::kSerializationID, 4);
+
+ // write out version
+ stream.WriteLittleEndianInt32(1);
+
+ for (int i = 0; i < influences_array_size; ++i) {
+ const Skin::Influences &influences = influences_array[i];
+
+ // Write the influence count for this Influences object
+ size_t influence_count = influences.size();
+ stream.WriteLittleEndianInt32(static_cast<int32>(influence_count));
+
+ for (int j = 0; j < influence_count; ++j) {
+ const Skin::Influence &influence = influences[j];
+ stream.WriteLittleEndianInt32(influence.matrix_index);
+ stream.WriteLittleEndianFloat32(influence.weight);
+ }
+ }
+}
+
+} // namespace o3d
diff --git a/o3d/serializer/cross/serializer_binary.h b/o3d/serializer/cross/serializer_binary.h
new file mode 100644
index 0000000..9e19392
--- /dev/null
+++ b/o3d/serializer/cross/serializer_binary.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// This file contains function declarations for serializing objects
+// with binary representations:
+// Buffer, Curve, Skin...
+
+#ifndef O3D_SERIALIZER_CROSS_SERIALIZER_BINARY_H_
+#define O3D_SERIALIZER_CROSS_SERIALIZER_BINARY_H_
+
+#include "import/cross/memory_buffer.h"
+
+namespace o3d {
+
+class Buffer;
+class Curve;
+class Skin;
+
+// Returns a MemoryBuffer object containing the serialized data
+// returns NULL on error
+void SerializeBuffer(const Buffer &buffer, MemoryBuffer<uint8> *output);
+void SerializeCurve(const Curve &curve, MemoryBuffer<uint8> *output);
+void SerializeSkin(const Skin &skin, MemoryBuffer<uint8> *output);
+
+} // namespace o3d
+
+#endif // O3D_SERIALIZER_CROSS_SERIALIZER_BINARY_H_
diff --git a/o3d/serializer/cross/serializer_test.cc b/o3d/serializer/cross/serializer_test.cc
new file mode 100644
index 0000000..4ab5abe
--- /dev/null
+++ b/o3d/serializer/cross/serializer_test.cc
@@ -0,0 +1,1067 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// This file contains the tests of class Serializer.
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "core/cross/buffer.h"
+#include "core/cross/curve.h"
+#include "core/cross/matrix4_translation.h"
+#include "core/cross/object_manager.h"
+#include "core/cross/pack.h"
+#include "core/cross/param_array.h"
+#include "core/cross/primitive.h"
+#include "core/cross/service_dependency.h"
+#include "core/cross/shape.h"
+#include "core/cross/skin.h"
+#include "core/cross/texture.h"
+#include "core/cross/transform.h"
+#include "import/cross/memory_buffer.h"
+#include "import/cross/memory_stream.h"
+#include "serializer/cross/serializer.h"
+#include "serializer/cross/serializer_binary.h"
+#include "serializer/cross/version.h"
+#include "tests/common/win/testing_common.h"
+#include "utils/cross/string_writer.h"
+#include "utils/cross/json_writer.h"
+
+using std::vector;
+
+namespace o3d {
+
+struct AddFileRecord {
+ String file_name_;
+ size_t file_size_;
+ vector<uint8> file_contents_;
+};
+
+class MockArchiveGenerator : public IArchiveGenerator {
+ public:
+ virtual void AddFile(const String& file_name,
+ size_t file_size) {
+ AddFileRecord record;
+ record.file_name_ = file_name;
+ record.file_size_ = file_size;
+ add_file_records_.push_back(record);
+ }
+
+ virtual int AddFileBytes(MemoryReadStream* stream, size_t numBytes) {
+ const uint8* direct_pointer = stream->GetDirectMemoryPointer();
+ add_file_records_.back().file_contents_.insert(
+ add_file_records_.back().file_contents_.begin(),
+ direct_pointer,
+ direct_pointer + numBytes);
+ return 0;
+ }
+
+ vector<AddFileRecord> add_file_records_;
+};
+
+class SerializerTest : public testing::Test {
+ protected:
+ SerializerTest()
+ : object_manager_(g_service_locator),
+ output_(StringWriter::CR_LF),
+ json_writer_(&output_, 2),
+ serializer_(g_service_locator, &json_writer_, &archive_generator_) {
+ json_writer_.BeginCompacting();
+ }
+
+ virtual void SetUp() {
+ pack_ = object_manager_->CreatePack();
+ }
+
+ virtual void TearDown() {
+ object_manager_->DestroyPack(pack_);
+ }
+
+ ServiceDependency<ObjectManager> object_manager_;
+ Pack* pack_;
+ StringWriter output_;
+ JsonWriter json_writer_;
+ MockArchiveGenerator archive_generator_;
+ Serializer serializer_;
+};
+
+TEST_F(SerializerTest,
+ ShouldSerializeParamWithOnlyValueIfItIsNotBoundOrLateAdded) {
+ Transform* transform = pack_->Create<Transform>();
+ ParamMatrix4* param = transform->GetParam<ParamMatrix4>("localMatrix");
+ param->set_value(Matrix4::identity());
+ serializer_.SerializeParam(param);
+ EXPECT_EQ("{\"value\":[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]}",
+ output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeClassOfLateAddedParams) {
+ Transform* transform = pack_->Create<Transform>();
+ ParamInteger* param = transform->CreateParam<ParamInteger>("param");
+ param->set_value(7);
+ serializer_.SerializeParam(param);
+ EXPECT_EQ("{\"class\":\"" O3D_NAMESPACE ".ParamInteger\",\"value\":7}",
+ output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeIdOfOutputParams) {
+ Transform* transform = pack_->Create<Transform>();
+ ParamInteger* param = transform->CreateParam<ParamInteger>("param");
+ param->set_value(7);
+ ParamInteger* other_param = transform->CreateParam<ParamInteger>(
+ "other_param");
+ other_param->Bind(param);
+ serializer_.SerializeParam(param);
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "{\"class\":\"" O3D_NAMESPACE
+ ".ParamInteger\",\"id\":%d,\"value\":7}",
+ param->id());
+
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeInputIdOfBoundParams) {
+ Transform* transform = pack_->Create<Transform>();
+ ParamInteger* param = transform->CreateParam<ParamInteger>("param");
+ ParamInteger* other_param = transform->CreateParam<ParamInteger>(
+ "other_param");
+ param->Bind(other_param);
+ serializer_.SerializeParam(param);
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "{\"class\":\"" O3D_NAMESPACE ".ParamInteger\",\"bind\":%d}",
+ other_param->id());
+
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesPointer) {
+ Transform* transform = pack_->Create<Transform>();
+ Serialize(&json_writer_, transform);
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted("{\"ref\":%d}", transform->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeNullPointerToNullKeyword) {
+ Serialize(&json_writer_, static_cast<ObjectBase*>(NULL));
+ EXPECT_EQ("null", output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesFloat) {
+ Serialize(&json_writer_, 1.25f);
+ EXPECT_EQ("1.25", output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesFloat2) {
+ Serialize(&json_writer_, Float2(1.25f, 2.5f));
+ EXPECT_EQ("[1.25,2.5]", output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesFloat3) {
+ Serialize(&json_writer_, Float3(1.25f, 2.5f, 5.0f));
+ EXPECT_EQ("[1.25,2.5,5]", output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesFloat4) {
+ Serialize(&json_writer_, Float4(1.25f, 2.5f, 5.0f, 10.0f));
+ EXPECT_EQ("[1.25,2.5,5,10]", output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesInteger) {
+ Serialize(&json_writer_, 7);
+ EXPECT_EQ("7", output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesBoolean) {
+ Serialize(&json_writer_, false);
+ EXPECT_EQ("false", output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesString) {
+ Serialize(&json_writer_, String("hello"));
+ EXPECT_EQ("\"hello\"", output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesMatrix4) {
+ Serialize(&json_writer_, Matrix4::identity());
+ EXPECT_EQ("[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]", output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesValidBoundingBox) {
+ BoundingBox bounding_box(Point3(-1.0f, -2.0f, -3.0f),
+ Point3(1.0f, 2.0f, 3.0f));
+ Serialize(&json_writer_, bounding_box);
+ EXPECT_EQ("[[-1,-2,-3],[1,2,3]]", output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesInvalidBoundingBox) {
+ BoundingBox bounding_box;
+ Serialize(&json_writer_, bounding_box);
+ EXPECT_EQ("[]", output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeStreamProperties) {
+ VertexBuffer* buffer = pack_->Create<VertexBuffer>();
+ StreamBank* streamBank = pack_->Create<StreamBank>();
+ Field* field = buffer->CreateField(FloatField::GetApparentClass(), 3);
+ streamBank->SetVertexStream(Stream::NORMAL, 9, field, 1);
+ const Stream* stream = streamBank->GetVertexStream(Stream::NORMAL, 9);
+ Serialize(&json_writer_, *stream);
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "{\"field\":%d,\"startIndex\":1,"
+ "\"semantic\":2,\"semanticIndex\":9}",
+ field->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeCurveProperties) {
+ Curve* curve = pack_->Create<Curve>();
+ curve->set_pre_infinity(Curve::CONSTANT);
+ curve->set_post_infinity(Curve::LINEAR);
+ curve->set_use_cache(true);
+ curve->SetSampleRate(0.1f);
+
+ serializer_.SerializeSection(curve, Serializer::PROPERTIES_SECTION);
+
+ EXPECT_EQ(
+ "\"preInfinity\":0,"
+ "\"postInfinity\":1,"
+ "\"useCache\":true,"
+ "\"sampleRate\":0.1",
+ output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeCurveCustomSection) {
+ // The purpose of this buffer is just to offset the following one in the
+ // binary file.
+ Curve* firstCurve = pack_->Create<Curve>();
+ firstCurve->Create<StepCurveKey>();
+
+ Curve* curve = pack_->Create<Curve>();
+
+ StepCurveKey* step_key = curve->Create<StepCurveKey>();
+ step_key->SetInput(1);
+ step_key->SetOutput(2);
+
+ LinearCurveKey* linear_key = curve->Create<LinearCurveKey>();
+ linear_key->SetInput(3);
+ linear_key->SetOutput(4);
+
+ BezierCurveKey* bezier_key = curve->Create<BezierCurveKey>();
+ bezier_key->SetInput(5);
+ bezier_key->SetInTangent(Float2(6, 7));
+ bezier_key->SetOutput(8);
+ bezier_key->SetOutTangent(Float2(9, 10));
+
+ serializer_.SerializePackBinary(pack_);
+ serializer_.SerializeSection(curve, Serializer::CUSTOM_SECTION);
+
+ // Make sure binaryRange is correct
+ MemoryBuffer<uint8> contents1;
+ MemoryBuffer<uint8> contents2;
+ SerializeCurve(*firstCurve, &contents1);
+ SerializeCurve(*curve, &contents2);
+ size_t length1 = contents1.GetLength();
+ size_t length2 = contents2.GetLength();
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"binaryRange\":[%d,%d]",
+ length1,
+ length1 + length2);
+
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeCurveKeysToSingleBinaryFile) {
+ // The purpose of this buffer is just to offset the following one in the
+ // binary file.
+ Curve* curve1 = pack_->Create<Curve>();
+ StepCurveKey* step_key = curve1->Create<StepCurveKey>();
+ step_key->SetInput(1);
+ step_key->SetOutput(2);
+
+ Curve* curve2 = pack_->Create<Curve>();
+ LinearCurveKey* linear_key = curve2->Create<LinearCurveKey>();
+ linear_key->SetInput(3);
+ linear_key->SetOutput(4);
+
+ serializer_.SerializePack(pack_);
+ EXPECT_EQ(1, archive_generator_.add_file_records_.size());
+ const AddFileRecord& record = archive_generator_.add_file_records_[0];
+
+ EXPECT_EQ("curve-keys.bin", record.file_name_);
+
+ // Test that the data matches what we get if we call SerializeCurve directly
+ // The file should contain the concatenated contents of both curves
+ MemoryBuffer<uint8> contents1;
+ MemoryBuffer<uint8> contents2;
+ SerializeCurve(*curve1, &contents1);
+ SerializeCurve(*curve2, &contents2);
+ size_t length1 = contents1.GetLength();
+ size_t length2 = contents2.GetLength();
+ size_t total_length = length1 + length2;
+ ASSERT_EQ(total_length, record.file_size_);
+ ASSERT_EQ(total_length, record.file_contents_.size());
+
+ uint8 *p1 = contents1;
+ uint8 *p2 = contents2;
+ const uint8* data = &record.file_contents_[0];
+
+ // Validate that first part of file data matches curve1 serialization
+ // and that second part matches curve2...
+ EXPECT_EQ(0, memcmp(p1, data, length1));
+ EXPECT_EQ(0, memcmp(p2, data + length1, length2));
+}
+
+TEST_F(SerializerTest, ShouldSerializeNoCurvesIfCustomSectionIfNoKeys) {
+ Curve* curve = pack_->Create<Curve>();
+ serializer_.SerializeSection(curve, Serializer::CUSTOM_SECTION);
+ EXPECT_EQ("\"binaryRange\":[0,0]", output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesIndexBuffer) {
+ // The purpose of this buffer is just to offset the following one in the
+ // binary file.
+ Buffer* firstBuffer = pack_->Create<IndexBuffer>();
+ firstBuffer->AllocateElements(1);
+
+ Buffer* buffer = pack_->Create<IndexBuffer>();
+ buffer->AllocateElements(2);
+ {
+ BufferLockHelper locker(buffer);
+ uint32* data = locker.GetDataAs<uint32>(Buffer::WRITE_ONLY);
+ data[0] = 3;
+ data[1] = 7;
+ }
+
+ serializer_.SerializePackBinary(pack_);
+ serializer_.SerializeSection(buffer, Serializer::CUSTOM_SECTION);
+
+ // Make sure binaryRange is correct
+ MemoryBuffer<uint8> contents1;
+ MemoryBuffer<uint8> contents2;
+ SerializeBuffer(*firstBuffer, &contents1);
+ SerializeBuffer(*buffer, &contents2);
+ size_t length1 = contents1.GetLength();
+ size_t length2 = contents2.GetLength();
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"fields\":[%d],"
+ "\"binaryRange\":[%d,%d]",
+ buffer->fields()[0]->id(),
+ length1,
+ length1 + length2);
+
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesAllIndexBufferBinaryToSingleFileInArchive) {
+ Buffer* buffer1 = pack_->Create<IndexBuffer>();
+ buffer1->AllocateElements(2);
+ {
+ BufferLockHelper locker(buffer1);
+ uint32* data = locker.GetDataAs<uint32>(Buffer::WRITE_ONLY);
+ data[0] = 1;
+ data[1] = 2;
+ }
+
+ Buffer* buffer2 = pack_->Create<IndexBuffer>();
+ buffer2->AllocateElements(1);
+ {
+ BufferLockHelper locker(buffer2);
+ uint32* data = locker.GetDataAs<uint32>(Buffer::WRITE_ONLY);
+ data[0] = 3;
+ }
+
+ serializer_.SerializePack(pack_);
+ EXPECT_EQ(1, archive_generator_.add_file_records_.size());
+ const AddFileRecord& record = archive_generator_.add_file_records_[0];
+ EXPECT_EQ("index-buffers.bin", record.file_name_);
+
+ // Test that the data matches what we get if we call SerializeBuffer directly
+ // The file should contain the concatenated contents of both buffers
+ MemoryBuffer<uint8> contents1;
+ MemoryBuffer<uint8> contents2;
+ SerializeBuffer(*buffer1, &contents1);
+ SerializeBuffer(*buffer2, &contents2);
+ size_t length1 = contents1.GetLength();
+ size_t length2 = contents2.GetLength();
+ size_t total_length = length1 + length2;
+ ASSERT_EQ(total_length, record.file_size_);
+ ASSERT_EQ(total_length, record.file_contents_.size());
+
+ uint8 *p1 = contents1;
+ uint8 *p2 = contents2;
+ const uint8* data = &record.file_contents_[0];
+
+ // Validate that first part of file data matches buffer1 serialization
+ // and that second part matches buffer2...
+ EXPECT_EQ(0, memcmp(p1, data, length1));
+ EXPECT_EQ(0, memcmp(p2, data + length1, length2));
+}
+
+namespace {
+class FakeNamedObject : public NamedObject {
+ public:
+ explicit FakeNamedObject(ServiceLocator* service_locator)
+ : NamedObject(service_locator) {}
+};
+}
+
+TEST_F(SerializerTest, ShouldSerializeNamedObjectProperties) {
+ FakeNamedObject named_object(g_service_locator);
+ named_object.set_name("ObjectName");
+ serializer_.SerializeSection(&named_object, Serializer::PROPERTIES_SECTION);
+ EXPECT_EQ("\"name\":\"ObjectName\"", output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeNoNameForNamelessNamedObjectProperties) {
+ FakeNamedObject named_object(g_service_locator);
+ serializer_.SerializeSection(&named_object, Serializer::PROPERTIES_SECTION);
+ EXPECT_EQ("", output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeIdPropertiesAndCustomSections) {
+ Curve* curve = pack_->Create<Curve>();
+ curve->set_pre_infinity(Curve::CONSTANT);
+ curve->set_post_infinity(Curve::LINEAR);
+ curve->set_use_cache(true);
+ curve->SetSampleRate(0.1f);
+
+ serializer_.SerializeObject(curve);
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"id\":%d,"
+ "\"properties\":{"
+ "\"preInfinity\":0,"
+ "\"postInfinity\":1,"
+ "\"useCache\":true,"
+ "\"sampleRate\":0.1"
+ "},"
+ "\"custom\":{"
+ "\"binaryRange\":[0,0]"
+ "}",
+ curve->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+namespace {
+class FakeObjectBase : public ObjectBase {
+ public:
+ explicit FakeObjectBase(ServiceLocator* service_locator)
+ : ObjectBase(service_locator) {
+ }
+};
+}
+
+TEST_F(SerializerTest,
+ ShouldNotSerializePropertiesAndCustomSectionsIfTheyAreNotUsed) {
+ FakeObjectBase object(g_service_locator);
+ serializer_.SerializeObject(&object);
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"id\":%d",
+ object.id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesEmptyPack) {
+ pack_->set_name("MyPack");
+
+ Transform* root = pack_->Create<Transform>();
+ root->set_name(String(Serializer::ROOT_PREFIX) + String("root"));
+
+ serializer_.SerializePack(pack_);
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "{"
+ "\"version\":%d,"
+ "\"o3d_rootObject_root\":%d,"
+ "\"objects\":{}"
+ "}",
+ kSerializerVersion,
+ root->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesObjectsInPackExceptRootGroupedByClass) {
+ FunctionEval* object1 = pack_->Create<FunctionEval>();
+ object1->set_name("Object1");
+
+ FunctionEval* object2 = pack_->Create<FunctionEval>();
+ object2->set_name("Object2");
+
+ Transform* root = pack_->Create<Transform>();
+ root->set_name(String(Serializer::ROOT_PREFIX) + String("root"));
+
+ serializer_.SerializePack(pack_);
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "{"
+ "\"version\":%d,"
+ "\"o3d_rootObject_root\":%d,"
+ "\"objects\":{"
+ "\"" O3D_NAMESPACE ".FunctionEval\":["
+ "{"
+ "\"id\":%d,"
+ "\"properties\":{"
+ "\"name\":\"Object1\""
+ "},"
+ "\"params\":{"
+ "\"o3d.functionObject\":{\"value\":null},"
+ "\"o3d.input\":{\"value\":0}"
+ "}"
+ "},"
+ "{"
+ "\"id\":%d,"
+ "\"properties\":{"
+ "\"name\":\"Object2\""
+ "},"
+ "\"params\":{"
+ "\"o3d.functionObject\":{\"value\":null},"
+ "\"o3d.input\":{\"value\":0}"
+ "}"
+ "}"
+ "]"
+ "}"
+ "}",
+ kSerializerVersion,
+ root->id(),
+ object1->id(),
+ object2->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesNonDynamicParams) {
+ Matrix4Translation* translation = pack_->Create<Matrix4Translation>();
+ translation->set_input_matrix(Matrix4::identity());
+ translation->set_translation(Float3(1, 2, 3));
+
+ serializer_.SerializeObject(translation);
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"id\":%d,"
+ "\"properties\":{"
+ "},"
+ "\"params\":{"
+ "\"o3d.inputMatrix\":{"
+ "\"value\":[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]},"
+ "\"o3d.translation\":{\"value\":[1,2,3]}"
+ "}",
+ translation->id());
+
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesDynamicParamsIfTheyAreOutputs) {
+ Matrix4Translation* translation = pack_->Create<Matrix4Translation>();
+ Param* input_matrix = translation->GetUntypedParam(
+ Matrix4Translation::kInputMatrixParamName);
+ Param* output_matrix = translation->GetUntypedParam(
+ Matrix4Translation::kOutputMatrixParamName);
+ input_matrix->Bind(output_matrix);
+
+ serializer_.SerializeObject(translation);
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"id\":%d,"
+ "\"properties\":{"
+ "},"
+ "\"params\":{"
+ "\"o3d.inputMatrix\":{\"bind\":%d},"
+ "\"o3d.outputMatrix\":{\"id\":%d},"
+ "\"o3d.translation\":{\"value\":[0,0,0]}"
+ "}",
+ translation->id(),
+ output_matrix->id(),
+ output_matrix->id());
+
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesParamArray) {
+ ParamArray* param_array = pack_->Create<ParamArray>();
+ param_array->CreateParam<ParamInteger>(0)->set_value(1);
+ param_array->CreateParam<ParamInteger>(1)->set_value(2);
+ serializer_.SerializeObject(param_array);
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"id\":%d,"
+ "\"properties\":{},"
+ "\"params\":[{\"class\":\"" O3D_NAMESPACE
+ ".ParamInteger\",\"value\":1},"
+ "{\"class\":\"" O3D_NAMESPACE
+ ".ParamInteger\",\"value\":2}]",
+ param_array->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializePrimitiveProperties) {
+ Primitive* primitive = pack_->Create<Primitive>();
+
+ Shape* shape = pack_->Create<Shape>();
+ primitive->SetOwner(shape);
+
+ IndexBuffer* index_buffer = pack_->Create<IndexBuffer>();
+ primitive->set_index_buffer(index_buffer);
+
+ primitive->set_start_index(4);
+ primitive->set_primitive_type(Primitive::LINELIST);
+ primitive->set_number_vertices(8);
+ primitive->set_number_primitives(9);
+
+ serializer_.SerializeSection(primitive, Serializer::PROPERTIES_SECTION);
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"owner\":{\"ref\":%d},"
+ "\"numberVertices\":8,"
+ "\"numberPrimitives\":9,"
+ "\"primitiveType\":2,"
+ "\"indexBuffer\":{\"ref\":%d},"
+ "\"startIndex\":4",
+ shape->id(),
+ index_buffer->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeShapeProperties) {
+ Shape* shape = pack_->Create<Shape>();
+
+ Element* element1 = pack_->Create<Primitive>();
+ shape->AddElement(element1);
+
+ Element* element2 = pack_->Create<Primitive>();
+ shape->AddElement(element2);
+
+ serializer_.SerializeSection(shape, Serializer::PROPERTIES_SECTION);
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"elements\":[{\"ref\":%d},{\"ref\":%d}]",
+ element1->id(),
+ element2->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeSkinProperties) {
+ Skin* skin = pack_->Create<Skin>();
+ skin->SetInverseBindPoseMatrix(0, Matrix4::identity());
+
+ serializer_.SerializeSection(skin, Serializer::PROPERTIES_SECTION);
+
+ EXPECT_EQ(
+ "\"inverseBindPoseMatrices\":[[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]]",
+ output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeSkinCustomSection) {
+ Skin* skin1 = pack_->Create<Skin>();
+ Skin::Influences influences(1);
+ influences[0] = Skin::Influence(1, 2);
+ skin1->SetVertexInfluences(0, influences);
+
+ Skin* skin2 = pack_->Create<Skin>();
+ influences[0] = Skin::Influence(3, 4);
+ skin2->SetVertexInfluences(1, influences);
+
+ serializer_.SerializePackBinary(pack_);
+ serializer_.SerializeSection(skin2, Serializer::CUSTOM_SECTION);
+
+ // Make sure binaryRange is correct
+ MemoryBuffer<uint8> contents1;
+ MemoryBuffer<uint8> contents2;
+ SerializeSkin(*skin1, &contents1);
+ SerializeSkin(*skin2, &contents2);
+ size_t length1 = contents1.GetLength();
+ size_t length2 = contents2.GetLength();
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"binaryRange\":[%d,%d]",
+ length1,
+ length1 + length2);
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeSkinToSingleBinaryFile) {
+ Skin* skin1 = pack_->Create<Skin>();
+ Skin::Influences influences(1);
+ influences[0] = Skin::Influence(1, 2);
+ skin1->SetVertexInfluences(0, influences);
+ skin1->SetInverseBindPoseMatrix(0, Matrix4::identity());
+
+ Skin* skin2 = pack_->Create<Skin>();
+ influences[0] = Skin::Influence(3, 4);
+ skin2->SetVertexInfluences(1, influences);
+ skin2->SetInverseBindPoseMatrix(0, Matrix4::identity());
+
+ serializer_.SerializePack(pack_);
+ EXPECT_EQ(1, archive_generator_.add_file_records_.size());
+ const AddFileRecord& record = archive_generator_.add_file_records_[0];
+ EXPECT_EQ("skins.bin", record.file_name_);
+
+ // Test that the data matches what we get if we call SerializeSkin directly
+ // The file should contain the concatenated contents of both skins
+ MemoryBuffer<uint8> contents1;
+ MemoryBuffer<uint8> contents2;
+ SerializeSkin(*skin1, &contents1);
+ SerializeSkin(*skin2, &contents2);
+ size_t length1 = contents1.GetLength();
+ size_t length2 = contents2.GetLength();
+ size_t total_length = length1 + length2;
+ ASSERT_EQ(total_length, record.file_size_);
+ ASSERT_EQ(total_length, record.file_contents_.size());
+
+ uint8 *p1 = contents1;
+ uint8 *p2 = contents2;
+ const uint8* data = &record.file_contents_[0];
+
+ // Validate that first part of file data matches skin1 serialization
+ // and that second part matches skin2...
+ EXPECT_EQ(0, memcmp(p1, data, length1));
+ EXPECT_EQ(0, memcmp(p2, data + length1, length2));
+}
+
+TEST_F(SerializerTest, ShouldSerializeSkinEval) {
+ SkinEval* skin_eval = pack_->Create<SkinEval>();
+ SourceBuffer* buffer1 = pack_->Create<SourceBuffer>();
+ SourceBuffer* buffer2 = pack_->Create<SourceBuffer>();
+ Field* field1 = buffer1->CreateField(FloatField::GetApparentClass(), 3);
+ Field* field2 = buffer2->CreateField(FloatField::GetApparentClass(), 3);
+ skin_eval->SetVertexStream(Stream::POSITION,
+ 0,
+ field1,
+ 0);
+ skin_eval->SetVertexStream(Stream::NORMAL,
+ 1,
+ field2,
+ 0);
+
+ serializer_.SerializeSection(skin_eval, Serializer::CUSTOM_SECTION);
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"vertexStreams\":["
+ "{"
+ "\"stream\":{"
+ "\"field\":%d,"
+ "\"startIndex\":0,"
+ "\"semantic\":1,"
+ "\"semanticIndex\":0"
+ "}"
+ "},"
+ "{"
+ "\"stream\":{"
+ "\"field\":%d,"
+ "\"startIndex\":0,"
+ "\"semantic\":2,"
+ "\"semanticIndex\":1"
+ "}"
+ "}"
+ "]",
+ field1->id(),
+ field2->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeBoundSkinEval) {
+ SkinEval* skin_eval1 = pack_->Create<SkinEval>();
+ SourceBuffer* buffer1 = pack_->Create<SourceBuffer>();
+ Field* field1 = buffer1->CreateField(FloatField::GetApparentClass(), 3);
+ skin_eval1->SetVertexStream(Stream::POSITION,
+ 0,
+ field1,
+ 0);
+
+ SkinEval* skin_eval2 = pack_->Create<SkinEval>();
+ SourceBuffer* buffer2 = pack_->Create<SourceBuffer>();
+ Field* field2 = buffer2->CreateField(FloatField::GetApparentClass(), 3);
+ skin_eval2->SetVertexStream(Stream::POSITION,
+ 0,
+ field2,
+ 0);
+
+ skin_eval1->BindStream(skin_eval2, Stream::POSITION, 0);
+ ParamVertexBufferStream* param = skin_eval2->GetVertexStreamParam(
+ Stream::POSITION, 0);
+
+ serializer_.SerializeSection(skin_eval1, Serializer::CUSTOM_SECTION);
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"vertexStreams\":["
+ "{"
+ "\"stream\":{"
+ "\"field\":%d,"
+ "\"startIndex\":0,"
+ "\"semantic\":1,"
+ "\"semanticIndex\":0"
+ "},"
+ "\"bind\":%d"
+ "}"
+ "]",
+ field1->id(),
+ skin_eval2->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeStreamBank) {
+ StreamBank* stream_bank = pack_->Create<StreamBank>();
+ VertexBuffer* vertex_buffer_1 = pack_->Create<VertexBuffer>();
+ Field* field_1 = vertex_buffer_1->CreateField(FloatField::GetApparentClass(),
+ 3);
+ VertexBuffer* vertex_buffer_2 = pack_->Create<VertexBuffer>();
+ Field* field_2 = vertex_buffer_1->CreateField(FloatField::GetApparentClass(),
+ 3);
+ stream_bank->SetVertexStream(Stream::POSITION,
+ 0,
+ field_1,
+ 0);
+ stream_bank->SetVertexStream(Stream::NORMAL,
+ 1,
+ field_2,
+ 0);
+
+ serializer_.SerializeSection(stream_bank, Serializer::CUSTOM_SECTION);
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"vertexStreams\":["
+ "{"
+ "\"stream\":{"
+ "\"field\":%d,"
+ "\"startIndex\":0,"
+ "\"semantic\":1,"
+ "\"semanticIndex\":0"
+ "}"
+ "},"
+ "{"
+ "\"stream\":{"
+ "\"field\":%d,"
+ "\"startIndex\":0,"
+ "\"semantic\":2,"
+ "\"semanticIndex\":1"
+ "}"
+ "}"
+ "]",
+ field_1->id(),
+ field_2->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeBoundStreamBank) {
+ SkinEval* skin_eval = pack_->Create<SkinEval>();
+ SourceBuffer* source_buffer = pack_->Create<SourceBuffer>();
+ Field* source_field = source_buffer->CreateField(
+ FloatField::GetApparentClass(), 3);
+ skin_eval->SetVertexStream(Stream::POSITION,
+ 0,
+ source_field,
+ 0);
+
+ StreamBank* stream_bank = pack_->Create<StreamBank>();
+ VertexBuffer* vertex_buffer = pack_->Create<VertexBuffer>();
+ Field* vertex_field = vertex_buffer->CreateField(
+ FloatField::GetApparentClass(), 3);
+ stream_bank->SetVertexStream(Stream::POSITION,
+ 0,
+ vertex_field,
+ 0);
+ stream_bank->BindStream(skin_eval, Stream::POSITION, 0);
+
+ serializer_.SerializeSection(stream_bank, Serializer::CUSTOM_SECTION);
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"vertexStreams\":["
+ "{"
+ "\"stream\":{"
+ "\"field\":%d,"
+ "\"startIndex\":0,"
+ "\"semantic\":1,"
+ "\"semanticIndex\":0"
+ "},"
+ "\"bind\":%d"
+ "}"
+ "]",
+ vertex_field->id(),
+ skin_eval->id());
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeTexture2DCustomnSection) {
+ Texture* texture = pack_->CreateTexture2D(256, 256, Texture::ARGB8,
+ 2, false);
+
+ serializer_.SerializeSection(texture, Serializer::CUSTOM_SECTION);
+
+ EXPECT_EQ(
+ "\"width\":256,"
+ "\"height\":256,"
+ "\"format\":2,"
+ "\"levels\":2,"
+ "\"renderSurfacesEnabled\":false",
+ output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeTextureCUBECustomnSection) {
+ Texture* texture = pack_->CreateTextureCUBE(256, Texture::ARGB8,
+ 2, false);
+
+ serializer_.SerializeSection(texture, Serializer::CUSTOM_SECTION);
+
+ EXPECT_EQ(
+ "\"edgeLength\":256,"
+ "\"format\":2,"
+ "\"levels\":2,"
+ "\"renderSurfacesEnabled\":false",
+ output_.ToString());
+}
+
+TEST_F(SerializerTest, ShouldSerializeTransformProperties) {
+ Transform* transform = pack_->Create<Transform>();
+ Transform* transform2 = pack_->Create<Transform>();
+ ShapeArray shapes;
+ Shape* shape1 = pack_->Create<Shape>();
+ shapes.push_back(Shape::Ref(shape1));
+ Shape* shape2 = pack_->Create<Shape>();
+ shapes.push_back(Shape::Ref(shape2));
+ transform->SetShapes(shapes);
+ transform->SetParent(transform2);
+
+ serializer_.SerializeSection(transform, Serializer::PROPERTIES_SECTION);
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"shapes\":[{\"ref\":%d},{\"ref\":%d}],"
+ "\"parent\":{\"ref\":%d}",
+ shape1->id(),
+ shape2->id(),
+ transform2->id());
+
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesVertexBuffer) {
+ // This buffer exists only to offset the second buffer in the buffer binary
+ // file.
+ Buffer* firstBuffer = pack_->Create<VertexBuffer>();
+ firstBuffer->CreateField(FloatField::GetApparentClass(), 1);
+ firstBuffer->AllocateElements(1);
+
+ Buffer* buffer = pack_->Create<VertexBuffer>();
+ Field* field = buffer->CreateField(FloatField::GetApparentClass(), 1);
+ buffer->AllocateElements(2);
+ {
+ BufferLockHelper locker(buffer);
+ float* data = locker.GetDataAs<float>(Buffer::WRITE_ONLY);
+ data[0] = 1.25f;
+ data[1] = -3.0f;
+ }
+
+ serializer_.SerializePackBinary(pack_);
+ serializer_.SerializeSection(buffer, Serializer::CUSTOM_SECTION);
+
+ // Make sure binaryRange is correct
+ MemoryBuffer<uint8> contents1;
+ MemoryBuffer<uint8> contents2;
+ SerializeBuffer(*firstBuffer, &contents1);
+ SerializeBuffer(*buffer, &contents2);
+ size_t length1 = contents1.GetLength();
+ size_t length2 = contents2.GetLength();
+
+ StringWriter expected(StringWriter::CR_LF);
+ expected.WriteFormatted(
+ "\"fields\":[%d],"
+ "\"binaryRange\":[%d,%d]",
+ field->id(), length1, length1 + length2);
+
+ EXPECT_EQ(expected.ToString(), output_.ToString());
+}
+
+TEST_F(SerializerTest, SerializesAllVertexBufferBinaryToSingleFileInArchive) {
+ Buffer* buffer1 = pack_->Create<VertexBuffer>();
+ buffer1->CreateField(FloatField::GetApparentClass(), 1);
+ buffer1->AllocateElements(2);
+ {
+ BufferLockHelper locker(buffer1);
+ float* data = locker.GetDataAs<float>(Buffer::WRITE_ONLY);
+ data[0] = 1;
+ data[1] = 2;
+ }
+
+ Buffer* buffer2 = pack_->Create<VertexBuffer>();
+ buffer2->CreateField(FloatField::GetApparentClass(), 1);
+ buffer2->AllocateElements(1);
+ {
+ BufferLockHelper locker(buffer2);
+ float* data = locker.GetDataAs<float>(Buffer::WRITE_ONLY);
+ data[0] = 3;
+ }
+
+ serializer_.SerializePack(pack_);
+ EXPECT_EQ(1, archive_generator_.add_file_records_.size());
+ const AddFileRecord& record = archive_generator_.add_file_records_[0];
+ EXPECT_EQ("vertex-buffers.bin", record.file_name_);
+
+ // Test that the data matches what we get if we call SerializeBuffer directly
+ // The file should contain the concatenated contents of both buffers
+ MemoryBuffer<uint8> contents1;
+ MemoryBuffer<uint8> contents2;
+ SerializeBuffer(*buffer1, &contents1);
+ SerializeBuffer(*buffer2, &contents2);
+ size_t length1 = contents1.GetLength();
+ size_t length2 = contents2.GetLength();
+ size_t total_length = length1 + length2;
+ ASSERT_EQ(total_length, record.file_size_);
+ ASSERT_EQ(total_length, record.file_contents_.size());
+
+ uint8 *p1 = contents1;
+ uint8 *p2 = contents2;
+ const uint8* data = &record.file_contents_[0];
+
+ // Validate that first part of file data matches buffer1 serialization
+ // and that second part matches buffer2...
+ EXPECT_EQ(0, memcmp(p1, data, length1));
+ EXPECT_EQ(0, memcmp(p2, data + length1, length2));
+}
+} // namespace o3d
diff --git a/o3d/serializer/cross/version.h b/o3d/serializer/cross/version.h
new file mode 100644
index 0000000..6792023
--- /dev/null
+++ b/o3d/serializer/cross/version.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// This file must only contain the serializer version number. Modifying this
+// file causes the assets to be rebuilt. So you must modify this file by
+// incrementing the version number every time you make a change that changes
+// the serializer format.
+
+#ifndef O3D_SERIALIZER_CROSS_VERSION_H_
+#define O3D_SERIALIZER_CROSS_VERSION_H_
+
+namespace o3d {
+const int kSerializerVersion = 5;
+}
+
+#endif // O3D_SERIALIZER_CROSS_VERSION_H_