blob: 05d7bccd250a9d84668cd8346a64ae67b379e18b [file] [log] [blame]
// Copyright 2016 Google Inc. 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 "base/bits.h"
#include "base/memory/scoped_vector.h"
#include "cobalt/loader/mesh/mesh_decoder.h"
#include "cobalt/loader/mesh/projection_codec/projection_decoder.h"
#include "third_party/glm/glm/vec2.hpp"
#include "third_party/glm/glm/vec3.hpp"
namespace cobalt {
namespace loader {
namespace mesh {
namespace {
// Builds meshes from calls by the projection decoder.
class MeshDecoderSink : public projection_codec::ProjectionDecoder::Sink {
public:
typedef std::vector<scoped_refptr<render_tree::Mesh> > MeshCollection;
typedef ScopedVector<MeshCollection> MeshCollectionList;
// Decode a complete box that includes the header.
static bool Decode(const uint8* data, size_t data_size,
MeshCollectionList* dest_mesh_collection_list);
// Decodes the contents of a projection box. (i.e. everything after the
// full box header.)
static bool DecodeBoxContents(uint8_t version, uint32_t flags,
const uint8* data, size_t data_size,
MeshCollectionList* dest_mesh_collection_list);
explicit MeshDecoderSink(MeshCollectionList* dest_mesh_collection_list);
~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;
static scoped_refptr<render_tree::Mesh> ExtractSingleMesh(
const MeshCollectionList& list);
private:
MeshCollectionList* mesh_collection_list_; // not owned
std::vector<float> coordinates_;
std::vector<render_tree::Mesh::Vertex> vertices_;
scoped_ptr<MeshCollection> mesh_collection_;
std::vector<render_tree::Mesh::Vertex> mesh_vertices_;
render_tree::Mesh::DrawMode mesh_draw_mode_;
uint32 crc_;
DISALLOW_COPY_AND_ASSIGN(MeshDecoderSink);
};
MeshDecoderSink::MeshDecoderSink(MeshCollectionList* dest_mesh_collection_list)
: mesh_collection_list_(dest_mesh_collection_list) {}
MeshDecoderSink::~MeshDecoderSink() {}
bool MeshDecoderSink::IsCached(uint32 crc) {
crc_ = crc;
return false;
}
void MeshDecoderSink::BeginMeshCollection() {
CHECK(!mesh_collection_);
coordinates_.clear();
vertices_.clear();
mesh_collection_.reset(new MeshCollection);
}
void MeshDecoderSink::AddCoordinate(float value) {
coordinates_.push_back(value);
}
void MeshDecoderSink::AddVertex(const projection_codec::IndexedVert& v) {
render_tree::Mesh::Vertex vertex;
vertex.position_coord =
glm::vec3(coordinates_[v.x], coordinates_[v.y], coordinates_[v.z]);
vertex.texture_coord = glm::vec2(coordinates_[v.u], coordinates_[v.v]);
vertices_.push_back(vertex);
}
void MeshDecoderSink::BeginMeshInstance() { CHECK(mesh_vertices_.empty()); }
void MeshDecoderSink::SetTextureId(uint8 texture_id) {
// No texture id other than the default video texture supported.
DCHECK(!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 v_index) {
mesh_vertices_.push_back(vertices_[v_index]);
}
void MeshDecoderSink::EndMeshInstance() {
mesh_collection_->push_back(scoped_refptr<render_tree::Mesh>(
new render_tree::Mesh(mesh_vertices_, mesh_draw_mode_, crc_)));
}
void MeshDecoderSink::EndMeshCollection() {
mesh_collection_list_->push_back(mesh_collection_.release());
coordinates_.clear();
vertices_.clear();
}
bool MeshDecoderSink::Decode(const uint8* data, size_t data_size,
MeshCollectionList* dest_mesh_collection_list) {
MeshDecoderSink sink(dest_mesh_collection_list);
return projection_codec::ProjectionDecoder::DecodeToSink(data, data_size,
&sink);
}
bool MeshDecoderSink::DecodeBoxContents(
uint8_t version, uint32_t flags, const uint8* data, size_t data_size,
MeshCollectionList* dest_mesh_collection_list) {
MeshDecoderSink sink(dest_mesh_collection_list);
return projection_codec::ProjectionDecoder::DecodeBoxContentsToSink(
version, flags, data, data_size, &sink);
}
scoped_refptr<render_tree::Mesh> MeshDecoderSink::ExtractSingleMesh(
const MeshCollectionList& list) {
if (list.size() == 1) {
const MeshCollection& collection = *list[0];
if (collection.size() == 1) {
return collection[0];
}
}
return NULL;
}
} // namespace
MeshDecoder::MeshDecoder(render_tree::ResourceProvider* resource_provider,
const SuccessCallback& success_callback,
const ErrorCallback& error_callback)
: resource_provider_(resource_provider),
success_callback_(success_callback),
error_callback_(error_callback),
suspended_(false) {
DCHECK(resource_provider_);
DCHECK(!success_callback_.is_null());
DCHECK(!error_callback_.is_null());
}
void MeshDecoder::DecodeChunk(const char* data, size_t size) {
if (!raw_data_) {
raw_data_ = make_scoped_ptr(new std::vector<uint8>());
}
size_t start_size = raw_data_->size();
raw_data_->resize(start_size + size);
memcpy(&((*raw_data_)[start_size]), data, size);
}
void MeshDecoder::ReleaseRawData() { raw_data_.reset(); }
void MeshDecoder::Finish() {
if (suspended_) {
DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended.";
return;
}
if (!resource_provider_) {
error_callback_.Run("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.
error_callback_.Run("MeshDecoder passed an empty buffer, cannot decode.");
return;
}
std::string error_string;
MeshDecoderSink::MeshCollectionList mesh_collection_list;
if (MeshDecoderSink::DecodeBoxContents(
0, 0, &raw_data_->at(4), // Skip version and flags.
raw_data_->size(), &mesh_collection_list)) {
scoped_refptr<render_tree::Mesh> mesh =
MeshDecoderSink::ExtractSingleMesh(mesh_collection_list);
if (mesh) {
success_callback_.Run(mesh);
return;
} else {
error_callback_.Run(
"MeshDecoder found unexpected number of meshes in "
"projection box.");
}
}
// Error must hace occured in MeshDecoderSink::Decode.
error_callback_.Run("MeshDecoder passed an invalid mesh projection box.");
}
bool MeshDecoder::Suspend() {
DCHECK(!suspended_);
suspended_ = true;
resource_provider_ = NULL;
ReleaseRawData();
return true;
}
void MeshDecoder::Resume(render_tree::ResourceProvider* resource_provider) {
if (!suspended_) {
DCHECK_EQ(resource_provider_, resource_provider);
return;
}
suspended_ = false;
resource_provider_ = resource_provider;
}
} // namespace mesh
} // namespace loader
} // namespace cobalt