summaryrefslogtreecommitdiffstats
path: root/o3d/serializer
diff options
context:
space:
mode:
authorgspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-27 23:15:42 +0000
committergspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-27 23:15:42 +0000
commit05b47f7a8c5451f858dc220df0e3a97542edace6 (patch)
treea2273d619f0625c9d44d40842845ccce2eac1045 /o3d/serializer
parent5cdc8bdb4c847cefe7f4542bd10c9880c2c557a0 (diff)
downloadchromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.zip
chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.gz
chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.bz2
This is the O3D source tree's initial commit to the Chromium tree. It
is not built or referenced at all by the chrome build yet, and doesn't yet build in it's new home. We'll change that shortly. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17035 0039d316-1c4b-4281-b951-d872f2087c98
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_