EDIT for moderators:
Yes that "one might think that swapbuffer is slow" was me. Heh. :D Could someone change the topicname to "Optimizing OpenGL"? Thanks.

Hey guys,

I'm trying to optimize this OpenGL program, so the problem isn't C, but the program is.

The program loads a vanilla WaveFront .obj file, stores all it's data in an array, creates a display list, uses glDrawElements to put it in the display list and then calls the display list as often as it can. For my test file, this "how often as it can" gives an FPS of about 37. While this is acceptable, another program that does the same manages to do it at 60 FPS.

How do I achieve that performance in this program? The bottleneck appears to be the RenderScene function. It only shows when using glFinish(), else one might think SwapBuffer() takes that amount of time (SwapBuffer waits, of course, for the videocard to finish its rendering). Anyway, from beginning RenderScene to the return of SwapBuffer takes too much time. Too much CPU time that is! My Intel blah blah dual core has once core fully eaten up by this code, and I have no clue why.

Here is the output from the sourcecode below:

WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 16
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 16
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 32
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 16
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 16
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 32
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 15
WM_TIMER took:  Time elapsed: 32
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 0
WM_TIMER took:  Time elapsed: 16
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 16
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 16
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 31
WM_TIMER took:  Time elapsed: 32
WM_TIMER took:  Time elapsed: 15
WM_TIMER took:  Time elapsed: 32
WM_TIMER took:  Time elapsed: 31
FPS: 36

How can I speed this up?
Thanks in Advance,
Nick

Code:
BowViceJet.c

#include <stdio.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glext.h>
#include <time.h>
#include "loadObj.h"

#define TIMEELAPSED {printf("\tTime elapsed: %lu\n", clock() - startTime);}

int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow);
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

void dbgError(char *msg);
void MySetPixelFormat(HDC hDC);

void RenderScene();
void ChangeSize(int width, int height);
int initGL();
void pressKey(WPARAM key);

const static char className[] = "OpenGLClass";
const static char appName[]   = "OpenGL Window";

GLfloat xRot, yRot, zRot;
GLfloat xDis, yDis, zDis;

float *vertices, *normals;
int *triangleindex, *quadindex;
unsigned int quadcount, trianglecount;

int err;
char *globcmdLine;

unsigned int globDisplayLists = 0;

char optFrontFace = 0; //CCW default

void dbgError(char *msg){
  fprintf(stderr, "Error @ %s. GetLastError(): %lu", msg, GetLastError());
  return;
}

int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow){
  globcmdLine = cmdLine;
  MSG msg;
  WNDCLASS wc;
  HWND hWnd;

  wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  wc.lpfnWndProc   = (WNDPROC) WndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = instance;
  wc.hIcon         = NULL;
  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = NULL;
  wc.lpszMenuName  = NULL;
  wc.lpszClassName = className;

  if(RegisterClass(&wc) == 0){
    dbgError("RegisterClass()");
    return -1;
  }

  const DWORD style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
  const int xPos   = 0;
  const int yPos   = 0;
  const int width  = 640;
  const int height = 480;

  hWnd = CreateWindow(className, appName, style, xPos, yPos, width, height, NULL, NULL, instance, NULL);

  if(hWnd == NULL){
    dbgError("CreateWindow()");
    return -1;
  }

  ShowWindow(hWnd, SW_SHOW);
  UpdateWindow(hWnd);

  int ret;

  while((ret = GetMessage(&msg, NULL, 0, 0)) > 0){
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  if(ret == -1){
    dbgError("GetMessage()");
    return -1;
  }
  printf("Exiting from WinMain! G'bye.\n");
  return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
  static HGLRC hRC = NULL;
  static HDC   hDC = NULL;
  clock_t startTime;

  switch(msg){
    case WM_TIMER:
      startTime = clock();
      RenderScene();
      xRot += 0.5;
      yRot += 0.5;
      zRot += 0.5;
      SwapBuffers(hDC);
      printf("WM_TIMER took: "); TIMEELAPSED
      break;

    case WM_CREATE:
      hDC = GetDC(hWnd);
      MySetPixelFormat(hDC);
      hRC = wglCreateContext(hDC);
      if (hRC == NULL){
        unsigned long err = GetLastError();
        char *msg = calloc(sizeof(char), 512);
        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, msg, 512, 0);
        printf("wglCreateContext() failed: error nr. %lu:\n\t%s\n", err, msg);
      }
      if (wglMakeCurrent(hDC, hRC) == FALSE){
        unsigned long err = GetLastError();
        char *msg = calloc(sizeof(char), 512);
        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, msg, 512, 0);
        printf("wglMakeCurrent() failed: error nr. %lu:\n\t%s\n", err, msg);
      }

      if(initGL() < 0){
        printf("Error loading in model. Exiting");
        exit(1);
      }
      else{
        printf("Model loaded and ready to go!\n");
      }
      //fire as often as it can
      SetTimer(hWnd, 0, 1, NULL);
      printf("Running with GL_VENDOR: %s\n", glGetString(GL_VENDOR));
      break;

    case WM_DESTROY:
      wglMakeCurrent(hDC, NULL);
      wglDeleteContext(hRC);
      PostQuitMessage(0);
      break;

    case WM_PAINT:
      //printf("WM_PAINTING. ;-)\n");
      RenderScene();
      SwapBuffers(hDC);
      ValidateRect(hWnd, NULL);
      break;

    case WM_SIZE:
      ChangeSize(LOWORD(lParam), HIWORD(lParam));
      break;

    case WM_KEYDOWN:
      pressKey(wParam);
      break;

    default:
      return (DefWindowProc(hWnd, msg, wParam, lParam));
      break;
  }

  return 0;
}

void MySetPixelFormat(HDC hDC){
  int pixelFormat;

  static PIXELFORMATDESCRIPTOR pfd = {
    sizeof(PIXELFORMATDESCRIPTOR),
    1,
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_TYPE_RGBA,
    32,                           //depth of color
    0,0,0,0,0,0,0,0,0,0,0,0,0,
    16           ,               //depth of depthbuffer
    0,0,0,0,0,0,0};

  pixelFormat = ChoosePixelFormat(hDC, &pfd);
  if (SetPixelFormat(hDC, pixelFormat, &pfd) == FALSE){
    unsigned long err = GetLastError();
    char *msg = calloc(sizeof(char), 512);
    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, msg, 512, 0);
    printf("SetPixelFormat() failed: error nr. %lu:\n\t%s\n", err, msg);
  }
  return;
}

void RenderScene(){
  //printf("Rendering scene...\n");
  static clock_t nextSecond = 0;
  static unsigned int framesPerSecond = 0;
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  clock_t startTime = clock();

  //glMatrixMode(GL_MODELVIEW);

  if(optFrontFace){
    glFrontFace(GL_CW);
  }
  else{
    glFrontFace(GL_CCW);
  }

  glPushMatrix();
  glTranslatef(xDis, yDis, zDis-20);

  glRotatef(xRot, 1, 0, 0);
  glRotatef(yRot, 0, 1, 0);
  glRotatef(zRot, 0, 0, 1);

  glColor3f(.5, .6, .6);

  //printf("Quadindex: %p\tTriangleindex: %p\n", quadindex, triangleindex);
  //printf("Quadcount: %u\tTrianglecount: %u\n", quadcount, trianglecount);
  glCallList(globDisplayLists);

  glPopMatrix();

  //glDisableClientState(GL_VERTEX_ARRAY);
  //glDisableClientState(GL_NORMAL_ARRAY);
  framesPerSecond++;

  if(clock() >= nextSecond){
      nextSecond = clock() + CLOCKS_PER_SEC;
      printf("FPS: %u\n", framesPerSecond);
      framesPerSecond = 0;
  }

  return;
}

void ChangeSize(int width, int height){
  if (height == 0) height = 1;
  GLfloat aspect = (GLfloat)width/(GLfloat)height;

  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  gluPerspective(60, aspect, 0.1, 1000);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  return;
}

int initGL(){
  xRot = yRot = zRot = 0;
  xDis = yDis = zDis = 0;

  vertices = normals = NULL;
  triangleindex = quadindex = NULL;
  quadcount = trianglecount = 0;


  glFrontFace(GL_CCW);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
  glEnable(GL_LIGHTING);

  //lighting colors
  GLfloat ambient[] = {0.35, 0.35, 0.35, 1.0};
  GLfloat diffuse[] = {0.5, 0.5, 0.5, 1.0};
  //GLfloat specular[] = {1, 1, 1, 1};
  GLfloat light0Pos[] = {-60, 10, 10, 1};
  GLfloat light1Pos[] = {60, 10, 10, 1};

  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
  glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
  //glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
  glLightfv(GL_LIGHT0, GL_POSITION, light0Pos);
  glEnable(GL_LIGHT0);

  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
  glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
  glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
  //glLightfv(GL_LIGHT1, GL_SPECULAR, specular);
  glLightfv(GL_LIGHT1, GL_POSITION, light1Pos);
  glEnable(GL_LIGHT1);

  glEnable(GL_COLOR_MATERIAL);
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

  //GLfloat specref[] = {1, 1, 1, 1};
  //glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
  //glMateriali(GL_FRONT, GL_SHININESS, 120);

  glClearColor(0.0, 0.0, 0.0, 1.0);
  err = loadObj(globcmdLine, &vertices, &normals, &triangleindex, &quadindex, &quadcount, &trianglecount);
  if(err < 0){
    printf("loadObj failed to load object!");
    return -1;
  }
  if(vertices == NULL || normals == NULL || (triangleindex == NULL && quadindex == NULL)){
    printf("Error! One of the essential pointers == NULL!");
    return -1;
  }

  //printf("Enabling vertex and normal arrays...");
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_NORMAL_ARRAY);
  //printf("Done\n");

  //printf("Vertices: %p\tNormals: %p\n", vertices, normals);
  glVertexPointer(3, GL_FLOAT, 0, vertices);
  glNormalPointer(GL_FLOAT, 0, normals);

  globDisplayLists = glGenLists(1);

  glNewList(globDisplayLists, GL_COMPILE);
      glDrawElements(GL_QUADS, quadcount*4, GL_UNSIGNED_INT, quadindex);
      glDrawElements(GL_TRIANGLES, trianglecount*3, GL_UNSIGNED_INT, triangleindex);
  glEndList();

  return 0;
}

void pressKey(WPARAM key){

#define KEYPRESS(x, a, b, c){\
  case x:\
    a b c;\
    break;\
}

#define KEYPAIR(keyone, keytwo, var, val){\
  KEYPRESS(keyone, var, +=, val);\
  KEYPRESS(keytwo, var, -=, val);\
}

#define OPTION(key, optname){\
  KEYPRESS(key, optname, ^=, 1);\
}

static float STRENGTH = 1;
  switch (key){
    //rotation
    KEYPAIR('Q', 'E', yRot, (STRENGTH*2));
    KEYPAIR('A', 'D', xRot, (STRENGTH*2));
    KEYPAIR('Z', 'C', zRot, (STRENGTH*2));

    //displacement
    KEYPAIR(VK_RIGHT, VK_LEFT, xDis, STRENGTH);
    KEYPAIR(VK_UP, VK_DOWN, yDis, STRENGTH);
    KEYPAIR(VK_CONTROL, VK_NUMPAD0, zDis, STRENGTH);

    //options
    OPTION('F', optFrontFace);

    //STRENGTH adjustment :D
    KEYPAIR('O', 'P', STRENGTH, 0.01);
  }
  printf("Strength: %f\n", STRENGTH);

#define ROUNDTHREESIXTY(x) {  \
  if (x <= -360.0 || x >= 360.0){\
    x = 0;\
  }\
}
  ROUNDTHREESIXTY(yRot);
  ROUNDTHREESIXTY(xRot);
  ROUNDTHREESIXTY(zRot);

#undef ROUNDTHREESIXTY
#undef OPTION
#undef KEYPAIR
#undef KEYPRESS

  return;
}

loadObj.h isn't included for it doesn't matter for the rendering code and is rather long.

Recommended Answers

All 10 Replies

My only suggestion is to try a profiler so that you can see exactly where your program is spending all of its time. If you have access to the code from the other program, profiling it as well might be enlightening.

If you're using a Windows port of GCC like MinGW, you already have a profiler: gprof. Google it for details. I'm sure the Microsoft compilers come with one too, but I don't know what it is.

Gmon.out doesn't contain any useful info, all the times are zero. :S I'm using it from Code::Blocks on my Windows XP x64 machine.

Like this btw:

Flat profile:

Each sample counts as 0.01 seconds.
 no time accumulated

  %   cumulative   self              self     total           
 time   seconds   seconds    calls  Ts/call  Ts/call  name    
  0.00      0.00     0.00     1926     0.00     0.00  _RenderScene
  0.00      0.00     0.00        1     0.00     0.00  _WinMain@16
  0.00      0.00     0.00        1     0.00     0.00  _initGL
  0.00      0.00     0.00        1     0.00     0.00  _loadObj

Strange, when I run Gprof from the command line, seconds do show up, but it doesn't seem to be compatible with winmain instead of main. :(

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 91.30      0.63     0.63        1   630.00   670.00  loadObj
  5.80      0.67     0.04   297473     0.00     0.00  countSpaces
  1.45      0.68     0.01                             isspace
  1.45      0.69     0.01                             malloc
  0.00      0.69     0.00     2093     0.00     0.00  RenderScene
  0.00      0.69     0.00        1     0.00     0.00  ChangeSize
  0.00      0.69     0.00        1     0.00     0.00  MySetPixelFormat
  0.00      0.69     0.00        1     0.00     0.00  WinMain@16
  0.00      0.69     0.00        1     0.00   670.00  initGL

Well, I was rewriting it to GLUT anyway. Thanks for the trouble. Suggestions on how to improve the above code are still welcome of course...

>Well, I was rewriting it to GLUT anyway. Thanks for the trouble. Suggestions on how to improve the above code are still welcome of course...

Perhaps, placing all your macros definition at the top the code would make it more be neat and professional. That was from a quite glance. That’s a huge lot code!

ssharish

Erm.. well now I created them right before they are used, and undefined them right after that.. That's neat I think? They are placed where they are used. That way you don't have to scroll up to view it's implementation.

Agreed nonetheless that the code is mess and it could be cleaned up more. But I don't think the macros are that disturbing.

On a sidenote: How do I get gprof to work with C++ using GLUT and threading? Hahaha.. it doesn't display times now even when I run from the console.. :( I'm downloading VTune as we speak, but I'd like gprof to work, being free and all. :)

Please attach the loadObj.h file. I know I can't help you, but I believe I can learn from you.

Hey, of course. It's pretty straightforward tho, only loading vertices, faces and normals, but not polygons and other things (like textures for instance).

The main bottleneck is I/O: sscanf. The only way I see to speed this program up is to:

Multithread it

Store the .obj files as binary data instead of their ASCII representation, so you won't have to use scanf anymore.

loadObj.c

#include <stdio.h> //obvious
#include <stdlib.h> //malloc and such
#include <string.h> //memory copy stuff
#include <time.h> //time stuff, ofc.
#include <ctype.h> //isspace, isdigit
#include <math.h> //only for fabs

#define TIMEELAPSED {printf("\tTime elapsed: %lu\n", (clock() - startTime)*1000 / CLOCKS_PER_SEC);}

//this is in BowViceJet.c
void dbgError(char *msg);

int countSpaces(char *str){
  int cnt = 0;
  while(*str != '\0'){
    if(*str == ' ') cnt++;
    str++;
  }

  //remove trailing spaces
  str--;
  while(isspace(*str)){
    if(*str == ' ') cnt--;
    str--;
  }

  return cnt;
}

int loadObj(const char const *loc,\
            float **retvertices, float **retgouraudnormals,\
            int **rettriangleindex, int **retquadindex,\
            int *quadcount, int *trianglecount){
  float *vertices, *gouraudnormals;
  int *triangleindex, *quadindex;
  float *normals, *verttext;
  int *trianglenormals, *quadnormals;

  clock_t startTime = clock();
  int fileSize = 0;
  int n, i, j;
  printf("Opening file: %s\n", loc);

  FILE *pFile = fopen(loc, "r");
  if(pFile == NULL){
      dbgError("fopen()");
      exit(1);
  }

  fseek(pFile, 0, SEEK_END);
  fileSize = ftell(pFile);
  rewind(pFile);

  char *data = (char*) calloc(sizeof(char), fileSize + 1);

  fread(data, 1, fileSize, pFile);
  if(ferror(pFile)){
      dbgError("fread()");
      exit(1);
  }

  fclose(pFile);

  printf("Done reading, parsing file now.\n");
  TIMEELAPSED

  printf("Removing #IND if there...");
  char *pIND = data;
  n = 0;
  while(1){
    pIND = strstr(pIND, "#IND");
    if(pIND != NULL){
      memcpy(pIND, "0000", sizeof(char) * 4);
      n++;
    }
    else break;
  }
  printf("Removed %d #INDs\n", n);
  TIMEELAPSED

  printf("Counting lines and determining maximum length...");
  int maxLen, lenCount;
  int lines;

  for (i = lines = lenCount = maxLen = 0; i <= fileSize; i++){
    if (data[i] == '\n'){
      lines++;
      lenCount++;
      if(lenCount > maxLen) maxLen = lenCount;
      lenCount = 0;
    }
    lenCount++;
  }

  printf("Counted %d lines, maximum length: %d\n", lines, maxLen);
  TIMEELAPSED

  printf("Allocating %d strings of %d chars...", lines, maxLen);
  char **parsedText = (char**) calloc(sizeof(char*), lines);
  for(n = 0; n < lines; n++){
    parsedText[n] = (char*) malloc(sizeof(char) * maxLen);
    if (parsedText[n] == NULL){
      printf("Malloc failed to allocate %d bytes for parsedText[%d].\n", maxLen, n);
      return -1;
    }
  }
  printf("Done\n");
  TIMEELAPSED

  printf("Reading in 2D array...");
  for(i = n = j = 0; n < lines; i++){
    if(data[i] == '\n'){
      parsedText[n][j] = '\0';

      n++;
      j = 0;
      //puts(parsedText[n]);
      continue;
    }
    //printf("parsedText[%d][%d] = textTree[%d] = %c\n", n, j, i, data[i]);
    parsedText[n][j] = data[i];
    j++;
  }
  printf("Done\n");
  TIMEELAPSED

  //free the data, we don't need it anymore since it's stored in parsedText
  free(data);

/*   for(i = 0; i < lines; i++){
    puts(parsedText[i]);
  } */

  printf("Counting...");

  int vertlines, normallines, facelines, texturelines;
  vertlines = normallines = facelines = texturelines = 0;

  unsigned int faceverts, maxFaceverts, trianglecnt, quadcnt;
  faceverts = maxFaceverts = trianglecnt = quadcnt = 0;

  //stores for every face line how many verts it has.
  int *facevertcnt = calloc(sizeof(int), lines);
  for(i = 0; i < lines; i++){
    if(parsedText[i][0] == '#') continue;

    if(parsedText[i][0] == 'v' && parsedText[i][1] == ' '){
      vertlines++;
    }

    else if(parsedText[i][0] == 'v' && parsedText[i][1] == 'n'){
      normallines++;
    }

    else if(parsedText[i][0] == 'f' && parsedText[i][1] == ' '){
      facelines++;
      for(n = 2; !isdigit(parsedText[i][n]); n++);
      faceverts = countSpaces(&parsedText[i][n]);
      if(faceverts == 2){
        trianglecnt++;
        //printf("triangle: %s\n", parsedText[i]);
      }
      else if(faceverts == 3){
        quadcnt++;
        //printf("quad: %s", parsedText[i]);
      }
      if(faceverts > maxFaceverts) maxFaceverts = faceverts;
      facevertcnt[i] = faceverts;
      //getchar();
    }

    else if(parsedText[i][0] == 'v' && parsedText[i][1] == 't'){
      texturelines++;
    }
  }
  int vertvals = vertlines * 3;
  int normalvals = normallines * 3;
  int texturevals = texturelines * 2;
  int triangleverts = trianglecnt * 3;
  int quadverts = quadcnt * 4;

  *trianglecount = trianglecnt;
  *quadcount = quadcnt;

  printf("Counted:\n\t%d vertices\n\t%d normals\n\t%d faces (%d triangles, %d quads)\n", vertlines, normallines, facelines, trianglecnt, quadcnt);
  TIMEELAPSED
  if(maxFaceverts > 4){
    printf("WARNING:\n  Model won't be imported correctly due to polygons with more than 4 vertices.\n");
  }

  printf("Allocating memory for data...");
  vertices = calloc(sizeof(float), vertvals);
  normals = calloc(sizeof(float), normalvals);
  verttext = calloc(sizeof(float), texturevals);
  triangleindex = calloc(sizeof(int), triangleverts);
  trianglenormals = calloc(sizeof(int), triangleverts);

  quadindex = calloc(sizeof(int), quadverts);
  quadnormals = calloc(sizeof(int), quadverts);
  if(vertices == NULL || normals == NULL || verttext == NULL || triangleindex == NULL || trianglenormals == NULL || quadindex == NULL || quadnormals == NULL){
    printf("Failed to allocate enough memory, exiting...\n");
    return -1;
  }


  int vertcount, normalcount, textcount, tricnt, quacnt;

  printf("Done\n");
  TIMEELAPSED

  printf("Reading in data...");
  for(vertcount = normalcount = textcount = tricnt = quacnt = i = 0; i < lines; i++){
    //comment
    if(parsedText[i][0] == '#') continue;
    if(parsedText[i][0] == ' ') continue;

    //vertex
    if(vertlines)
    if(parsedText[i][0] == 'v' && parsedText[i][1] == ' '){
      sscanf(&parsedText[i][2], "%f%f%f", (vertices+vertcount), (vertices+vertcount+1), (vertices+vertcount+2));
      vertcount += 3;
    }

    //normal
    if(normallines)
    if(parsedText[i][0] == 'v' && parsedText[i][1] == 'n'){
      sscanf(&parsedText[i][2], "%f%f%f", (normals+normalcount), (normals+normalcount+1), (normals+normalcount+2));
      normalcount += 3;
    }

    //texture
    if(texturelines)
    if(parsedText[i][0] == 'v' && parsedText[i][1] == 't'){
      sscanf(&parsedText[i][2], "%f%f", (verttext+textcount), (verttext+textcount+1));
      textcount += 2;
    }

    //faces
    if(trianglecnt || quadcnt)
    if(parsedText[i][0] == 'f' && parsedText[i][1] == ' '){
      if(facevertcnt[i] == 2){
        //skip all leading spaces
        for(n = 2; isspace(parsedText[i][n]); n++);
        //read in the vertexnr
        sscanf(&parsedText[i][n], "%d", (triangleindex+tricnt));

        if(normallines){
          //skip 2 slashes
          for(; parsedText[i][n] != '/'; n++); n++;
          for(; parsedText[i][n] != '/'; n++); n++;
          //read in the normalnr
          sscanf(&parsedText[i][n], "%d", (trianglenormals+tricnt));
        }

        for(;!isspace(parsedText[i][n]); n++);
        sscanf(&parsedText[i][n], "%d", (triangleindex+tricnt+1));
        n++;

        if(normallines){
          //skip 2 slashes
          for(; parsedText[i][n] != '/'; n++); n++;
          for(; parsedText[i][n] != '/'; n++); n++;
          //read in the normalnr
          sscanf(&parsedText[i][n], "%d", (trianglenormals+tricnt+1));
        }

        for(;!isspace(parsedText[i][n]); n++);
        sscanf(&parsedText[i][n], "%d", (triangleindex+tricnt+2));

        if(normallines){
          //skip 2 slashes
          for(; parsedText[i][n] != '/'; n++); n++;
          for(; parsedText[i][n] != '/'; n++); n++;
          //read in the normalnr
          sscanf(&parsedText[i][n], "%d", (trianglenormals+tricnt+2));
        }
        for(j = 0; j < 3; j++){
          //-4 on line 4 becomes 0.
          if(triangleindex[tricnt+j] < 0){
            triangleindex[tricnt+j] = i - triangleindex[tricnt+j];
          }
          else{
            triangleindex[tricnt+j]--;
            trianglenormals[tricnt+j]--;
          }
        }

        tricnt += 3;
      }
      if(facevertcnt[i] == 3){
        //skip all leading spaces
        for(n = 2; isspace(parsedText[i][n]); n++);
        sscanf(&parsedText[i][n], "%d", (quadindex+quacnt));

        if(normallines){
          //skip 2 slashes
          for(; parsedText[i][n] != '/'; n++); n++;
          for(; parsedText[i][n] != '/'; n++); n++;
          //read in the normalnr
          sscanf(&parsedText[i][n], "%d", (quadnormals+quacnt));
        }

        for(;!isspace(parsedText[i][n]); n++);
        sscanf(&parsedText[i][n], "%d", (quadindex+quacnt+1));
        n++;

        if(normallines){
          //skip 2 slashes
          for(; parsedText[i][n] != '/'; n++); n++;
          for(; parsedText[i][n] != '/'; n++); n++;
          //read in the normalnr
          sscanf(&parsedText[i][n], "%d", (quadnormals+quacnt+1));
        }

        for(;!isspace(parsedText[i][n]); n++);
        sscanf(&parsedText[i][n], "%d", (quadindex+quacnt+2));
        n++;

        if(normallines){
          //skip 2 slashes
          for(; parsedText[i][n] != '/'; n++); n++;
          for(; parsedText[i][n] != '/'; n++); n++;
          //read in the normalnr
          sscanf(&parsedText[i][n], "%d", (quadnormals+quacnt+2));
        }

        for(;!isspace(parsedText[i][n]); n++);
        sscanf(&parsedText[i][n], "%d", (quadindex+quacnt+3));

        if(normallines){
          //skip 2 slashes
          for(; parsedText[i][n] != '/'; n++); n++;
          for(; parsedText[i][n] != '/'; n++); n++;
          //read in the normalnr
          sscanf(&parsedText[i][n], "%d", (quadnormals+quacnt+3));
        }
        for(j = 0; j < 4; j++){
          //-4 on line 4 becomes 0.
          if(quadindex[quacnt+j] < 0){
            quadindex[quacnt+j] = i - quadindex[quacnt+j];
          }
          quadindex[quacnt+j]--;
          quadnormals[quacnt+j]--;
        }

        quacnt += 4;
      }
    }
  }
  printf("Done\n");
  TIMEELAPSED

  if(normallines){
    printf("Calculating Gouraud normals for each vertex...");
    gouraudnormals = calloc(sizeof(float), vertvals);
    float normalx, normaly, normalz;
    if(trianglecnt != 0){
      //printf("::DBG:: calcing trianglenormals\n");
      for(i = 0; i < triangleverts; i++){
        //printf("::DBG::accessing GRnor[%d{%d}] & normals[%d]\n", triangleindex[i]*3, normalvals, trianglenormals[i]*3);
        normalx = normals[trianglenormals[i]*3];
        normaly = normals[trianglenormals[i]*3+1];
        normalz = normals[trianglenormals[i]*3+2];

        gouraudnormals[triangleindex[i]*3]   += normalx;
        gouraudnormals[triangleindex[i]*3+1] += normaly;
        gouraudnormals[triangleindex[i]*3+2] += normalz;
      }
    }

    if(quadcnt != 0){
      //printf("::DBG:: calcing quadnormals\n");
      for(i = 0; i < quadverts; i++){
        //printf("::DBG::accessing GRnor[%d{%d}] & normals[%d]\n", quadindex[i]*3, vertvals, quadnormals[i]*3);
        normalx = normals[quadnormals[i]*3];
        normaly = normals[quadnormals[i]*3+1];
        normalz = normals[quadnormals[i]*3+2];

        gouraudnormals[quadindex[i]*3]   += normalx;
        gouraudnormals[quadindex[i]*3+1] += normaly;
        gouraudnormals[quadindex[i]*3+2] += normalz;
      }
    }
    printf("Done\n");
    TIMEELAPSED

    printf("Normalizing normals...");
    for(i = 0; i < normalvals; i += 3){
      normalx = fabs(gouraudnormals[i]);
      normaly = fabs(gouraudnormals[i+1]);
      normalz = fabs(gouraudnormals[i+2]);

      float max = normalx;
      if (normaly > max) max = normaly;
      if (normalz > max) max = normalz;

      //normalize
      if(max != 0){
        gouraudnormals[i]   /= max;
        gouraudnormals[i+1] /= max;
        gouraudnormals[i+2] /= max;
      }
    }

    printf("Done\n");
    TIMEELAPSED
  }
  else{
    printf("No normals detected! This version doesn't calculate normals, too lazy. :(\n");
  }

/*   for(n = 0; n < vertvals; n += 3){
    printf("v %f %f %f\n", vertices[n], vertices[n+1], vertices[n+2]);
  } */


/*   for(n = 0; n < normalvals; n += 3){
    printf("vn[%d] %f %f %f\n", n/3, normals[n], normals[n+1], normals[n+2]);
  } */

/*
  for(n = 0; n < texturevals; n += 2){
    printf("vt %f %f\n", verttext[n], verttext[n+1]);
  } */

/*
  for(n = 0; n < triangleverts; n += 3){
    printf("triangle: %d %d %d\n\t"
           "Normals: %d %d %d\n",\
           triangleindex[n], triangleindex[n+1], triangleindex[n+2],\
           trianglenormals[n], trianglenormals[n+1], trianglenormals[n+2]);
  } */

/*   for(n = 0; n < quadverts; n += 4){
    printf("Quad: %d %d %d %d\n\t"
           "Normals: %d %d %d %d\n",\
           quadindex[n], quadindex[n+1], quadindex[n+2], quadindex[n+3],\
           quadnormals[n], quadnormals[n+1], quadnormals[n+2], quadnormals[n+3]);
  } */

/*   if(normallines)
  for(n = 0; n < vertlines; n++){
    printf("Normals for v[%d]: %f %f %f\n", n, gouraudnormals[n*3], gouraudnormals[n*3+1], gouraudnormals[n*3+2]);
  } */

  printf("File succesfully loaded, returning.\n");

  TIMEELAPSED
  *retvertices = vertices;
  *retgouraudnormals = gouraudnormals;
  *rettriangleindex = triangleindex;
  *retquadindex = quadindex;
  return 0;
}

Thanks for the code, I guess It will take me sometime to understand the code.

Feel free to ask questions, my email and msn in in your inbox.

just to mention - be sure you guys free all allocated memory, as in first post almost on every error you'll get memory leak, because you call calloc

char *msg = calloc(sizeof(char), 512);

without freeing it.

Similar thing with parsedText in Clockowl post from Jul 25th, 2008 2:00 pm.


Best

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.