Why My Program Is Running Slow Whenever I Use Heavy OBJ Files?

Hello Professionals,

Good day. I am trying to use different OBJ files to my program in order to read the details of OBJ files (particularly the faces and vertices), then I try to view them using the OpenGL viewer. When I use basic OBJs such as dodecahedron, it works okay (not slow), but when I am using bunny, armadillo, etc., it is extremely slow. What should I do to improve my program?
By "OBJ file" you mean data file format that defines geometry for 3D objects? https://en.wikipedia.org/wiki/OBJ

You somehow read data from file(s) into memory, and then transfer data from memory into GPU. There are, I assume, multiple ways to do the latter in OpenGL.

It should be obvious that a larger set of data takes more time to process. The question is, how do the methods that you have selected do scale up and whether there were more suitable alternatives?

Do we know how you do things now? No. That impairs our ability to help.


Have you profiled your program to see which steps are most time-consuming / are most affected by size of data?
Hello @keskiverto. I would like to know if there "were more suitable alternatives"?
How will I be able to "profile" my program in order to see why it is time-consuming and how the size of the data affects?
do you have a solid 3-d graphics card on the system you are developing on? Doesn't have to be the best, but if your card is bad enough even a simple object can be costly.

Most graphics do a mix of geometry and textures. That is, use less polygons and more textures and it will render faster. Every surface of every polygon means normals (affects lighting) and vertices and processing. High res textures can be costly too; its a delicate balance. A screenfull of recursively generated high face count spheres can bring even a gaming computer to its knees. with the right texture a square box can look like a sphere can costs 6 normals/textures vs 50+ normals and textures (for a good looking sphere), for a really simple example.

some of the objects you find online and in demo programs are all polygons and some are quite intense on the hardware.
Last edited on
Are the tri or quad counts of your bunny excessively high? For comparison, a weapon in some fps shooter might be around 15k tris. Other characters might be around 6-10k tris.

Going off of what jonnin said, if you can play relatively-recent games (doesn't even need to be a AAA HD game) on average settings, and can load the same OBJ in a program like Blender without lag, that means the problem is definitely on your program's side. Otherwise, we don't have enough information to know.

Since you're only dealing with one object, it sounds like your program might be constantly re-allocating your vertices or textures. But that's just a guess, because we have no source code.
Last edited on
do you have a solid 3-d graphics card on the system you are developing on?

Upon checking my laptop's properties, here's what I saw:

Processor: Intel(R) Core(TM) i7-5557U CPU @ 3.10GHz (4 CPUs) 3.09 GHz
Installed RAM: 8.00 GB
System type: 64-bit operating system, x64-based processor
Page file: 7829MB used, 11528MB available

Then after typing dxdiag, I saw this:

Name: Intel(R) Iris(TM) Graphics 6100
Manufacturer: Intel Corporation
Chip Type: Intel(R) Iris(TM) Graphics Family
DAC Type: Full Display Device
Approx. Total Memory: 4174 MB
Display Memory (VRAM): 128 MB
Shared Memory: 4046 MB
Current Display Mode: 2560 x 1440 (32 bit) (60Hz)
Monitor: Generic PnP Monitor


Are these specs okay for rendering heavy OBJ files?
Here's the structure of my source code...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#include <iostream>
#include <fstream>
#include <cmath>
#include "CIsoSurface.h"
#include <iomanip>
#include <GL/glut.h>
#include "importOBJ.h"

using namespace std;
int lines_faces = 0, lines_faces1 = 0;

void mouseCB(int button, int stat, int x, int y);
void mouseMotionCB(int x, int y);

bool mouseLeftDown;
bool mouseRightDown;
float mouseX, mouseY;
float cameraAngleX = 0;
float cameraAngleY = 0;
float cameraDistance;
bool dlUsed;

GLdouble vertex[35000000][3] = { //for random 35000000
	{},
};

int face[35000000][3] = { //for random
	{},
};


void idle()
{
	glutPostRedisplay();
}



void display(void)
{

	//Start Importing values from any OBJ Files...

	vector<Point> vertices;
	vector<Triangle> faces;
	string filename = "C:/Users/user/Downloads/bunny/bunny.obj"; //string filename = "file.OBJ";

	getFacesAndVertices(filename, vertices, faces);

	cout << "Vertices:\n";
	int point = 0;
	for (Point p : vertices)
	{
		vertex[point][0] = p.x;
		vertex[point][1] = p.y;
		vertex[point][2] = p.z;
		//cout << vertex[point][0] << "  " << vertex[point][1] << "  " << vertex[point][2] << '\n';
		point++;
	}
	
	cout << "\n\n";
	int triangles = 0;
	cout << "Triangles\n";
	for (Triangle t : faces) {
		face[triangles][0] = t.i-1;
		face[triangles][1] = t.j-1;
		face[triangles][2] = t.k-1;
		//cout << face[triangles][0] << "  " << face[triangles][1] << "  " << face[triangles][2] << '\n';
		triangles++;
		lines_faces1++;
	}

	// lighting
	// setup lighting 0 ; STARTS HERE!!!!
	glEnable(GL_LIGHT0);
	const GLfloat LightAmbient[4] = { 0.1f, 0.1f, 0.1f, 1.0f };
	const GLfloat LightDiffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	const GLfloat LightSpecular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular);
	const GLfloat diffuseCoeff[] = { 0.2, 0.4, 0.9, 1.0 };
	const GLfloat specularCoeff[] = { 1.0,1.0,1.0,1.0 };
	//Color material of object when light is enabled
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, diffuseCoeff);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularCoeff);
	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 25.0);


	const GLfloat light_x = 1.0f;
	const GLfloat light_y = 1.0f;
	const GLfloat light_z = 1.0f;
	const GLfloat LightPosition[4] = { light_x, light_y, light_z, 1 };

	glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);

	//glEnable(GL_LIGHTING); //This enables lighting
	// setup lighting 0 ; ENDS HERE!!!!
	int i;
	int j;


	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	glLoadIdentity();

	/*Viewpoint position and line of sight direction */
	gluLookAt(3.0, 4.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

	/*Rotation of figure */
	//glRotated((double)r, 0.0, 1.0, 0.0);

	//insert from teapot
	// tramsform camera ; this helps to activate the the mouseCB and mouseMotionCB :) 
	glTranslatef(0, 0, cameraDistance);
	glRotatef(cameraAngleX, 1, 0, 0);   // pitch
	glRotatef(cameraAngleY, 0, 1, 0);   // heading
										

	glColor3d(0.0, 1.0, 0.0);

	
	glBegin(GL_TRIANGLES); //original GL_QUADS
	for (j = 0; j <lines_faces1; ++j) { //original j<6
		for (i = 0; i <3; ++i) { //original i<4
			glVertex3dv(vertex[face[j][i]]);
		}
	}

	glEnd();
	glEnable(GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
	//TO SHOW LINES IN MARCHING CUBES!
	//glColor3f(0.0, 0.0, 0.0);
	//glBegin(GL_LINE_LOOP);
	//for (j = 0; j <lines_faces1; ++j) { //original j<6
	//	for (i = 0; i <3; ++i) { //original i<4
	//		glVertex3dv(vertex[face[j][i]]);
	//	}
	//}
	//glEnd();

	lines_faces1 = 0;
	point = 0, triangles = 0;
	glutSwapBuffers();

}

void resize(int w, int h)
{
	glViewport(0, 0, w, h);

	/*Setting perspective transformation matrix */
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(30.0, (double)w / (double)h, 1.0, 100.0);

	/*Model view transformation matrix setting */
	glMatrixMode(GL_MODELVIEW);
}


void keyboard(unsigned char key, int x, int y)
{
	switch (key) {
	case 'q':
	case 'Q':
	case '\033': /*'\ 033' is the ASCII code of ESC */
		exit(0);
	default:
		break;
	}
}
void init(void)
{
	glClearColor(1.0, 1.0, 1.0, 1.0);
}
void mouseCB(int button, int state, int x, int y)
{
	mouseX = x;
	mouseY = y;

	if (button == GLUT_LEFT_BUTTON)
	{
		if (state == GLUT_DOWN)
		{
			mouseLeftDown = true;
		}
		else if (state == GLUT_UP)
			mouseLeftDown = false;
	}

	else if (button == GLUT_RIGHT_BUTTON)
	{
		if (state == GLUT_DOWN)
		{
			mouseRightDown = true;
		}
		else if (state == GLUT_UP)
			mouseRightDown = false;
	}
}


void mouseMotionCB(int x, int y)
{
	if (mouseLeftDown)
	{
		cameraAngleY += (x - mouseX);
		cameraAngleX += (y - mouseY);
		mouseX = x;
		mouseY = y;
	}
	if (mouseRightDown)
	{
		cameraDistance += (y - mouseY) * 0.2f;
		mouseY = y;
	}

	glutPostRedisplay();
}

///////////////////////////////////////////////////////////////////////////////
// initialize global variables
///////////////////////////////////////////////////////////////////////////////
bool initSharedMem()
{
	mouseLeftDown = mouseRightDown = false;
	dlUsed = true;

	return true;
}


int main(int argc, char * argv[])
{

	initSharedMem();
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowSize(1000, 1000);
	glutInitWindowPosition(100, 100);
	glutCreateWindow(argv[0]);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
	glEnable(GL_DEPTH_TEST);
	glutDisplayFunc(display);
	glutReshapeFunc(resize);
	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouseCB);
	glutMotionFunc(mouseMotionCB);
	init();
	glutMainLoop();
	system("pause");
	return 0;
}


For the "importOBJ.h", here's my code I created:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;

struct Point { double x, y, z; };       // Spatial coordinates

struct Triangle { int i, j, k; };       // Vertex numbers for a triangle
										
void getFacesAndVertices(string filename, vector<Point> &vertices, vector<Triangle> &faces)
{
	faces.clear();   vertices.clear();                      // comment out to add to an existing collection

	string line;
	char c;
	int i, j, k;
	double x, y, z;
	string si, sj, sk;

	ifstream in(filename);
	while (getline(in, line))                           // read whole line
	{
	if (line.find_first_of("vVnNfF") == string::npos) continue;     // skip pointless lines

		istringstream ss(line);                            // put line into a stream for input
		ss >> c;                                             // get first character
		switch (c)
		{
		case 'v':                                         // VERTICES
		case 'V':                                         //    (either case)
			ss >> x >> y >> z;                             // read from internal stream
			vertices.push_back({ x, y, z });             // add to vector
			break;
		case 'n':                                         // VERTICES NORMALS
		case 'N':
			break;
		case 'f':                                         // FACES
		case 'F':                                         //    (either case)
		ss >> si >> sj >> sk;                          // FIRST, read into individual strings
	i = stoi(si);  j = stoi(sj);  k = stoi(sk);  // Get the FIRST integer from each string
			faces.push_back({ i, j, k });                // add to vector
			break;
		default:
			break;
		}
	}
	in.close();
}


Last edited on
https://www.notebookcheck.net/Intel-Iris-Graphics-6100.125591.0.html


Overall, the Iris 6100 is about as fast as a dedicated GeForce 820M and outperforms the former Iris Graphics 5100 by 20 - 25 percent.

Older and less demanding games such as Diablo III, Counter-Strike: GO, [or] Dota 2 can be played fluently in 1366 x 768 pixels and high settings. More demanding titles, however, may require lower settings or won't run at a decent framerate at all.


Diablo III and CS:GO were made in 2012.
Dota 2 was made in 2013.
So it's a bit old, but nothing unbearable.

Before you start shopping for a new graphics card,

1. What does "extremely slow" mean? What actual frame rate are you getting?

2. I want to re-iterate that I sincerely doubt that one object, whether hi-poly or not, in itself is making your program extremely slow. It's possible that you're re-allocating textures or vertices every frame.

There's 1000 ways to use OpenGL wrongly; we can't see your code, so we don't know. I also suggest the OpenGL forum, because my in-depth knowledge of OpenGL is not vast, although I know the basics.

Edit: I see your source code now. Was editing my post.
Last edited on
Please let me know if you are having problems running my code. Thank you and God bless. :)
P.S. Regarding the sample OBJ files. Here are the links...

Dodecahedron: https://github.com/angerangel/LCR3D/blob/master/3D-models/dodecahedron.obj

Bunny: http://graphics.stanford.edu/pub/3Dscanrep/bunny.tar.gz

For replacing the OBJ file in my program, the path "C:/Users/user/Downloads/bunny/bunny.obj" can be changed to any desired path location of a specific OBJ file.

For the CIsoSurface.h and CIsoSurface.cpp, they can be seen here:
http://paulbourke.net/geometry/polygonise/rchandra.zip

Thanks a lot and God bless. :)
I didn't realize you were using old OpenGL. I can't comment on that specifically, but I see a huge, red flag.

Assuming that your display(void) function is being called every frame, you are loading your OBJ file every frame, when display() calls getFacesAndVertices().

I can already guarantee you that your File I/O is the slowest part of your program. That's causing the disastrous performance, unless I'm misreading your source code.

You should only have to load the data from your file once (for example, in main), and then store it in your vector objects or whatever.
After the initial load, you should only have to read from what's already in your vectors. Let me know if that makes sense or not.
Last edited on
Thank you @Ganado for our insights. Hmm, if my FILE I/O is the problem, then how will I load my data once?
Here are two different programs. How many times is the value of 'bar' set in them?
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <string>

void foo() {
  std::string bar = "Hello\n";
  std::cout << bar;
}

int main() {
  for ( int i=0; i<10; ++i ) foo();
}


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <string>

std::string bar;

void foo() {
  std::cout << bar;
}

int main() {
  bar = "Hello\n";
  for ( int i=0; i<10; ++i ) foo();
}
So... What's the consensus on something like this? Is it better to pass pointers or to use a global, when it's going to be a dozen megs of bunny rabbit read from a file?
Copy value, send reference, pass (smart) pointer, use global ... there are multiple ways to relay information.

Overall, use globals when you have to. That is, almost never.

(const) Reference sounds nice?
Here are two different programs. How many times is the value of 'bar' set in them?


@keskiverto Thanks for the sample programs. So this means that the first one is more costly than the bottom code? Because the initialization of bar is repeated many times during foo call?
Yes. In that sample it isn't significant, but your program calls getFacesAndVertices() (disk access is slow) to do "initialization" and the display() probably runs way more than 10 times?
Thank you @keskiverto, I somehow understand what you want to say. Hmm, I am wondering if there's a code in order to know the number of seconds it takes for my program to run? (From the time I clicked the "Run" button in C++)?
C++ does not have "Run button"; IDE's do. IDE's might have profiling tools too.

On command line one can use tools like GNU 'time':
The GNU time utility runs another program, collects information about
the resources used by that program while it is running, and displays
the results.


One can, at least with GCC, compile a binary that has profiling instructions. When the binary is run, it saves counter data. Another tool can show from data how many times each function was called, by whom, and how much CPU time was used.
I wouldn't worry, for now, about how long it takes to initially load your program's window. If you start making a more complicated program, and start noticing speed decreases, then you can start to profile which parts are slow.

But you could figure it out, the following link shows both C++ and newer C++11 solutions:
https://stackoverflow.com/questions/2808398/easily-measure-elapsed-time

But in case it isn't clear, in your current design you should be calling getFacesAndVertices() only once, probably in your main where you initialize the other parts of your program. This means you have to put vertices and faces in a broader scope, because GLUT's display function is pretty awful to use by itself (you already have some global variables). Often, such variables can be wrapped in a class to avoid making everything be a global variable.


Also, currently, your vertex array is sizeof(double) * [35,000,000] * [3] bytes = 840,000,000, or about 840 MB. Then, your face array has half this amount (int). This is pretty big, generally large arrays like these should be on the heap. BUT: I don't see why you need both your vertex and face arrays and the std::vectors. I would just keep the std::vectors.

Edit: In fact, the only reason your program doesn't crash with your vertex and face arrays is that global variables can be implicitly put on the heap.
Last edited on
Topic archived. No new replies allowed.