• 11
• 9
• 10
• 9
• 10
• ### Similar Content

• Hello,
We are looking for people to be apart of a team, to help create a horror game we are looking for 3d modelers, coders, artist, animators, fx artist, level designers, and audio design, there will be a payment plan once release of game                                                                                                                                                                                                                                                                                              if your interested come join our discord                                                                                                                                                                                                                                                                         We hope to see you there
https://discord.gg/6rcc6xr
-Epicghost505
• By lxjk
Hi guys,
There are many ways to do light culling in tile-based shading. I've been playing with this idea for a while, and just want to throw it out there.
Because tile frustums are general small compared to light radius, I tried using cone test to reduce false positives introduced by commonly used sphere-frustum test.
On top of that, I use distance to camera rather than depth for near/far test (aka. sliced by spheres).
This method can be naturally extended to clustered light culling as well.
The following image shows the general ideas

Performance-wise I get around 15% improvement over sphere-frustum test. You can also see how a single light performs as the following: from left to right (1) standard rendering of a point light; then tiles passed the test of (2) sphere-frustum test; (3) cone test; (4) spherical-sliced cone test

I put the details in my blog post (https://lxjk.github.io/2018/03/25/Improve-Tile-based-Light-Culling-with-Spherical-sliced-Cone.html), GLSL source code included!

Eric

• I am developing a mini golf game in Scenekit. I have applied dynamic physics body to the ball and static physics body to the grass surface and the brick walls show in this image.
Issue:
When I apply the force to the ball, the ball’s linear and angular speeds change as shown in the graphs.  The ball’s speeds don’t reduce to zero (so that the ball can stop) but remains constant after certain value.
Ball linear speed graph
Ball angular speed graph
Analysis Tests:
When I increase the values to both the rolling friction and the friction, the ball speed is reduced quickly but remains constant after certain value (similar to the above graphs). When I increase the values of the linear damping and the angular damping, the ball speed behavior is same as the point #1. When I set the gravity value to -9.8 m/s2, the ball’s linear speed remains constant after 0.1 m/s. If I reduce the gravity value to -5 m/s2, the ball’s linear speed remains constant after 0.05 m/s. The friction, linear friction, linear damping and angular damping are same throughout the motion of the ball.
There is 1 millimeter overlapping between the ball and the surface of the golf course.
Questions:
From the analysis test #3, I think the gravity is causing the constant ball speed issue. Is my assumption correct? If yes, how can I fix the issue? I can’t remove the gravity field as without the gravity field the ball will not roll along the grass and it will slide. Why the friction and the damping properties are not affecting the ball speed after certain value?
Are there any other physics properties can cause such issue?
From the analysis test #5, are there any chances that the ball is receiving upward push to correct the position of the ball?
Solutions:
If I increase the physics timestep from 60 FPS to 200 FPS, the issue is resolved. I am not able to understand how this change can fix this issue? After reducing the gravity value to -1 m/s2 and physics simulation speed to 4 (4 times fast physics simulation), the issue is fixed. Again, I am not able to understand how this change fix the issue? I would appreciate any suggestions and thoughts on this topic. Thank you.

# OpenGL Loading data from GLTF into OpenGL buffers

## Recommended Posts

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 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

{
string sFileAsString;
stringstream sStream;
ifstream fin(sFilename);

sStream << fin.rdbuf();
fin.close();

sFileAsString = sStream.str();

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?

Test.gltf

Test.bin

##### Share on other sites

So, I played around with the exporter this morning (I also included UV coordinates), and it seems to work, but only for simple models using glDrawElements. I also, made sure to use the same type for the indices, obviously. I think it might just be a problem with the exporter, maybe it only works with models made a specific way. I didn't show it before, but only some of the faces were rendering, I probably selected unsigned byte by accident for the exporter, and was trying to read the data back into a unsigned short vector.

Some of the lines shouldn't be there on the house model below, I'm using the same draw call just a different shader that only uses the color green for the line instead of the texture color, other than that both shaders are the same.

this is how it looks in blender, there are no lines in the doorway of the model.

## Create an account or sign in to comment

You need to be a member in order to leave a comment

## Create an account

Sign up for a new account in our community. It's easy!

Register a new account