The new requirements are as follow: - CMake 3.19 - Ninja 1.10 - Android Studio 4.2 - Android NDK 22.1 - Gradle 7.0 - Kotlin 1.5
226 lines
7.9 KiB
C++
226 lines
7.9 KiB
C++
/*
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
*
|
|
* 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 "DracoCache.h"
|
|
|
|
#if GLTFIO_DRACO_SUPPORTED
|
|
#include <draco/compression/decode.h>
|
|
#endif
|
|
|
|
#include <utils/Log.h>
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
using std::unique_ptr;
|
|
using std::vector;
|
|
|
|
using namespace utils;
|
|
|
|
namespace gltfio {
|
|
|
|
DracoMesh* DracoCache::findOrCreateMesh(const cgltf_buffer_view* key) {
|
|
auto iter = mCache.find(key);
|
|
if (iter != mCache.end()) {
|
|
return iter->second.get();
|
|
}
|
|
assert(key->buffer && key->buffer->data);
|
|
const uint8_t* compressedData = key->offset + (uint8_t*) key->buffer->data;
|
|
DracoMesh* mesh = DracoMesh::decode(compressedData, key->size);
|
|
mCache.emplace(key, mesh);
|
|
return mesh;
|
|
}
|
|
|
|
DracoMesh::DracoMesh(struct DracoMeshDetails* details) : mDetails(details) {}
|
|
|
|
#if GLTFIO_DRACO_SUPPORTED
|
|
|
|
struct DracoMeshDetails {
|
|
unique_ptr<draco::Mesh> mesh;
|
|
vector<unique_ptr<cgltf_buffer_view>> views;
|
|
vector<unique_ptr<cgltf_buffer>> buffers;
|
|
};
|
|
|
|
DracoMesh::~DracoMesh() {
|
|
for (auto& buffer : mDetails->buffers) {
|
|
free(buffer->data);
|
|
}
|
|
}
|
|
|
|
// Gets the number of components in the given cgltf vector type, or -1 for matrices.
|
|
static int getNumComponents(cgltf_type ctype) {
|
|
return ((int) ctype) <= 4 ? ((int) ctype) : -1;
|
|
}
|
|
|
|
// Allocates and populates the given buffer view with indices from the given Draco mesh.
|
|
template<typename T>
|
|
static void convertFaces(cgltf_accessor* target, const draco::Mesh* mesh) {
|
|
assert(target->stride == sizeof(T));
|
|
|
|
const cgltf_size size = mesh->num_faces() * 3 * sizeof(T);
|
|
cgltf_buffer_view* view = target->buffer_view;
|
|
cgltf_buffer* buffer = view->buffer;
|
|
*buffer = { nullptr, size, nullptr, malloc(size) };
|
|
*view = { nullptr, buffer, 0, size, 0, cgltf_buffer_view_type_indices };
|
|
T* dest = (T*) buffer->data;
|
|
for (uint32_t id = 0, n = mesh->num_faces(); id < n; ++id) {
|
|
draco::Mesh::Face face = mesh->face(draco::FaceIndex(id));
|
|
*dest++ = face[0].value();
|
|
*dest++ = face[1].value();
|
|
*dest++ = face[2].value();
|
|
}
|
|
}
|
|
|
|
// Converts vertex attributes into the desired format and populates the given cgltf buffer.
|
|
template<typename T>
|
|
static void convertAttribs(cgltf_accessor* target, const draco::PointAttribute* attr, uint32_t n) {
|
|
const int8_t ncomps = attr->num_components();
|
|
assert(ncomps <= 4 && ncomps == getNumComponents(target->type));
|
|
assert(target->stride == attr->num_components() * sizeof(T));
|
|
|
|
const uint32_t size = target->stride * n;
|
|
cgltf_buffer_view* view = target->buffer_view;
|
|
cgltf_buffer* buffer = view->buffer;
|
|
*buffer = { nullptr, size, nullptr, malloc(size) };
|
|
*view = { nullptr, buffer, 0, size, 0, cgltf_buffer_view_type_vertices };
|
|
T* dest = (T*) buffer->data;
|
|
for (draco::PointIndex i(0); i < n; ++i, dest += ncomps) {
|
|
attr->ConvertValue(attr->mapped_index(i), ncomps, dest);
|
|
}
|
|
}
|
|
|
|
DracoMesh* DracoMesh::decode(const uint8_t* data, size_t dataSize) {
|
|
draco::DecoderBuffer buffer;
|
|
buffer.Init((const char*) data, dataSize);
|
|
draco::Decoder decoder;
|
|
const auto geotype = decoder.GetEncodedGeometryType(&buffer);
|
|
if (!geotype.ok() || geotype.value() != draco::EncodedGeometryType::TRIANGULAR_MESH) {
|
|
return nullptr;
|
|
}
|
|
auto meshStatus = decoder.DecodeMeshFromBuffer(&buffer);
|
|
if (!meshStatus.ok()) {
|
|
return nullptr;
|
|
}
|
|
return new DracoMesh(new DracoMeshDetails { std::move(meshStatus).value() });
|
|
}
|
|
|
|
bool DracoMesh::getFaceIndices(cgltf_accessor* target) const {
|
|
// Return early if we've already decompressed this data.
|
|
if (target->buffer_view) {
|
|
return true;
|
|
}
|
|
|
|
draco::Mesh* mesh = mDetails->mesh.get();
|
|
|
|
// Check the accessor's index count against the number of faces in the Draco mesh.
|
|
// It would be tricky to be robust against a mismatch; see the class comment for DracoMesh.
|
|
uint32_t count = mesh->num_faces() * 3;
|
|
if (target->count != count) {
|
|
slog.e << "The glTF accessor wants " << target->count << " indices, "
|
|
<< "but the decoded Draco mesh has " << count << " indices." << io::endl;
|
|
return false;
|
|
}
|
|
|
|
cgltf_buffer_view* view = new cgltf_buffer_view;
|
|
cgltf_buffer* buffer = view->buffer = new cgltf_buffer;
|
|
|
|
mDetails->views.emplace_back(view);
|
|
mDetails->buffers.emplace_back(buffer);
|
|
|
|
target->offset = 0;
|
|
target->buffer_view = view;
|
|
|
|
switch (target->component_type) {
|
|
case cgltf_component_type_r_16u: convertFaces<uint16_t>(target, mesh); break;
|
|
case cgltf_component_type_r_32u: convertFaces<uint32_t>(target, mesh); break;
|
|
case cgltf_component_type_r_8u: convertFaces<uint8_t>(target, mesh); break;
|
|
default:
|
|
slog.e << "Unexpected component type for Draco indices." << io::endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DracoMesh::getVertexAttributes(uint32_t attributeId, cgltf_accessor* target) const {
|
|
// Return early if we've already decompressed this data.
|
|
if (target->buffer_view) {
|
|
return true;
|
|
}
|
|
|
|
// Return early if no such attribute exists.
|
|
draco::Mesh* mesh = mDetails->mesh.get();
|
|
const draco::PointAttribute* attr = mesh->GetAttributeByUniqueId(attributeId);
|
|
if (!attr) {
|
|
slog.e << "Unknown Draco point attribute." << io::endl;
|
|
return false;
|
|
}
|
|
|
|
// Check if the accessor's vertex count matches with the Draco vertex count. If a mismatch
|
|
// occurs we try to recover by adjusting the vertex count. Even though this is a spec violation,
|
|
// this often occurs in the wild. If we need to be even more robust, see the class comment for
|
|
// DracoMesh.
|
|
uint32_t count = mesh->num_points();
|
|
if (target->count != count) {
|
|
slog.e << "The glTF accessor wants " << target->count << " vertices, "
|
|
<< "but the decoded Draco mesh has " << count << " vertices." << io::endl;
|
|
|
|
// It is tempting to degrade gracefully by processing only the lesser of the two
|
|
// counts, but doing so would lead to invalid indices in the index buffer.
|
|
return false;
|
|
}
|
|
|
|
cgltf_buffer_view* view = new cgltf_buffer_view;
|
|
cgltf_buffer* buffer = view->buffer = new cgltf_buffer;
|
|
|
|
mDetails->views.emplace_back(view);
|
|
mDetails->buffers.emplace_back(buffer);
|
|
|
|
target->offset = 0;
|
|
target->buffer_view = view;
|
|
|
|
switch (target->component_type) {
|
|
case cgltf_component_type_r_8: convertAttribs<int8_t>(target, attr, count); break;
|
|
case cgltf_component_type_r_8u: convertAttribs<uint8_t>(target, attr, count); break;
|
|
case cgltf_component_type_r_16: convertAttribs<int16_t>(target, attr, count); break;
|
|
case cgltf_component_type_r_16u: convertAttribs<uint16_t>(target, attr, count); break;
|
|
case cgltf_component_type_r_32u: convertAttribs<uint32_t>(target, attr, count); break;
|
|
case cgltf_component_type_r_32f: convertAttribs<float>(target, attr, count); break;
|
|
default:
|
|
slog.e << "Unexpected component type for Draco vertices." << io::endl;
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#else // #if GLTFIO_DRACO_SUPPORTED
|
|
|
|
DracoMesh::~DracoMesh() {}
|
|
struct DracoMeshDetails {};
|
|
DracoMesh* DracoMesh::decode(const uint8_t* data, size_t dataSize) { return nullptr; }
|
|
|
|
bool DracoMesh::getFaceIndices(cgltf_accessor* target) const {
|
|
return false;
|
|
}
|
|
|
|
bool DracoMesh::getVertexAttributes(uint32_t attributeId, cgltf_accessor* target) const {
|
|
return false;
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace gltfio
|