blob: 25bb314cac69ffd22068c34b396ba9ad4395c8e9 [file] [log] [blame]
// 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