示例#1
0
void GLCpuPosInstancedArraysBench::setupSingleVbo(const GrGLInterface* gl,
                                                  const SkMatrix* viewMatrices) {
    // Constants for our various shader programs
    Vertex vertices[kVerticesPerTri * kNumTri];
    for (uint32_t i = 0; i < kNumTri; i++) {
        Vertex* v = &vertices[i * kVerticesPerTri];
        v[0].fPositions.set(-1.0f, -1.0f);
        v[1].fPositions.set( 1.0f, -1.0f);
        v[2].fPositions.set( 1.0f,  1.0f);

        SkPoint* position = reinterpret_cast<SkPoint*>(v);
        viewMatrices[i].mapPointsWithStride(position, sizeof(Vertex), kVerticesPerTri);

        // set colors
        float color = i == kNumTri - 1 ? 1.0f : 0.0f;
        for (uint32_t j = 0; j < kVerticesPerTri; j++) {
            uint32_t offset = 0;
            v->fColors[offset++] = color; v->fColors[offset++] = 0.0f; v->fColors[offset++] = 0.0f;
            v++;
        }
    }

    GrGLuint vbo;
    // setup VBO
    GR_GL_CALL(gl, GenBuffers(1, &vbo));
    GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, vbo));
    GR_GL_CALL(gl, EnableVertexAttribArray(0));
    GR_GL_CALL(gl, EnableVertexAttribArray(1));
    GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex),
                                       (GrGLvoid*)0));
    GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex),
                                       (GrGLvoid*)(sizeof(SkPoint))));
    GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(vertices), vertices, GR_GL_STATIC_DRAW));
    fBuffers.push_back(vbo);
}
示例#2
0
GrGLBuffer::GrGLBuffer(GrGLGpu* gpu, size_t size, GrGpuBufferType intendedType,
                       GrAccessPattern accessPattern, const void* data)
        : INHERITED(gpu, size, intendedType, accessPattern)
        , fIntendedType(intendedType)
        , fBufferID(0)
        , fUsage(gr_to_gl_access_pattern(intendedType, accessPattern))
        , fGLSizeInBytes(0)
        , fHasAttachedToTexture(false) {
    GL_CALL(GenBuffers(1, &fBufferID));
    if (fBufferID) {
        GrGLenum target = gpu->bindBuffer(fIntendedType, this);
        CLEAR_ERROR_BEFORE_ALLOC(gpu->glInterface());
        // make sure driver can allocate memory for this buffer
        GL_ALLOC_CALL(gpu->glInterface(), BufferData(target,
                                                     (GrGLsizeiptr) size,
                                                     data,
                                                     fUsage));
        if (CHECK_ALLOC_ERROR(gpu->glInterface()) != GR_GL_NO_ERROR) {
            GL_CALL(DeleteBuffers(1, &fBufferID));
            fBufferID = 0;
        } else {
            fGLSizeInBytes = size;
        }
    }
    VALIDATE();
    this->registerWithCache(SkBudgeted::kYes);
    if (!fBufferID) {
        this->resourcePriv().removeScratchKey();
    }
}
示例#3
0
static uint32_t setup_quad_index_buffer(const GrGLInterface* gl) {
    static const int kMaxQuads = 1;//1 << 12; // max possible: (1 << 14) - 1;
    GR_STATIC_ASSERT(4 * kMaxQuads <= 65535);
    static const uint16_t kPattern[] = { 0, 1, 2, 0, 2, 3 };
    static const int kPatternSize = 6;
    static const int kVertCount = 4;
    static const int kIndicesCount = kPatternSize * kMaxQuads;
    int size = kPatternSize * kMaxQuads * sizeof(uint16_t);

    uint16_t* data = SkNEW_ARRAY(uint16_t, kMaxQuads * kPatternSize);

    for (int i = 0; i < kMaxQuads; ++i) {
        int baseIdx = i * kPatternSize;
        uint16_t baseVert = (uint16_t)(i * kVertCount);
        for (int j = 0; j < kPatternSize; ++j) {
            data[baseIdx+j] = baseVert + kPattern[j];
        }
    }

    GrGLuint quadIBO;
    GR_GL_CALL(gl, GenBuffers(1, &quadIBO));
    GR_GL_CALL(gl, BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, quadIBO));
    GR_GL_CALL(gl, BufferData(GR_GL_ELEMENT_ARRAY_BUFFER, size, data, GR_GL_STATIC_DRAW));

    SkDELETE_ARRAY(data);
    return kIndicesCount;
}
示例#4
0
void GLCpuPosInstancedArraysBench::setupInstanceVbo(const GrGLInterface* gl,
                                                    const SkMatrix* viewMatrices) {
    // We draw all of the instances at a single place because we aren't allowed to have per vertex
    // per instance attributes
    SkPoint positions[kVerticesPerTri];
    positions[0].set(-1.0f, -1.0f);
    positions[1].set( 1.0f, -1.0f);
    positions[2].set( 1.0f,  1.0f);
    viewMatrices[0].mapPointsWithStride(positions, sizeof(SkPoint), kVerticesPerTri);

    // setup colors so we can detect we are actually drawing instances(the last triangle will be
    // a different color)
    GrGLfloat colors[kVerticesPerTri * kNumTri];
    for (uint32_t i = 0; i < kNumTri; i++) {
        // set colors
        uint32_t offset = i * kVerticesPerTri;
        float color = i == kNumTri - 1 ? 1.0f : 0.0f;
        colors[offset++] = color; colors[offset++] = 0.0f; colors[offset++] = 0.0f;
    }

    GrGLuint posVBO;
    // setup position VBO
    GR_GL_CALL(gl, GenBuffers(1, &posVBO));
    GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, posVBO));
    GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(positions), positions, GR_GL_STATIC_DRAW));
    GR_GL_CALL(gl, EnableVertexAttribArray(0));
    GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, 2 * sizeof(GrGLfloat),
                                       (GrGLvoid*)0));

    // setup color VBO
    GrGLuint instanceVBO;
    GR_GL_CALL(gl, GenBuffers(1, &instanceVBO));
    GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, instanceVBO));
    GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(colors), colors, GR_GL_STATIC_DRAW));
    GR_GL_CALL(gl, EnableVertexAttribArray(1));
    GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, 3 * sizeof(GrGLfloat),
                                       (GrGLvoid*)0));
    GR_GL_CALL(gl, VertexAttribDivisor(1, 1));
    fBuffers.push_back(posVBO);
    fBuffers.push_back(instanceVBO);
}
示例#5
0
void GLCpuPosInstancedArraysBench::setupDoubleVbo(const GrGLInterface* gl,
                                                  const SkMatrix* viewMatrices) {
    // Constants for our various shader programs
    SkPoint positions[kVerticesPerTri * kNumTri];
    GrGLfloat colors[kVerticesPerTri * kNumTri * 3];
    for (uint32_t i = 0; i < kNumTri; i++) {
        SkPoint* position = &positions[i * kVerticesPerTri];
        position[0].set(-1.0f, -1.0f);
        position[1].set( 1.0f, -1.0f);
        position[2].set( 1.0f,  1.0f);
        viewMatrices[i].mapPointsWithStride(position, sizeof(SkPoint), kVerticesPerTri);

        // set colors
        float color = i == kNumTri - 1 ? 1.0f : 0.0f;
        uint32_t offset = i * kVerticesPerTri * 3;
        for (uint32_t j = 0; j < kVerticesPerTri; j++) {
            colors[offset++] = color; colors[offset++] = 0.0f; colors[offset++] = 0.0f;
        }
    }

    GrGLuint posVBO, colorVBO;
    // setup position VBO
    GR_GL_CALL(gl, GenBuffers(1, &posVBO));
    GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, posVBO));
    GR_GL_CALL(gl, EnableVertexAttribArray(0));
    GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, 2 * sizeof(GrGLfloat),
                                       (GrGLvoid*)0));
    GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(positions), positions, GR_GL_STATIC_DRAW));

    // setup color VBO
    GR_GL_CALL(gl, GenBuffers(1, &colorVBO));
    GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, colorVBO));
    GR_GL_CALL(gl, EnableVertexAttribArray(1));
    GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, 3 * sizeof(GrGLfloat),
                                       (GrGLvoid*)0));
    GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(colors), colors, GR_GL_STATIC_DRAW));

    fBuffers.push_back(posVBO);
    fBuffers.push_back(colorVBO);
}
void GL3CharacterRenderer::buildHead() {
	GL(GenVertexArrays(1, &headVao));
	GL(GenBuffers(1, &headVbo));
	GL(BindVertexArray(headVao));
	GL(BindBuffer(GL_ARRAY_BUFFER, headVbo));
	GL(VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 40, 0));
	GL(VertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 40, (void *) 12));
	GL(VertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 40, (void *) 24));
	GL(EnableVertexAttribArray(0));
	GL(EnableVertexAttribArray(1));
	GL(EnableVertexAttribArray(2));
	GL(BindVertexArray(0));

	VertexData vertexData[36];
	int vertexIndices[6] = {0, 1, 2, 0, 2, 3};
	int index = 0;
	for (int d = 0; d < 6; d++) {
		vec3f normal(0.0);
		normal[d % 3] = DIRS[d][d % 3];
		vec3f vertices[4];
		for (int i = 0; i < 4; i++) {
			vertices[i] = vec3f(
				(DIR_QUAD_CORNER_CYCLES_3D[d][i][0] - 0.5f) * HEAD_SIZE[0],
				(DIR_QUAD_CORNER_CYCLES_3D[d][i][1] - 0.5f) * HEAD_SIZE[1],
				(DIR_QUAD_CORNER_CYCLES_3D[d][i][2] - 0.5f) * HEAD_SIZE[2] + HEAD_Z_OFFSET
			) * (1.0f / RESOLUTION);
		}
		for (int j = 0; j < 6; j++) {
			vertexData[index].xyz[0] = vertices[vertexIndices[j]][0];
			vertexData[index].xyz[1] = vertices[vertexIndices[j]][1];
			vertexData[index].xyz[2] = vertices[vertexIndices[j]][2];
			vertexData[index].nxyz[0] = normal[0];
			vertexData[index].nxyz[1] = normal[1];
			vertexData[index].nxyz[2] = normal[2];
			vertexData[index].rgba[0] = CHARACTER_COLOR[0];
			vertexData[index].rgba[1] = CHARACTER_COLOR[1];
			vertexData[index].rgba[2] = CHARACTER_COLOR[2];
			vertexData[index].rgba[3] = 1.0f;
			index++;
		}
	}

	GL(BufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW));
	GL(BindBuffer(GL_ARRAY_BUFFER, 0));
}
示例#7
0
GrGLBuffer::GrGLBuffer(GrGLGpu* gpu, size_t size, GrBufferType intendedType,
                       GrAccessPattern accessPattern, bool cpuBacked, const void* data)
    : INHERITED(gpu, size, intendedType, accessPattern, cpuBacked),
      fCPUData(nullptr),
      fIntendedType(intendedType),
      fBufferID(0),
      fSizeInBytes(size),
      fUsage(gr_to_gl_access_pattern(intendedType, accessPattern)),
      fGLSizeInBytes(0),
      fHasAttachedToTexture(false) {
    if (this->isCPUBacked()) {
        // Core profile uses vertex array objects, which disallow client side arrays.
        SkASSERT(!gpu->glCaps().isCoreProfile());
        if (gpu->caps()->mustClearUploadedBufferData()) {
            fCPUData = sk_calloc_throw(fSizeInBytes);
        } else {
            fCPUData = sk_malloc_flags(fSizeInBytes, SK_MALLOC_THROW);
        }
        if (data) {
            memcpy(fCPUData, data, fSizeInBytes);
        }
    } else {
        GL_CALL(GenBuffers(1, &fBufferID));
        if (fBufferID) {
            GrGLenum target = gpu->bindBuffer(fIntendedType, this);
            CLEAR_ERROR_BEFORE_ALLOC(gpu->glInterface());
            // make sure driver can allocate memory for this buffer
            GL_ALLOC_CALL(gpu->glInterface(), BufferData(target,
                                                         (GrGLsizeiptr) fSizeInBytes,
                                                         data,
                                                         fUsage));
            if (CHECK_ALLOC_ERROR(gpu->glInterface()) != GR_GL_NO_ERROR) {
                GL_CALL(DeleteBuffers(1, &fBufferID));
                fBufferID = 0;
            } else {
                fGLSizeInBytes = fSizeInBytes;
            }
        }
    }
    VALIDATE();
    this->registerWithCache(SkBudgeted::kYes);
}
示例#8
0
/*******************************************************************************
 *
 * Private functions.
 *
 ******************************************************************************/
int
rb_ogl3_create_buffer
  (struct rb_context* ctxt,
   const struct rb_ogl3_buffer_desc* desc,
   const void* init_data,
   struct rb_buffer** out_buffer)
{
  struct rb_buffer* buffer = NULL;

  if(!ctxt
  || !desc
  || !out_buffer
  || (desc->target == RB_OGL3_NB_BUFFER_TARGETS)
  || (desc->usage == RB_USAGE_IMMUTABLE && init_data == NULL))
    return -1;

  buffer = MEM_ALLOC(ctxt->allocator, sizeof(struct rb_buffer));
  if(!buffer)
    return -1;
  ref_init(&buffer->ref);
  RB(context_ref_get(ctxt));
  buffer->ctxt = ctxt;

  buffer->target = rb_to_ogl3_buffer_target(desc->target);
  buffer->usage = rb_to_ogl3_usage(desc->usage);
  buffer->size = (GLsizei)desc->size;
  buffer->binding = desc->target;

  OGL(GenBuffers(1, &buffer->name));
  OGL(BindBuffer(buffer->target, buffer->name));
  OGL(BufferData(buffer->target, buffer->size, init_data, buffer->usage));
  OGL(BindBuffer
    (buffer->target, 
     ctxt->state_cache.buffer_binding[buffer->binding]));

  *out_buffer = buffer;
  return 0;
}
示例#9
0
void GLVec4ScalarBench::setupSingleVbo(const GrGLInterface* gl, const SkMatrix* viewMatrices) {
    // triangles drawn will alternate between the top-right half of the screen and the bottom-left
    // half of the screen
    Vertex vertices[kVerticesPerTri * kNumTriPerDraw];
    for (uint32_t i = 0; i < kNumTriPerDraw; i++) {
        Vertex* v = &vertices[i * kVerticesPerTri];
        if (i % 2 == 0) {
            v[0].fPositions.set(-1.0f, -1.0f);
            v[1].fPositions.set( 1.0f, -1.0f);
            v[2].fPositions.set( 1.0f,  1.0f);
        } else {
            v[0].fPositions.set(-1.0f, -1.0f);
            v[1].fPositions.set( 1.0f, 1.0f);
            v[2].fPositions.set( -1.0f, 1.0f);
        }
        SkPoint* position = reinterpret_cast<SkPoint*>(v);
        viewMatrices[i].mapPointsWithStride(position, sizeof(Vertex), kVerticesPerTri);

        GrGLfloat color[3] = {1.0f, 0.0f, 1.0f};
        for (uint32_t j = 0; j < kVerticesPerTri; j++) {
            v->fColors[0] = color[0];
            v->fColors[1] = color[1];
            v->fColors[2] = color[2];
            v++;
        }
    }

    GR_GL_CALL(gl, GenBuffers(1, &fVboId));
    GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, fVboId));
    GR_GL_CALL(gl, EnableVertexAttribArray(0));
    GR_GL_CALL(gl, EnableVertexAttribArray(1));
    GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex),
                                       (GrGLvoid*)0));
    GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex),
                                       (GrGLvoid*)(sizeof(SkPoint))));
    GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(vertices), vertices, GR_GL_STATIC_DRAW));
}
示例#10
0
void GLGpuPosInstancedArraysBench::setup(const GrGLInterface* gl) {
    setup_framebuffer(gl, kScreenWidth, kScreenHeight);

    // compile and use shaders
    GrGLint shaderProgram = compile_shader(gl, gpu_vertex_shader, fragment_shader);

    // translations
    int index = 0;
    GrGLfloat viewMatrices[fNumQuads * fSkMatrixNumCells];
    setup_matrices(fNumQuads, [&index, &viewMatrices](const SkMatrix& m) {
        GrGLGetMatrix<3>(&viewMatrices[index], m);
        index += fSkMatrixNumCells;
    });

    // Constants for our various shader programs
    GrGLfloat quad_vertices[] = {
            // Positions // Colors
            -1.0f,  1.0f,  1.0f, 0.0f, 0.0f,
             1.0f, -1.0f,  0.0f, 1.0f, 0.0f,
            -1.0f, -1.0f,  0.0f, 0.0f, 1.0f,

            -1.0f,  1.0f,  1.0f, 0.0f, 0.0f,
             1.0f, -1.0f,  0.0f, 1.0f, 0.0f,
             1.0f,  1.0f,  0.0f, 1.0f, 1.0f
    };

    // update vertex data
    GrGLuint quadVAO, quadVBO;
    GR_GL_CALL(gl, GenVertexArrays(1, &quadVAO));
    GR_GL_CALL(gl, GenBuffers(1, &quadVBO));
    GR_GL_CALL(gl, BindVertexArray(quadVAO));
    GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, quadVBO));
    GR_GL_CALL(gl, EnableVertexAttribArray(0));
    GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, 5 * sizeof(GrGLfloat), (GrGLvoid*)0));
    GR_GL_CALL(gl, EnableVertexAttribArray(1));
    GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, 5 * sizeof(GrGLfloat), (GrGLvoid*)(2 * sizeof(GrGLfloat))));
    GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(quad_vertices), quad_vertices, GR_GL_STATIC_DRAW));

    // Also set instance data
    GrGLuint instanceVBO;
    GR_GL_CALL(gl, GenBuffers(1, &instanceVBO));
    GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, instanceVBO));
    GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(GrGLfloat) * fSkMatrixNumCells * fNumQuads,
                              &viewMatrices[0], GR_GL_STATIC_DRAW));
    GR_GL_CALL(gl, EnableVertexAttribArray(2));
    GR_GL_CALL(gl, EnableVertexAttribArray(3));
    GR_GL_CALL(gl, EnableVertexAttribArray(4));
    GR_GL_CALL(gl, VertexAttribPointer(2, 3, GR_GL_FLOAT, GR_GL_FALSE, 9 * sizeof(GrGLfloat), (GrGLvoid*)0));
    GR_GL_CALL(gl, VertexAttribPointer(3, 3, GR_GL_FLOAT, GR_GL_FALSE, 9 * sizeof(GrGLfloat), (GrGLvoid*)(3 * sizeof(GrGLfloat))));
    GR_GL_CALL(gl, VertexAttribPointer(4, 3, GR_GL_FLOAT, GR_GL_FALSE, 9 * sizeof(GrGLfloat), (GrGLvoid*)(6 * sizeof(GrGLfloat))));
    GR_GL_CALL(gl, VertexAttribDivisor(2, 1));
    GR_GL_CALL(gl, VertexAttribDivisor(3, 1));
    GR_GL_CALL(gl, VertexAttribDivisor(4, 1));

    // draw
    GR_GL_CALL(gl, ClearColor(0.03f, 0.03f, 0.03f, 1.0f));
    GR_GL_CALL(gl, Clear(GR_GL_COLOR_BUFFER_BIT));

    // set us up to draw
    GR_GL_CALL(gl, UseProgram(shaderProgram));
    GR_GL_CALL(gl, BindVertexArray(quadVAO));
}
示例#11
0
GLuint GLInterfaceWrapper::CreateBuffer() const {
  GLuint buffer = 0;
  GenBuffers( 1, &buffer );
  return buffer;
}
示例#12
0
bool Animation::Mesh::InitFromScene(const aiScene* scene, const std::string& filename)
{
    Clear();
    GenBuffers();

    m_meshes.resize(scene->mNumMeshes);

    std::vector<unsigned int> indices;
    std::vector<VertexInfo> vertices;

    m_numVertices = 0;
    m_numIndices = 0;

    Assign(m_GlobalInverseTransform, scene->mRootNode->mTransformation);
    m_GlobalInverseTransform = glm::inverse(m_GlobalInverseTransform);

    // Count the number of vertices and indices
    for (unsigned int i = 0; i < m_meshes.size(); i++)
    {
        m_meshes[i].materialIndex = scene->mMeshes[i]->mMaterialIndex;
        m_meshes[i].numIndices = scene->mMeshes[i]->mNumFaces * 3;
        m_meshes[i].baseVertex = m_numVertices;
        m_meshes[i].baseIndex = m_numIndices;

        m_numVertices += scene->mMeshes[i]->mNumVertices;
        m_numIndices += m_meshes[i].numIndices;
    }

    // Reserve space in the vectors for the vertex attributes and indices
    vertices.reserve(m_numVertices);
    indices.reserve(m_numIndices);

    InitNodeHierarchy(m_rootNode, scene->mRootNode);

    // Initialize the meshes in the scene one by one
    for (unsigned int i = 0; i < m_meshes.size(); i++)
    {
        const aiMesh* paiMesh = scene->mMeshes[i];
        InitMesh(i, paiMesh, vertices, indices);
    }

    if (!InitMaterials(scene, filename))
        return false;

    // Populate the buffers with vertex attributes and the indices
    glBindVertexArray(m_VAO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), &indices[0], GL_STATIC_DRAW);

        // Load data
        glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(VertexInfo) * vertices.size(), &vertices[0], GL_STATIC_DRAW);

        glEnableVertexAttribArray(POSITION_LOCATION);
        glVertexAttribPointer(POSITION_LOCATION, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo),
            (GLvoid*)0);

        glEnableVertexAttribArray(TEX_COORD_LOCATION);
        glVertexAttribPointer(TEX_COORD_LOCATION, 2, GL_FLOAT, GL_FALSE, sizeof(VertexInfo),
            (GLvoid*)offsetof(VertexInfo, texCoords));

        glEnableVertexAttribArray(NORMAL_LOCATION);
        glVertexAttribPointer(NORMAL_LOCATION, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo),
            (GLvoid*)offsetof(VertexInfo, normal));

        glEnableVertexAttribArray(BONE_ID_LOCATION);
        glVertexAttribIPointer(BONE_ID_LOCATION, 4, GL_UNSIGNED_INT, sizeof(VertexInfo),    // Cannot pass more than 4!!
            (GLvoid*)offsetof(VertexInfo, boneData.IDs));

        glEnableVertexAttribArray(BONE_WEIGHT_LOCATION);
        glVertexAttribPointer(BONE_WEIGHT_LOCATION, 4, GL_FLOAT, GL_FALSE, sizeof(VertexInfo),    // Cannot pass more than 4!!
            (GLvoid*)offsetof(VertexInfo, boneData.weights));
    glBindVertexArray(0);       // Make sure the VAO is not changed from the outside

    return glGetError() == GL_NO_ERROR;
}