| // Copyright 2016 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "base/bits.h" |
| #include "base/memory/ptr_util.h" |
| #include "cobalt/loader/mesh/mesh_decoder.h" |
| #include "cobalt/loader/mesh/projection_codec/projection_decoder.h" |
| #include "cobalt/render_tree/resource_provider.h" |
| |
| namespace cobalt { |
| namespace loader { |
| namespace mesh { |
| |
| namespace { |
| |
| // Builds meshes from calls by the projection decoder. |
| class MeshDecoderSink : public projection_codec::ProjectionDecoder::Sink { |
| public: |
| typedef MeshProjection::MeshCollection MeshCollection; |
| typedef MeshProjection::MeshCollectionList MeshCollectionList; |
| |
| // Decodes the contents of a projection box. (i.e. everything after the |
| // full box header.) |
| static bool DecodeBoxContents( |
| render_tree::ResourceProvider* resource_provider, uint8_t version, |
| uint32_t flags, const uint8* data, size_t data_size, |
| MeshCollectionList* dest_mesh_collection_list, uint32* crc); |
| |
| // Decodes the contents of a projection box. (i.e. everything after the |
| // full box header.) |
| static scoped_refptr<MeshProjection> DecodeMeshProjectionFromBoxContents( |
| render_tree::ResourceProvider* resource_provider, uint8_t version, |
| uint32_t flags, const uint8* data, size_t data_size); |
| |
| explicit MeshDecoderSink(render_tree::ResourceProvider* resource_provider, |
| MeshCollectionList* dest_mesh_collection_list, |
| uint32* crc); |
| |
| ~MeshDecoderSink() override; |
| |
| bool IsCached(uint32 crc) override; |
| |
| void BeginMeshCollection() override; |
| void AddCoordinate(float value) override; |
| void AddVertex(const projection_codec::IndexedVert& v) override; |
| |
| void BeginMeshInstance() override; |
| void SetTextureId(uint8 texture_id) override; |
| void SetMeshGeometryType(projection_codec::MeshGeometryType type) override; |
| void AddVertexIndex(size_t v_index) override; |
| void EndMeshInstance() override; |
| |
| void EndMeshCollection() override; |
| |
| private: |
| render_tree::ResourceProvider* resource_provider_; |
| |
| // Components of a mesh projection (possibly several mesh collections). |
| MeshCollectionList* mesh_collection_list_; |
| uint32* crc_; |
| |
| // Components of a mesh collection. |
| std::unique_ptr<MeshCollection> mesh_collection_; |
| std::vector<float> collection_coordinates_; |
| std::vector<projection_codec::IndexedVert> collection_vertices_; |
| |
| // Components of a mesh. |
| std::unique_ptr<std::vector<render_tree::Mesh::Vertex> > mesh_vertices_; |
| render_tree::Mesh::DrawMode mesh_draw_mode_; |
| uint8 mesh_texture_id_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MeshDecoderSink); |
| }; |
| |
| MeshDecoderSink::MeshDecoderSink( |
| render_tree::ResourceProvider* resource_provider, |
| MeshCollectionList* dest_mesh_collection_list, uint32* crc) |
| : resource_provider_(resource_provider), |
| mesh_collection_list_(dest_mesh_collection_list), |
| crc_(crc) { |
| DCHECK(resource_provider); |
| DCHECK(dest_mesh_collection_list); |
| DCHECK(crc); |
| } |
| |
| MeshDecoderSink::~MeshDecoderSink() {} |
| |
| bool MeshDecoderSink::IsCached(uint32 crc) { |
| *crc_ = crc; |
| return false; |
| } |
| |
| void MeshDecoderSink::BeginMeshCollection() { |
| CHECK(!mesh_collection_); |
| CHECK(collection_coordinates_.empty()); |
| CHECK(collection_vertices_.empty()); |
| |
| mesh_collection_.reset(new MeshCollection); |
| } |
| |
| void MeshDecoderSink::AddCoordinate(float value) { |
| collection_coordinates_.push_back(value); |
| } |
| |
| void MeshDecoderSink::AddVertex(const projection_codec::IndexedVert& vertex) { |
| collection_vertices_.push_back(vertex); |
| } |
| |
| void MeshDecoderSink::BeginMeshInstance() { |
| CHECK(!mesh_vertices_); |
| mesh_vertices_.reset(new std::vector<render_tree::Mesh::Vertex>); |
| } |
| |
| void MeshDecoderSink::SetTextureId(uint8 texture_id) { |
| DCHECK_EQ(texture_id, 0); |
| mesh_texture_id_ = texture_id; |
| } |
| |
| void MeshDecoderSink::SetMeshGeometryType( |
| projection_codec::MeshGeometryType geometry_type) { |
| switch (geometry_type) { |
| case projection_codec::kTriangles: |
| mesh_draw_mode_ = render_tree::Mesh::kDrawModeTriangles; |
| break; |
| case projection_codec::kTriangleStrip: |
| mesh_draw_mode_ = render_tree::Mesh::kDrawModeTriangleStrip; |
| break; |
| case projection_codec::kTriangleFan: |
| mesh_draw_mode_ = render_tree::Mesh::kDrawModeTriangleFan; |
| break; |
| default: |
| NOTREACHED() << "Unrecognized mesh geometry emitted by " |
| "ProjectionDecoder."; |
| } |
| } |
| |
| void MeshDecoderSink::AddVertexIndex(size_t index) { |
| const projection_codec::IndexedVert& vertex = collection_vertices_[index]; |
| mesh_vertices_->push_back(render_tree::Mesh::Vertex( |
| collection_coordinates_[vertex.x], collection_coordinates_[vertex.y], |
| collection_coordinates_[vertex.z], collection_coordinates_[vertex.u], |
| collection_coordinates_[vertex.v])); |
| } |
| |
| void MeshDecoderSink::EndMeshInstance() { |
| mesh_collection_->push_back(resource_provider_->CreateMesh( |
| std::move(mesh_vertices_), mesh_draw_mode_)); |
| } |
| |
| void MeshDecoderSink::EndMeshCollection() { |
| mesh_collection_list_->emplace_back(mesh_collection_.release()); |
| collection_vertices_.clear(); |
| collection_coordinates_.clear(); |
| } |
| |
| bool MeshDecoderSink::DecodeBoxContents( |
| render_tree::ResourceProvider* resource_provider, uint8_t version, |
| uint32_t flags, const uint8* data, size_t data_size, |
| MeshCollectionList* dest_mesh_collection_list, uint32* crc) { |
| MeshDecoderSink sink(resource_provider, dest_mesh_collection_list, crc); |
| return projection_codec::ProjectionDecoder::DecodeBoxContentsToSink( |
| version, flags, data, data_size, &sink); |
| } |
| |
| scoped_refptr<MeshProjection> |
| MeshDecoderSink::DecodeMeshProjectionFromBoxContents( |
| render_tree::ResourceProvider* resource_provider, uint8_t version, |
| uint32_t flags, const uint8* data, size_t data_size) { |
| MeshDecoderSink::MeshCollectionList mesh_collection_list; |
| uint32 crc = kuint32max; |
| |
| if (!DecodeBoxContents(resource_provider, version, flags, data, data_size, |
| &mesh_collection_list, &crc)) { |
| return NULL; |
| } |
| |
| return scoped_refptr<MeshProjection>( |
| new MeshProjection(std::move(mesh_collection_list), crc)); |
| } |
| |
| } // namespace |
| |
| MeshDecoder::MeshDecoder( |
| render_tree::ResourceProvider* resource_provider, |
| const MeshAvailableCallback& mesh_available_callback, |
| const loader::Decoder::OnCompleteFunction& load_complete_callback) |
| : resource_provider_(resource_provider), |
| mesh_available_callback_(mesh_available_callback), |
| load_complete_callback_(load_complete_callback), |
| is_suspended_(!resource_provider_) { |
| DCHECK(!mesh_available_callback_.is_null()); |
| DCHECK(!load_complete_callback.is_null()); |
| } |
| |
| void MeshDecoder::DecodeChunk(const char* data, size_t size) { |
| if (is_suspended_) { |
| DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended."; |
| return; |
| } |
| |
| if (!raw_data_) { |
| raw_data_ = base::WrapUnique(new std::vector<uint8>()); |
| } |
| |
| size_t start_size = raw_data_->size(); |
| |
| raw_data_->resize(start_size + size); |
| if (raw_data_->empty()) { |
| return; |
| } |
| memcpy(&((*raw_data_)[start_size]), data, size); |
| } |
| |
| void MeshDecoder::Finish() { |
| if (is_suspended_) { |
| DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended."; |
| return; |
| } |
| |
| if (!resource_provider_) { |
| load_complete_callback_.Run( |
| std::string("No resource provider was passed to the MeshDecoder.")); |
| return; |
| } |
| |
| if (raw_data_->size() <= 4) { |
| // Mesh projection boxes cannot be empty, since they carry at least the |
| // version and flags. |
| load_complete_callback_.Run( |
| std::string("MeshDecoder passed an empty buffer, cannot decode.")); |
| return; |
| } |
| |
| // Skip version and flags (first 4 bytes of the box). |
| scoped_refptr<MeshProjection> mesh_projection = |
| MeshDecoderSink::DecodeMeshProjectionFromBoxContents( |
| resource_provider_, 0, 0, &raw_data_->at(4), raw_data_->size() - 4); |
| if (mesh_projection) { |
| mesh_available_callback_.Run(mesh_projection); |
| load_complete_callback_.Run(base::nullopt); |
| } else { |
| // Error must have occured in MeshDecoderSink::Decode. |
| load_complete_callback_.Run( |
| std::string("MeshDecoder passed an invalid mesh projection box.")); |
| } |
| } |
| |
| bool MeshDecoder::Suspend() { |
| DCHECK(!is_suspended_); |
| DCHECK(resource_provider_); |
| |
| is_suspended_ = true; |
| resource_provider_ = NULL; |
| |
| ReleaseRawData(); |
| return true; |
| } |
| |
| void MeshDecoder::Resume(render_tree::ResourceProvider* resource_provider) { |
| DCHECK(is_suspended_); |
| DCHECK(!resource_provider_); |
| DCHECK(resource_provider); |
| |
| is_suspended_ = false; |
| resource_provider_ = resource_provider; |
| } |
| |
| void MeshDecoder::ReleaseRawData() { raw_data_.reset(); } |
| |
| } // namespace mesh |
| } // namespace loader |
| } // namespace cobalt |