Example #1
0
void SceneParser::parseFile() {
	//
	// at the top level, the scene can have a camera, 
	// background color and a group of objects
	// (we add lights and other things in future assignments)
	//
	char token[MAX_PARSER_TOKEN_LENGTH];
	while (getToken(token)) {
		if (!strcmp(token, "OrthographicCamera")) {
			parseOrthographicCamera();
		}
		else if (!strcmp(token, "PerspectiveCamera")) {
			parsePerspectiveCamera();
		}
		else if (!strcmp(token, "Background")) {
			parseBackground();
		}
		else if (!strcmp(token, "Lights")) {
			parseLights();
		}
		else if (!strcmp(token, "Materials")) {
			parseMaterials();
		}
		else if (!strcmp(token, "Group")) {
			group = parseGroup();
		}
		else {
			printf("Unknown token in parseFile: '%s'\n", token);
			exit(0);
		}
	}
}
Example #2
0
void scene::parse(const char *filename)
{
    //some default values in case parsing fails
    myObjGroup = NULL;
    bgColor = Vec3f(0.5,0.5,0.5);
    ambLight = Vec3f(0.5,0.5,0.5);
    eye = Vec3f(0,0,0);
    lookAt = Vec3f(0,0,-1);
    up = Vec3f(0,1,0);
    fovy = 45;
    file = NULL;
    curline = 1;

    //the file-extension needs to be "ray"
    assert(filename != NULL);
    const char *ext = &filename[strlen(filename)-4];
    assert(!strcmp(ext,".ray"));
    fopen_s(&file, filename, "r");
    assert(file != NULL);

    char token[MAX_PARSER_TOKEN_LENGTH];

    //prime the parser pipe
    parse_char = fgetc(file);

    while (getToken(token))
    {
        if(!strcmp(token, "Background"))
            parseBackground();
        else if(!strcmp(token, "Camera"))
            parseCamera();
        else if(!strcmp(token, "Materials"))
            parseMaterials();
        else if(!strcmp(token, "Group"))
            myObjGroup = parseGroup();
        else if(!strcmp(token, "Lights"))
            parseLights();
        else
        {
            cout<<"Unknown token in parseFile: '"<<token<<
                "' at input line "<<curline<<"\n";
            exit(-1);
        }
    }
}
Example #3
0
OBJMesh* objMeshLoad(const char* filename)
{
	int linenum = 0;
	int warning = 0;
	int fatalError = 0;
	
	//open the ascii file
	FILE* file = fopen(filename, "r");
	
	//check it actually opened
	if (!file)
	{
		perror(filename);
		return NULL;
	}
	
	//the mesh we're going to return
	OBJMesh* mesh = (OBJMesh*)malloc(sizeof(OBJMesh));
	memset(mesh, 0, sizeof(OBJMesh));
	
	//we don't know how much data the obj file has, so we start with one and keep realloc-ing double
	ReallocArray vertices, faceSets, vertexHash, positions, normals, texCoords, triangles;
	initArray(&vertexHash, sizeof(VertHash*)); //array of hash record pointers (for freeing)
	initArray(&positions, sizeof(float) * 3);
	initArray(&normals, sizeof(float) * 3);
	initArray(&texCoords, sizeof(float) * 2);
	initArray(&triangles, sizeof(unsigned int) * 3);
	initArray(&faceSets, sizeof(OBJFaceSet));
	vertices.size = 0; //vertices are allocated later (at first face)
	
	//obj indices start at 1. we'll use the zero element for "error", giving with zero data
	positions.size = 1;
	normals.size = 1;
	texCoords.size = 1;
	allocArray(&positions);
	allocArray(&normals);
	allocArray(&texCoords);
	memset(positions.data, 0, positions.blockSize);
	memset(normals.data, 0, normals.blockSize);
	memset(texCoords.data, 0, texCoords.blockSize);
	
	//vertex combinations v/t/n are not always the same index. different vertex
	//combinations are hashed and reused, saving memory
	//see uthash.h (http://uthash.sourceforge.net/)
	VertHash* vertexRecords = NULL;
	
	//materials are referenced by name. this hash maps a name to material index
	MatHash* materialRecords = NULL;
	
	//whether the mesh contains normals or texture coordinates, and hence
	//the vertex data stride, is decided at the first face line ("f ...")
	int reachedFirstFace = 0;
	
	//current shading state
	int smoothShaded = 1;
	
	//start reading, line by line
	char* tmpTok;
	char line[OBJ_MAX_LINE_LEN];
	while (fgets(line, OBJ_MAX_LINE_LEN, file) != NULL)
	{
		//split line by spaces
		strtok_r(line, " ", &tmpTok);
		
		//what data does this line give us, if any?
		if (line[0] == 'v')
		{
			//this line contains vertex data
			if (line[1] == '\0') //\0 as strtok replaced the space
			{
				//position data. allocate more memory if needed
				positions.size++;
				allocArray(&positions);
				
				//read data from string
				((float*)positions.data)[(positions.size-1)*3+0] = readTokFloat(&tmpTok, &warning);
				((float*)positions.data)[(positions.size-1)*3+1] = readTokFloat(&tmpTok, &warning);
				((float*)positions.data)[(positions.size-1)*3+2] = readTokFloat(&tmpTok, &warning);
			}
			else if (line[1] == 'n')
			{
				//normal data. allocate more memory if needed
				normals.size++;
				allocArray(&normals);
				
				//read data from string
				((float*)normals.data)[(normals.size-1)*3+0] = readTokFloat(&tmpTok, &warning);
				((float*)normals.data)[(normals.size-1)*3+1] = readTokFloat(&tmpTok, &warning);
				((float*)normals.data)[(normals.size-1)*3+2] = readTokFloat(&tmpTok, &warning);
			}
			else if (line[1] == 't')
			{
				//texture data. allocate more memory if needed
				texCoords.size++;
				allocArray(&texCoords);
				
				//read data from string
				((float*)texCoords.data)[(texCoords.size-1)*2+0] = readTokFloat(&tmpTok, &warning);
				((float*)texCoords.data)[(texCoords.size-1)*2+1] = readTokFloat(&tmpTok, &warning);
			}
		}
		else if (line[0] == 'f')
		{
			//this line contains face data. this may have many vertices
			//but we just want triangles. so we triangulate.
			//NOTE: ALL vertex data must be given before being referenced by a face
			//NOTE: At least one vt or vn must be specified before the first f, or the rest will be ignored
			if (!reachedFirstFace)
			{
				//must have previously specified vertex positions
				if (positions.size == 1)
				{
					fatalError = 1;
					break;
				}
				
				//calculate vertex stride
				mesh->hasNormals = (normals.size > 1) ? 1 : 0;
				mesh->hasTexCoords = (texCoords.size > 1) ? 1 : 0;
				mesh->normalOffset = 3 * sizeof(float);
				mesh->texcoordOffset = mesh->normalOffset + mesh->hasNormals * 3 * sizeof(float);
				mesh->stride = mesh->texcoordOffset + mesh->hasTexCoords * 2 * sizeof(float);
				initArray(&vertices, mesh->stride);
				reachedFirstFace = 1;
			}
			
			//start by extracting groups of vertex data (pos/tex/norm)
			int i = 0;
			char* vert[OBJ_MAX_POLYGON];
			while ((vert[i] = strtok_r(NULL, " ", &tmpTok)) != NULL && i < OBJ_MAX_POLYGON)
				++i;
			
			//triangulate using the "fan" method
			int triVert = 0; //triVert contains the current face's vertex index. may not equal v as vertices can be ignored. 
			int triangulate[2];
			for (int v = 0; v < i; ++v)
			{
				//split groups by "/" and store in inds[3]
				int t = 0;
				int inds[3];
				char* tok = strtok_r(vert[v], "/", &tmpTok);
				while (tok != NULL && t < 3)
				{
					inds[t++] = atoi(tok);
					tok = strtok_r(NULL, "/", &tmpTok);
				}
				while (t < 3)
					inds[t++] = 0;
				
				//set provided, yet unused indices as invalid. this
				//prevents unnecessary unique vertices being created
				if (!mesh->hasTexCoords)
					inds[1] = -1;
				if (!mesh->hasNormals)
					inds[2] = -1;
					
				//ignore vertex if position indices are out of bounds
				if (inds[0] < 0 || inds[0] >= positions.size)
				{
					warning = 1;
					continue;
				}
				
				//use zero for out of bound normals and texture coordinates
				if ((mesh->hasTexCoords && (inds[1] < 0 || inds[1] >= texCoords.size)) ||
					(mesh->hasNormals && (inds[2] < 0 || inds[2] >= normals.size)))
				{
					warning = 1;
				}
					
				//since vertices will be reused a lot, we need to hash the v/t/n combination
				int uniqueVertIndex;
				
				//check if the vertex already exists in hash
				VertHash h;
				VertHash* found = NULL;
				memset(&h, 0, sizeof(VertHash));
				h.key.v = inds[0];
				h.key.t = inds[1];
				h.key.n = inds[2];
				//printf("looking up %i/%i/%i\n", h.key.v, h.key.t, h.key.n);
				HASH_FIND(hh, vertexRecords, &h.key, sizeof(VertHashKey), found);
				//printf("done looking up\n");
				if (found)
				{
					//found. use that vertex
					//printf("vert %i/%i/%i already exists as %i\n", inds[0], inds[1], inds[2], found->index);
					uniqueVertIndex = found->index;
				}
				else
				{
					//printf("new vert %i/%i/%i\n", inds[0], inds[1], inds[2]);
				
					//not found. create a new vertex
					uniqueVertIndex = vertices.size++;
					allocArray(&vertices);
					
					//copy data for vertex
					memcpy(((float*)vertices.data) + uniqueVertIndex * mesh->stride / sizeof(float), 
						((float*)positions.data) + inds[0] * 3,
						sizeof(float) * 3);
					if (mesh->hasTexCoords)
						memcpy(((float*)vertices.data) + (uniqueVertIndex * mesh->stride + mesh->texcoordOffset) / sizeof(float),
						((float*)texCoords.data) + inds[1] * 2,
						sizeof(float) * 2);
					if (mesh->hasNormals)
						memcpy(((float*)vertices.data) + (uniqueVertIndex * mesh->stride + mesh->normalOffset) / sizeof(float),
						((float*)normals.data) + inds[2] * 3,
						sizeof(float) * 3);
					
					//add vertex to hash table
					vertexHash.size++;
					allocArray(&vertexHash);
					VertHash* newRecord = (VertHash*)malloc(sizeof(VertHash));
					((VertHash**)vertexHash.data)[vertexHash.size-1] = newRecord; //store pointer to quickly free hash records later
					
					h.index = uniqueVertIndex;
					*newRecord = h;
					
					HASH_ADD(hh, vertexRecords, key, sizeof(VertHashKey), newRecord);
				}
				
				if (triVert == 0)
				{
					//store the first vertex
					triangulate[0] = uniqueVertIndex;
				}
				else if (triVert > 1)
				{
					//printf("tri %i->%i->%i\n", triangulate[0], triangulate[1], uniqueVertIndex);
					
					//this is at least the 3rd vertex - we have a new triangle to add
					//always create triangles between the current, previous and first vertex
					triangles.size++;
					allocArray(&triangles);
				
					//read data from string
					((unsigned int*)triangles.data)[(triangles.size-1)*3+0] = triangulate[0];
					((unsigned int*)triangles.data)[(triangles.size-1)*3+1] = triangulate[1];
					((unsigned int*)triangles.data)[(triangles.size-1)*3+2] = uniqueVertIndex;
					
				}
				//store the last vertex
				triangulate[1] = uniqueVertIndex;
				++triVert;
			}
		}
		else if (strcmp(line, "usemtl") == 0)
		{
			//the material state has changed - create a new faceset
			char* mtlname = strtok_r(NULL, " ", &tmpTok);
			trimRight(mtlname);
			
			//find the corresponding material
			MatHash* matRecord;
			HASH_FIND_STR(materialRecords, mtlname, matRecord);
			if (matRecord)
			{
				//printf("material: '%s'\n", mtlname);
				setFaceSet(&faceSets, matRecord->index, smoothShaded, triangles.size * 3);
			}
			else
			{
				warning = 1;
				printf("Undefined material: '%s'\n", mtlname);
				setFaceSet(&faceSets, -1, -1, triangles.size * 3);
			}
		}
		else if (strcmp(line, "mtllib") == 0)
		{
			//load all materials from the given .mtl file
			if (mesh->numMaterials > 0)
			{
				fatalError = 1; //shouldn't specify multiple .mtl files
				break;
			}
			char* mtlname = strtok_r(NULL, " ", &tmpTok);
			trimRight(mtlname);
			if (mtlname)
				parseMaterials(mesh, mtlname);
				
			//add all materials to the hash
			MatHash* matRecord;
			for (int m = 0; m < mesh->numMaterials; ++m)
			{
				HASH_FIND_STR(materialRecords, mesh->materials[m].name, matRecord);
				if (matRecord)
				{
					warning = 1;
					printf("Multiple materials with name '%s'\n", mesh->materials[m].name);
					continue; //can't have multiple definitions of the same material
				}
				matRecord = (MatHash*)malloc(sizeof(MatHash));
				matRecord->name = mesh->materials[m].name;
				matRecord->index = m;
				HASH_ADD_KEYPTR(hh, materialRecords, matRecord->name, strlen(matRecord->name), matRecord);
			}
		}
		else if (line[0] == 's')
		{
			#if !IGNORE_SMOOTHING
			//smooth shading has been changed - create a new faceset
			char* smooth = strtok_r(NULL, " ", &tmpTok);
			trimRight(smooth);
			smoothShaded = atoi(smooth);
			if (strcmp(smooth, "on") == 0) smoothShaded = 1;
			if (strcmp(smooth, "off") == 0) smoothShaded = 0;
			setFaceSet(&faceSets, -1, smoothShaded, triangles.size * 3);
			#endif
		}
		//options o and g are ignored
		
		linenum++; //for warnings/errors
	}
	setFaceSet(&faceSets, -1, -1, triangles.size * 3); //update end of final faceset
	
	//fill the rest of the mesh structure
	exactAllocArray(&vertices);
	exactAllocArray(&triangles);
	exactAllocArray(&faceSets);
	mesh->vertices = (float*)vertices.data;
	mesh->indices = (unsigned int*)triangles.data;
	mesh->facesets = (OBJFaceSet*)faceSets.data;
	mesh->numVertices = vertices.size;
	mesh->numIndices = triangles.size * 3;
	mesh->numFacesets = faceSets.size;
	
	//cleanup
	//NOTE: vertices and indices are used in the returned mesh data and are not freed
	
	MatHash* matRecord;
	MatHash* matTmp;
	HASH_ITER(hh, materialRecords, matRecord, matTmp) {
	  HASH_DEL(materialRecords, matRecord);
	  free(matRecord);
	}