[+] glTF. Support for import meshes with Open3DGC compression.
parent
2e452205aa
commit
5e4fd5fa3c
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
Open Asset Import Library (assimp)
|
||||
----------------------------------------------------------------------
|
||||
|
||||
|
@ -477,6 +477,15 @@ namespace glTF
|
|||
|
||||
bool LoadFromStream(IOStream& stream, size_t length = 0, size_t baseOffset = 0);
|
||||
|
||||
/// \fn void ReplaceData(uint8_t* pBufferData_Offset, size_t pBufferData_Count, uint8_t pPrepend_Data, size_t pPrepend_Count)
|
||||
/// Replace part of buffer data. For example: decoded/encoded data.
|
||||
/// \param [in] pBufferData_Offset - index of first element in buffer from which new data will be placed.
|
||||
/// \param [in] pBufferData_Count - count of bytes in buffer which will be replaced.
|
||||
/// \param [in] pReplace_Data - pointer to array with new data for buffer.
|
||||
/// \param [in] pReplace_Count - count of bytes in new data.
|
||||
/// \return true - if successfully replaced, false if input arguments is out of range.
|
||||
bool ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t* pReplace_Data, const size_t pReplace_Count);
|
||||
|
||||
size_t AppendData(uint8_t* data, size_t length);
|
||||
void Grow(size_t amount);
|
||||
|
||||
|
@ -634,7 +643,18 @@ namespace glTF
|
|||
std::vector<Primitive> primitives;
|
||||
|
||||
Mesh() {}
|
||||
void Read(Value& obj, Asset& r);
|
||||
|
||||
/// \fn void Read(Value& pJSON_Object, Asset& pAsset_Root)
|
||||
/// Get mesh data from JSON-object and place them to root asset.
|
||||
/// \param [in] pJSON_Object - reference to pJSON-object from which data are read.
|
||||
/// \param [out] pAsset_Root - reference to root assed where data will be stored.
|
||||
void Read(Value& pJSON_Object, Asset& pAsset_Root);
|
||||
|
||||
/// \fn void Decode_O3DGC(Value& pJSON_Object_CompressedData, Asset& pAsset_Root)
|
||||
/// Decode part of "bufferView" which encoded with Open3DGC algorythm.
|
||||
/// \param [in] pJSON_Object_CompressedData - reference to JSON-object which is "compressedData" block.
|
||||
/// \param [out] pAsset_Root - reference to root assed where data will be stored.
|
||||
void Decode_O3DGC(Value& pJSON_Object_CompressedData, Asset& pAsset_Root);
|
||||
};
|
||||
|
||||
struct Node : public Object
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
Open Asset Import Library (assimp)
|
||||
----------------------------------------------------------------------
|
||||
|
||||
|
@ -40,6 +40,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include "StringUtils.h"
|
||||
|
||||
// Header files, Open3DGC.h,
|
||||
#include <Open3DGC/o3dgcSC3DMCDecoder.h>
|
||||
|
||||
namespace glTF {
|
||||
|
||||
namespace {
|
||||
|
@ -324,6 +327,26 @@ inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseO
|
|||
return true;
|
||||
}
|
||||
|
||||
inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t* pReplace_Data, const size_t pReplace_Count)
|
||||
{
|
||||
const size_t new_data_size = byteLength + pReplace_Count - pBufferData_Count;
|
||||
|
||||
uint8_t* new_data;
|
||||
|
||||
if((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) return false;
|
||||
|
||||
new_data = new uint8_t[new_data_size];
|
||||
// Copy data which place before replacing part.
|
||||
memcpy(new_data, mData.get(), pBufferData_Offset);
|
||||
// Copy new data.
|
||||
memcpy(&new_data[pBufferData_Offset], pReplace_Data, pReplace_Count);
|
||||
// Copy data which place after replacing part.
|
||||
memcpy(&new_data[pBufferData_Offset + pReplace_Count], &mData.get()[pBufferData_Offset + pBufferData_Count], pBufferData_Offset);
|
||||
// Apply new data
|
||||
mData.reset(new_data);
|
||||
byteLength = new_data_size;
|
||||
}
|
||||
|
||||
inline size_t Buffer::AppendData(uint8_t* data, size_t length)
|
||||
{
|
||||
size_t offset = this->byteLength;
|
||||
|
@ -676,9 +699,35 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
inline void Mesh::Read(Value& obj, Asset& r)
|
||||
inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root)
|
||||
{
|
||||
if (Value* primitives = FindArray(obj, "primitives")) {
|
||||
//
|
||||
// Mesh extensions
|
||||
//
|
||||
// At first check for extensions. They can affect on interpretaion of mesh data.
|
||||
Value* extensions = FindObject(pJSON_Object, "extensions");
|
||||
|
||||
if(extensions != nullptr)
|
||||
{
|
||||
// At first check if data of mesh is compressed. Because buffer data must be decoded before another get data from it.
|
||||
// Only Open3DGC supported at now.
|
||||
Value* o3dgc = FindObject(*extensions, "Open3DGC-compression");
|
||||
|
||||
if(o3dgc != nullptr)
|
||||
{
|
||||
// Search compressed data
|
||||
Value* comp_data = FindObject(*o3dgc, "compressedData");
|
||||
|
||||
if(comp_data == nullptr) throw DeadlyImportError("GLTF: \"Open3DGC-compression\" must has \"compressedData\".");
|
||||
|
||||
Decode_O3DGC(*comp_data, pAsset_Root);
|
||||
}// if(o3dgc == nullptr)
|
||||
}// if(extensions != nullptr)
|
||||
|
||||
//
|
||||
// Mesh primitives.
|
||||
//
|
||||
if (Value* primitives = FindArray(pJSON_Object, "primitives")) {
|
||||
this->primitives.resize(primitives->Size());
|
||||
for (unsigned int i = 0; i < primitives->Size(); ++i) {
|
||||
Value& primitive = (*primitives)[i];
|
||||
|
@ -698,22 +747,109 @@ inline void Mesh::Read(Value& obj, Asset& r)
|
|||
if (GetAttribVector(prim, attr, vec, undPos)) {
|
||||
size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
|
||||
if ((*vec).size() <= idx) (*vec).resize(idx + 1);
|
||||
(*vec)[idx] = r.accessors.Get(it->value.GetString());
|
||||
(*vec)[idx] = pAsset_Root.accessors.Get(it->value.GetString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Value* indices = FindString(primitive, "indices")) {
|
||||
prim.indices = r.accessors.Get(indices->GetString());
|
||||
prim.indices = pAsset_Root.accessors.Get(indices->GetString());
|
||||
}
|
||||
|
||||
if (Value* material = FindString(primitive, "material")) {
|
||||
prim.material = r.materials.Get(material->GetString());
|
||||
prim.material = pAsset_Root.materials.Get(material->GetString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Mesh::Decode_O3DGC(Value& pJSON_Object_CompressedData, Asset& pAsset_Root)
|
||||
{
|
||||
// Compressed data contain description of part of "bufferView" which is encoded. This part must be decoded and
|
||||
// new data must replace old encoded part. In fact \"compressedData\" is similar to "accessor" structure.
|
||||
const char* bufview_id;
|
||||
uint32_t byte_offset, count, count_indices, count_vertices;
|
||||
const char* mode_str;
|
||||
const char* type_str;
|
||||
ComponentType component_type;
|
||||
|
||||
#define MESH_READ_COMPRESSEDDATA_MEMBER(pID, pOut) \
|
||||
if(!ReadMember(pJSON_Object_CompressedData, pID, pOut)) { throw DeadlyImportError(std::string("GLTF: \"compressedData\" must has \"") + pID + "\"."); }
|
||||
|
||||
MESH_READ_COMPRESSEDDATA_MEMBER("bufferView", bufview_id);
|
||||
MESH_READ_COMPRESSEDDATA_MEMBER("byteOffset", byte_offset);
|
||||
MESH_READ_COMPRESSEDDATA_MEMBER("componentType", component_type);
|
||||
MESH_READ_COMPRESSEDDATA_MEMBER("count", count);
|
||||
MESH_READ_COMPRESSEDDATA_MEMBER("indicesCount", count_indices);
|
||||
MESH_READ_COMPRESSEDDATA_MEMBER("verticesCount", count_vertices);
|
||||
MESH_READ_COMPRESSEDDATA_MEMBER("mode", mode_str);
|
||||
MESH_READ_COMPRESSEDDATA_MEMBER("type", type_str);
|
||||
|
||||
#undef MESH_READ_COMPRESSEDDATA_MEMBER
|
||||
|
||||
// Check some values
|
||||
if(strcmp(type_str, "SCALAR")) throw DeadlyImportError("GLTF: only \"SCALAR\" type is supported for compressed data.");
|
||||
if(component_type != ComponentType_UNSIGNED_BYTE) throw DeadlyImportError("GLTF: only \"UNSIGNED_BYTE\" component type is supported for compressed data.");
|
||||
if((strcmp(mode_str, "binary") != 0) && (strcmp(mode_str, "ascii") != 0))
|
||||
{
|
||||
throw DeadlyImportError(std::string("GLTF: for compressed data supported modes is: \"ascii\", \"binary\". Not the: \"") + mode_str + "\".");
|
||||
}
|
||||
|
||||
// Search for "bufferView" by ID.
|
||||
Ref<BufferView> bufview = pAsset_Root.bufferViews.Get(bufview_id);
|
||||
|
||||
//
|
||||
// Decode data. Adapted piece of code from COLLADA2GLTF converter.
|
||||
//
|
||||
// void testDecode(shared_ptr <GLTFMesh> mesh, BinaryStream &bstream)
|
||||
o3dgc::SC3DMCDecoder<uint16_t> decoder;
|
||||
o3dgc::IndexedFaceSet <unsigned short> ifs;
|
||||
uint8_t* output_data;
|
||||
size_t size_vertex, size_normal, size_texcoord, size_indices, output_data_size;
|
||||
o3dgc::BinaryStream bstream;
|
||||
|
||||
// Read data from buffer and place it in BinaryStream for decoder.
|
||||
bstream.LoadFromBuffer(&bufview->buffer->GetPointer()[bufview->byteOffset + byte_offset], count);
|
||||
|
||||
// After decoding header we can get size of primitives
|
||||
if(decoder.DecodeHeader(ifs, bstream) != o3dgc::O3DGC_OK) throw DeadlyImportError("GLTF: can not decode Open3DGC header.");
|
||||
|
||||
size_vertex = ifs.GetNCoord() * 3 * sizeof(float);
|
||||
size_normal = ifs.GetNNormal() * 3 * sizeof(float);
|
||||
size_texcoord = ifs.GetNFloatAttribute(0) * 2 * sizeof(float);
|
||||
size_indices = ifs.GetNCoordIndex() * 3 * sizeof(unsigned short);
|
||||
|
||||
output_data_size = size_vertex + size_normal + size_texcoord + size_indices;
|
||||
output_data = new uint8_t[output_data_size];
|
||||
|
||||
float* uncompressed_vertices = (float* const)(output_data + size_indices);// size_indices => vertex offset
|
||||
|
||||
ifs.SetCoordIndex((uint16_t* const)output_data);
|
||||
ifs.SetCoord((float* const)uncompressed_vertices);
|
||||
|
||||
if(ifs.GetNNormal() > 0) ifs.SetNormal((float* const)(output_data + size_indices + size_vertex));
|
||||
|
||||
if(ifs.GetNFloatAttribute(0)) ifs.SetFloatAttribute(0, (float* const)(output_data + size_indices + size_vertex + size_normal));
|
||||
|
||||
// Decode data
|
||||
if(decoder.DecodePlayload(ifs, bstream) != o3dgc::O3DGC_OK) throw DeadlyImportError("GLTF: can not decode Open3DGC data.");
|
||||
|
||||
// Set/extend buffer
|
||||
bufview->buffer->ReplaceData(byte_offset, count, output_data, output_data_size);
|
||||
// Also correct size of current "bufferView" ...
|
||||
bufview->byteLength = output_data_size;
|
||||
// and offset for all other "bufferViews" which are placed after edited.
|
||||
const size_t difference = output_data_size - count;
|
||||
|
||||
for(size_t idx_bv = 0; idx_bv < pAsset_Root.bufferViews.Size(); idx_bv++)
|
||||
{
|
||||
size_t off = pAsset_Root.bufferViews[idx_bv].byteOffset;
|
||||
|
||||
if(off > (byte_offset + count)) pAsset_Root.bufferViews[idx_bv].byteOffset += difference;
|
||||
}
|
||||
|
||||
delete [] output_data;
|
||||
}
|
||||
|
||||
inline void Camera::Read(Value& obj, Asset& r)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue