diff options
Diffstat (limited to 'o3d/serializer')
-rw-r--r-- | o3d/serializer/build.scons | 65 | ||||
-rw-r--r-- | o3d/serializer/cross/serializer.cc | 787 | ||||
-rw-r--r-- | o3d/serializer/cross/serializer.h | 200 | ||||
-rw-r--r-- | o3d/serializer/cross/serializer_binary.cc | 266 | ||||
-rw-r--r-- | o3d/serializer/cross/serializer_binary.h | 56 | ||||
-rw-r--r-- | o3d/serializer/cross/serializer_test.cc | 1067 | ||||
-rw-r--r-- | o3d/serializer/cross/version.h | 45 |
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_ |