Add support for 32-bit indices.
This was way overdue. Breaking up large meshes into many 65535-vertex primitives can save a few bytes, but it's really a lot of complication for minor benefit. With this change the user can force short or long indices, and the default is to use shorts for smaller meshes and longs for longer.
This commit is contained in:
parent
0deaf186a8
commit
608c6f1797
|
@ -39,7 +39,7 @@
|
|||
#include "glTF/SkinData.h"
|
||||
#include "glTF/TextureData.h"
|
||||
|
||||
typedef unsigned short TriangleIndex;
|
||||
typedef uint32_t TriangleIndex;
|
||||
|
||||
extern bool verboseOutput;
|
||||
|
||||
|
@ -290,7 +290,11 @@ ModelData *Raw2Gltf(
|
|||
}
|
||||
|
||||
std::vector<RawModel> materialModels;
|
||||
raw.CreateMaterialModels(materialModels, (1 << (sizeof(TriangleIndex) * 8)), options.keepAttribs, true);
|
||||
raw.CreateMaterialModels(
|
||||
materialModels,
|
||||
options.useLongIndices == UseLongIndicesOptions::NEVER,
|
||||
options.keepAttribs,
|
||||
true);
|
||||
|
||||
if (verboseOutput) {
|
||||
fmt::printf("%7d vertices\n", raw.GetVertexCount());
|
||||
|
@ -646,20 +650,6 @@ ModelData *Raw2Gltf(
|
|||
combine, tag, outputHasAlpha);
|
||||
};
|
||||
|
||||
// acquire derived texture of two RawTextureUsage as *TextData, or nullptr if neither exists
|
||||
auto merge3Tex = [&](
|
||||
const std::string tag,
|
||||
RawTextureUsage u1,
|
||||
RawTextureUsage u2,
|
||||
RawTextureUsage u3,
|
||||
const pixel_merger &combine,
|
||||
bool outputHasAlpha
|
||||
) -> std::shared_ptr<TextureData> {
|
||||
return getDerivedTexture(
|
||||
{ material.textures[u1], material.textures[u2], material.textures[u3] },
|
||||
combine, tag, outputHasAlpha);
|
||||
};
|
||||
|
||||
std::shared_ptr<PBRMetallicRoughness> pbrMetRough;
|
||||
if (options.usePBRMetRough) {
|
||||
// albedo is a basic texture, no merging needed
|
||||
|
@ -744,17 +734,14 @@ ModelData *Raw2Gltf(
|
|||
materialsByName[materialHash(material)] = mData;
|
||||
}
|
||||
|
||||
for (size_t surfaceIndex = 0; surfaceIndex < materialModels.size(); surfaceIndex++) {
|
||||
const RawModel &surfaceModel = materialModels[surfaceIndex];
|
||||
|
||||
for (const auto &surfaceModel : materialModels) {
|
||||
assert(surfaceModel.GetSurfaceCount() == 1);
|
||||
const RawSurface &rawSurface = surfaceModel.GetSurface(0);
|
||||
const int surfaceId = rawSurface.id;
|
||||
const long surfaceId = rawSurface.id;
|
||||
|
||||
const RawMaterial &rawMaterial = surfaceModel.GetMaterial(surfaceModel.GetTriangle(0).materialIndex);
|
||||
const MaterialData &mData = require(materialsByName, materialHash(rawMaterial));
|
||||
|
||||
|
||||
MeshData *mesh = nullptr;
|
||||
auto meshIter = meshBySurfaceId.find(surfaceId);
|
||||
if (meshIter != meshBySurfaceId.end()) {
|
||||
|
@ -770,6 +757,11 @@ ModelData *Raw2Gltf(
|
|||
mesh = meshPtr.get();
|
||||
}
|
||||
|
||||
bool useLongIndices =
|
||||
(options.useLongIndices == UseLongIndicesOptions::ALWAYS)
|
||||
|| (options.useLongIndices == UseLongIndicesOptions::AUTO
|
||||
&& surfaceModel.GetVertexCount() > 65535);
|
||||
|
||||
std::shared_ptr<PrimitiveData> primitive;
|
||||
if (options.useDraco) {
|
||||
int triangleCount = surfaceModel.GetTriangleCount();
|
||||
|
@ -786,13 +778,13 @@ ModelData *Raw2Gltf(
|
|||
dracoMesh->SetFace(draco::FaceIndex(ii), face);
|
||||
}
|
||||
|
||||
AccessorData &indexes = *gltf->accessors.hold(new AccessorData(GLT_USHORT));
|
||||
AccessorData &indexes = *gltf->accessors.hold(new AccessorData(useLongIndices ? GLT_UINT : GLT_USHORT));
|
||||
indexes.count = 3 * triangleCount;
|
||||
primitive.reset(new PrimitiveData(indexes, mData, dracoMesh));
|
||||
} else {
|
||||
const AccessorData &indexes = *gltf->AddAccessorWithView(
|
||||
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ELEMENT_ARRAY_BUFFER),
|
||||
GLT_USHORT, getIndexArray(surfaceModel));
|
||||
useLongIndices ? GLT_UINT : GLT_USHORT, getIndexArray(surfaceModel));
|
||||
primitive.reset(new PrimitiveData(indexes, mData));
|
||||
};
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ struct ComponentType {
|
|||
};
|
||||
|
||||
const ComponentType CT_USHORT = {ComponentType::GL_UNSIGNED_SHORT, 2};
|
||||
const ComponentType CT_UINT = {ComponentType::GL_UNSIGNED_INT, 4};
|
||||
const ComponentType CT_FLOAT = {ComponentType::GL_FLOAT, 4};
|
||||
|
||||
// Map our low-level data types for glTF output
|
||||
|
@ -64,7 +65,7 @@ struct GLType {
|
|||
unsigned int byteStride() const { return componentType.size * count; }
|
||||
|
||||
void write(uint8_t *buf, const float scalar) const { *((float *) buf) = scalar; }
|
||||
void write(uint8_t *buf, const uint16_t scalar) const { *((uint16_t *) buf) = scalar; }
|
||||
void write(uint8_t *buf, const uint32_t scalar) const { *((uint32_t *) buf) = scalar; }
|
||||
|
||||
template<class T, int d>
|
||||
void write(uint8_t *buf, const mathfu::Vector<T, d> &vector) const {
|
||||
|
@ -101,6 +102,7 @@ struct GLType {
|
|||
|
||||
const GLType GLT_FLOAT = {CT_FLOAT, 1, "SCALAR"};
|
||||
const GLType GLT_USHORT = {CT_USHORT, 1, "SCALAR"};
|
||||
const GLType GLT_UINT = {CT_UINT, 1, "SCALAR"};
|
||||
const GLType GLT_VEC2F = {CT_FLOAT, 2, "VEC2"};
|
||||
const GLType GLT_VEC3F = {CT_FLOAT, 3, "VEC3"};
|
||||
const GLType GLT_VEC4F = {CT_FLOAT, 4, "VEC4"};
|
||||
|
|
|
@ -327,15 +327,15 @@ void RawModel::Condense()
|
|||
void RawModel::TransformGeometry(ComputeNormalsOption normals)
|
||||
{
|
||||
switch(normals) {
|
||||
case NEVER:
|
||||
case ComputeNormalsOption::NEVER:
|
||||
break;
|
||||
case MISSING:
|
||||
case ComputeNormalsOption::MISSING:
|
||||
if ((vertexAttributes & RAW_VERTEX_ATTRIBUTE_NORMAL) != 0) {
|
||||
break;
|
||||
}
|
||||
// otherwise fall through
|
||||
case BROKEN:
|
||||
case ALWAYS:
|
||||
case ComputeNormalsOption::BROKEN:
|
||||
case ComputeNormalsOption::ALWAYS:
|
||||
size_t computedNormalsCount = this->CalculateNormals(normals == ComputeNormalsOption::BROKEN);
|
||||
vertexAttributes |= RAW_VERTEX_ATTRIBUTE_NORMAL;
|
||||
|
||||
|
@ -395,7 +395,7 @@ struct TriangleModelSortNeg
|
|||
};
|
||||
|
||||
void RawModel::CreateMaterialModels(
|
||||
std::vector<RawModel> &materialModels, const int maxModelVertices, const int keepAttribs, const bool forceDiscrete) const
|
||||
std::vector<RawModel> &materialModels, bool shortIndices, const int keepAttribs, const bool forceDiscrete) const
|
||||
{
|
||||
// Sort all triangles based on material first, then surface, then first vertex index.
|
||||
std::vector<RawTriangle> sortedTriangles;
|
||||
|
@ -467,7 +467,7 @@ void RawModel::CreateMaterialModels(
|
|||
}
|
||||
|
||||
if (i == 0 ||
|
||||
model->GetVertexCount() > maxModelVertices - 3 ||
|
||||
(shortIndices && model->GetVertexCount() >= 0xFFFE) ||
|
||||
sortedTriangles[i].materialIndex != sortedTriangles[i - 1].materialIndex ||
|
||||
(sortedTriangles[i].surfaceIndex != sortedTriangles[i - 1].surfaceIndex &&
|
||||
(forceDiscrete || surfaces[sortedTriangles[i].surfaceIndex].discrete ||
|
||||
|
|
|
@ -17,13 +17,19 @@
|
|||
/**
|
||||
* The variuos situations in which the user may wish for us to (re-)compute normals for our vertices.
|
||||
*/
|
||||
enum ComputeNormalsOption {
|
||||
enum class ComputeNormalsOption {
|
||||
NEVER, // do not ever compute any normals (results in broken glTF for some sources)
|
||||
BROKEN, // replace zero-length normals in any mesh that has a normal layer
|
||||
MISSING, // if a mesh lacks normals, compute them all
|
||||
ALWAYS // compute a new normal for every vertex, obliterating whatever may have been there before
|
||||
};
|
||||
|
||||
enum class UseLongIndicesOptions {
|
||||
NEVER, // only ever use 16-bit indices
|
||||
AUTO, // use shorts or longs depending on vertex count
|
||||
ALWAYS, // only ever use 32-bit indices
|
||||
};
|
||||
|
||||
/**
|
||||
* User-supplied options that dictate the nature of the glTF being generated.
|
||||
*/
|
||||
|
@ -52,6 +58,8 @@ struct GltfOptions
|
|||
bool useBlendShapeTangents { false };
|
||||
/** When to compute vertex normals from geometry. */
|
||||
ComputeNormalsOption computeNormals = ComputeNormalsOption::BROKEN;
|
||||
/** When to use 32-bit indices. */
|
||||
UseLongIndicesOptions useLongIndices = UseLongIndicesOptions::AUTO;
|
||||
};
|
||||
|
||||
enum RawVertexAttribute
|
||||
|
@ -470,7 +478,7 @@ public:
|
|||
// Multiple surfaces with the same material will turn into a single model.
|
||||
// However, surfaces that are marked as 'discrete' will turn into separate models.
|
||||
void CreateMaterialModels(
|
||||
std::vector<RawModel> &materialModels, const int maxModelVertices, const int keepAttribs, const bool forceDiscrete) const;
|
||||
std::vector<RawModel> &materialModels, bool shortIndices, const int keepAttribs, const bool forceDiscrete) const;
|
||||
|
||||
private:
|
||||
Vec3f getFaceNormal(int verts[3]) const;
|
||||
|
|
25
src/main.cpp
25
src/main.cpp
|
@ -75,6 +75,9 @@ int main(int argc, char *argv[])
|
|||
(
|
||||
"blend-shape-tangents", "Include blend shape tangents, if reported present by the FBX SDK.",
|
||||
cxxopts::value<bool>(gltfOptions.useBlendShapeTangents))
|
||||
(
|
||||
"long-indices", "Whether to use 32-bit indices (never|auto|always).",
|
||||
cxxopts::value<std::vector<std::string>>())
|
||||
(
|
||||
"compute-normals", "When to compute normals for vertices (never|broken|missing|always).",
|
||||
cxxopts::value<std::vector<std::string>>())
|
||||
|
@ -137,16 +140,30 @@ Copyright (c) 2016-2017 Oculus VR, LLC.
|
|||
fmt::printf("Suppressing --flip-v transformation of texture coordinates.\n");
|
||||
}
|
||||
|
||||
for (const std::string &choice : options["long-indices"].as<std::vector<std::string>>()) {
|
||||
if (choice == "never") {
|
||||
gltfOptions.useLongIndices = UseLongIndicesOptions::NEVER;
|
||||
} else if (choice == "auto") {
|
||||
gltfOptions.useLongIndices = UseLongIndicesOptions::AUTO;
|
||||
} else if (choice == "always") {
|
||||
gltfOptions.useLongIndices = UseLongIndicesOptions::ALWAYS;
|
||||
} else {
|
||||
fmt::printf("Unknown --long-indices: %s\n", choice);
|
||||
fmt::printf(options.help());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.count("compute-normals") > 0) {
|
||||
for (const std::string &choice : options["compute-normals"].as<std::vector<std::string>>()) {
|
||||
if (choice == "never") {
|
||||
gltfOptions.computeNormals = NEVER;
|
||||
gltfOptions.computeNormals = ComputeNormalsOption::NEVER;
|
||||
} else if (choice == "broken") {
|
||||
gltfOptions.computeNormals = BROKEN;
|
||||
gltfOptions.computeNormals = ComputeNormalsOption::BROKEN;
|
||||
} else if (choice == "missing") {
|
||||
gltfOptions.computeNormals = MISSING;
|
||||
gltfOptions.computeNormals = ComputeNormalsOption::MISSING;
|
||||
} else if (choice == "always") {
|
||||
gltfOptions.computeNormals = ALWAYS;
|
||||
gltfOptions.computeNormals = ComputeNormalsOption::ALWAYS;
|
||||
} else {
|
||||
fmt::printf("Unknown --compute-normals: %s\n", choice);
|
||||
fmt::printf(options.help());
|
||||
|
|
Loading…
Reference in New Issue