Darwin226's picture

Loading .obj models

I have this code that parses an .obj file and is supposed to load it into OpenGL. The problem is that it doesn't draw so if anyone could please check to see if anything is wrong I would appreciate that.

The parsing works as it should and the vertices array is fine, I'm probably doing something wrong with the way I load that array into OpenGL.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using GridDistorsion;
 
namespace GridDistorsion {
 
	struct Model {
 
		public int Id;
		public int Vertices;
	}
 
	struct Vertex {
 
		public Vertex ( float X, float Y, float Z, float R, float G, float B, float A, float UVX, float UVY ) {
 
			this.X = X;
			this.Y = Y;
			this.Z = Z;
			this.R = R;
			this.G = G;
			this.B = B;
			this.A = A;
			this.UVX = UVX;
			this.UVY = UVY;
		}
 
		float X;
		float Y;
		float Z;
		float R;
		float G;
		float B;
		float A;
		float UVX;
		float UVY;
	}
 
	class ModelViewer {
 
		public static Model LoadModel ( string filename ) {
 
			StreamReader file = new StreamReader( filename );
 
			List<Vector3> points = new List<Vector3>();
			List<Vector3> normals = new List<Vector3>();
			List<Vector2> texcoords = new List<Vector2>();
			List<Vertex[]> triangles = new List<Vertex[]>();
 
			float x, y, z;
			int v, t, n;
			string keyword;
 
			while ( file.ReadWord( out keyword ) ) {
 
				if ( keyword == "v" ) {
 
					file.ReadWord( out x );
					file.ReadWord( out y );
					file.ReadWord( out z );
					points.Add( new Vector3( x, y, z ) );
				}
				else if ( keyword == "vt" ) {
 
					file.ReadWord( out x );
					file.ReadWord( out y );
					texcoords.Add( new Vector2( x, y ) );
					Console.WriteLine( texcoords.Count );
				}
				else if ( keyword == "vn" ) {
 
					file.ReadWord( out x );
					file.ReadWord( out y );
					file.ReadWord( out z );
					normals.Add( new Vector3( x, y, z ) );
				}
				else if ( keyword == "f" ) {
 
					Vector3 point;
					Vector2 uv = new Vector2();
					Vector3 normal = new Vector3();
					Vertex[] tr = new Vertex[ 3 ];
 
					for ( int face = 0 ; face < 3 ; face++ ) {
 
						string tmp;
						file.ReadWord( out tmp );
						v = int.Parse( tmp );
						point = points[ v - 1 ];
 
						if ( texcoords.Count > 0 ) {
 
							//string temp;
							//file.ReadWord( out temp );
							//c = temp[ 0 ];
							file.ReadWord( out t );
							uv = texcoords[ t - 1 ];
						}
						if ( normals.Count > 0 ) {
 
							//string temp;
							//file.ReadWord( out temp );
							//c = temp[ 0 ];
							file.ReadWord( out n );
							normal = normals[ n - 1 ];
						}
 
						tr[ face ] = new Vertex( point.X, point.Y, point.Z, 1, 1, 1, 1, uv.X, uv.Y );
					}
 
					triangles.Add( tr );
				}
			}
 
			file.Close();
 
			int vertexcount = triangles.Count * 3;
 
			Vertex[] vertices = new Vertex[ vertexcount ];
 
			for ( int i = 0 ; i < triangles.Count() ; i++ ) {
 
				vertices[ i * 3 ] = triangles[ i ][ 0 ];
				vertices[ i * 3 + 1 ] = triangles[ i ][ 1 ];
				vertices[ i * 3 + 2 ] = triangles[ i ][ 2 ];
			}
 
			Model model = new Model();
			model = CreateModel( vertices, vertexcount );
 
			return model;
		}
 
		static Model CreateModel ( Vertex[] points, int count ) {
 
			if ( count % 3 != 0 ) return new Model();
 
			Model model;
			model.Vertices = count;
 
			GL.GenBuffers( 1, out model.Id );
			GL.BindBuffer( BufferTarget.ArrayBuffer, model.Id );
			GL.BufferData( BufferTarget.ArrayBuffer, new IntPtr( sizeof( float ) * 9 * count ), points, BufferUsageHint.StaticDraw );
 
			return model;
		}
 
		public static void DrawModel ( Model model ) {
 
			GL.BindBuffer( BufferTarget.ArrayBuffer, model.Id );
 
			GL.VertexPointer( 3, VertexPointerType.Float, sizeof( float ) * 9, 0 );
			GL.ColorPointer( 4, ColorPointerType.Float, sizeof( float ) * 9, sizeof( float ) * 3 );
			GL.TexCoordPointer( 2, TexCoordPointerType.Float, sizeof( float ) * 9, sizeof( float ) * 7 );
 
			GL.DrawArrays( BeginMode.Triangles, 0, model.Vertices );
 
		}
	}
}

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Tal's picture

I don't know if this is the real problem, but I know you should add this line:
[StructLayout(LayoutKind.Sequential)]
before
struct Vertex {
Becouse .Net/Mono structurs size(and where is every field placed) are different in any console, unless you use attributes.
I hope this is the problem and it will be fixed.
By the way- thanks for this code(importing .obj models).

Darwin226's picture

Unfortunately that didn't work. (Btw, I included InteropServices)
As for the code you can only thank me for the translation to C#, the original code was given to my by Overv on Facepunch forums in C++.

Hortus Longus's picture

VertexArray not enabled?

PeterMacGonagan's picture

Did you find an issue?

PeterMacGonagan's picture

Hello,

May be, the source code below could interests someone, so I post it.
Here are 3 examples of how to import .obj in your project (two are coded in c# and one in C with a C++ object).
I post the C code because it's very fast... I don't know why it is so fast compared to C# (I speak about 50ms to load compared to 10 sec!!!). If somebody can explain me, it would be great.
The source code are not from me. I've modified it because there where not working!

In C

 #ifndef OBJ_LOADER_H
#define OBJ_LOADER_H
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "list.h"
 
#define WHITESPACE " \t\n\r"
 
#define OBJ_FILENAME_LENGTH 500
#define MATERIAL_NAME_SIZE 255
#define OBJ_LINE_SIZE 500
#define MAX_VERTEX_COUNT 4 //can only handle quads or triangles
 
char strequal(const char *s1, const char *s2)
{
	if(strcmp(s1, s2) == 0)
		return 1;
	return 0;
}
 
char contains(const char *haystack, const char *needle)
{
	if(strstr(haystack, needle) == NULL)
		return 0;
	return 1;
}
 
struct obj_face
{
   int vertex_index[MAX_VERTEX_COUNT];
   int normal_index[MAX_VERTEX_COUNT];
   int texture_index[MAX_VERTEX_COUNT];
   int vertex_count;
   int material_index;
};
 
struct obj_sphere
{
   int pos_index;
   int up_normal_index;
   int equator_normal_index;
   int texture_index[MAX_VERTEX_COUNT];
   int material_index;
};
 
struct obj_plane
{
   int pos_index;
   int normal_index;
   int rotation_normal_index;
   int texture_index[MAX_VERTEX_COUNT];
   int material_index;
};
 
struct obj_vector
{
   double e[3];
};
 
struct obj_material
{
   char name[MATERIAL_NAME_SIZE];
   char texture_filename[OBJ_FILENAME_LENGTH];
   double amb[3];
   double diff[3];
   double spec[3];
   double reflect;
   double refract;
   double trans;
   double shiny;
   double glossy;
   double refract_index;
};
 
struct obj_camera
{
   int camera_pos_index;
   int camera_look_point_index;
   int camera_up_norm_index;
};
 
struct obj_light_point
{
   int pos_index;
   int material_index;
};
 
struct obj_light_disc
{
   int pos_index;
   int normal_index;
   int material_index;
};
 
struct obj_light_quad
{
   int vertex_index[MAX_VERTEX_COUNT];
   int material_index;
};
 
struct obj_growable_scene_data
{
//	vector extreme_dimensions[2];
   char scene_filename[OBJ_FILENAME_LENGTH];
   char material_filename[OBJ_FILENAME_LENGTH];
 
   list vertex_list;
   list vertex_normal_list;
   list vertex_texture_list;
 
   list face_list;
   list sphere_list;
   list plane_list;
 
   list light_point_list;
   list light_quad_list;
   list light_disc_list;
 
   list material_list;
 
   obj_camera* camera;
};
 
struct obj_scene_data
{
   obj_vector **vertex_list;
   obj_vector **vertex_normal_list;
   obj_vector **vertex_texture_list;
 
   obj_face **face_list;
   obj_sphere **sphere_list;
   obj_plane **plane_list;
 
   obj_light_point **light_point_list;
   obj_light_quad **light_quad_list;
   obj_light_disc **light_disc_list;
 
   obj_material **material_list;
 
   int vertex_count;
   int vertex_normal_count;
   int vertex_texture_count;
 
   int face_count;
   int sphere_count;
   int plane_count;
 
   int light_point_count;
   int light_quad_count;
   int light_disc_count;
 
   int material_count;
 
   obj_camera *camera;
};
 
class objLoader
{
public:
	objLoader() {}
	~objLoader()
	{
		delete_obj_data(&data);
	}
 
	int load(char* filename)
   {
	   int no_error = 1;
	   no_error = parse_obj_scene(&data, filename);
	   if(no_error)
	   {
		   this->vertexCount = data.vertex_count;
		   this->normalCount = data.vertex_normal_count;
		   this->textureCount = data.vertex_texture_count;
 
		   this->faceCount = data.face_count;
		   this->sphereCount = data.sphere_count;
		   this->planeCount = data.plane_count;
 
		   this->lightPointCount = data.light_point_count;
		   this->lightDiscCount = data.light_disc_count;
		   this->lightQuadCount = data.light_quad_count;
 
		   this->materialCount = data.material_count;
 
		   this->vertexList = data.vertex_list;
		   this->normalList = data.vertex_normal_list;
		   this->textureList = data.vertex_texture_list;
 
		   this->faceList = data.face_list;
		   this->sphereList = data.sphere_list;
		   this->planeList = data.plane_list;
 
		   this->lightPointList = data.light_point_list;
		   this->lightDiscList = data.light_disc_list;
		   this->lightQuadList = data.light_quad_list;
 
		   this->materialList = data.material_list;
 
		   this->camera = data.camera;
	   }
 
	   return no_error;
   }   
 
   void obj_free_half_list(list *listo)
   {
	   list_delete_all(listo);
	   free(listo->names);
   }
 
   int obj_convert_to_list_index(int current_max, int index)
   {
	   if(index == 0)  //no index
		   return -1;
 
	   if(index < 0)  //relative to current list position
		   return current_max + index;
 
	   return index - 1;  //normal counting index
   }
 
   void obj_convert_to_list_index_v(int current_max, int *indices)
   {
	   for(int i=0; i<MAX_VERTEX_COUNT; i++)
		   indices[i] = obj_convert_to_list_index(current_max, indices[i]);
   }
 
   void obj_set_material_defaults(obj_material *mtl)
   {
	   mtl->amb[0] = 0.2;
	   mtl->amb[1] = 0.2;
	   mtl->amb[2] = 0.2;
	   mtl->diff[0] = 0.8;
	   mtl->diff[1] = 0.8;
	   mtl->diff[2] = 0.8;
	   mtl->spec[0] = 1.0;
	   mtl->spec[1] = 1.0;
	   mtl->spec[2] = 1.0;
	   mtl->reflect = 0.0;
	   mtl->trans = 1;
	   mtl->glossy = 98;
	   mtl->shiny = 0;
	   mtl->refract_index = 1;
	   mtl->texture_filename[0] = '\0';
   }
 
   int obj_parse_vertex_index(int *vertex_index, int *texture_index, int *normal_index)
   {
	   char *temp_str;
	   char *token;
	   int vertex_count = 0;
 
 
	   while( (token = strtok(NULL, WHITESPACE)) != NULL)
	   {
		   if(texture_index != NULL)
			   texture_index[vertex_count] = 0;
		   if(normal_index != NULL)
		   normal_index[vertex_count] = 0;
 
		   vertex_index[vertex_count] = atoi( token );
 
		   if(contains(token, "//"))  //normal only
		   {
			   temp_str = strchr(token, '/');
			   temp_str++;
			   normal_index[vertex_count] = atoi( ++temp_str );
		   }
		   else if(contains(token, "/"))
		   {
			   temp_str = strchr(token, '/');
			   texture_index[vertex_count] = atoi( ++temp_str );
 
			   if(contains(temp_str, "/"))
			   {
				   temp_str = strchr(temp_str, '/');
				   normal_index[vertex_count] = atoi( ++temp_str );
			   }
		   }
 
		   vertex_count++;
	   }
 
	   return vertex_count;
   }
 
   obj_face* obj_parse_face(obj_growable_scene_data *scene)
   {
	   int vertex_count;
	   obj_face *face = (obj_face*)malloc(sizeof(obj_face));
 
	   vertex_count = obj_parse_vertex_index(face->vertex_index, face->texture_index, face->normal_index);
	   obj_convert_to_list_index_v(scene->vertex_list.item_count, face->vertex_index);
	   obj_convert_to_list_index_v(scene->vertex_texture_list.item_count, face->texture_index);
	   obj_convert_to_list_index_v(scene->vertex_normal_list.item_count, face->normal_index);
	   face->vertex_count = vertex_count;
 
	   return face;
   }
 
   obj_sphere* obj_parse_sphere(obj_growable_scene_data *scene)
   {
	   int temp_indices[MAX_VERTEX_COUNT];
 
	   obj_sphere *obj = (obj_sphere*)malloc(sizeof(obj_sphere));
	   obj_parse_vertex_index(temp_indices, obj->texture_index, NULL);
	   obj_convert_to_list_index_v(scene->vertex_texture_list.item_count, obj->texture_index);
	   obj->pos_index = obj_convert_to_list_index(scene->vertex_list.item_count, temp_indices[0]);
	   obj->up_normal_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, temp_indices[1]);
	   obj->equator_normal_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, temp_indices[2]);
 
	   return obj;
   }
 
   obj_plane* obj_parse_plane(obj_growable_scene_data *scene)
   {
	   int temp_indices[MAX_VERTEX_COUNT];
 
	   obj_plane *obj = (obj_plane*)malloc(sizeof(obj_plane));
	   obj_parse_vertex_index(temp_indices, obj->texture_index, NULL);
	   obj_convert_to_list_index_v(scene->vertex_texture_list.item_count, obj->texture_index);
	   obj->pos_index = obj_convert_to_list_index(scene->vertex_list.item_count, temp_indices[0]);
	   obj->normal_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, temp_indices[1]);
	   obj->rotation_normal_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, temp_indices[2]);
 
	   return obj;
   }
 
   obj_light_point* obj_parse_light_point(obj_growable_scene_data *scene)
   {
	   obj_light_point *o= (obj_light_point*)malloc(sizeof(obj_light_point));
	   o->pos_index = obj_convert_to_list_index(scene->vertex_list.item_count, atoi( strtok(NULL, WHITESPACE)) );
	   return o;
   }
 
   obj_light_quad* obj_parse_light_quad(obj_growable_scene_data *scene)
   {
	   obj_light_quad *o = (obj_light_quad*)malloc(sizeof(obj_light_quad));
	   obj_parse_vertex_index(o->vertex_index, NULL, NULL);
	   obj_convert_to_list_index_v(scene->vertex_list.item_count, o->vertex_index);
 
	   return o;
   }
 
   obj_light_disc* obj_parse_light_disc(obj_growable_scene_data *scene)
   {
	   int temp_indices[MAX_VERTEX_COUNT];
 
	   obj_light_disc *obj = (obj_light_disc*)malloc(sizeof(obj_light_disc));
	   obj_parse_vertex_index(temp_indices, NULL, NULL);
	   obj->pos_index = obj_convert_to_list_index(scene->vertex_list.item_count, temp_indices[0]);
	   obj->normal_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, temp_indices[1]);
 
	   return obj;
   }
 
   obj_vector* obj_parse_vector()
   {
	   obj_vector *v = (obj_vector*)malloc(sizeof(obj_vector));
	   v->e[0] = atof( strtok(NULL, WHITESPACE));
	   v->e[1] = atof( strtok(NULL, WHITESPACE));
	   v->e[2] = atof( strtok(NULL, WHITESPACE));
	   return v;
   }
 
   void obj_parse_camera(obj_growable_scene_data *scene, obj_camera *camera)
   {
	   int indices[3];
	   obj_parse_vertex_index(indices, NULL, NULL);
	   camera->camera_pos_index = obj_convert_to_list_index(scene->vertex_list.item_count, indices[0]);
	   camera->camera_look_point_index = obj_convert_to_list_index(scene->vertex_list.item_count, indices[1]);
	   camera->camera_up_norm_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, indices[2]);
   }
 
   int obj_parse_mtl_file(char *filename, list *material_list)
   {
	   int line_number = 0;
	   char *current_token;
	   char current_line[OBJ_LINE_SIZE];
	   char material_open = 0;
	   obj_material *current_mtl = NULL;
	   FILE *mtl_file_stream;
 
	   // open scene
	   mtl_file_stream = fopen( filename, "r");
	   if(mtl_file_stream == 0)
	   {
		   fprintf(stderr, "Error reading file: %s\n", filename);
		   return 0;
	   }
 
	   list_make(material_list, 10, 1);
 
	   while( fgets(current_line, OBJ_LINE_SIZE, mtl_file_stream) )
	   {
		   current_token = strtok( current_line, " \t\n\r");
		   line_number++;
 
		   //skip comments
		   if( current_token == NULL || strequal(current_token, "//") || strequal(current_token, "#"))
			   continue;
 
 
		   //start material
		   else if( strequal(current_token, "newmtl"))
		   {
			   material_open = 1;
			   current_mtl = (obj_material*) malloc(sizeof(obj_material));
			   obj_set_material_defaults(current_mtl);
 
			   // get the name
			   strncpy(current_mtl->name, strtok(NULL, " \t"), MATERIAL_NAME_SIZE);
			   list_add_item(material_list, current_mtl, current_mtl->name);
		   }
 
		   //ambient
		   else if( strequal(current_token, "Ka") && material_open)
		   {
			   current_mtl->amb[0] = atof( strtok(NULL, " \t"));
			   current_mtl->amb[1] = atof( strtok(NULL, " \t"));
			   current_mtl->amb[2] = atof( strtok(NULL, " \t"));
		   }
 
		   //diff
		   else if( strequal(current_token, "Kd") && material_open)
		   {
			   current_mtl->diff[0] = atof( strtok(NULL, " \t"));
			   current_mtl->diff[1] = atof( strtok(NULL, " \t"));
			   current_mtl->diff[2] = atof( strtok(NULL, " \t"));
		   }
 
		   //specular
		   else if( strequal(current_token, "Ks") && material_open)
		   {
			   current_mtl->spec[0] = atof( strtok(NULL, " \t"));
			   current_mtl->spec[1] = atof( strtok(NULL, " \t"));
			   current_mtl->spec[2] = atof( strtok(NULL, " \t"));
		   }
		   //shiny
		   else if( strequal(current_token, "Ns") && material_open)
		   {
			   current_mtl->shiny = atof( strtok(NULL, " \t"));
		   }
		   //transparent
		   else if( strequal(current_token, "d") && material_open)
		   {
			   current_mtl->trans = atof( strtok(NULL, " \t"));
		   }
		   //reflection
		   else if( strequal(current_token, "r") && material_open)
		   {
			   current_mtl->reflect = atof( strtok(NULL, " \t"));
		   }
		   //glossy
		   else if( strequal(current_token, "sharpness") && material_open)
		   {
			   current_mtl->glossy = atof( strtok(NULL, " \t"));
		   }
		   //refract index
		   else if( strequal(current_token, "Ni") && material_open)
		   {
			   current_mtl->refract_index = atof( strtok(NULL, " \t"));
		   }
		   // illumination type
		   else if( strequal(current_token, "illum") && material_open)
		   {
		   }
		   // texture map
		   else if( strequal(current_token, "map_Ka") && material_open)
		   {
			   strncpy(current_mtl->texture_filename, strtok(NULL, " \t"), OBJ_FILENAME_LENGTH);
		   }
		   else
		   {
			   fprintf(stderr, "Unknown command '%s' in material file %s at line %i:\n\t%s\n",
					   current_token, filename, line_number, current_line);
			   //return 0;
		   }
	   }
 
	   fclose(mtl_file_stream);
 
	   return 1;
 
   }
 
   int obj_parse_obj_file(obj_growable_scene_data *growable_data, char* filename)
   {
	   FILE* obj_file_stream;
	   int current_material = -1; 
	   char *current_token = NULL;
	   char current_line[OBJ_LINE_SIZE];
	   int line_number = 0;
	   // open scene
	   obj_file_stream = fopen( filename, "r");
	   if(obj_file_stream == 0)
	   {
		   fprintf(stderr, "Error reading file: %s\n", filename);
		   return 0;
	   }
 
   /*		
	   extreme_dimensions[0].x = INFINITY; extreme_dimensions[0].y = INFINITY; extreme_dimensions[0].z = INFINITY;
	   extreme_dimensions[1].x = -INFINITY; extreme_dimensions[1].y = -INFINITY; extreme_dimensions[1].z = -INFINITY;
 
			   if(v->x < extreme_dimensions[0].x) extreme_dimensions[0].x = v->x;
			   if(v->x > extreme_dimensions[1].x) extreme_dimensions[1].x = v->x;
			   if(v->y < extreme_dimensions[0].y) extreme_dimensions[0].y = v->y;
			   if(v->y > extreme_dimensions[1].y) extreme_dimensions[1].y = v->y;
			   if(v->z < extreme_dimensions[0].z) extreme_dimensions[0].z = v->z;
			   if(v->z > extreme_dimensions[1].z) extreme_dimensions[1].z = v->z;*/
 
 
	   //parser loop
	   while( fgets(current_line, OBJ_LINE_SIZE, obj_file_stream) )
	   {
		   current_token = strtok( current_line, " \t\n\r");
		   line_number++;
 
		   //skip comments
		   if( current_token == NULL || current_token[0] == '#')
			   continue;
 
		   //parse objects
		   else if( strequal(current_token, "v") ) //process vertex
		   {
			   list_add_item(&growable_data->vertex_list,  obj_parse_vector(), NULL);
		   }
 
		   else if( strequal(current_token, "vn") ) //process vertex normal
		   {
			   list_add_item(&growable_data->vertex_normal_list,  obj_parse_vector(), NULL);
		   }
 
		   else if( strequal(current_token, "vt") ) //process vertex texture
		   {
			   list_add_item(&growable_data->vertex_texture_list,  obj_parse_vector(), NULL);
		   }
 
		   else if( strequal(current_token, "f") ) //process face
		   {
			   obj_face *face = obj_parse_face(growable_data);
			   face->material_index = current_material;
			   list_add_item(&growable_data->face_list, face, NULL);
		   }
 
		   else if( strequal(current_token, "sp") ) //process sphere
		   {
			   obj_sphere *sphr = obj_parse_sphere(growable_data);
			   sphr->material_index = current_material;
			   list_add_item(&growable_data->sphere_list, sphr, NULL);
		   }
 
		   else if( strequal(current_token, "pl") ) //process plane
		   {
			   obj_plane *pl = obj_parse_plane(growable_data);
			   pl->material_index = current_material;
			   list_add_item(&growable_data->plane_list, pl, NULL);
		   }
 
		   else if( strequal(current_token, "p") ) //process point
		   {
			   //make a small sphere to represent the point?
		   }
 
		   else if( strequal(current_token, "lp") ) //light point source
		   {
			   obj_light_point *o = obj_parse_light_point(growable_data);
			   o->material_index = current_material;
			   list_add_item(&growable_data->light_point_list, o, NULL);
		   }
 
		   else if( strequal(current_token, "ld") ) //process light disc
		   {
			   obj_light_disc *o = obj_parse_light_disc(growable_data);
			   o->material_index = current_material;
			   list_add_item(&growable_data->light_disc_list, o, NULL);
		   }
 
		   else if( strequal(current_token, "lq") ) //process light quad
		   {
			   obj_light_quad *o = obj_parse_light_quad(growable_data);
			   o->material_index = current_material;
			   list_add_item(&growable_data->light_quad_list, o, NULL);
		   }
 
		   else if( strequal(current_token, "c") ) //camera
		   {
			   growable_data->camera = (obj_camera*) malloc(sizeof(obj_camera));
			   obj_parse_camera(growable_data, growable_data->camera);
		   }
 
		   else if( strequal(current_token, "usemtl") ) // usemtl
		   {
			   current_material = list_find(&growable_data->material_list, strtok(NULL, WHITESPACE));
		   }
 
		   else if( strequal(current_token, "mtllib") ) // mtllib
		   {
			   strncpy(growable_data->material_filename, strtok(NULL, WHITESPACE), OBJ_FILENAME_LENGTH);
			   obj_parse_mtl_file(growable_data->material_filename, &growable_data->material_list);
			   continue;
		   }
 
		   else if( strequal(current_token, "o") ) //object name
		   { }
		   else if( strequal(current_token, "s") ) //smoothing
		   { }
		   else if( strequal(current_token, "g") ) // group
		   { }		
 
		   else
		   {
			   printf("Unknown command '%s' in scene code at line %i: \"%s\".\n",
					   current_token, line_number, current_line);
		   }
	   }
 
	   fclose(obj_file_stream);
 
	   return 1;
   }
 
 
   void obj_init_temp_storage(obj_growable_scene_data *growable_data)
   {
	   list_make(&growable_data->vertex_list, 10, 1);
	   list_make(&growable_data->vertex_normal_list, 10, 1);
	   list_make(&growable_data->vertex_texture_list, 10, 1);
 
	   list_make(&growable_data->face_list, 10, 1);
	   list_make(&growable_data->sphere_list, 10, 1);
	   list_make(&growable_data->plane_list, 10, 1);
 
	   list_make(&growable_data->light_point_list, 10, 1);
	   list_make(&growable_data->light_quad_list, 10, 1);
	   list_make(&growable_data->light_disc_list, 10, 1);
 
	   list_make(&growable_data->material_list, 10, 1);	
 
	   growable_data->camera = NULL;
   }
 
   void obj_free_temp_storage(obj_growable_scene_data *growable_data)
   {
	   obj_free_half_list(&growable_data->vertex_list);
	   obj_free_half_list(&growable_data->vertex_normal_list);
	   obj_free_half_list(&growable_data->vertex_texture_list);
 
	   obj_free_half_list(&growable_data->face_list);
	   obj_free_half_list(&growable_data->sphere_list);
	   obj_free_half_list(&growable_data->plane_list);
 
	   obj_free_half_list(&growable_data->light_point_list);
	   obj_free_half_list(&growable_data->light_quad_list);
	   obj_free_half_list(&growable_data->light_disc_list);
 
	   obj_free_half_list(&growable_data->material_list);
   }
 
   void delete_obj_data(obj_scene_data *data_out)
   {
	   int i;
 
	   for(i=0; i<data_out->vertex_count; i++)
		   free(data_out->vertex_list[i]);
	   free(data_out->vertex_list);
	   for(i=0; i<data_out->vertex_normal_count; i++)
		   free(data_out->vertex_normal_list[i]);
	   free(data_out->vertex_normal_list);
	   for(i=0; i<data_out->vertex_texture_count; i++)
		   free(data_out->vertex_texture_list[i]);
	   free(data_out->vertex_texture_list);
 
	   for(i=0; i<data_out->face_count; i++)
		   free(data_out->face_list[i]);
	   free(data_out->face_list);
	   for(i=0; i<data_out->sphere_count; i++)
		   free(data_out->sphere_list[i]);
	   free(data_out->sphere_list);
	   for(i=0; i<data_out->plane_count; i++)
		   free(data_out->plane_list[i]);
	   free(data_out->plane_list);
 
	   for(i=0; i<data_out->light_point_count; i++)
		   free(data_out->light_point_list[i]);
	   free(data_out->light_point_list);
	   for(i=0; i<data_out->light_disc_count; i++)
		   free(data_out->light_disc_list[i]);
	   free(data_out->light_disc_list);
	   for(i=0; i<data_out->light_quad_count; i++)
		   free(data_out->light_quad_list[i]);
	   free(data_out->light_quad_list);
 
	   for(i=0; i<data_out->material_count; i++)
		   free(data_out->material_list[i]);
	   free(data_out->material_list);
 
	   free(data_out->camera);
   }
 
   void obj_copy_to_out_storage(obj_scene_data *data_out, obj_growable_scene_data *growable_data)
   {
	   data_out->vertex_count = growable_data->vertex_list.item_count;
	   data_out->vertex_normal_count = growable_data->vertex_normal_list.item_count;
	   data_out->vertex_texture_count = growable_data->vertex_texture_list.item_count;
 
	   data_out->face_count = growable_data->face_list.item_count;
	   data_out->sphere_count = growable_data->sphere_list.item_count;
	   data_out->plane_count = growable_data->plane_list.item_count;
 
	   data_out->light_point_count = growable_data->light_point_list.item_count;
	   data_out->light_disc_count = growable_data->light_disc_list.item_count;
	   data_out->light_quad_count = growable_data->light_quad_list.item_count;
 
	   data_out->material_count = growable_data->material_list.item_count;
 
	   data_out->vertex_list = (obj_vector**)growable_data->vertex_list.items;
	   data_out->vertex_normal_list = (obj_vector**)growable_data->vertex_normal_list.items;
	   data_out->vertex_texture_list = (obj_vector**)growable_data->vertex_texture_list.items;
 
	   data_out->face_list = (obj_face**)growable_data->face_list.items;
	   data_out->sphere_list = (obj_sphere**)growable_data->sphere_list.items;
	   data_out->plane_list = (obj_plane**)growable_data->plane_list.items;
 
	   data_out->light_point_list = (obj_light_point**)growable_data->light_point_list.items;
	   data_out->light_disc_list = (obj_light_disc**)growable_data->light_disc_list.items;
	   data_out->light_quad_list = (obj_light_quad**)growable_data->light_quad_list.items;
 
	   data_out->material_list = (obj_material**)growable_data->material_list.items;
 
	   data_out->camera = growable_data->camera;
   }
 
   int parse_obj_scene(obj_scene_data* data_out, char* filename)
   {
	   obj_growable_scene_data growable_data;
 
	   obj_init_temp_storage(&growable_data);
	   if( obj_parse_obj_file(&growable_data, filename) == 0)
		   return 0;
 
	   //print_vector(NORMAL, "Max bounds are: ", &growable_data->extreme_dimensions[1]);
	   //print_vector(NORMAL, "Min bounds are: ", &growable_data->extreme_dimensions[0]);
 
	   obj_copy_to_out_storage(data_out, &growable_data);
	   obj_free_temp_storage(&growable_data);
	   return 1;
   }
 
	obj_vector **vertexList;
	obj_vector **normalList;
	obj_vector **textureList;
 
	obj_face **faceList;
	obj_sphere **sphereList;
	obj_plane **planeList;
 
	obj_light_point **lightPointList;
	obj_light_quad **lightQuadList;
	obj_light_disc **lightDiscList;
 
	obj_material **materialList;
 
	int vertexCount;
	int normalCount;
	int textureCount;
 
	int faceCount;
	int sphereCount;
	int planeCount;
 
	int lightPointCount;
	int lightQuadCount;
	int lightDiscCount;
 
	int materialCount;
 
	obj_camera *camera;
 
private:
	obj_scene_data data;
};
 
#endif

csharp1

 using System;
using System.IO;
using System.Collections.Generic;
using OpenTK;
 
 
 
namespace Meshomatic
{
    public class ObjLoader
    {
 
        private string cfm(string str)
        {
            return str.Replace('.', ',');
        }
 
        public MeshData LoadStream(Stream stream)
        {
            StreamReader reader = new StreamReader(stream);
            List<Vector3> points = new List<Vector3>();
            List<Vector3> normals = new List<Vector3>();
            List<Vector2> texCoords = new List<Vector2>();
            List<Tri> tris = new List<Tri>();
            string line;
            char[] splitChars = { ' ' };
            while ((line = reader.ReadLine()) != null)
            {
                line = line.Trim(splitChars);
                line = line.Replace("  ", " ");
 
                string[] parameters = line.Split(splitChars);
 
                switch (parameters[0])
                {
                    case "p": // Point
                        break;
 
                    case "v": // Vertex
                        float x = float.Parse(cfm(parameters[1]));
                        float y = float.Parse(cfm(parameters[2]));
                        float z = float.Parse(cfm(parameters[3]));
                        points.Add(new Vector3(x, y, z));
                        break;
 
                    case "vt": // TexCoord
                        float u = float.Parse(cfm(parameters[1]));
                        float v = float.Parse(cfm(parameters[2]));
                        texCoords.Add(new Vector2(u, v));
                        break;
 
                    case "vn": // Normal
                        float nx = float.Parse(cfm(parameters[1]));
                        float ny = float.Parse(cfm(parameters[2]));
                        float nz = float.Parse(cfm(parameters[3]));
                        normals.Add(new Vector3(nx, ny, nz));
                        break;
 
                    case "f": // Face
                        tris.AddRange(parseFace(parameters));
                        break;
                }
            }
 
            Vector3[] p = points.ToArray();
            Vector2[] tc = texCoords.ToArray();
            Vector3[] n = normals.ToArray();
            Tri[] f = tris.ToArray();
 
            return new MeshData(p, n, tc, f);
        }
 
        public MeshData LoadFile(string file)
        {
            // Silly me, using() closes the file automatically.
            using (FileStream s = File.Open(file, FileMode.Open))
            {
                return LoadStream(s);
            }
        }
 
        private static Tri[] parseFace(string[] indices)
        {
            Point[] p = new Point[indices.Length - 1];
            for (int i = 0; i < p.Length; i++)
            {
                p[i] = parsePoint(indices[i + 1]);
            }
            return Triangulate(p);
            //return new Face(p);
        }
 
        // Takes an array of points and returns an array of triangles.
        // The points form an arbitrary polygon.
        private static Tri[] Triangulate(Point[] ps)
        {
            List<Tri> ts = new List<Tri>();
            if (ps.Length < 3)
            {
                throw new Exception("Invalid shape!  Must have >2 points");
            }
 
            Point lastButOne = ps[1];
            Point lastButTwo = ps[0];
            for (int i = 2; i < ps.Length; i++)
            {
                Tri t = new Tri(lastButTwo, lastButOne, ps[i]);
                lastButOne = ps[i];
                lastButTwo = ps[i - 1];
                ts.Add(t);
            }
            return ts.ToArray();
        }
 
        private static Point parsePoint(string s)
        {
            char[] splitChars = { '/' };
            string[] parameters = s.Split(splitChars);
            int vert = int.Parse(parameters[0]) - 1;
            int tex = int.Parse(parameters[1]) - 1;
            int norm = int.Parse(parameters[2]) - 1;
            return new Point(vert, norm, tex);
        }
    }
}

csharp2

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using OpenTK.Math;
 
using Drawables.Models;
using Drawables.Materials;
 
namespace Drawables.Models.OBJ
{
    public class OBJFile
    {
        char[] splitChars = { ' ' };
        char[] deleteChars = {'\t','\r','\n', ' '};
 
        string[] splitOnDelim(string data, string delim, int count)
        {
            return data.Split(delim.ToCharArray(), count);
        }
 
        string[] splitOnDelim(string data, string delim)
        {
            return data.Split(delim.ToCharArray());
        }
 
        string clean(string data)
        {
            return data.Trim(deleteChars);
        }
 
        float floatParseEx(String str)
        {
            if (str == string.Empty)
                return 0;
 
            return float.Parse(str.Replace('.', ','));
        }
 
        Vector3 readV3D(string data)
        {
            Vector3 v = new Vector3();
            string[] n = splitOnDelim(data, " ", 3);
            if (n.Length > 2)
            {
                v.X = floatParseEx(n[0]);
                v.Y = floatParseEx(n[1]);
                v.Z = floatParseEx(n[2]);
            }
            return v;
        }
 
        GLColor readColor(string data)
        {
            string[] n = splitOnDelim(data, " ", 5);
            if (n.Length > 2)
            {
                float R = floatParseEx(n[0]);
                float G = floatParseEx(n[1]);
                float B = floatParseEx(n[2]);
                float A = 1.0f;
                if (n.Length > 3)
                    A = floatParseEx(n[3]);
 
                return new GLColor(R, G, B, A);
            }
            return GLColor.White;
        }
 
        Vector2 readV2D(string data)
        {
            Vector2 v = new Vector2();
            string[] n = data.Split(splitChars);
            if (n.Length > 2)
                n[1] = (n[1].Split(splitChars))[0];
 
            if (n.Length > 1)
            {
                v.X = floatParseEx(n[0]);
                v.Y = 1.0f - floatParseEx(n[1]);
            }
            return v;
        }
 
        List<FaceVert> readFaces(string data)
        {
            List<FaceVert> faces = new List<FaceVert>();
            string[] nubs = splitOnDelim(data, " ");
            foreach (string n in nubs)
            {
                if (n.Contains("#"))
                    break;
 
                FaceVert f = new FaceVert();
                string[] i = splitOnDelim(n, "/", 3);
                if (i.Length > 0 && i[0] != string.Empty)
                    f.vert = int.Parse(i[0]);
                if (i.Length > 1 && i[1] != string.Empty)
                    f.uv = int.Parse(i[1]);
                if (i.Length > 2 && i[2] != string.Empty)
                    f.normal = int.Parse(i[2]);
 
                faces.Add(f);
            }
 
            return faces;
        }
 
        bool readMaterialFile( FileInfo file, Model model )
        {
            if (!file.Exists)
                return false;
 
            FileStream fs = file.OpenRead();
            if (fs == null)
                return false;
 
            StreamReader sr = new StreamReader(fs);
 
            Material currentMat = null;
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();
                line = clean(line);
 
                string[] nubs = splitOnDelim(line, " ",2);
 
                if (nubs.Length >1)
                {
                    string code = nubs[0];
                    if (code == "newmtl")
                    {
                        if (currentMat != null)
                            model.addMaterial(currentMat);
                        currentMat = new Material();
                        currentMat.name = nubs[1];
                    }
                    else if (currentMat != null)
                    {
                        if (code == "map_Kd")
                            currentMat.textureName = Path.Combine(Path.GetDirectoryName(file.FullName),nubs[1]);
                        else if (code == "Kd") //diffuse reflectivity using RGB values
                            currentMat.baseColor = readColor(nubs[1]);
                        else if (code == "Ka") //ambient reflectivity using a spectral curve
                            currentMat.ambinent = readColor(nubs[1]);
                        else if (code == "Ks") //specular reflectivity using RGB values
                            currentMat.specular = readColor(nubs[1]);
                        else if (code == "Ke")
                            currentMat.emission = readColor(nubs[1]);
                        else if (code == "Ns") //specular exponent for the current material
                            currentMat.shine = floatParseEx(nubs[1]);
                        else if (code == "Ni") //index of refraction
                            currentMat.index_of_refraction = floatParseEx(nubs[1]);
                        else if (code == "d" || code == "Tr") // defines the transparency of the material to be alpha. The default is 1.0 (not transparent at all). Some formats use d instead of Tr; 
                            currentMat.transparency = floatParseEx(nubs[1]);
                        else if (code == "Tf") //Transmission filter
                            currentMat.transmission_filter = readColor(nubs[1]);
                    }
                }
            }
            if (currentMat != null)
                model.addMaterial(currentMat);
 
            return true;
        }
 
        Vector3 getIndex(int index, List<Vector3> list)
        {
            if (index <= 0 && index + list.Count >= 0)
                return list[list.Count - 1 + index];
 
            if (index-1 > list.Count)
                return list[list.Count - 1];
 
            return list[index-1];
        }
 
        Vector2 getIndex(int index, List<Vector2> list)
        {
            if (index <= 0 && index + list.Count >= 0)
                return list[list.Count - 1 + index];
 
            if (index - 1 > list.Count)
                return list[list.Count - 1];
 
            return list[index - 1];
        }
 
        public Model read(FileInfo file)
        {
            Model model = new Model();
 
            model.name = Path.GetFileNameWithoutExtension(file.FullName);
 
            FileStream fs = file.OpenRead();
            StreamReader sr = new StreamReader(fs);
 
            // save off the verts, as they may span groups
            // we'll add em to each submesh as the faces get added
            // this way each submesh only has the verts for it's faces
            List<Vector3> verts = new List<Vector3>();
            List<Vector3> norms = new List<Vector3>();
            List<Vector2> uvs = new List<Vector2>();
 
            Mesh currentMesh = null;
 
            string currentObjectName = string.Empty;
            string currentGroupName = string.Empty;
            string currentMapName = string.Empty;
 
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();
                line = line.Trim(splitChars);
                line = line.Replace("  ", " ");
 
                string[] nubs = splitOnDelim(line, " ", 2);
                if (nubs.Length > 0)
                {
                    if (nubs.Length > 1)
                    {
                        string code = nubs[0];
                        if (code == "o")
                            currentObjectName = nubs[1];
                        else if (code == "mtllib")
                            readMaterialFile(new FileInfo(Path.Combine(Path.GetDirectoryName(file.FullName), nubs[1])), model);
                        else if (code == "usemtl")
                            currentMesh = model.getMeshForMaterial(nubs[1]);
                        else if (code == "usemap")
                            currentMapName = nubs[1];
                        else if (code == "g")
                            currentGroupName = nubs[1];
                        else if (code == "v")
                            verts.Add(readV3D(nubs[1]));
                        else if (code == "vn")
                            norms.Add(readV3D(nubs[1]));
                        else if (code == "vt")
                            uvs.Add(readV2D(nubs[1]));
                        else if (code == "f")
                        {
                            if (currentMesh == null) // this means that the geometry has no material, so make it white
                                currentMesh = model.getMesh(new Material());
 
                            Face face = new Face();
 
                            face.verts = readFaces(nubs[1]);
                            foreach (FaceVert f in face.verts)
                            {
                                f.vert = currentMesh.addVert(getIndex(f.vert, verts));
                                f.uv = currentMesh.addUV(getIndex(f.uv, uvs));
                                f.normal = currentMesh.addNormal(getIndex(f.normal, norms));
                            }
                            currentMesh.addFace(currentGroupName, face);
                        }
                    }
                }
            }
 
            sr.Close();
            fs.Close();
            return model;
        }
    }
}
Tal's picture

Peter, have you ever heard about Vala? I think it would be really great for you!
I personally would move to Vala, but it doesn't have a good and stable IDE for now.
OpenTK and Mono are very comfort for now, but if you really want speed go ahead and try it!
http://live.gnome.org/Vala