I'm trying to load data from a .gltf file into a struct to use to load a .bin file. I don't think there is a problem with how the vertex positions are loaded, but with the indices. This is what I get when drawing with glDrawArrays(GL_LINES, ...):
Also, using glDrawElements gives a similar result. Since it looks like its drawing triangles using the wrong vertices for each face, I'm assuming it needs an index buffer/element buffer. (I'm not sure why there is a line going through part of it, it doesn't look like it belongs to a side, re-exported it without texture coordinates checked, and its not there)
I'm using jsoncpp to load the GLTF file, its format is based on JSON. Here is the gltf struct I'm using, and how I parse the file:
#define GLTF_TARGET_ARRAY_BUFFER (34962)
#define GLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
#define GLTF_COMPONENT_TYPE_BYTE (5120)
#define GLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
#define GLTF_COMPONENT_TYPE_SHORT (5122)
#define GLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
#define GLTF_COMPONENT_TYPE_INT (5124)
#define GLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
#define GLTF_COMPONENT_TYPE_FLOAT (5126)
#define GLTF_COMPONENT_TYPE_DOUBLE (5127)
#define GLTF_PARAMETER_TYPE_BYTE (5120)
#define GLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
#define GLTF_PARAMETER_TYPE_SHORT (5122)
#define GLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
#define GLTF_PARAMETER_TYPE_INT (5124)
#define GLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
#define GLTF_PARAMETER_TYPE_FLOAT (5126)
#define GLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
#define GLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
#define GLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
struct GLTF
{
struct Accessor
{
USHORT bufferView;
USHORT componentType;
UINT count;
vector<INT> max;
vector<INT> min;
string type;
};
vector<Accessor> m_accessors;
struct Asset
{
string copyright;
string generator;
string version;
}m_asset;
struct BufferView
{
UINT buffer;
UINT byteLength;
UINT byteOffset;
UINT target;
};
vector<BufferView> m_bufferViews;
struct Buffer
{
UINT byteLength;
string uri;
};
vector<Buffer> m_buffers;
vector<string> m_Images;
struct Material
{
string name;
string alphaMode;
Vec4 baseColorFactor;
UINT baseColorTexture;
UINT normalTexture;
float metallicFactor;
};
vector<Material> m_materials;
struct Meshes
{
string name;
struct Primitive
{
vector<UINT> attributes_indices;
UINT indices;
UINT material;
};
vector<Primitive> primitives;
};
vector<Meshes> m_meshes;
struct Nodes
{
int mesh;
string name;
Vec3 translation;
};
vector<Nodes> m_nodes;
struct Scenes
{
UINT index;
string name;
vector<UINT> nodes;
};
vector<Scenes> m_scenes;
vector<UINT> samplers;
struct Textures
{
UINT sampler;
UINT source;
};
vector<Textures> m_textures;
map<UINT, string> attributes_map;
map<UINT, string> textures_map;
};
GLTF m_gltf; // This is actually in the Mesh class
bool Mesh::Load(string sFilename)
{
string sFileAsString;
stringstream sStream;
ifstream fin(sFilename);
sStream << fin.rdbuf();
fin.close();
sFileAsString = sStream.str();
Json::Reader r;
Json::Value root;
if (!r.parse(sFileAsString, root))
{
string errors = r.getFormatedErrorMessages();
if (errors != "")
{
// TODO: Log errors
return false;
}
}
if (root.isNull())
return false;
Json::Value object;
Json::Value value;
// Load Accessors array, these are referenced by attributes with their index value
object = root.get("accessors", Json::Value()); // store object with key "accessors", if not found it will default to Json::Value()
if (!object.isNull())
{
for (Json::ValueIterator it = object.begin(); it != object.end(); it++)
{
GLTF::Accessor accessor;
value = (*it).get("bufferView", Json::Value());
if (!value.isNull())
accessor.bufferView = value.asUINT();
else
return false;
value = (*it).get("componentType", Json::Value());
if (!value.isNull())
accessor.componentType = value.asUINT();
else
return false;
value = (*it).get("count", Json::Value());
if (!value.isNull())
accessor.count = value.asUINT();
else
return false;
value = (*it).get("type", Json::Value());
if (!value.isNull())
accessor.type = value.asString();
else
return false;
m_gltf.accessors.push_back(accessor);
}
}
else
return false;
object = root.get("bufferViews", Json::Value());
if(!object.isNull())
{
for (Json::ValueIterator it = object.begin(); it != object.end(); it++)
{
GLTF::BufferView bufferView;
value = (*it).get("buffer", Json::Value());
if(!value.isNull())
bufferView.buffer = value.asUInt();
else
return false;
value = (*it).get("byteLength", Json::Value());
if(!value.isNull())
bufferView.byteLength = value.asUInt();
else
return false;
value = (*it).get("byteOffset", Json::Value());
if(!value.isNull())
bufferView.byteOffset = value.asUInt();
else
return false;
value = (*it).get("target", Json::Value());
if(!value.isNull())
bufferView.target = value.asUInt();
else
return false;
m_gltf.m_bufferViews.push_back(bufferView);
}
}
else
return false;
object = root.get("buffers", Json::Value());
if(!object.isNull())
{
for (Json::ValueIterator it = object.begin(); it != object.end(); it++)
{
GLTF::Buffer buffer;
value = (*it).get("byteLength", Json::Value());
if(!value.isNull())
buffer.byteLength = value.asUInt();
else
return false;
// Store the filename of the .bin file
value = (*it).get("uri", Json::Value());
if(!value.isNull())
buffer.uri = value.asString();
else
return false;
}
}
else
return false;
object = root.get("meshes", Json::Value());
if(!object.isNull())
{
for(Json::ValueIterator it = object.begin(); it != object.end(); it++)
{
GLTF::Meshes mesh;
value = (*it).get("primitives", Json::Value());
for(Json::ValueIterator value_it = value.begin(); value_it != value.end(); value_it++)
{
GLTF::Meshes::Primitive primitive;
Json::Value attributes;
attributes = (*value_it).get("attributes", Json::Value());
vector<string> memberNames = attributes.getMemberNames();
for(size_t i = 0; i < memberNames.size(); i++)
{
Json::Value member;
member = attributes.get(memeberNames[i], Json::Value());
if(!member.isNull())
{
primitive.attributes_indices.push_back(member.asUInt());
m_gltf.attributes_map[member.asUInt()] = memberNames[i]; // Each of these referes to an accessor by indice, so each indice should be unique, and they are when loading a cube
}
else
return false;
}
// Indice of the accessor used for indices
Json::Value indices;
indices = (*value_it).get("indices", Json::Value());
primitive.indices = indices.asUInt();
mesh.primitives.push_back(primitive);
}
m_gltf.m_meshes.push_back(mesh);
}
}
vector<float> vertexData;
vector<USHORT> indiceData;
int vertexBufferSizeTotal = 0;
int elementBufferSizeTotal = 0;
GLTF::Meshes mesh = m_gltf.m_meshes[0];
vector<GLTF::Meshes::Primitive> primitives = mesh.primitives; // trying to make the code easier to read
for (size_t p = 0; p < primitive.size(); p++)
{
vector<UINT> attributes = primitives[p].attributes_indices;
for(size_t a = 0; a < attributes.size(); a++)
{
GLTF::Accessor accessor = m_gltf.m_accessors[attributes[a]];
GLTF::BufferView bufferView = m_gltf.m_bufferViews[accessor.bufferView];
UINT target = bufferView.target;
if(target == GLTF_TARGET_ARRAY_BUFFER)
vertexBufferSizeTotal += bufferView.byteLength;
}
UINT indice = primitives[p].indices;
GLTF::BufferView bufferView = m_gltf.m_bufferViews[indice];
UINT target = bufferView.target;
if(target == GLTF_TARGET_ELEMENT_ARRAY_BUFFER)
elementBufferSizeTotal += bufferView.byteLength;
}
// These have already been generated
glBindVertexArray(g_pGame->m_VAO);
glBindBuffer(GL_ARRAY_BUFFER, g_pGame->m_VBO);
glBufferData(GL_ARRAY_BUFFER, vertexBufferSizeTotal, nullptr, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_pGame->m_EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementBufferSizeTotal, nullptr, GL_STATIC_DRAW);
int offset = 0;
int offset_indice = 0;
for (size_t p = 0; p < primitive.size(); p++)
{
vector<UINT> attributes = primitives[p].attributes_indices;
int pos = sFilename.find_last_of('\\') + 1;
string sFolder = sFilename.substr(0, pos);
for (size_t a = 0; a < attributes.size(); a++)
{
LoadBufferView(sFolder, attributes[a], data, offset);
}
UINT indice = primitives[p].indices;
GLTF::BufferView bufferView_indice = m_gltf.m_bufferViews[indice];
UINT target_indice = bufferView_indice.target;
bool result = LoadBufferView(sFolder, indice, data, offset_indice);
if(!result)
return false;
}
return true;
}
bool Mesh::LoadBufferView(string sFolder, UINT a, vector<float> &vertexData, vector<float> &indiceData, int &offset_indice)
{
ifstream fin;
GLTF::Accessor accessor = m_gltf.m_accessors[a];
GLTF::BufferView bufferView = m_gltf.m_bufferViews[accessor.bufferView];
GLTF::Buffer buffer = m_gltf.m_buffers[bufferView.buffer];
const size_t count = accessor.count;
UINT target = bufferView.target;
int elementSize;
int componentSize;
int numComponents;
string sFilename_bin = sFolder + buffer.uri;
fin.open(sFilename_bin, ios::binary);
if (fin.fail())
{
return false;
}
fin.seekg(bufferView.byteOffset, ios::beg);
switch (accessor.componentType)
{
case GLTF_COMPONENT_TYPE_BYTE:
componentSize = sizeof(GLbyte);
break;
case GLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
componentSize = sizeof(GLubyte);
break;
case GLTF_COMPONENT_TYPE_SHORT:
componentSize = sizeof(GLshort);
break;
case GLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
componentSize = sizeof(GLushort);
break;
case GLTF_COMPONENT_TYPE_INT:
componentSize = sizeof(GLint);
break;
case GLTF_COMPONENT_TYPE_UNSIGNED_INT:
componentSize = sizeof(GLuint);
break;
case GLTF_COMPONENT_TYPE_FLOAT:
componentSize = sizeof(GLfloat);
break;
case GLTF_COMPONENT_TYPE_DOUBLE:
componentSize = sizeof(GLfloat);
break;
default:
componentSize = 0;
break;
}
if (accessor.type == "SCALAR")
numComponents = 1;
else if (accessor.type == "VEC2")
numComponents = 2;
else if (accessor.type == "VEC3")
numComponents = 3;
else if (accessor.type == "VEC4")
numComponents = 4;
else if (accessor.type == "MAT2")
numComponents = 4;
else if (accessor.type == "MAT3")
numComponents = 9;
else if (accessor.type == "MAT4")
numComponents = 16;
else
return false;
vector<float> fSubdata;
// I'm pretty sure this is one of the problems, or related to it. If I use vector<USHORT> only half of the vector if filled, if I use GLubyte, the entire vector is filled, but the data might not be right
vector<GLubyte> nSubdata;
elementSize = (componentSize) * (numComponents);
// Only fill the vector I'm using
if (accessor.type == "SCALAR")
{
nSubdata.resize(count * numComponents);
fin.read(reinterpret_cast<char*>(&nSubdata[0]), count/* * elementSize*/); // I commented this out since I'm not sure which size the .bin is storing the indice values, and I kept getting runtime errors, no matter what type I used for nSubdata
}
else
{
fSubdata.resize(count * numComponents);
fin.read(reinterpret_cast<char*>(&fSubdata[0]), count * elementSize);
}
switch (target)
{
case GLTF_TARGET_ARRAY_BUFFER:
{
vertexData.insert(vertexData.end(), fSubdata.begin(), fSubdata.end());
glBindBuffer(GL_ARRAY_BUFFER, g_pGame->m_VBO);
glBufferSubData(GL_ARRAY_BUFFER, offset, fSubdata.size() * componentSize, &fSubdata[0]);
int attribute_index = 0; // I'm only loading vertex positions, the only attribute stored in the files for now
glEnableVertexAttribArray(attribute_index);
glVertexAttribPointer(0, numComponents, GL_FLOAT, GL_FALSE, componentSize * numComponents, (void*)(offset));
}break;
case GLTF_TARGET_ELEMENT_ARRAY_BUFFER:
{
indiceData.insert(indiceData.end(), nSubdata.begin(), nSubdata.end());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_pGame->m_EBO);
// This is another area where I'm not sure of the correct values, but if componentSize is the correct size for the type being used it should be correct glBufferSubData is expecting the size in bytes, right?
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, nSubdata.size() * componentSize, &nSubdata[0]);
}break;
default:
return false;
}
if (accessor.type == "SCALAR")
offset += nSubdata.size() * componentSize;
else
offset += fSubdata.size() * componentSize;
fin.close();
return true;
}
these are the draw calls, I only use one at a time, but neither is currently display properly, g_pGame->m_indices is the same as indiceData vector, and vertexCount contains the correct vertex count, but I forgot to copy the lines of code containing where I set them, which is at the end of Mesh::Load(), I double checked the values to make sure.
glBindVertexArray(g_pGame->m_VAO);
glDrawElements(GL_LINES, g_pGame->m_indices.size(), GL_UNSIGNED_BYTE, (void*)0); // Only shows with GL_UNSIGNED_BYTE
glDrawArrays(GL_LINES, 0, g_pGame->m_vertexCount);
So, I'm asking what type should I use for the indices? it doesn't seem to be unsigned short, which is what I selected with the Khronos Group Exporter for blender. Also, am I reading part or all of the .bin file wrong?