Updated meshoptimizer.

This commit is contained in:
Бранимир Караџић 2019-12-02 22:21:57 -08:00
parent de9f0195cf
commit 5f8326f20a
8 changed files with 327 additions and 179 deletions

View File

@ -229,6 +229,79 @@ static void decodeVertexRejectMalformedHeaders()
assert(meshopt_decodeVertexBuffer(decoded, vertex_count, sizeof(PV), &brokenbuffer[0], brokenbuffer.size()) < 0);
}
static void decodeVertexBitGroups()
{
unsigned char data[16 * 4];
// this tests 0/2/4/8 bit groups in one stream
for (size_t i = 0; i < 16; ++i)
{
data[i * 4 + 0] = 0;
data[i * 4 + 1] = (unsigned char)(i * 1);
data[i * 4 + 2] = (unsigned char)(i * 2);
data[i * 4 + 3] = (unsigned char)(i * 8);
}
std::vector<unsigned char> buffer(meshopt_encodeVertexBufferBound(16, 4));
buffer.resize(meshopt_encodeVertexBuffer(&buffer[0], buffer.size(), data, 16, 4));
unsigned char decoded[16 * 4];
assert(meshopt_decodeVertexBuffer(decoded, 16, 4, &buffer[0], buffer.size()) == 0);
assert(memcmp(decoded, data, sizeof(data)) == 0);
}
static void decodeVertexBitGroupSentinels()
{
unsigned char data[16 * 4];
// this tests 0/2/4/8 bit groups and sentinels in one stream
for (size_t i = 0; i < 16; ++i)
{
if (i == 7 || i == 13)
{
data[i * 4 + 0] = 42;
data[i * 4 + 1] = 42;
data[i * 4 + 2] = 42;
data[i * 4 + 3] = 42;
}
else
{
data[i * 4 + 0] = 0;
data[i * 4 + 1] = (unsigned char)(i * 1);
data[i * 4 + 2] = (unsigned char)(i * 2);
data[i * 4 + 3] = (unsigned char)(i * 8);
}
}
std::vector<unsigned char> buffer(meshopt_encodeVertexBufferBound(16, 4));
buffer.resize(meshopt_encodeVertexBuffer(&buffer[0], buffer.size(), data, 16, 4));
unsigned char decoded[16 * 4];
assert(meshopt_decodeVertexBuffer(decoded, 16, 4, &buffer[0], buffer.size()) == 0);
assert(memcmp(decoded, data, sizeof(data)) == 0);
}
static void decodeVertexLarge()
{
unsigned char data[128 * 4];
// this tests 0/2/4/8 bit groups in one stream
for (size_t i = 0; i < 128; ++i)
{
data[i * 4 + 0] = 0;
data[i * 4 + 1] = (unsigned char)(i * 1);
data[i * 4 + 2] = (unsigned char)(i * 2);
data[i * 4 + 3] = (unsigned char)(i * 8);
}
std::vector<unsigned char> buffer(meshopt_encodeVertexBufferBound(128, 4));
buffer.resize(meshopt_encodeVertexBuffer(&buffer[0], buffer.size(), data, 128, 4));
unsigned char decoded[128 * 4];
assert(meshopt_decodeVertexBuffer(decoded, 128, 4, &buffer[0], buffer.size()) == 0);
assert(memcmp(decoded, data, sizeof(data)) == 0);
}
static void clusterBoundsDegenerate()
{
const float vbd[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
@ -310,6 +383,8 @@ static void customAllocator()
// customAlloc & customFree should not get called anymore
meshopt_optimizeVertexFetch(vb, ib, 3, vb, 3, 12);
assert(allocCount == 6 && freeCount == 6);
allocCount = freeCount = 0;
}
static void emptyMesh()
@ -356,7 +431,7 @@ static void simplifyPointsStuck()
assert(meshopt_simplifyPoints(0, vb, 3, 12, 0) == 0);
}
void runTests()
static void runTestsOnce()
{
decodeIndexV0();
decodeIndex16();
@ -370,6 +445,9 @@ void runTests()
decodeVertexMemorySafe();
decodeVertexRejectExtraBytes();
decodeVertexRejectMalformedHeaders();
decodeVertexBitGroups();
decodeVertexBitGroupSentinels();
decodeVertexLarge();
clusterBoundsDegenerate();
@ -381,3 +459,25 @@ void runTests()
simplifySloppyStuck();
simplifyPointsStuck();
}
namespace meshopt
{
extern unsigned int cpuid;
}
void runTests()
{
runTestsOnce();
#if !(defined(__AVX__) || defined(__SSSE3__)) && (defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__))
// When SSSE3/AVX support isn't enabled unconditionally, we use a cpuid-based fallback
// It's useful to be able to test scalar code in this case, so we temporarily fake the feature bits
// and restore them later
unsigned int cpuid = meshopt::cpuid;
meshopt::cpuid = 0;
runTestsOnce();
meshopt::cpuid = cpuid;
#endif
}

View File

@ -1,3 +1,4 @@
// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
#include "meshoptimizer.h"
void meshopt_setAllocator(void* (*allocate)(size_t), void (*deallocate)(void*))

View File

@ -1,3 +1,4 @@
// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
#include "meshoptimizer.h"
#include <assert.h>

View File

@ -23,6 +23,14 @@
#include <intrin.h> // __cpuid
#endif
// GCC 4.9+ and clang 3.8+ support targeting SIMD instruction sets from individual functions
#if !defined(SIMD_SSE) && !defined(SIMD_AVX) && ((defined(__clang__) && __clang_major__ * 100 + __clang_minor__ >= 308) || (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 409)) && (defined(__i386__) || defined(__x86_64__))
#define SIMD_SSE
#define SIMD_FALLBACK
#define SIMD_TARGET __attribute__((target("ssse3")))
#include <cpuid.h> // __cpuid
#endif
#if !defined(SIMD_NEON) && defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
#define SIMD_NEON
#endif
@ -32,6 +40,10 @@
#define SIMD_WASM
#endif
#ifndef SIMD_TARGET
#define SIMD_TARGET
#endif
#ifdef SIMD_SSE
#include <tmmintrin.h>
#endif
@ -446,6 +458,7 @@ static bool gDecodeBytesGroupInitialized = decodeBytesGroupBuildTables();
#endif
#ifdef SIMD_SSE
SIMD_TARGET
static __m128i decodeShuffleMask(unsigned char mask0, unsigned char mask1)
{
__m128i sm0 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(&kDecodeBytesGroupShuffle[mask0]));
@ -457,6 +470,7 @@ static __m128i decodeShuffleMask(unsigned char mask0, unsigned char mask1)
return _mm_unpacklo_epi64(sm0, sm1r);
}
SIMD_TARGET
static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsigned char* buffer, int bitslog2)
{
switch (bitslog2)
@ -814,6 +828,7 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi
#endif
#if defined(SIMD_SSE) || defined(SIMD_AVX)
SIMD_TARGET
static void transpose8(__m128i& x0, __m128i& x1, __m128i& x2, __m128i& x3)
{
__m128i t0 = _mm_unpacklo_epi8(x0, x1);
@ -827,6 +842,7 @@ static void transpose8(__m128i& x0, __m128i& x1, __m128i& x2, __m128i& x3)
x3 = _mm_unpackhi_epi16(t1, t3);
}
SIMD_TARGET
static __m128i unzigzag8(__m128i v)
{
__m128i xl = _mm_sub_epi8(_mm_setzero_si128(), _mm_and_si128(v, _mm_set1_epi8(1)));
@ -884,6 +900,7 @@ static v128_t unzigzag8(v128_t v)
#endif
#if defined(SIMD_SSE) || defined(SIMD_AVX) || defined(SIMD_NEON) || defined(SIMD_WASM)
SIMD_TARGET
static const unsigned char* decodeBytesSimd(const unsigned char* data, const unsigned char* data_end, unsigned char* buffer, size_t buffer_size)
{
assert(buffer_size % kByteGroupSize == 0);
@ -929,6 +946,7 @@ static const unsigned char* decodeBytesSimd(const unsigned char* data, const uns
return data;
}
SIMD_TARGET
static const unsigned char* decodeVertexBlockSimd(const unsigned char* data, const unsigned char* data_end, unsigned char* vertex_data, size_t vertex_count, size_t vertex_size, unsigned char last_vertex[256])
{
assert(vertex_count > 0 && vertex_count <= kVertexBlockMaxSize);
@ -1027,6 +1045,21 @@ static const unsigned char* decodeVertexBlockSimd(const unsigned char* data, con
}
#endif
#if defined(SIMD_SSE) && defined(SIMD_FALLBACK)
static unsigned int getCpuFeatures()
{
int cpuinfo[4] = {};
#if defined(_MSC_VER) && !defined(__clang__)
__cpuid(cpuinfo, 1);
#else
__cpuid(1, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]);
#endif
return cpuinfo[2];
}
unsigned int cpuid = getCpuFeatures();
#endif
} // namespace meshopt
size_t meshopt_encodeVertexBuffer(unsigned char* buffer, size_t buffer_size, const void* vertices, size_t vertex_count, size_t vertex_size)
@ -1140,9 +1173,7 @@ int meshopt_decodeVertexBuffer(void* destination, size_t vertex_count, size_t ve
const unsigned char* (*decode)(const unsigned char*, const unsigned char*, unsigned char*, size_t, size_t, unsigned char[256]) = 0;
#if defined(SIMD_SSE) && defined(SIMD_FALLBACK)
int cpuinfo[4] = {};
__cpuid(cpuinfo, 1);
decode = (cpuinfo[2] & (1 << 9)) ? decodeVertexBlockSimd : decodeVertexBlock;
decode = (cpuid & (1 << 9)) ? decodeVertexBlockSimd : decodeVertexBlock;
#elif defined(SIMD_SSE) || defined(SIMD_AVX) || defined(SIMD_NEON) || defined(SIMD_WASM)
decode = decodeVertexBlockSimd;
#else

View File

@ -9,7 +9,66 @@
#include "basisu_format.h"
#include "khr_df.h"
#include "ktx2_format.h"
// KTX Specification: 2. File Structure
struct Ktx2Header
{
uint8_t identifier[12];
uint32_t vkFormat;
uint32_t typeSize;
uint32_t pixelWidth;
uint32_t pixelHeight;
uint32_t pixelDepth;
uint32_t layerCount;
uint32_t faceCount;
uint32_t levelCount;
uint32_t supercompressionScheme;
uint32_t dfdByteOffset;
uint32_t dfdByteLength;
uint32_t kvdByteOffset;
uint32_t kvdByteLength;
uint64_t sgdByteOffset;
uint64_t sgdByteLength;
};
struct Ktx2LevelIndex
{
uint64_t byteOffset;
uint64_t byteLength;
uint64_t uncompressedByteLength;
};
enum
{
Ktx2SupercompressionSchemeBasis = 1,
};
// KTX Specification: 3.1. identifier
static const uint8_t Ktx2FileIdentifier[12] = {
0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A,
};
// KTX Specification: 3.12.2. Basis Universal Global Data
struct Ktx2BasisGlobalHeader
{
uint32_t globalFlags;
uint16_t endpointCount;
uint16_t selectorCount;
uint32_t endpointsByteLength;
uint32_t selectorsByteLength;
uint32_t tablesByteLength;
uint32_t extendedByteLength;
};
struct Ktx2BasisImageDesc
{
uint32_t imageFlags;
uint32_t rgbSliceByteOffset;
uint32_t rgbSliceByteLength;
uint32_t alphaSliceByteOffset;
uint32_t alphaSliceByteLength;
};
template <typename T>
static void read(const std::string& data, size_t offset, T& result)
@ -99,16 +158,17 @@ std::string basisToKtx(const std::string& basis, bool srgb)
uint32_t height = slices[0].m_orig_height;
uint32_t levels = has_alpha ? uint32_t(slices.size()) / 2 : uint32_t(slices.size());
KTX_header2 ktx_header = {KTX2_IDENTIFIER_REF};
Ktx2Header ktx_header = {};
memcpy(ktx_header.identifier, Ktx2FileIdentifier, sizeof(Ktx2FileIdentifier));
ktx_header.typeSize = 1;
ktx_header.pixelWidth = width;
ktx_header.pixelHeight = height;
ktx_header.layerCount = 0;
ktx_header.faceCount = 1;
ktx_header.levelCount = levels;
ktx_header.supercompressionScheme = KTX_SUPERCOMPRESSION_BASIS;
ktx_header.supercompressionScheme = Ktx2SupercompressionSchemeBasis;
size_t header_size = sizeof(KTX_header2) + levels * sizeof(ktxLevelIndexEntry);
size_t header_size = sizeof(Ktx2Header) + levels * sizeof(Ktx2LevelIndex);
std::vector<uint32_t> dfd;
createDfd(dfd, has_alpha ? 4 : 3, srgb);
@ -138,17 +198,17 @@ std::string basisToKtx(const std::string& basis, bool srgb)
size_t dfd_size = dfd.size() * sizeof(uint32_t);
size_t bgd_size =
sizeof(ktxBasisGlobalHeader) + sizeof(ktxBasisSliceDesc) * levels +
sizeof(Ktx2BasisGlobalHeader) + sizeof(Ktx2BasisImageDesc) * levels +
basis_header.m_endpoint_cb_file_size + basis_header.m_selector_cb_file_size + basis_header.m_tables_file_size;
ktx_header.dataFormatDescriptor.byteOffset = uint32_t(header_size);
ktx_header.dataFormatDescriptor.byteLength = uint32_t(dfd_size);
ktx_header.dfdByteOffset = uint32_t(header_size);
ktx_header.dfdByteLength = uint32_t(dfd_size);
ktx_header.keyValueData.byteOffset = uint32_t(header_size + dfd_size);
ktx_header.keyValueData.byteLength = uint32_t(kvp_size);
ktx_header.kvdByteOffset = uint32_t(header_size + dfd_size);
ktx_header.kvdByteLength = uint32_t(kvp_size);
ktx_header.supercompressionGlobalData.byteOffset = (header_size + dfd_size + kvp_size + 7) & ~7;
ktx_header.supercompressionGlobalData.byteLength = bgd_size;
ktx_header.sgdByteOffset = (header_size + dfd_size + kvp_size + 7) & ~7;
ktx_header.sgdByteLength = bgd_size;
// KTX2 header
write(ktx, ktx_header);
@ -157,7 +217,7 @@ std::string basisToKtx(const std::string& basis, bool srgb)
for (size_t i = 0; i < levels; ++i)
{
ktxLevelIndexEntry le = {}; // This will be patched later
Ktx2LevelIndex le = {}; // This will be patched later
write(ktx, le);
}
@ -170,7 +230,7 @@ std::string basisToKtx(const std::string& basis, bool srgb)
ktx.resize((ktx.size() + 7) & ~7);
// supercompression global data
ktxBasisGlobalHeader sgd_header = {};
Ktx2BasisGlobalHeader sgd_header = {};
sgd_header.globalFlags = basis_header.m_flags;
sgd_header.endpointCount = uint16_t(basis_header.m_total_endpoints);
sgd_header.selectorCount = uint16_t(basis_header.m_total_selectors);
@ -185,8 +245,8 @@ std::string basisToKtx(const std::string& basis, bool srgb)
for (size_t i = 0; i < levels; ++i)
{
ktxBasisSliceDesc sgd_slice = {}; // This will be patched later
write(ktx, sgd_slice);
Ktx2BasisImageDesc sgd_image = {}; // This will be patched later
write(ktx, sgd_image);
}
ktx.append(basis.substr(basis_header.m_endpoint_cb_file_ofs, basis_header.m_endpoint_cb_file_size));
@ -199,12 +259,14 @@ std::string basisToKtx(const std::string& basis, bool srgb)
// mip levels
for (size_t i = 0; i < levels; ++i)
{
size_t slice_index = (levels - i - 1) * (has_alpha + 1);
size_t level_index = levels - i - 1;
size_t slice_index = level_index * (has_alpha + 1);
const basist::basis_slice_desc& slice = slices[slice_index];
const basist::basis_slice_desc* slice_alpha = has_alpha ? &slices[slice_index + 1] : 0;
assert(slice.m_image_index == 0);
assert(slice.m_level_index == levels - i - 1);
assert(slice.m_level_index == level_index);
size_t file_offset = ktx.size();
@ -213,29 +275,31 @@ std::string basisToKtx(const std::string& basis, bool srgb)
if (slice_alpha)
ktx.append(basis.substr(slice_alpha->m_file_ofs, slice_alpha->m_file_size));
ktxLevelIndexEntry le = {};
Ktx2LevelIndex le = {};
le.byteOffset = file_offset;
le.byteLength = ktx.size() - file_offset;
le.uncompressedByteLength = 0;
write(ktx, ktx_level_offset + i * sizeof(ktxLevelIndexEntry), le);
write(ktx, ktx_level_offset + level_index * sizeof(Ktx2LevelIndex), le);
ktxBasisSliceDesc sgd_slice = {};
sgd_slice.sliceByteOffset = 0;
sgd_slice.sliceByteLength = slice.m_file_size;
Ktx2BasisImageDesc sgd_image = {};
sgd_image.rgbSliceByteOffset = 0;
sgd_image.rgbSliceByteLength = slice.m_file_size;
if (slice_alpha)
{
sgd_slice.alphaSliceByteOffset = slice.m_file_size;
sgd_slice.alphaSliceByteLength = slice_alpha->m_file_size;
sgd_image.alphaSliceByteOffset = slice.m_file_size;
sgd_image.alphaSliceByteLength = slice_alpha->m_file_size;
}
write(ktx, sgd_level_offset + i * sizeof(ktxBasisSliceDesc), sgd_slice);
write(ktx, sgd_level_offset + level_index * sizeof(Ktx2BasisImageDesc), sgd_image);
if (i + 1 != levels)
ktx.resize((ktx.size() + 7) & ~7);
}
ktx.resize((ktx.size() + 7) & ~7);
return ktx;
}

View File

@ -125,6 +125,7 @@ typedef enum cgltf_result
cgltf_result_file_not_found,
cgltf_result_io_error,
cgltf_result_out_of_memory,
cgltf_result_legacy_gltf,
} cgltf_result;
typedef enum cgltf_buffer_view_type
@ -763,7 +764,7 @@ cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_s
uint32_t version = tmp;
if (version != GlbVersion)
{
return cgltf_result_unknown_format;
return version < GlbVersion ? cgltf_result_legacy_gltf : cgltf_result_unknown_format;
}
// Total length
@ -1707,6 +1708,7 @@ cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size
#define CGLTF_ERROR_JSON -1
#define CGLTF_ERROR_NOMEM -2
#define CGLTF_ERROR_LEGACY -3
#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; }
#define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */
@ -1826,7 +1828,10 @@ static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* toke
static int cgltf_parse_json_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, size_t element_size, void** out_array, cgltf_size* out_size)
{
(void)json_chunk;
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
if (tokens[i].type != JSMN_ARRAY)
{
return tokens[i].type == JSMN_OBJECT ? CGLTF_ERROR_LEGACY : CGLTF_ERROR_JSON;
}
if (*out_array)
{
return CGLTF_ERROR_JSON;
@ -4049,6 +4054,11 @@ static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* token
}
}
if (out_asset->version && atof(out_asset->version) < 2)
{
return CGLTF_ERROR_LEGACY;
}
return i;
}
@ -4315,7 +4325,13 @@ cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk,
if (i < 0)
{
cgltf_free(data);
return (i == CGLTF_ERROR_NOMEM) ? cgltf_result_out_of_memory : cgltf_result_invalid_gltf;
switch (i)
{
case CGLTF_ERROR_NOMEM: return cgltf_result_out_of_memory;
case CGLTF_ERROR_LEGACY: return cgltf_result_legacy_gltf;
default: return cgltf_result_invalid_gltf;
}
}
if (cgltf_fixup_pointers(data) < 0)

View File

@ -177,12 +177,12 @@ struct BufferView
size_t bytes;
};
const char* getError(cgltf_result result)
const char* getError(cgltf_result result, cgltf_data* data)
{
switch (result)
{
case cgltf_result_file_not_found:
return "file not found";
return data ? "resource not found" : "file not found";
case cgltf_result_io_error:
return "I/O error";
@ -196,6 +196,15 @@ const char* getError(cgltf_result result)
case cgltf_result_out_of_memory:
return "out of memory";
case cgltf_result_legacy_gltf:
return "legacy GLTF";
case cgltf_result_data_too_short:
return data ? "buffer too short" : "not a GLTF file";
case cgltf_result_unknown_format:
return data ? "unknown resource format" : "not a GLTF file";
default:
return "unknown error";
}
@ -776,29 +785,18 @@ void mergeMeshes(Mesh& target, const Mesh& mesh)
void mergeMeshes(std::vector<Mesh>& meshes, const Settings& settings)
{
size_t write = 0;
for (size_t i = 0; i < meshes.size(); ++i)
{
if (meshes[i].streams.empty())
Mesh& target = meshes[i];
if (target.streams.empty())
continue;
Mesh& target = meshes[write];
if (i != write)
{
Mesh& mesh = meshes[i];
// note: this copy is expensive; we could use move in C++11 or swap manually which is a bit painful...
target = mesh;
mesh.streams.clear();
mesh.indices.clear();
}
size_t target_vertices = target.streams[0].data.size();
size_t target_indices = target.indices.size();
size_t last_merged = i;
for (size_t j = i + 1; j < meshes.size(); ++j)
{
Mesh& mesh = meshes[j];
@ -807,6 +805,7 @@ void mergeMeshes(std::vector<Mesh>& meshes, const Settings& settings)
{
target_vertices += mesh.streams[0].data.size();
target_indices += mesh.indices.size();
last_merged = j;
}
}
@ -815,7 +814,7 @@ void mergeMeshes(std::vector<Mesh>& meshes, const Settings& settings)
target.indices.reserve(target_indices);
for (size_t j = i + 1; j < meshes.size(); ++j)
for (size_t j = i + 1; j <= last_merged; ++j)
{
Mesh& mesh = meshes[j];
@ -830,6 +829,39 @@ void mergeMeshes(std::vector<Mesh>& meshes, const Settings& settings)
assert(target.streams[0].data.size() == target_vertices);
assert(target.indices.size() == target_indices);
}
}
void filterEmptyMeshes(std::vector<Mesh>& meshes)
{
size_t write = 0;
for (size_t i = 0; i < meshes.size(); ++i)
{
Mesh& mesh = meshes[i];
if (mesh.streams.empty())
continue;
if (mesh.streams[0].data.empty())
continue;
if (mesh.type == cgltf_primitive_type_triangles && mesh.indices.empty())
continue;
if (i != write)
{
// the following code is roughly equivalent to meshes[write] = std::move(mesh)
std::vector<Stream> streams;
streams.swap(mesh.streams);
std::vector<unsigned int> indices;
indices.swap(mesh.indices);
meshes[write] = mesh;
meshes[write].streams.swap(streams);
meshes[write].indices.swap(indices);
}
write++;
}
@ -2940,7 +2972,7 @@ void writeImage(std::string& json, std::vector<BufferView>& views, const cgltf_i
if (settings.texture_basis)
{
std::string full_path = getFullPath(image.uri, input_path);
std::string basis_path = getFileName(image.uri) + (settings.texture_ktx2 ? ".ktx" : ".basis");
std::string basis_path = getFileName(image.uri) + (settings.texture_ktx2 ? ".ktx2" : ".basis");
std::string basis_full_path = getFullPath(basis_path.c_str(), output_path);
if (readFile(full_path.c_str(), img_data))
@ -3693,6 +3725,7 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
mergeMeshMaterials(data, meshes);
mergeMeshes(meshes, settings);
filterEmptyMeshes(meshes);
markNeededNodes(data, nodes, meshes, settings);
@ -3710,6 +3743,8 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
processMesh(meshes[i], settings);
}
filterEmptyMeshes(meshes); // some meshes may become empty after processing
if (settings.verbose)
{
printMeshStats(meshes, "output");
@ -4106,6 +4141,29 @@ std::string getBufferSpec(const char* bin_path, size_t bin_size, const char* fal
return json;
}
bool needsDummyBuffers(cgltf_data* data)
{
for (size_t i = 0; i < data->accessors_count; ++i)
{
cgltf_accessor* accessor = &data->accessors[i];
if (accessor->buffer_view && accessor->buffer_view->buffer->data == NULL)
return true;
if (accessor->is_sparse)
{
cgltf_accessor_sparse* sparse = &accessor->sparse;
if (sparse->indices_buffer_view->buffer->data == NULL)
return true;
if (sparse->values_buffer_view->buffer->data == NULL)
return true;
}
}
return false;
}
int gltfpack(const char* input, const char* output, const Settings& settings)
{
cgltf_data* data = 0;
@ -4117,17 +4175,19 @@ int gltfpack(const char* input, const char* output, const Settings& settings)
{
cgltf_options options = {};
cgltf_result result = cgltf_parse_file(&options, input, &data);
result = (result == cgltf_result_success) ? cgltf_validate(data) : result;
result = (result == cgltf_result_success) ? cgltf_load_buffers(&options, data, input) : result;
result = (result == cgltf_result_success) ? cgltf_validate(data) : result;
const char* error = NULL;
if (result != cgltf_result_success)
error = getError(result);
error = getError(result, data);
else if (requiresExtension(data, "KHR_draco_mesh_compression"))
error = "file requires Draco mesh compression support";
else if (requiresExtension(data, "MESHOPT_compression"))
error = "file has already been compressed using gltfpack";
else if (needsDummyBuffers(data))
error = "buffer has no data";
if (error)
{

View File

@ -1,125 +0,0 @@
/*
* Copyright (c) 2010-2018 The Khronos Group Inc.
*
* 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.
*/
/*
* Author: Mark Callow from original code by Georg Kolling
*/
/*
* Converted from ktxint.h + basis_sgd.h by extracting meaningful structures for gltfpack
*/
#pragma once
#include <stdint.h>
#define KTX2_IDENTIFIER_REF { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }
#define KTX2_HEADER_SIZE (80)
typedef enum ktxSupercmpScheme {
KTX_SUPERCOMPRESSION_NONE = 0, /*!< No supercompression. */
KTX_SUPERCOMPRESSION_BASIS = 1, /*!< Basis Universal supercompression. */
KTX_SUPERCOMPRESSION_LZMA = 2, /*!< LZMA supercompression. */
KTX_SUPERCOMPRESSION_ZLIB = 3, /*!< Zlib supercompression. */
KTX_SUPERCOMPRESSION_ZSTD = 4, /*!< ZStd supercompression. */
KTX_SUPERCOMPRESSION_BEGIN_RANGE = KTX_SUPERCOMPRESSION_NONE,
KTX_SUPERCOMPRESSION_END_RANGE = KTX_SUPERCOMPRESSION_ZSTD,
KTX_SUPERCOMPRESSION_BEGIN_VENDOR_RANGE = 0x10000,
KTX_SUPERCOMPRESSION_END_VENDOR_RANGE = 0x1ffff,
KTX_SUPERCOMPRESSION_BEGIN_RESERVED = 0x20000,
} ktxSupercmpScheme;
/**
* @internal
* @~English
* @brief 32-bit KTX 2 index entry.
*/
typedef struct ktxIndexEntry32 {
uint32_t byteOffset; /*!< Offset of item from start of file. */
uint32_t byteLength; /*!< Number of bytes of data in the item. */
} ktxIndexEntry32;
/**
* @internal
* @~English
* @brief 64-bit KTX 2 index entry.
*/
typedef struct ktxIndexEntry64 {
uint64_t byteOffset; /*!< Offset of item from start of file. */
uint64_t byteLength; /*!< Number of bytes of data in the item. */
} ktxIndexEntry64;
/**
* @internal
* @~English
* @brief KTX 2 file header.
*
* See the KTX 2 specification for descriptions.
*/
typedef struct KTX_header2 {
uint8_t identifier[12];
uint32_t vkFormat;
uint32_t typeSize;
uint32_t pixelWidth;
uint32_t pixelHeight;
uint32_t pixelDepth;
uint32_t layerCount;
uint32_t faceCount;
uint32_t levelCount;
uint32_t supercompressionScheme;
ktxIndexEntry32 dataFormatDescriptor;
ktxIndexEntry32 keyValueData;
ktxIndexEntry64 supercompressionGlobalData;
} KTX_header2;
/* This will cause compilation to fail if the struct size doesn't match */
typedef int KTX_header2_SIZE_ASSERT [sizeof(KTX_header2) == KTX2_HEADER_SIZE];
/**
* @internal
* @~English
* @brief KTX 2 level index entry.
*/
typedef struct ktxLevelIndexEntry {
uint64_t byteOffset; /*!< Offset of level from start of file. */
uint64_t byteLength;
/*!< Number of bytes of compressed image data in the level. */
uint64_t uncompressedByteLength;
/*!< Number of bytes of uncompressed image data in the level. */
} ktxLevelIndexEntry;
typedef struct ktxBasisGlobalHeader {
uint32_t globalFlags;
uint16_t endpointCount;
uint16_t selectorCount;
uint32_t endpointsByteLength;
uint32_t selectorsByteLength;
uint32_t tablesByteLength;
uint32_t extendedByteLength;
} ktxBasisGlobalHeader;
// This header is followed by imageCount "slice" descriptions.
// 1, or 2 slices per image (i.e. layer, face & slice).
// These offsets are relative to start of a mip level as given by the
// main levelIndex.
typedef struct ktxBasisSliceDesc {
uint32_t sliceFlags;
uint32_t sliceByteOffset;
uint32_t sliceByteLength;
uint32_t alphaSliceByteOffset;
uint32_t alphaSliceByteLength;
} ktxBasisSliceDesc;