I'm a beginner in OpenGL and I am trying to draw a colored square I followed the tutorial at OpenGL Book and I am using the example to draw here. Since this draws a triangle I modified the code to draw 4 vertices. I made a Rectangle class that can output it's data in array format. This is the data that I am trying to pass into the glBufferData function.
Unfortunately when I use my class's data it does not draw anything on the screen. I'm even checking in gDebugger, I'm looking at my VBO and the data is not correct.
To test, I extracted the vertices out of my class and used them in a local array instead of the pointer returned from my class.
My data is now :
Vertex Vertices[] =
{
{ { -0.5f, 0.5f, 0.0f, 1.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } },
{ { 0.5f, 0.5f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } },
{ { -0.5f, -0.5f, 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } },
{ { 0.5f, -0.5f, 0.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }
};
instead of
Vertex* Vertices = rec->GetVertexData();
(I checked by hand both arrays, they have the exact same values, the problem is not in my Rectangle's code) and now the call glBufferData(GL_ARRAY_BUFFER, BufferSize, Vertices, GL_STATIC_DRAW); works and I see the correct rectangle on screen. With the data in gDebugger being
After some research I realized that sizeof does not report the correct size given a pointer so I fixed that. I hardcoded (for now) BufferSize = 128; in order to test quicker. Point is, it works with the Vertex array and still not with the Vertex pointer from my class.
I've googled examples of the glBufferData and literally none of them shows you how to use the data from a pointer, you'd think that would be a good example.
So how can I pass an array of Vertex to my glBufferData that's being returned as a pointer from another class ? I'm using OpenGL version 4 with GLEW and GLUT.
Update :
So the error was on my part, I'll post my debugging here in hopes it can help other people. I added a Watch of name Vertice, 8. Which showed me that my array had bogus values. Doing a step by step debugging showed me that when glGenBuffers was called it was overwriting my values. I went back and checked my code and I realized that my mistake was calling Vertex vec[4]; instead of Vertex *vec = new Vertex[4]; and the stack's values was overwritten by another function.
It is often mistaken that arrays are pointers, when in reality they are not. Array expressions decay into a pointer in many circumstances (sizeof being one of the exceptions to this rule) but there are significant differences between array themselves and pointers. For further information, read this excellent post.
As for the problem in hand, once the array decays into a pointer, it's shape (the first dimension) is lost, which is the reason sizeof returns the size of a pointer and not that of the entire array.
float arr[3] = { 1.0f, 2.0f, 3.0f }; // sizeof(arr) = 3 * sizeof(float)
float *ptr = arr; // sizeof(ptr) = sizeof(float*)
how can I pass an array of Vertex to my glBufferData that's being returned as a pointer from another class ?
You've three options to do this. Make the class return the pointer to the array (as you do now) and add another function to expose the count of the elements in it i.e. its length, with which you may calculate the size in bytes. However, this is not a very elegant solution, since in C++ one can have references to array, which brings us to the next solution (live example):
float (&MyClass::GetData()) [3] const
{
return arr;
}
The sizeof operator on this reference would rightly return the size of the whole array. You might use std::array instead to avoid the arcane expression like so std::array<float, 3>& MyClass::GetData() const;.
Another wiser option is to do away with arrays altogether and use std::vector, to which you can return a reference:
std::vector<float> const& MyClass::GetData() const
{
return vec;
}
At the other end
auto const &vec = obj->GetData();
float *data = vec.data();
size_t data_len = vec.size() * sizeof(float);
The additional advantage in using std::vector is that the length can vary at runtime; also lesser bugs due to the hairy nature of arrays.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I'm learning openGL from this website https://learnopengl.com
To draw a simple triangle, the first example provided by this website is using a float array:
float triangle[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};
Of course it work well. But beside this example I have tried to use a basic structur carrying glm::vec3 as follow :
struct Position {
glm::vec3 pos;
};
Then to store the same data as the float array I just did a simple array of Position as follow :
Position leTriangle[3];
leTriangle[0].pos = glm::vec3(-0.5f, -0.5f, 0.0f);
leTriangle[1].pos = glm::vec3(0.5f, -0.5f, 0.0f);
leTriangle[2].pos = glm::vec3(0.0f, 0.5f, 0.0f);
(Of course all this was just to test if it could work)
Then after having sent my array of Position to the GPU using glBufferData and glVertexAttribPointer the triangle wasn't appearing anymore. So, juste to see if the array I sent where similar to the exemple floating array, I did this :
float* test = (float*)&leTriangle;
for(int e = 0; e < ((12/4) * 3); e++,test++){
std::cout << *test << std::endl;
}
And instead of printing the same floating array, it print this :
0.0f,-0.5f,-0.5f,// left
0.0f,-0.5f,0.5f, // right
0.0f,0.5f,0.0f // top
X and Z value has been inverted. So I guessed the order of the GLM data is not what I thought.
Can someone explain me why data in GLM are ordered this way? Should I respect this order or maybe I should try to reverse it? Maybe it's not important, is it something I should have in mind when placing 3D object?
Data in the glm::vec3 struct is ordered correctly. You could easily verify this by checking up memory, pointed by your test variable:
You are getting strange results, because something in your test is wrong, you are clearly reading glm::vec3 fields backward. Code in your question does not exhibit this behavior. Perhaps you should check how you assign your data.
I have this 2D array of GLfloats:
static constexpr GLfloat facenormals[6][12] = {
{
0.0f, 1.0f, 0.0f, // TOP
},
{
0.0f, -1.0f, 0.0f, // BOTTOM
},
{
0.0f, 0.0f, 1.0f, // FRONT
},
{
0.0f, 0.0f, -1.0f, // BACK
},
{
1.0f, 0.0f, 0.0f, // RIGHT
},
{
-1.0f, 0.0f, 0.0f, // LEFT
}
};
and an std::vector<GLfloat>. My goal is to add the data from one of the sub-arrays of my 2D array to the end of the vector. My first attempt was this:
normals.insert(
normals.end(),
&CubeData::facenormals[direction],
&CubeData::facenormals[direction] + 12
);
But when building the solution I get the error "cannot convert from 'const GLfloat [12]' to '_Objty". I tried changing the arguments of the insert() call to this:
normals.insert(
normals.end(),
CubeData::facenormals + 12 * direction,
CubeData::facenormals + 12 * (direction + 1)
);
but I get the same error when compiling.
How do I do this correctly, and what does the error mean?
_Objty is the name for the vector's type parameter in MSVC's particular implementation of the standard library. So the compiler is telling you you can't convert a value of type GLfloat[12] to whatever the vector is storing.
But why were you trying to insert arrays?
The problem lies in those extra & in the call to insert. This'll fix it:
normals.insert(
normals.end(),
CubeData::facenormals[direction],
CubeData::facenormals[direction] + 12
);
CubeData::facenormals is an array of arrays, so CubeData::facenormals[direction] is an array. That would normally decay in a pointer automatically, which would give you what you want, but by prepending a &, you instead get a pointer to that array. That pointer gets dereferenced into an array.
By removing &, you let the array decay to a GLfloat*, and then that gets dereferenced into something that you can assign to a GLfloat.
I have an array of cube objects initialised like so (index 0 not used here as that's for the player):
game_object[1] = new GameObject();
game_object[1]->setPosition(vec3(7.0f, 0.0f, 0.0f));
game_object[2] = new GameObject();
game_object[2]->setPosition(vec3(14.0f, 0.0f, 0.0f));
game_object[3] = new GameObject();
game_object[3]->setPosition(vec3(21.0f, 0.0f, 0.0f));
game_object[4] = new GameObject();
game_object[4]->setPosition(vec3(36.0f, 0.0f, 0.0f));
game_object[5] = new GameObject();
game_object[5]->setPosition(vec3(42.0f, 0.0f, 0.0f));
I have a render function in which they are drawn:
glDrawElements(GL_TRIANGLES, 3 * INDICES, GL_UNSIGNED_INT, NULL);
In my update they move to the left as expected. To do this I am just adding another vector to their positions:
for (int i = 1; i < MAX_CUBES; i++)
{
game_object[i]->setPosition(game_object[i]->getPosition() + vec3(-0.03, 0.0, 0.00));
}
However, I want the cubes to repeat this until the user exits the game. I made a reset function to send them back to their starting positions:
void Game::reset()
{
game_object[0]->setPosition(vec3(0.0f, 0.0f, 0.0f));
game_object[1]->setPosition(vec3(7.0f, 0.0f, 0.0f));
game_object[2]->setPosition(vec3(14.0f, 0.0f, 0.0f));
game_object[3]->setPosition(vec3(21.0f, 0.0f, 0.0f));
game_object[4]->setPosition(vec3(36.0f, 0.0f, 0.0f));
game_object[5]->setPosition(vec3(42.0f, 0.0f, 0.0f));
}
This function gets called in the update when the final cube's position is off screen to the left:
if (game_object[5]->getPosition().x <= 0.0)
{
reset();
}
However, this isn't working. Nothing resets after the last cube goes to the left.
Not sure how you are using game_object here but looks very error prone. If you have MAX_CUBES = 5 (as you do have 5 cubes), then that for-loop will miss the last one. Adding further objects (e.g. for gaps, vertical rules, hazards, etc.) will make it even more so.
for (int i = 1; i < MAX_CUBES; i++)
{
game_object[i]->setPosition(game_object[i]->getPosition() + vec3(-0.03, 0.0, 0.00));
}
If MAX_CUBES = 5, then it will move index 1, 2, 3, 4, and not 5, which is the one you check in the condition. 5 will just stay at 42 permanently (is that off-screen?).
Stepping through the code in a debugger will make a problem like this pretty clear regardless, and is an essential tool for programming. Maybe the code just never reaches the if (game_object[5]->getPosition().x <= 0.0) check in the first place? Is there any return in that update function, or is that condition inside another one of some sorts?
Because in your comment you noted that game_object[5]->getPosition().x returns a correct value, the most likely problem is with your reset() function and the setPosition function you are using.
1. Check if set position is working in the first place
Perhaps there is an error with setPosition().
After you set the position using setPosition() and then log the object's coordinates using getPosition() does it return the position you expect?
If not, something is wrong with setPosition.
If so, then...
2. You probably changed the position but failed to render it!
This is a very common problem lol
There is a very high chance you changed the position of the object BUT didn't update what's shown on the screen!
3. Side note for scalability
There is a much more efficient and scalable way of doing a reset if you have eventually have more than 5 objects, by placing their reset values in an array and looping through them:
#define MAX_CUBES 6
double resetPositions_x[MAX_CUBES] = {0.0, 7.0, 14.0, 21.0, 36.0, 42.0};
void Game::reset()
{
for(int i=0;i<MAX_CUBES;i++){
game_object[i]->setPosition(vec3(resetPositions_x[i], 0.0f, 0.0f));
}
}
(Also, it seems every x reset position is a multiple of 7 except 36.0 -> is that a mistake?)
I'm trying to do something very simple but I'm doing something wrong.
Header file:
class Example
{
public:
typedef struct
{
float Position[3];
float Color[4];
float TexCoord[2];
} IndicatorVertex;
void doSomething();
};
.cpp file:
void Example::doSomething()
{
IndicatorVertex *vertices;
vertices = IndicatorVertex[] {
{{-1.0, 1.0, 1.0}, {1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
{{1.0, 1.0, 1.0}, {1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
};
}
Upon compilation, I'm getting Error:(12, 13) unexpected type name 'IndicatorVertex': expected expression.
(I'm intentionally not using std::vector etc; I'm deliberately using C features in a c++11 setting.)
You can't create a dynamic array like you do, you need to define an actual array like
IndicatorVertex vertices[] = { ... };
If you later need a pointer then remember that arrays naturally decays to pointers to their first element. So if you, for example, want to call a function which expects a IndicatorVertex* argument, just pass in vertices and it will still work as expected.
If you want to have different arrays and make vertices point to one of them, then you have to define the arrays as shown above, and make vertices point to one of them. Like
IndicatorVertex vertices1[] = { ... };
IndicatorVertex vertices2[] = { ... };
// ...
IndicatorVertex* vertices = vertices1;
// ...
vertices = vertices2;
The code below is from a book. When I try to run it, it fails on the line
osg::ref_ptr geom = new osg::Geometry();
and, the output window does not seem to contain much information on why it crashes, other than telling me that it did. Any idea what I may be doing wrong in the code below? Thanks in advance.
Here is the windows error popup when I try to run this in Visual Studio 2010(windows 7 64)
Windows has triggered a breakpoint in OSGPracticeLab.exe.
This may be due to a corruption of the heap, which indicates a bug in OSGPracticeLab.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while OSGPracticeLab.exe has focus.
The output window may have more diagnostic information.
On attempting to debug the code, I was able to trace the problem to the new function call. In the code below, it seems the while loop is skipped over, and a null value is returned for p(no memory allocated, and so my Geometry object in the code below this, is not instantiated.
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{ // try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{ // report no memory
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
Below is my Program to draw some shapes and display.
#include <osg/ShapeDrawable>
#include <osg/Geode>
#include <osgViewer/Viewer>
int main()
{
//An octahedron is a polyhedron having eight triangle faces.
//It is really a nice example to show why primitive indexing is important
// we will sketch the octahedron structure now
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(6);
//octahedron has six vertices, each shaed by four triangles.
//withe the help of an index array and the osg::DrawElementsUInt class, we can allocate
//a vertex array with only six elements
(*vertices)[0].set( 0.0f, 0.0f, 1.0f);
(*vertices)[1].set(-0.5f,-0.5f, 0.0f);
(*vertices)[2].set( 0.5f,-0.5f, 0.0f);
(*vertices)[3].set( 0.5f, 0.5f, 0.0f);
(*vertices)[4].set(-0.5f, 0.5f, 0.0f);
(*vertices)[5].set( 0.0f, 0.0f,-1.0f);
//The osg::DrawElementsUInt accepts a size parameter besides the drawing mode parameter, too.
//After that, we will specify the indices of vertices to describe all eight triangle faces.
osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(GL_TRIANGLES, 24);
(*indices)[0] = 0; (*indices)[1] = 1; (*indices)[2] = 2;
(*indices)[3] = 0; (*indices)[4] = 2; (*indices)[5] = 3;
(*indices)[6] = 0; (*indices)[7] = 3; (*indices)[8] = 4;
(*indices)[9] = 0; (*indices)[10]= 4; (*indices)[11]= 1;
(*indices)[12]= 5; (*indices)[13]= 2; (*indices)[14]= 1;
(*indices)[15]= 5; (*indices)[16]= 3; (*indices)[17]= 2;
(*indices)[18]= 5; (*indices)[19]= 4; (*indices)[20]= 3;
(*indices)[21]= 5; (*indices)[22]= 1; (*indices)[23]= 4;
//To create a geometry with a default white color, we only set the vertex array
//and the osg::DrawElementsUInt primitive set. The normal array is also required but is not easy
//to compute manually. We will use a smoothed normal calculator to automatically obtain it. This calculator
//will be described in the next section, Using polygonal techniques.
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();
geom->setVertexArray( vertices.get() );
geom->addPrimitiveSet( indices.get() );
//osgUtil::SmoothingVisitor::smooth( *geom );
//Add the geometry to an osg::Geode object and make it the scene root
osg::ref_ptr<osg::Geode> root = new osg::Geode;
root->addDrawable( geom.get() );
osgViewer::Viewer viewer;
viewer.setSceneData( root.get() );
return viewer.run();
}
int drawShapeUsingVertices()
{
//Create the vertex array and push the four corner points to the back of the array by using vector like operations:
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
vertices->push_back( osg::Vec3(0.0f, 0.0f, 0.0f) );
vertices->push_back( osg::Vec3(1.0f, 0.0f, 0.0f) );
vertices->push_back( osg::Vec3(1.0f, 0.0f, 1.0f) );
vertices->push_back( osg::Vec3(0.0f, 0.0f, 1.0f) );
//We have to indicate the normal of each vertex; otherwise OpenGL will use a default (0, 0, 1) normal vector
//and the lighting equation calculation may be incorrect. The four vertices actually face the same direction,
//so a single normal vector is enough. We will also set the setNormalBinding() method to BIND_OVERALL later.
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
normals->push_back( osg::Vec3(0.0f,-1.0f, 0.0f) );
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
//here We will indicate a unique color value to each vertex and make them colored. By default,
//OpenGL will use smooth coloring and blend colors at each vertex together:
colors->push_back( osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f) );
colors->push_back( osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f) );
colors->push_back( osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f) );
colors->push_back( osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f) );
//Next, we create the osg::Geometry object and set the prepared vertex, normal, and color arrays to it.
//We also indicate that the single normal should be bound to the entire geometry and that the colors
//should be bound per vertex:
osg::ref_ptr<osg::Geometry> quad = new osg::Geometry;
quad->setVertexArray( vertices.get() );
quad->setNormalArray( normals.get() );
quad->setNormalBinding( osg::Geometry::BIND_OVERALL );
quad->setColorArray( colors.get() );
quad->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
//The last step required to finish a geometry and add it to the scene graph is to specify the primitive set.
//A newly allocated osg::DrawArrays instance with the drawing mode set to GL_QUADS is used here, in order to
//render the four vertices as quad corners in a counter-clockwise order:
quad->addPrimitiveSet( new osg::DrawArrays(GL_QUADS, 0, 4) );
//Add the geometry to an osg::Geode object and render it in the scene viewer:
osg::ref_ptr<osg::Geode> root = new osg::Geode;
root->addDrawable( quad.get() );
osgViewer::Viewer viewer;
viewer.setSceneData( root.get() );
return viewer.run();
}
I didn't have any problems with the code. Took it from the beginners guide and it works fine:
#include <osg/Geometry>
#include <osg/Geode>
#include <osgViewer/Viewer>
int main()
{
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
vertices->push_back( osg::Vec3(0.0f, 0.0f, 0.0f) );
vertices->push_back( osg::Vec3(1.0f, 0.0f, 0.0f) );
vertices->push_back( osg::Vec3(1.0f, 0.0f, 1.0f) );
vertices->push_back( osg::Vec3(0.0f, 0.0f, 1.0f) );
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
normals->push_back( osg::Vec3(0.0f,-1.0f, 0.0f) );
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
colors->push_back( osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f) );
colors->push_back( osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f) );
colors->push_back( osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f) );
colors->push_back( osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f) );
osg::ref_ptr<osg::Geometry> quad = new osg::Geometry;
quad->setVertexArray( vertices.get() );
quad->setNormalArray( normals.get() );
quad->setNormalBinding( osg::Geometry::BIND_OVERALL );
quad->setColorArray( colors.get() );
quad->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
quad->addPrimitiveSet( new osg::DrawArrays(GL_QUADS, 0, 4) );
osg::ref_ptr<osg::Geode> root = new osg::Geode;
root->addDrawable( quad.get() );
osgViewer::Viewer viewer;
viewer.setSceneData( root.get() );
return viewer.run();
}
I recommend you check your project properties.
Have you included additional include directories: $(OSG_ROOT)\include;$(OSG_SOURCE)\include;$(OSG_ROOT)\include\osg;
If you're in Debug mode, do you have this in your preprocessor definitions? _DEBUG;WIN32;
Did you specify your linker additional directory: $(OSG_ROOT)\lib
Did you specify linker additional dependencies?: osgWidgetd.lib;osgVolumed.lib;osgViewerd.lib;osgUtild.lib;osgTextd.lib;osgTerraind.lib;osgSimd.lib;osgShadowd.lib;osgPresentationd.lib;osgParticled.lib;osgManipulatord.lib;osgGAd.lib;osgFXd.lib;osgDBd.lib;osgd.lib;osgAnimationd.lib;OpenThreadsd.lib;;;;;;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
Have you specified Configuration properties > debugging > Working directory as: $(OSG_ROOT)\bin
If an extreme case, it may be because your Visual Studio installation is corrupted. Try reinstalling Visual Studio and if the OSG installation was corrupted, then reinstall OSG (build from source). Mentioning this because a friend of mine had problems running OSG because his Visual Studio was corrupted. Reinstalling fixed it.
Does osg build? Did you run the "Install" project from within OSG? Even if you did, the permissions can be borked in Win7 - you might have to manually install to Program Files.
Your sample posted above compiled perfectly for me on Win7 / VS 2008 / Win32-Release build config, built against version 3.1.0 of OSG. I just replaced the main from one of the Example Projects in the OSG solution with the code you pasted above, it builds and runs without the error you listed.
I am using OSG from the trunk - probably at least a minor version ahead of any of the prebuilds, but it should work from the prebuilds if you have your paths, etc., set right. You could, of course, also try starting from the authors' download of the examples: http://www.skew-matrix.com/OSGQSG/ - they already have the project files, etc., set up correctly.
You don't define the osg::Geometry class in your code, so the most likely problem is that you aren't properly linking to the object or library where it is defined.