3d point cloud of an object using Kinect - c++

I've got a point cloud of an object using kinect and I want to create a 3d representation of the point cloud and save the image. I'm not sure how I can approach this task. I want it to be able to detect an object and derive the point cloud of the object. For example for 3d scanning purposes, it would be able to 3d scan the object from all directions and then get a point cloud from it.
main.cpp
#include "main.h"
#include "glut.h"
#include <cmath>
#include <cstdio>
#include <Windows.h>
#include <Ole2.h>
#include <NuiApi.h>
#include <NuiImageCamera.h>
#include <NuiSensor.h>
// OpenGL Variables
long depthToRgbMap[width*height*2];
// We'll be using buffer objects to store the kinect point cloud
GLuint vboId;
GLuint cboId;
// Kinect variables
HANDLE depthStream;
HANDLE rgbStream;
INuiSensor* sensor;
bool initKinect() {
// Get a working kinect sensor
int numSensors;
if (NuiGetSensorCount(&numSensors) < 0 || numSensors < 1) return false;
if (NuiCreateSensorByIndex(0, &sensor) < 0) return false;
// Initialize sensor
sensor->NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH | NUI_INITIALIZE_FLAG_USES_COLOR);
sensor->NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH, // Depth camera or rgb camera?
NUI_IMAGE_RESOLUTION_640x480, // Image resolution
0, // Image stream flags, e.g. near mode
2, // Number of frames to buffer
NULL, // Event handle
&depthStream);
sensor->NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR, // Depth camera or rgb camera?
NUI_IMAGE_RESOLUTION_640x480, // Image resolution
0, // Image stream flags, e.g. near mode
2, // Number of frames to buffer
NULL, // Event handle
&rgbStream);
return sensor;
}
void getDepthData(GLubyte* dest) {
float* fdest = (float*) dest;
long* depth2rgb = (long*) depthToRgbMap;
NUI_IMAGE_FRAME imageFrame;
NUI_LOCKED_RECT LockedRect;
if (sensor->NuiImageStreamGetNextFrame(depthStream, 0, &imageFrame) < 0) return;
INuiFrameTexture* texture = imageFrame.pFrameTexture;
texture->LockRect(0, &LockedRect, NULL, 0);
if (LockedRect.Pitch != 0) {
const USHORT* curr = (const USHORT*) LockedRect.pBits;
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
// Get depth of pixel in millimeters
USHORT depth = NuiDepthPixelToDepth(*curr++);
// Store coordinates of the point corresponding to this pixel
Vector4 pos = NuiTransformDepthImageToSkeleton(i, j, depth<<3, NUI_IMAGE_RESOLUTION_640x480);
*fdest++ = pos.x/pos.w;
*fdest++ = pos.y/pos.w;
*fdest++ = pos.z/pos.w;
// Store the index into the color array corresponding to this pixel
NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolution(
NUI_IMAGE_RESOLUTION_640x480, NUI_IMAGE_RESOLUTION_640x480, NULL,
i, j, depth<<3, depth2rgb, depth2rgb+1);
depth2rgb += 2;
}
}
}
texture->UnlockRect(0);
sensor->NuiImageStreamReleaseFrame(depthStream, &imageFrame);
}
void getRgbData(GLubyte* dest) {
float* fdest = (float*) dest;
long* depth2rgb = (long*) depthToRgbMap;
NUI_IMAGE_FRAME imageFrame;
NUI_LOCKED_RECT LockedRect;
if (sensor->NuiImageStreamGetNextFrame(rgbStream, 0, &imageFrame) < 0) return;
INuiFrameTexture* texture = imageFrame.pFrameTexture;
texture->LockRect(0, &LockedRect, NULL, 0);
if (LockedRect.Pitch != 0) {
const BYTE* start = (const BYTE*) LockedRect.pBits;
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
// Determine rgb color for each depth pixel
long x = *depth2rgb++;
long y = *depth2rgb++;
// If out of bounds, then don't color it at all
if (x < 0 || y < 0 || x > width || y > height) {
for (int n = 0; n < 3; ++n) *(fdest++) = 0.0f;
}
else {
const BYTE* curr = start + (x + width*y)*4;
for (int n = 0; n < 3; ++n) *(fdest++) = curr[2-n]/255.0f;
}
}
}
}
texture->UnlockRect(0);
sensor->NuiImageStreamReleaseFrame(rgbStream, &imageFrame);
}
void getKinectData() {
GLubyte* ptr;
glBindBuffer(GL_ARRAY_BUFFER, vboId);
ptr = (GLubyte*) glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
if (ptr) {
getDepthData(ptr);
}
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, cboId);
ptr = (GLubyte*) glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
if (ptr) {
getRgbData(ptr);
}
glUnmapBuffer(GL_ARRAY_BUFFER);
}
void rotateCamera() {
static double angle = 0.;
static double radius = 3.;
double x = radius*sin(angle);
double z = radius*(1-cos(angle)) - radius/2;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(x,0,z,0,0,radius/2,0,1,0);
angle += 0.05;
}
void drawKinectData() {
getKinectData();
rotateCamera();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glVertexPointer(3, GL_FLOAT, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER, cboId);
glColorPointer(3, GL_FLOAT, 0, NULL);
glPointSize(1.f);
glDrawArrays(GL_POINTS, 0, width*height);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
}
int main(int argc, char* argv[]) {
if (!init(argc, argv)) return 1;
if (!initKinect()) return 1;
// OpenGL setup
glClearColor(0,0,0,0);
glClearDepth(1.0f);
// Set up array buffers
const int dataSize = width*height * 3 * 4;
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, dataSize, 0, GL_DYNAMIC_DRAW);
glGenBuffers(1, &cboId);
glBindBuffer(GL_ARRAY_BUFFER, cboId);
glBufferData(GL_ARRAY_BUFFER, dataSize, 0, GL_DYNAMIC_DRAW);
// Camera setup
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, width /(GLdouble) height, 0.1, 1000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0,0,0,0,0,1,0,1,0);
// Main loop
execute();
return 0;
}

Related

How to update buffer data in rendering loop?

so I am trying to render Koch's snowflake. whenever the user clicks the "w" button it should generate a new generation using this function:
void nextGen(int& gen, vector<KochLine>& v, GLFWwindow* window, float angle) {
gen++;
if(glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS && gen <= 6) {
vector<KochLine> nextGen;
int p = power(4, gen);
for (int j = 0; j < v.size(); j++) {
if (j < p) {
v[j].generate(nextGen, angle);
} else {
v[j].generate(nextGen, -angle);
}
}
v = nextGen;
}
}
can anyone tell me how to do that?
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include ".\classes\KochLine.h"
#include ".\classes\Shader.h"
#define PI 3.1415926538
using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}
void closeWindow(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
}
int power(int a, int b) {
int ans = 1;
while(b > 0) {
int lb = (b & 1);
if(lb) {
ans *= a;
}
a *= a;
b = b >> 1;
}
return ans;
}
void nextGen(int& gen, vector<KochLine>& v, GLFWwindow* window, float angle) {
gen++;
if(glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS && gen <= 6) {
vector<KochLine> nextGen;
int p = power(4, gen);
for (int j = 0; j < v.size(); j++) {
if (j < p) {
v[j].generate(nextGen, angle);
} else {
v[j].generate(nextGen, -angle);
}
}
v = nextGen;
}
}
int main() {
int gen = 0, type = -1;
while(type != 1 && type != 2) {
cout << "Enter which type of koch snowflake you want\n";
cout << "[1]Koch's Snowflake\n";
cout << "[2]Anti-Koch's Snowflake\n";
cin >> type;
}
// koch curve stuff
Vertex v1{-0.75f, -0.75f};
Vertex v2{0.75f, -0.75f};
Vertex v3{0.0f, 0.75f};
KochLine e2{v1, v2};
KochLine e1{v1, v3};
KochLine e3{v2, v3};
vector<KochLine> v;
v.push_back(e1);
v.push_back(e2);
v.push_back(e3);
float angle = PI / 3.0f;
if(type == 2) {
angle *= -1;
}
/*for(int i = 0; i < gen; i++) {
vector<KochLine> nextGen;
int p = power(4, i);
for(int j = 0; j < v.size(); j++) {
if(j < p) {
v[j].generate(nextGen, angle);
} else {
v[j].generate(nextGen, -angle);
}
}
v = nextGen;
}*/
float* vertecies = (float*)malloc(v.size() * 6 * sizeof(float));
int p = 0;
for(int i = 0; i < v.size() * 6; i += 6) {
vertecies[i] = v[p].p1.x;
vertecies[i + 1] = v[p].p1.y;
vertecies[i + 2] = 0.0f;
vertecies[i + 3] = v[p].p2.x;
vertecies[i + 4] = v[p].p2.y;
vertecies[i + 5] = 0.0f;
p++;
}
// openGL stuff
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "Cobweb_Diagram", NULL, NULL);
if (window == NULL) {
cout << "Failed to create GLFW window" << endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
cout << "Failed to initialize GLAD" << endl;
return -1;
}
Shader ourShader("./shaders/my_vertex.vs", "./shaders/my_fragment.fs");
int k = v.size();
unsigned int VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, k * 6 * sizeof(float), vertecies, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(0));
glEnableVertexAttribArray(0);
while (!glfwWindowShouldClose(window)) {
closeWindow(window);
nextGen(gen, v, window, angle);
float* vertecies = (float*)malloc(v.size() * 6 * sizeof(float));
int p = 0;
for (int i = 0; i < v.size() * 6; i += 6) {
vertecies[i] = v[p].p1.x;
vertecies[i + 1] = v[p].p1.y;
vertecies[i + 2] = 0.0f;
vertecies[i + 3] = v[p].p2.x;
vertecies[i + 4] = v[p].p2.y;
vertecies[i + 5] = 0.0f;
p++;
}
glBufferData(GL_ARRAY_BUFFER, k * 6 * sizeof(float), vertecies, GL_STATIC_DRAW);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(VAO);
ourShader.use();
glDrawArrays(GL_LINES, 0, v.size() * 2);
glBindVertexArray(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
Edit: I tried to run glBufferData in render loop but it doesn't work.

How to build a tessellated rectangle

i am trying to build a tessellated rectangle based from the answer to this question.
How to build data for a tessellated rectangle
My final result has only the half rectangle drawn and the geometry looks broken.
This is how it looks.
This is my code which i have tried to port to VAO and VBO.
Generating the Data.
std::vector<float> verticesRect;
std::vector<unsigned int> indicesRect;
nSegments = 16; mSegments = 16;
void Rectangle2::Generate()
{
const int n8 = nSegments * 8; // size of VBO gfx data
const int sz0 = mSegments * n8; // size of VBO gfx data
const int sz1 = (mSegments - 1) * (nSegments - 1) * 6;// size of indices
verticesRect.clear();
indicesRect.clear();
int a,i, j, k, b;
GLfloat x, y, z, dx, dy, l;
glm::vec3 u, v, nor;
dx = 2.0 * (width / float(nSegments - 1));
dy = 2.0 * (height / float(mSegments - 1));
for (a = 0,y = -height, j = 0; j < mSegments; j++, y += dy)
for (x = -width, i = 0; i < nSegments; i++, x += dx)
{
z = 20.0 * sin((x * x) + (y * y));
verticesRect.push_back(x); a++;
verticesRect.push_back(y); a++;
verticesRect.push_back(z); a++;
// Normal ( will be recomputed later)
verticesRect.push_back(0.0); a++;
verticesRect.push_back(0.0); a++;
verticesRect.push_back(1.0); a++;
// TexCoord
verticesRect.push_back((x + width) / (width + width)); a++;
verticesRect.push_back((y + height) / (height + height)); a++;
}
// triangulation indices
for(a = 0, j = 1; j < mSegments; j++ )
for (i = 1; i < nSegments; i++)
{
b = ((nSegments * j) + i) * 8;
// First triangle per quad
indicesRect.push_back(b - 8); a++;
indicesRect.push_back(b - 8 - n8); a++;
indicesRect.push_back(b); a++;
// Second triangle per quad
indicesRect.push_back(b - 8 - n8); a++;
indicesRect.push_back(b - n8); a++;
indicesRect.push_back(b); a++;
// recompute inner normals
for (k = 0; k < 3; k++) {
u[k] = verticesRect[indicesRect[a - 6] + k] - verticesRect[indicesRect[a - 4] + k];
v[k] = verticesRect[indicesRect[a - 5] + k] - verticesRect[indicesRect[a - 4] + k];
}
glm::vec3 cross1 = crossProduct(u, v);
cross1 = glm::normalize(cross1);
for (k = 0; k < 3; k++) {
u[k] = verticesRect[indicesRect[a - 3] + k] - verticesRect[indicesRect[a - 1] + k];
v[k] = verticesRect[indicesRect[a - 2] + k] - verticesRect[indicesRect[a - 1] + k];
}
glm::vec3 cross2 = crossProduct(u, v);
cross2 = glm::normalize(cross2);
for (k = 0; k < 3; k++) {
verticesRect[indicesRect[a - 1] + 3 + k] = 0.5 * (cross1[k] + cross2[k]);
}
}
}
Creating the VAO and VBO
void Rectangle2::init()
{
Generate();
glGenVertexArrays(1, &m_VAO);
glGenBuffers(1, &m_VBO);
glGenBuffers(1, &m_EBO);
glBindVertexArray(m_VAO);
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
glBufferData(GL_ARRAY_BUFFER, verticesRect.size() * sizeof(float), &verticesRect[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * indicesRect.size(), &indicesRect[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
isInited = true;
}
Drawing the Object.
glBindVertexArray(m_VAO);
glDrawElements(GL_TRIANGLES, indicesRect.size() - 1, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
The problem is with indices... You ported my old code which uses direct indices but you use VBO instead which uses logical indices ... so the remedy is just divide all indices by 8 (stride size) at the end of Generate function (or reindex the whole genere to use logical indices instead but thats more coding ...) Here full C++/VCL working code to cross reference:
//---------------------------------------------------------------------------
#include <vcl.h> // VCL stuff (ignore)
#include <math.h>
#pragma hdrstop // VCL stuff (ignore)
#include "Unit1.h" // VCL stuff (header of this window)
#include "gl_simple.h" // my GL init (source included)
//---------------------------------------------------------------------------
#pragma package(smart_init) // VCL stuff (ignore)
#pragma resource "*.dfm" // VCL stuff (ignore)
TForm1 *Form1; // VCL stuff (this window)
//---------------------------------------------------------------------------
const int m=16,n=16; // points per grid axis
const int n8=n*8; // size of VBO gfx data
const int sz0=m*n8; // size of VBO gfx data
const int sz1=(m-1)*(n-1)*6;// size of indices
GLfloat dat[sz0];
GLuint idx[sz1];
//---------------------------------------------------------------------------
GLfloat divide(GLfloat a,GLfloat b){ if (fabs(b)<1e-10) return 0.0; else return a/b; }
void normalize(GLfloat *c,GLfloat *a) // c = a/|a|
{
GLfloat l=divide(1.0,sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])));
c[0]=a[0]*l;
c[1]=a[1]*l;
c[2]=a[2]*l;
}
void cross(GLfloat *c,GLfloat *a,GLfloat *b) // c = cross(a,b)
{
GLfloat q[3];
q[0]=(a[1]*b[2])-(a[2]*b[1]);
q[1]=(a[2]*b[0])-(a[0]*b[2]);
q[2]=(a[0]*b[1])-(a[1]*b[0]);
for(int i=0;i<3;i++) c[i]=q[i];
}
void genere(GLfloat w,GLfloat h)
{
int i,j,k,a,b;
GLfloat x,y,z,dx,dy,l;
GLfloat u[3],v[3],nor[3];
// gfx data
dx=2.0*w/GLfloat(n-1);
dy=2.0*h/GLfloat(m-1);
for (a=0,y=-h,j=0;j<m;j++,y+=dy)
for ( x=-w,i=0;i<n;i++,x+=dx)
{
// Vertex
// z= 0.3*sin((x*x)+(y*y));
z=20.0*sin((x*x)+(y*y));
dat[a]=x; a++;
dat[a]=y; a++;
dat[a]=z; a++;
// Normal (will be recomputed latter)
dat[a]=0.0; a++;
dat[a]=0.0; a++;
dat[a]=1.0; a++;
// TexCoord
dat[a]=(x+w)/(w+w); a++;
dat[a]=(y+h)/(h+h); a++;
}
// triangulation indices
for (a=0,j=1;j<m;j++)
for ( i=1;i<n;i++)
{
// b = location of point[i,j] in dat[]
b=((n*j)+i)*8;
// first triangle per quad
idx[a]=b-8; a++;
idx[a]=b-8-n8; a++;
idx[a]=b; a++;
// second triangle per quad
idx[a]=b-8-n8; a++;
idx[a]=b-n8; a++;
idx[a]=b; a++;
// recompute inner normals
for (k=0;k<3;k++)
{
u[k]=dat[idx[a-6]+k]-dat[idx[a-4]+k];
v[k]=dat[idx[a-5]+k]-dat[idx[a-4]+k];
}
cross(nor,u,v); normalize(nor,nor);
for (k=0;k<3;k++)
{
u[k]=dat[idx[a-3]+k]-dat[idx[a-1]+k];
v[k]=dat[idx[a-2]+k]-dat[idx[a-1]+k];
}
cross(u,u,v); normalize(u,u);
for (k=0;k<3;k++) dat[idx[a-1]+3+k]=0.5*(nor[k]+u[k]);
}
// copy edge normals
for (j=0,i=1;i<n;i++)
{
// b = location of point[i,j] in dat[]
b=((n*j)+i)*8;
// copy
for (k=0;k<3;k++) dat[b+3+k]=dat[b+3+k+n8];
}
for (i=0,j=1;j<m;j++)
{
// b = location of point[i,j] in dat[]
b=((n*j)+i)*8;
// copy
for (k=0;k<3;k++) dat[b+3+k]=dat[b+3+k+8];
}
for (i=0;i<sz1;i++) idx[i]/=8; // !!! this is what you need to add !!!
}
//---------------------------------------------------------------------------
GLuint m_VAO=0,m_VBO=0,m_EBO=0;
void genere_VBO()
{
GLuint i;
glGenVertexArrays(1,&m_VAO);
glBindVertexArray(m_VAO);
glGenBuffers(1,&m_VBO);
glBindBuffer(GL_ARRAY_BUFFER,m_VBO);
glBufferData(GL_ARRAY_BUFFER,sizeof(dat),dat,GL_STATIC_DRAW);
i=0; glEnableVertexAttribArray(i); // vertex
glVertexAttribPointer(i,3,GL_FLOAT,GL_FALSE,8*sizeof(dat[0]),(void*)(0*sizeof(dat[0])));
i=2; glEnableVertexAttribArray(i); // normal
glVertexAttribPointer(i,3,GL_FLOAT,GL_FALSE,8*sizeof(dat[0]),(void*)(3*sizeof(dat[0])));
i=8; glEnableVertexAttribArray(i); // texcoord0
glVertexAttribPointer(i,2,GL_FLOAT,GL_FALSE,8*sizeof(dat[0]),(void*)(6*sizeof(dat[0])));
glGenBuffers(1,&m_EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(idx),idx,GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(8);
}
//---------------------------------------------------------------------------
void gl_draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
float light[4]={-0.3,-0.7,0.0,0.0};
glLightfv(GL_LIGHT0,GL_POSITION,light);
glTranslatef(0.0,+6.0,-30.0);
glRotatef(135.0,1.0,0.0,0.0);
static float ang=0;
glRotatef(ang,0.0,0.0,1.0); ang=fmod(ang+1.5,360.0);
/*
// old api render (just for debug ignore this)
int i,j;
glColor3f(0.1,0.5,0.7);
glBegin(GL_TRIANGLES);
for (i=0;i<sz1;i++)
{
j=idx[i]*8; // !!! I added *8 as indices are logical now)
glNormal3fv(dat+j+3);
glTexCoord3fv(dat+j+6);
glVertex3fv(dat+j);
}
glEnd();
*/
// new api render
glColor3f(0.1,0.5,0.7);
glBindVertexArray(m_VAO);
glDrawElements(GL_TRIANGLES,sizeof(idx)/sizeof(idx[0]),GL_UNSIGNED_INT,0); // indices (choose just one line not both !!!)
glBindVertexArray(0);
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// this is called on window startup
gl_init(Handle); // init OpenGL 1.0
genere(1.0,1.0);
genere_VBO();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// this is called before window exits
gl_exit(); // exit OpenGL
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// this is called on each window resize (and also after startup)
gl_resize(ClientWidth,ClientHeight);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
// this is called whnewer app needs repaint
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::tim_redrawTimer(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
And preview:
using fixed function and nVidia Default attribute locations (was too lazy to make shaders for this)...

OpenGL glBufferSubData Offset issue

I have a problem copying/upload all the data into the buffer using glBufferSubData(). I want to copy chunk by chunk to the buffer. So I have used this approach. I am seeing a blank screen when I try to render. Please find my code below. do you see any problem in calculating the buffer offset? Or is this not the way to copy data into the buffer?
Below is the data structure
struct DisplayIndexID {
int idx;
DrawStateT drawState;
//Every display Index ID has its own draw models.
std::vector<std::unique_ptr<vertexModel>> readytoDrawModels;
};
void initVboData(std::vector<DisplayIndexID> & v)
{
glBindBuffer(GL_ARRAY_BUFFER, geomVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(QVector3D) * 4096, NULL, GL_DYNAMIC_DRAW);
std::vector<QVector3D> vecToDraw;
GLintptr offset = 0;
for (int i = 0; i < v.size(); i++)
for (auto& vModel : v[i].readytoDrawModels)
{
if (vModel) {
vecToDraw = vModel->getVertices();
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(QVector3D) * vecToDraw.size(), &vecToDraw[0]);
offset += sizeof(QVector3D) * vecToDraw.size(); // is this offset calculation fine?
}
}
}
//Below is my draw function
void drawDisplayLists(std::vector<DisplayIndexID> & v)
{
initVboData(v);
for (int i = 0; i < v.size(); i++)
{
//Make context current
makeCurrent();
bool isTextureUsed = false;
//Apply Projection Matrix.
GLint mvp_mat = 0;
GLint mvp_matText = 0;
///***********PRINT AREA***********************/
for (auto& vModel : v[i].readytoDrawModels)
{
// Code related to Shaders
......
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(QVector3D), nullptr); how to get the offset and add offset here.
switch (vModel->getDrawMode())
{
case 0: //GL_POINTS
glDrawArrays(GL_POINTS, 0, vModel->getVertices().size());
break;
case 1: //GL_LINES
glDrawArrays(GL_LINES, 0, vModel->getVertices().size());
break;
case 2: //GL_LINE_LOOP
glDrawArrays(GL_LINE_LOOP, 0, vModel->getVertices().size());
break;
}
}
}
}
Considering that you have persistent VBO initialized like that:
GLintptr offset = 0;
for (int i = 0; i < v.size(); i++) {
for (auto& vModel : v[i].readytoDrawModels) {
if (vModel) {
vecToDraw = vModel->getVertices();
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(QVector3D) * vecToDraw.size(), &vecToDraw[0]);
offset += sizeof(QVector3D) * vecToDraw.size();
}
}
}
then you should replicate the same offsets within drawing loop.
This can be done either by passing offset in bytes to glVertexAttribPointer() computed exactly in the same way as during VBO initialization:
GLintptr offset = 0;
for (int i = 0; i < v.size(); i++) {
for (auto& vModel : v[i].readytoDrawModels) {
if (vModel) {
vecToDraw = vModel->getVertices();
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(QVector3D), offset);
glDrawArrays(GL_LINES, 0, vecToDraw.size());
offset += sizeof(QVector3D) * vecToDraw.size();
}
}
}
or by passing offset in number of vertices to glDrawArrays(), in case if all models have the same vertex definition as in your code snippet (in vec3 Position):
GLintptr aFirstIndex = 0;
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(QVector3D), nullptr);
for (int i = 0; i < v.size(); i++) {
for (auto& vModel : v[i].readytoDrawModels) {
if (vModel) {
vecToDraw = vModel->getVertices();
glDrawArrays(GL_LINES, aFirstIndex, vecToDraw.size());
aFirstIndex += vecToDraw.size();
}
}
}
Make sure that allocated VBO has a proper size and 4096 is replaced by a real size:
glBufferData(GL_ARRAY_BUFFER, sizeof(QVector3D) * 4096, NULL, GL_DYNAMIC_DRAW);
By the way, grouping models by GL_POINTS/GL_LINES array type and same presentation attributes (color/material/etc.) would allow rendering each group with a single glDrawArrays() call, instead of numerous glDrawArrays() for every model, which would be more optimal from performance point of view.

How to draw a cube in OpenGL using IBOs and VBOs

I've got a basic opengl setup working with shaders, IBOs, and VBOs. And can do simple things such as drawing a 2D rectangle or square or anything of that sense. However, when I implement a vbo and an ibo containing data for a cube nothing shows up on the screen. I've created a cube class which stores the vertex and indices for the cube.
Note: I have not implemented a projection matrix in the shader yet, I am waiting till I get the distorted 3d cube on the screen.
Here's my code-
main.cpp
#include <iostream>
#include <SDL2/SDL.h>
#include <GL/glew.h>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>
#include "shader.h"
#include "cube.h"
#undef main
int main() {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* mainWindow = SDL_CreateWindow("game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 512, 512, SDL_WINDOW_OPENGL);
SDL_GLContext mainContext = SDL_GL_CreateContext(mainWindow);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
glewExperimental = GL_TRUE;
glewInit();
GLuint VBO, VAO, IBO;
cube block1(0, 0, -2.5, .2);
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*36, block1.indices, GL_STATIC_DRAW);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 24, block1.vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
shader sqreProg("shaders/shader.vert", "shaders/shader.frag");
bool running = true;
while (running) {
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
sqreProg.useShader();
glBindVertexArray(VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glUseProgram(0);
SDL_GL_SwapWindow(mainWindow);
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
running = false;
break;
}
}
}
SDL_GL_DeleteContext(mainContext);
SDL_DestroyWindow(mainWindow);
SDL_Quit();
}
Shader.h
#pragma once
#include <SDL2/SDL.h>
#include <GL/glew.h>
#include <string>
#include <fstream>
#include <streambuf>
#include <iostream>
class shader {
public:
shader(const char* vShader, const char* fShader) {
vShaderCode = readFile(vShader);
fShaderCode = readFile(fShader);
shaderProgramID = glCreateProgram();
addShader(vShaderCode.c_str(), GL_VERTEX_SHADER);
addShader(fShaderCode.c_str(), GL_FRAGMENT_SHADER);
glLinkProgram(shaderProgramID);
}
void useShader() { glUseProgram(shaderProgramID); }
private:
GLuint shaderProgramID;
std::string vShaderCode;
std::string fShaderCode;
std::string readFile(const char* fileName) {
std::ifstream file(fileName);
std::string fileContents;
fileContents.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return fileContents;
}
void addShader(const char* code, GLenum type) {
GLuint shaderID = glCreateShader(type);
const GLchar* theCode[1];
theCode[0] = code;
GLint codeLength[1];
codeLength[0] = strlen(code);
glShaderSource(shaderID, 1, theCode, codeLength);
glCompileShader(shaderID);
GLint result = 0;
GLchar eLog[1024] = { 0 };
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &result);
if (!result) {
glGetShaderInfoLog(shaderID, 1024, NULL, eLog);
fprintf(stderr, "error compile the %d shader: '%s'\n", type, eLog);
return;
}
glAttachShader(shaderProgramID, shaderID);
}
};
cube.h
#pragma once
#include <GL\glew.h>
class cube {
public:
cube() {
x = 0;
y = 0;
z = 0;
width = 0;
vertices = 0;
indices = 0;
}
cube(GLfloat X, GLfloat Y, GLfloat Z, float w) {
x = X;
y = Y;
z = Z;
width = w;
vertices = new GLfloat[24];
//1
vertices[0] = x;
vertices[1] = y;
vertices[2] = z;
//2
vertices[3] = x + width;
vertices[4] = y;
vertices[5] = z;
//3
vertices[6] = x;
vertices[7] = y - width;
vertices[8] = z;
//4
vertices[9] = x + width;
vertices[10] = y - width;
vertices[11] = z;
//5
vertices[12] = x;
vertices[13] = y;
vertices[14] = z - width;
//6
vertices[15] = x + width;
vertices[16] = y;
vertices[17] = z - width;
//7
vertices[18] = x;
vertices[19] = y - width;
vertices[20] = z - width;
//8
vertices[21] = x + width;
vertices[22] = y - width;
vertices[23] = z - width;
indices = new unsigned int[36];
//0
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
//1
indices[3] = 1;
indices[4] = 2;
indices[5] = 3;
//2
indices[6] = 4;
indices[7] = 5;
indices[8] = 6;
//3
indices[9] = 5;
indices[10] = 6;
indices[11] = 7;
//4
indices[12] = 4;
indices[13] = 0;
indices[14] = 1;
//5
indices[15] = 4;
indices[16] = 5;
indices[17] = 1;
//6
indices[18] = 6;
indices[19] = 2;
indices[20] = 3;
//7
indices[21] = 6;
indices[22] = 7;
indices[23] = 3;
//8
indices[24] = 1;
indices[25] = 5;
indices[26] = 3;
//9
indices[27] = 5;
indices[28] = 7;
indices[29] = 3;
//10
indices[30] = 4;
indices[31] = 0;
indices[32] = 2;
//11
indices[33] = 4;
indices[34] = 6;
indices[35] = 2;
}
GLfloat* vertices;
unsigned int* indices;
private:
GLfloat x;
GLfloat y;
GLfloat z;
float width;
};

OpenGL vertex array objects with tinyobjloader

In order to use modern openGl with tinyobjloader, I'm trying to change the viewer exemple.
I just change the LoadObjAndConvert function, to add vertex array objects as i seen in this tutorial, and to no longer use the buffer object that contains all the data (position, indices, color, uv) because it seems that we can no longer use it with modern openGL.
Result look like I have bad vertex index, the model is only partly rendered, and if the model has only one mesh (the stanford bunny) it does not even show up.
The code is too long, but it is the same as the tinyobjloader viewer exemple, so I will only post functions that are different.
Here is the LoadObjAndConvert function modified (modified parts are between lines to help) :
static bool LoadObjAndConvert(float bmin[3], float bmax[3],
std::vector<DrawObject>* drawObjects,
std::vector<tinyobj::material_t>& materials,
std::map<std::string, GLuint>& textures,
const char* filename) {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
timerutil tm;
tm.start();
std::string base_dir = GetBaseDir(filename);
if (base_dir.empty()) {
base_dir = ".";
}
#ifdef _WIN32
base_dir += "\\";
#else
base_dir += "/";
#endif
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename,
base_dir.c_str());
if (!err.empty()) {
std::cerr << err << std::endl;
}
tm.end();
if (!ret) {
std::cerr << "Failed to load " << filename << std::endl;
return false;
}
printf("Parsing time: %d [ms]\n", (int)tm.msec());
printf("# of vertices = %d\n", (int)(attrib.vertices.size()) / 3);
printf("# of normals = %d\n", (int)(attrib.normals.size()) / 3);
printf("# of texcoords = %d\n", (int)(attrib.texcoords.size()) / 2);
printf("# of materials = %d\n", (int)materials.size());
printf("# of shapes = %d\n", (int)shapes.size());
// Append `default` material
materials.push_back(tinyobj::material_t());
for (size_t i = 0; i < materials.size(); i++) {
printf("material[%d].diffuse_texname = %s\n", int(i),
materials[i].diffuse_texname.c_str());
}
// Load diffuse textures
{
for (size_t m = 0; m < materials.size(); m++) {
tinyobj::material_t* mp = &materials[m];
if (mp->diffuse_texname.length() > 0) {
// Only load the texture if it is not already loaded
if (textures.find(mp->diffuse_texname) == textures.end()) {
GLuint texture_id;
int w, h;
int comp;
std::string texture_filename = mp->diffuse_texname;
if (!FileExists(texture_filename)) {
// Append base dir.
texture_filename = base_dir + mp->diffuse_texname;
if (!FileExists(texture_filename)) {
std::cerr << "Unable to find file: " << mp->diffuse_texname
<< std::endl;
exit(1);
}
}
unsigned char* image =
stbi_load(texture_filename.c_str(), &w, &h, &comp, STBI_default);
if (!image) {
std::cerr << "Unable to load texture: " << texture_filename
<< std::endl;
exit(1);
}
std::cout << "Loaded texture: " << texture_filename << ", w = " << w
<< ", h = " << h << ", comp = " << comp << std::endl;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (comp == 3) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
GL_UNSIGNED_BYTE, image);
} else if (comp == 4) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
GL_UNSIGNED_BYTE, image);
} else {
assert(0); // TODO
}
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(image);
textures.insert(std::make_pair(mp->diffuse_texname, texture_id));
}
}
}
}
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
{
for (size_t s = 0; s < shapes.size(); s++) {
/*-----------------------------------------------------------*/
DrawObject o;// I keep this object for later purpose, texture, etc
//std::vector<float> buffer; // pos(3float), normal(3float), color(3float)
//I replace "buffer" by arrays:
std::vector<GLfloat> mesh_vertex;
std::vector<GLfloat> mesh_normals;
std::vector<GLfloat> mesh_colors;
std::vector<GLfloat> mesh_textCoords;
std::vector<GLuint> mesh_indices;
/*fill index array*/
for (long i = 0; i < shapes[s].mesh.indices.size(); i++)
{
mesh_indices.push_back(shapes[s].mesh.indices[i].vertex_index);
}
/*-----------------------------------------------------------*/
// Check for smoothing group and compute smoothing normals
std::map<int, vec3> smoothVertexNormals;
if (hasSmoothingGroup(shapes[s]) > 0) {
std::cout << "Compute smoothingNormal for shape [" << s << "]" << std::endl;
computeSmoothingNormals(attrib, shapes[s], smoothVertexNormals);
}
for (size_t f = 0; f < shapes[s].mesh.indices.size() / 3; f++) {
tinyobj::index_t idx0 = shapes[s].mesh.indices[3 * f + 0];
tinyobj::index_t idx1 = shapes[s].mesh.indices[3 * f + 1];
tinyobj::index_t idx2 = shapes[s].mesh.indices[3 * f + 2];
int current_material_id = shapes[s].mesh.material_ids[f];
if ((current_material_id < 0) ||
(current_material_id >= static_cast<int>(materials.size()))) {
// Invaid material ID. Use default material.
current_material_id =
materials.size() -
1; // Default material is added to the last item in `materials`.
}
// if (current_material_id >= materials.size()) {
// std::cerr << "Invalid material index: " << current_material_id <<
// std::endl;
//}
//
float diffuse[3];
for (size_t i = 0; i < 3; i++) {
diffuse[i] = materials[current_material_id].diffuse[i];
}
float tc[3][2];
if (attrib.texcoords.size() > 0) {
if ((idx0.texcoord_index < 0) || (idx1.texcoord_index < 0) ||
(idx2.texcoord_index < 0)) {
// face does not contain valid uv index.
tc[0][0] = 0.0f;
tc[0][1] = 0.0f;
tc[1][0] = 0.0f;
tc[1][1] = 0.0f;
tc[2][0] = 0.0f;
tc[2][1] = 0.0f;
} else {
assert(attrib.texcoords.size() >
size_t(2 * idx0.texcoord_index + 1));
assert(attrib.texcoords.size() >
size_t(2 * idx1.texcoord_index + 1));
assert(attrib.texcoords.size() >
size_t(2 * idx2.texcoord_index + 1));
// Flip Y coord.
tc[0][0] = attrib.texcoords[2 * idx0.texcoord_index];
tc[0][1] = 1.0f - attrib.texcoords[2 * idx0.texcoord_index + 1];
tc[1][0] = attrib.texcoords[2 * idx1.texcoord_index];
tc[1][1] = 1.0f - attrib.texcoords[2 * idx1.texcoord_index + 1];
tc[2][0] = attrib.texcoords[2 * idx2.texcoord_index];
tc[2][1] = 1.0f - attrib.texcoords[2 * idx2.texcoord_index + 1];
}
} else {
tc[0][0] = 0.0f;
tc[0][1] = 0.0f;
tc[1][0] = 0.0f;
tc[1][1] = 0.0f;
tc[2][0] = 0.0f;
tc[2][1] = 0.0f;
}
float v[3][3];
for (int k = 0; k < 3; k++) {
int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
int f2 = idx2.vertex_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
v[0][k] = attrib.vertices[3 * f0 + k];
v[1][k] = attrib.vertices[3 * f1 + k];
v[2][k] = attrib.vertices[3 * f2 + k];
bmin[k] = std::min(v[0][k], bmin[k]);
bmin[k] = std::min(v[1][k], bmin[k]);
bmin[k] = std::min(v[2][k], bmin[k]);
bmax[k] = std::max(v[0][k], bmax[k]);
bmax[k] = std::max(v[1][k], bmax[k]);
bmax[k] = std::max(v[2][k], bmax[k]);
}
float n[3][3];
{
bool invalid_normal_index = false;
if (attrib.normals.size() > 0) {
int nf0 = idx0.normal_index;
int nf1 = idx1.normal_index;
int nf2 = idx2.normal_index;
if ((nf0 < 0) || (nf1 < 0) || (nf2 < 0)) {
// normal index is missing from this face.
invalid_normal_index = true;
} else {
for (int k = 0; k < 3; k++) {
assert(size_t(3 * nf0 + k) < attrib.normals.size());
assert(size_t(3 * nf1 + k) < attrib.normals.size());
assert(size_t(3 * nf2 + k) < attrib.normals.size());
n[0][k] = attrib.normals[3 * nf0 + k];
n[1][k] = attrib.normals[3 * nf1 + k];
n[2][k] = attrib.normals[3 * nf2 + k];
}
}
} else {
invalid_normal_index = true;
}
if (invalid_normal_index && !smoothVertexNormals.empty()) {
// Use smoothing normals
int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
int f2 = idx2.vertex_index;
if (f0 >= 0 && f1 >= 0 && f2 >= 0) {
n[0][0] = smoothVertexNormals[f0].v[0];
n[0][1] = smoothVertexNormals[f0].v[1];
n[0][2] = smoothVertexNormals[f0].v[2];
n[1][0] = smoothVertexNormals[f1].v[0];
n[1][1] = smoothVertexNormals[f1].v[1];
n[1][2] = smoothVertexNormals[f1].v[2];
n[2][0] = smoothVertexNormals[f2].v[0];
n[2][1] = smoothVertexNormals[f2].v[1];
n[2][2] = smoothVertexNormals[f2].v[2];
invalid_normal_index = false;
}
}
if (invalid_normal_index) {
// compute geometric normal
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0];
n[1][1] = n[0][1];
n[1][2] = n[0][2];
n[2][0] = n[0][0];
n[2][1] = n[0][1];
n[2][2] = n[0][2];
}
}
for (int k = 0; k < 3; k++) {
/*-----------------------------------------------------------*/
// I leave old calls to "buffer" in comment for understanding
//buffer.push_back(v[k][0]);
//buffer.push_back(v[k][1]);
//buffer.push_back(v[k][2]);
mesh_vertex.push_back(v[k][0]);
mesh_vertex.push_back(v[k][1]);
mesh_vertex.push_back(v[k][2]);
//buffer.push_back(n[k][0]);
//buffer.push_back(n[k][1]);
//buffer.push_back(n[k][2]);
mesh_normals.push_back(n[k][0]);
mesh_normals.push_back(n[k][1]);
mesh_normals.push_back(n[k][2]);
// Combine normal and diffuse to get color.
float normal_factor = 0.2;
float diffuse_factor = 1 - normal_factor;
float c[3] = {n[k][0] * normal_factor + diffuse[0] * diffuse_factor,
n[k][1] * normal_factor + diffuse[1] * diffuse_factor,
n[k][2] * normal_factor + diffuse[2] * diffuse_factor};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
c[0] /= len;
c[1] /= len;
c[2] /= len;
}
//buffer.push_back(c[0] * 0.5 + 0.5);
//buffer.push_back(c[1] * 0.5 + 0.5);
//buffer.push_back(c[2] * 0.5 + 0.5);
mesh_colors.push_back(c[0] * 0.5 + 0.5);
mesh_colors.push_back(c[1] * 0.5 + 0.5);
mesh_colors.push_back(c[2] * 0.5 + 0.5);
//buffer.push_back(tc[k][0]);
//buffer.push_back(tc[k][1]);
mesh_textCoords.push_back(tc[k][0]);
mesh_textCoords.push_back(tc[k][1]);
/*-----------------------------------------------------------*/
}
}
o.vb_id = 0;
o.numTriangles = 0;
// OpenGL viewer does not support texturing with per-face material.
if (shapes[s].mesh.material_ids.size() > 0 &&
shapes[s].mesh.material_ids.size() > s) {
o.material_id = shapes[s].mesh.material_ids[0]; // use the material ID
// of the first face.
} else {
o.material_id = materials.size() - 1; // = ID for default material.
}
printf("shape[%d] material_id %d\n", int(s), int(o.material_id));
/*-----------------------------------------------------------*/
/*if (buffer.size() > 0) {
glGenBuffers(1, &o.vb_id);
glBindBuffer(GL_ARRAY_BUFFER, o.vb_id);
glBufferData(GL_ARRAY_BUFFER, buffer.size() * sizeof(float),
&buffer.at(0), GL_STATIC_DRAW);
o.numTriangles = buffer.size() / (3 + 3 + 3 + 2) /
3; // 3:vtx, 3:normal, 3:col, 2:texcoord
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s),
o.numTriangles);
}
drawObjects->push_back(o);*/
// Replace by :
GLuint positionVBO = 0;
GLuint texcoordVBO = 0;
GLuint normalVBO = 0;
GLuint indicesEBO = 0;
// Upload per-vertex positions
if (!mesh_vertex.empty())
{
glGenBuffers(1, &positionVBO);
glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
glBufferData(GL_ARRAY_BUFFER, mesh_vertex.size() * sizeof(GLfloat), &mesh_vertex[0], GL_STATIC_DRAW); // GL_DYNAMIC_DRAW ?
glBindBuffer(GL_ARRAY_BUFFER, 0);
positionVBO_array.push_back(positionVBO);
}
// Upload per-vertex texture coordinates
if (!mesh_textCoords.empty())
{
glGenBuffers(1, &texcoordVBO);
glBindBuffer(GL_ARRAY_BUFFER, texcoordVBO);
glBufferData(GL_ARRAY_BUFFER,
mesh_textCoords.size() * sizeof(float),
&mesh_textCoords[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// Upload per-vertex normals
if (!mesh_normals.empty())
{
glGenBuffers(1, &normalVBO);
glBindBuffer(GL_ARRAY_BUFFER, normalVBO);
glBufferData(GL_ARRAY_BUFFER, mesh_normals.size() * sizeof(GLfloat), &mesh_normals[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
normalVBO_array.push_back(normalVBO);
}
// Upload the indices that form triangles
if (!shapes[0].mesh.indices.empty())
{
glGenBuffers(1, &indicesEBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
shapes[s].mesh.indices.size() * sizeof(unsigned int),
shapes[s].mesh.indices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
indicesEBO_array.push_back(indicesEBO);
indicesEBOSize_array.push_back(shapes[s].mesh.indices.size());
}
// Hook up vertex/index buffers to a "vertex array object" (VAO)
// VAOs are the closest thing OpenGL has to a "mesh" object.
// VAOs feed data from buffers to the inputs of a vertex shader.
GLuint meshVAO;
vglGenVertexArrays(1, &meshVAO);
meshVAO_array.push_back(meshVAO);// I keep the ids in order to loop inside meshVAO_array in the draw function
// Attach position buffer as attribute 0
if (positionVBO != 0)
{
glBindVertexArray(meshVAO);
// Note: glVertexAttribPointer sets the current
// GL_ARRAY_BUFFER_BINDING as the source of data
// for this attribute.
// That's why we bind a GL_ARRAY_BUFFER before
// calling glVertexAttribPointer then
// unbind right after (to clean things up).
glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
sizeof(float) * 3, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Enable the attribute (they are disabled by default
// -- this is very easy to forget!!)
glEnableVertexAttribArray(0);
glBindVertexArray(0);
}
// Attach texcoord buffer as attribute 1
if (texcoordVBO != 0)
{
glBindVertexArray(meshVAO);
glBindBuffer(GL_ARRAY_BUFFER, texcoordVBO);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,
sizeof(float) * 2, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(1);
glBindVertexArray(0);
}
// Attach normal buffer as attribute 2
if (normalVBO != 0)
{
glBindVertexArray(meshVAO);
glBindBuffer(GL_ARRAY_BUFFER, normalVBO);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE,
sizeof(float) * 3, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(2);
glBindVertexArray(0);
}
if (indicesEBO != 0)
{
glBindVertexArray(meshVAO);
// Note: Calling glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
// when a VAO is bound attaches the index buffer to the VAO.
// From an API design perspective, this is subtle.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesEBO);
glBindVertexArray(0);
}
/*-----------------------------------------------------------*/
}
}
printf("bmin = %f, %f, %f\n", bmin[0], bmin[1], bmin[2]);
printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
return true;
}
(Sorry for this long code block)
And here is the while loop of the main function, the only difference with tinyobjloader is between the two lines:
unsigned int program = shaders::CreateShader("data/simple.vert", "data/simple.frag"); // just some really simples shaders
while (glfwWindowShouldClose(window) == GL_FALSE) {
glfwPollEvents();
glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
// camera & rotate
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
GLfloat mat[4][4];
gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0],
up[1], up[2]);
build_rotmatrix(mat, curr_quat);
glMultMatrixf(&mat[0][0]);
// Fit to -1, 1
glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent);
// Centerize object.
glTranslatef(-0.5 * (bmax[0] + bmin[0]), -0.5 * (bmax[1] + bmin[1]),
-0.5 * (bmax[2] + bmin[2]));
/*-----------------------------------------------------------*/
//Draw(gDrawObjects, materials, textures);
// Can now bind the vertex array object to
// the graphics pipeline, to render with it.
glUseProgram(program);
for (int s = 0; s < meshVAO_array.size(); s++)
{
glBindVertexArray(meshVAO_array[s]);
glDrawElements(GL_TRIANGLES, indicesEBOSize_array[s], GL_UNSIGNED_INT, 0);//mesh.IndexCount
glBindVertexArray(0);
}
glUseProgram(0);
// when done, unbind it from the graphics pipeline:
glBindVertexArray(0);
/*-----------------------------------------------------------*/
glfwSwapBuffers(window);
}
What am I doing wrong?
In the nested loops you all the indices of shapes[].mesh.indices are use to lokkup the attributes, which are stored in attrib.vertices, attrib.normals and attrib.texcoords.
This attributes are prepared and linearized. They are stored in there idexed order to the linear arrays mesh_vertex, mesh_normals, mesh_colors and mesh_textCoords.
But the indices are directly copied from shapes[].mesh.indices to mesh_indices
for (long i = 0; i < shapes[s].mesh.indices.size(); i++)
{
mesh_indices.push_back(shapes[s].mesh.indices[i].vertex_index);
}
The indices in mesh_indices still refer to the vertex coordinates stored in attrib.vertices but the have no meaning for the attributes in the new containers.
The original indices are not needed any more. The indices of the new attribute would be continuously ascending: [0, 1, 2, 3, 4, 5 ...]
It is sufficient to draw the array of generic vertex attribute data in its existing order:
// you have to know the number of attributes
// something like mesh_vertex.size() / 3;
GLsizei no_of_attributes = .... ;
glBindVertexArray(meshVAO_array[s]);
glDrawArrays(GL_TRIANGLES, 0, no_of_attributes);
glBindVertexArray(0);