void ISSKeypoints::ComputeISSKeypoints(){
    std::cout<<"computing keypoints of a model with "<<model->points.size()<<std::endl;
    double model_resolution= static_cast<double>(computeCloudResolution(model));
    if(!normals_set){
      CalcNormals();
    }
    if(iss_use_multipliers){
      iss_salient_radius_ = iss_sal_rad_multiplier_ * model_resolution;
      iss_border_radius_ = iss_bord_rad_multiplier_ * model_resolution;
      iss_normal_radius_= iss_norm_rad_multiplier_ * model_resolution;
      iss_non_max_radius_ = iss_non_max_multiplier_ * model_resolution;
    }
    else{
      iss_sal_rad_multiplier_ = iss_salient_radius_/model_resolution;
      iss_bord_rad_multiplier_ = iss_border_radius_/model_resolution;
      iss_norm_rad_multiplier_ = iss_normal_radius_/model_resolution;
      iss_non_max_multiplier_ = iss_non_max_radius_/model_resolution;
    }
    iss_detector.setSalientRadius (iss_salient_radius_);  
    iss_detector.setNonMaxRadius (iss_non_max_radius_);

    iss_detector.setNormalRadius (iss_normal_radius_);
    iss_detector.setBorderRadius (iss_border_radius_);

    iss_detector.setSearchMethod (tree);
    iss_detector.setThreshold21 (iss_gamma_21_);
    iss_detector.setThreshold32 (iss_gamma_32_);
    
    iss_detector.setMinNeighbors (iss_min_neighbors_);
    iss_detector.setNumberOfThreads (iss_threads_);
    iss_detector.setInputCloud (model);
    iss_detector.compute (*keypoints);
    ShowParameters();
    
}
Example #2
0
IndexedModel IndexedModel::Finalize()
{
	if(IsValid())
	{
		return *this;
	}
	
	if(m_texCoords.size() == 0)
	{
		for(unsigned int i = m_texCoords.size(); i < m_positions.size(); i++)
		{
			m_texCoords.push_back(Vector2f(0.0f, 0.0f));
		}
	}
	
	if(m_normals.size() == 0)
	{
		CalcNormals();
	}
	
	if(m_tangents.size() == 0)
	{
		CalcTangents();
	}
	
	return *this;
}
Example #3
0
void LMesh::Optimize(LOptimizationLevel value)
{
    switch (value)
    {
    case oNone:
        TransformVertices();
        break;
    case oSimple:
        //TransformVertices();
        CalcNormals(false);
        break;
    case oFull:
        //TransformVertices();
        CalcNormals(true);
        CalcTextureSpace();
        break;
    }
}
void CCourse::MakeCourseNormals () {
    if (nmls != NULL) delete[] nmls;
	try {
		nmls = new TVector3[nx * ny];
	} catch(...) {
		nmls = NULL;
		Message ("Allocation failed in MakeCourseNormals" , "");
	}
	CalcNormals ();
}
Example #5
0
void Mesh::Init()
{
	CalcNormals();
	glGenBuffers(1, &m_VBO);
    glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
	glBufferData(GL_ARRAY_BUFFER, m_vert_size * sizeof(Vertex), m_vertexes, GL_STATIC_DRAW);

	glGenBuffers(1, &m_IBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_i_size * sizeof(GLuint), m_indexes, GL_STATIC_DRAW);

}
Example #6
0
Mesh::Mesh(Vertex* vertices, int nVertices, INDEX* indices, int nIndices, bool calcNormals, bool calcTangents, const VertexFormat& vertexFormat)
{
    if(calcNormals)
        CalcNormals(vertices, nVertices, indices, nIndices);
    if(calcTangents)
        CalcTangents(vertices, nVertices, indices, nIndices);

    m_nVertices = nVertices;
    m_nIndices = nIndices;
    m_hVertexBuffer = Engine::GetRenderer()->CreateVertexBuffer(vertices, nVertices * sizeof(Vertex));
    m_hIndexBuffer =  Engine::GetRenderer()->CreateIndexBuffer(indices, nIndices * sizeof(int));
    m_pVertexFormat = &vertexFormat;
}
Example #7
0
void RawSceneLoader::load(Scene& r , const std::string fileName)
{	

	Vector3f positions[] = {
		Vector3f(-0.5f, -0.5f,  0.5f),		    // left front
		Vector3f(-0.5f, -0.5f, -0.5f),		    // left rear
		Vector3f( 0.5f, -0.5f,  0.5f),			// right front
		Vector3f( 0.5f, -0.5f, -0.5f),		    // right rear
		Vector3f( 0.0f,  0.5f,  0.0f),			// peak
	};
	
	Vector3f colors[] = {
		Vector3f(0.0f, 0.0f, 1.0f),
		Vector3f(0.0f, 0.0f, 1.0f),
		Vector3f(0.0f, 1.0f, 0.0f),
		Vector3f(1.0f, 1.0f, 0.0f),
		Vector3f(1.0f, 0.0f, 0.0f),
	};
	
	unsigned int indices[] = {
		1, 3, 0,
		0, 3, 2,
		1, 0, 4,
		3, 1, 4,
		2, 3, 4,
		0, 2, 4
	};

	Vector3f normals[sizeof(positions) / sizeof(Vector3f)];
	CalcNormals(indices, sizeof(indices) / sizeof(unsigned int), positions,sizeof(positions) / sizeof(Vector3f), normals);

	Vector2f tex[] = {
		Vector2f(0.0f, 0.0f),
		Vector2f(1.0f, 0.0f),
		Vector2f(1.0f, 0.0f),
		Vector2f(0.0f, 0.0f),
		Vector2f(0.5f, 1.0f),
	};

	Vector3f lightColor = Vector3f(1.0f, 1.0f, 1.0f);
	float ambientLightIntensity = 0.1f;
	float diffuseLightIntensity = 0.3f;
	float specularIntensity = 1.0f;
	float specularPower = 32.0f;
	Vector3f direction = Vector3f(0.0,0.0f,1.0f);

	r.meshes.push_back(Mesh(5, positions, colors, tex, normals, 6, indices));
	r.lights.push_back(Light(lightColor,ambientLightIntensity,diffuseLightIntensity,direction));
	r.textures.push_back(Texture());	// load texture from file
	r.materials.push_back(Material(specularIntensity, specularPower));
}
Example #8
0
    void CreateVertexBuffer(const unsigned int* pIndices, unsigned int IndexCount)
    {
        Vertex Vertices[4] = { Vertex(Vector3f(-10.0f, -2.0f, -10.0f), Vector2f(0.0f, 0.0f)),
                               Vertex(Vector3f(10.0f, -2.0f, -10.0f), Vector2f(1.0f, 0.0f)),
                               Vertex(Vector3f(10.0f, -2.0f, 10.0f), Vector2f(1.0f, 1.0f)),
                               Vertex(Vector3f(-10.0f, -2.0f, 10.0f), Vector2f(0.0f, 1.0f))};

        unsigned int VertexCount = ARRAY_SIZE_IN_ELEMENTS(Vertices);

        CalcNormals(pIndices, IndexCount, Vertices, VertexCount);

        glGenBuffers(1, &m_VBO);
        glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
    }
Example #9
0
	void Terrain::CreateVertices()
	{
		CU::GrowingArray<VertexPosNormUV> vertices(myHeightMap->myWidth * myHeightMap->myDepth);
		CU::GrowingArray<int> indices(myHeightMap->myWidth * myHeightMap->myDepth * 6);

		for (int z = 0; z < myHeightMap->myDepth; ++z)
		{
			for (int x = 0; x < myHeightMap->myWidth; ++x)
			{
				VertexPosNormUV vertex;
				vertex.myPos.x = float(x) * mySize.x / float(myHeightMap->myWidth);
				vertex.myPos.y = myHeightMap->myData[z * myHeightMap->myWidth + x] * myHeight / 255.f; 
				vertex.myPos.z = float(z) * mySize.y / float(myHeightMap->myDepth);
				vertex.myUV.x = float(x) / float(myHeightMap->myWidth);
				vertex.myUV.y = float(z) / float(myHeightMap->myDepth);
				vertices.Add(vertex);
			}
		}

		CalcNormals(vertices);

		for (int z = 0; z < myHeightMap->myDepth - 1; ++z)
		{
			for (int x = 0; x < myHeightMap->myWidth - 1; ++x)
			{
				indices.Add(z * myHeightMap->myWidth + x);
				indices.Add(z * myHeightMap->myWidth + x + 1);
				indices.Add((z + 1) * myHeightMap->myWidth + x);

				indices.Add((z + 1) * myHeightMap->myWidth + x);
				indices.Add(z * myHeightMap->myWidth + x + 1);
				indices.Add((z + 1) * myHeightMap->myWidth + x + 1);
			}
		}

		SetupVertexBuffer(vertices.Size(), sizeof(VertexPosNormUV), reinterpret_cast<char*>(&vertices[0])
			, "Terrain::VertexBuffer");
		SetupIndexBuffer(indices.Size(), reinterpret_cast<char*>(&indices[0]), "Terrain::IndexBuffer");

		mySurfaces[0]->SetVertexCount(vertices.Size());
		mySurfaces[0]->SetIndexCount(indices.Size());
	}
Example #10
0
File: svs.cpp Project: rizar/rossvs
void SVSBuilder::GenerateTrainingSet() {
    CalcDistanceToNN();

    srand(Params_.Seed);
    TrainingSetGenerator tsg(
            1, // temporary hack
            Params_.TakeProb,
            Params_.StepWidth);
    if (Params_.UseNormals) {
        CalcNormals();
        tsg.GenerateUsingNormals(*Input, *Normals, Params_.SupportSize);
    } else {
        tsg.GenerateFromSensor(*Input, LocalResolution);
    }

    Objects.reset(new PointCloud(tsg.Objects));
    Labels = tsg.Labels;
    Num2Grid_ = tsg.Num2Grid;
    Grid2Num_ = tsg.Grid2Num;
    Pixel2TrainNum = tsg.Pixel2Num;
}
Example #11
0
bool C3DSModel::CMesh::Complete()
{
	bool succ = m_nFace > 0 && m_nVertex > 0;
	if (succ)
	{
        CalcNormals();
	}
	if (m_pVB && m_pVertices != NULL)
	{
		m_pVB->Unlock();
		m_pVertices = NULL;
	}
	if (!succ)
	{
		SAFE_RELEASE(m_pVB);
		SAFE_RELEASE(m_pIB);
	}
    else
    {
        ComputeBoundBox();
    }

	return succ;
}
void G308_Geometry::ReadOBJ(const char *filename) {
	FILE* fp;
	char mode, vmode;
	char str[200];
	int v1, v2, v3, v4, n1, n2, n3, t1, t2, t3, t4;

	float x, y, z;
	float u, v;

	numVert = numNorm = numUV = numFace = 0;

	fp = fopen(filename, "r");
	if (fp == NULL)
		printf("Error reading %s file\n", filename);
	else
		printf("Reading %s file\n", filename);

	// Check number of vertex, normal, uvCoord, and Face
	while (fgets(str, 200, fp) != NULL) {
		sscanf(str, "%c%c", &mode, &vmode);
		switch (mode) {
		case 'v': /* vertex, uv, normal */
			if (vmode == 't') // uv coordinate
				numUV++;
			else if (vmode == 'n') // normal
				numNorm++;
			else if (vmode == ' ') // vertex
				numVert++;
			break;
		case 'f': /* faces */
			numFace++;
			break;
		}
	}

	m_nNumPoint = numVert;
	m_nNumUV = numUV;
	m_nNumPolygon = numFace;
	m_nNumNormal = numVert;

	printf("Number of Point %d, UV %d, Normal %d, Face %d\n", numVert, numUV,
			numNorm, numFace);
	//-------------------------------------------------------------
	//	Allocate memory for array
	//-------------------------------------------------------------

	if (m_pVertexArray != NULL)
		delete[] m_pVertexArray;
	m_pVertexArray = new G308_Point[m_nNumPoint];

	if (m_pNormalArray != NULL)
		delete[] m_pNormalArray;
	m_pNormalArray = new G308_Normal[m_nNumNormal];

	if (m_pFaceNormalArray != NULL)
		delete[] m_pFaceNormalArray;
	m_pFaceNormalArray = new G308_Normal[2*m_nNumPolygon];

	if (m_pUVArray != NULL)
		delete[] m_pUVArray;
	m_pUVArray = new G308_UVcoord[m_nNumUV];

	if (m_pTriangles != NULL)
		delete[] m_pTriangles;
	m_pTriangles = new G308_Triangle[2*m_nNumPolygon];

	if (m_pDisplaced != NULL)
		delete[] m_pDisplaced;
	m_pDisplaced = new G308_Point[m_nNumPoint];

	// VBO
	if (vertexSet != NULL)
		delete[] vertexSet;
	vertexSet = new GLfloat[m_nNumPoint*6];

	if (indices != NULL)
		delete[] indices;
	indices = new GLuint[m_nNumPolygon*6];


	//-----------------------------------------------------------
	//	Read obj file
	//-----------------------------------------------------------
	numVert = numNorm = numUV = numFace = 0;

	fseek(fp, 0L, SEEK_SET);
	while (fgets(str, 200, fp) != NULL) {
		sscanf(str, "%c%c", &mode, &vmode);
		switch (mode) {
		case 'v': /* vertex, uv, normal */
			if (vmode == 't') // uv coordinate
			{
				sscanf(str, "vt %f %f", &u, &v);
				m_pUVArray[numUV].u = u;
				m_pUVArray[numUV].v = v;
				numUV++;
				//numUV=0;
			} else if (vmode == 'n') // normal
			{
				sscanf(str, "vn %f %f %f", &x, &y, &z);
				m_pNormalArray[numNorm].x = x;
				m_pNormalArray[numNorm].y = y;
				m_pNormalArray[numNorm].z = z;
				numNorm++;
			} else if (vmode == ' ') // vertex
			{
				sscanf(str, "v %f %f %f", &x, &y, &z);
				//if (numVert<100) printf("v %f %f %f", x, y, z);
				m_pVertexArray[numVert].x = x;
				m_pVertexArray[numVert].y = y;
				m_pVertexArray[numVert].z = z;
				// displacements - zero
				m_pDisplaced[numVert].x = x;
				m_pDisplaced[numVert].y = y;
				m_pDisplaced[numVert].z = z;
				numVert++;
			}
			break;
		case 'f': /* faces : stored value is index - 1 since our index starts from 0, but obj starts from 1 */
			{

			int match = sscanf(str, "f %d/%d %d/%d %d/%d %d/%d", &v1, &t1, &v2, &t2, &v3, &t3, &v4, &t4);
			if (numFace==0) printf (str);

			// Vertex indicies for triangle:
			if (numVert != 0) {
				m_pTriangles[numFace].v1 = v1 - 1;
				m_pTriangles[numFace].v2 = v3 - 1;
				m_pTriangles[numFace].v3 = v4 - 1;
			}
			// Normal indicies for triangle
			m_pTriangles[numFace].n1 = v1 - 1;
			m_pTriangles[numFace].n2 = v3 - 1;
			m_pTriangles[numFace].n3 = v4 - 1;
			// UV indicies for triangle
			if (numUV != 0) {
				m_pTriangles[numFace].t1 = t1 - 1;
				m_pTriangles[numFace].t2 = t3 - 1;
				m_pTriangles[numFace].t3 = t4 - 1;
			}
			numFace++;

			// SECOND TRIANGLE OF
			// Vertex indicies for triangle:
			if (numVert != 0) {
				m_pTriangles[numFace].v1 = v1 - 1;
				m_pTriangles[numFace].v2 = v2 - 1;
				m_pTriangles[numFace].v3 = v3 - 1;
			}
			// Normal indicies for triangle
			m_pTriangles[numFace].n1 = v1 - 1;
			m_pTriangles[numFace].n2 = v2 - 1;
			m_pTriangles[numFace].n3 = v3 - 1;
			// UV indicies for triangle
			if (numUV != 0) {
				m_pTriangles[numFace].t1 = t1 - 1;
				m_pTriangles[numFace].t2 = t2 - 1;
				m_pTriangles[numFace].t3 = t3 - 1;
			}
			numFace++;

		}
		break;
		default:
			break;
		}
	}

	fclose(fp);
	printf("Reading OBJ file is DONE.\n");

	// NORMALS
	CalcNormals();
	//CalcDistances();
	CreateEyes();
	int i = 17784;

	/*
	printf (" %d %f \n", i, distances[i-EYES]);
	i = 17930;
	printf (" %d %f \n", i, distances[i-EYES]);
	i = 16921;
	printf (" %d %f \n", i, distances[i-EYES]);
	i = 17668;
	printf (" %d %f \n", i, distances[i-EYES]);
	i = 17771;
	printf (" %d %f \n", i, distances[i-EYES]);
	i = 17309;
	printf (" %d %f \n", i, distances[i-EYES]);
	i = 17142;
	printf (" %d %f \n", i, distances[i-EYES]);
	*/

	// initialize displacement data for RBF interpolation

	for (int i = 0; i<cp_num; i++)
	{
		controlPointPosX.push_back(m_pVertexArray[cp_numbers[i]].x);
		controlPointPosY.push_back(m_pVertexArray[cp_numbers[i]].y);
		controlPointPosZ.push_back(m_pVertexArray[cp_numbers[i]].z);

		controlPointDisplacementX.push_back(0.0f);
		controlPointDisplacementY.push_back(0.0f);
		controlPointDisplacementZ.push_back(0.0f);
	}

	// initialize interpolation functions
	rbfX = RBFInterpolator(controlPointPosX, controlPointPosY, controlPointPosZ, controlPointDisplacementX );
	rbfY = RBFInterpolator(controlPointPosX, controlPointPosY, controlPointPosZ, controlPointDisplacementY );
	rbfZ = RBFInterpolator(controlPointPosX, controlPointPosY, controlPointPosZ, controlPointDisplacementZ );

	// VBO
	for(int i = 0; i < m_nNumPoint-EYES; i++){
		/*
		if (i<=16134) {
			vertexSet[6*i] = 0;
			vertexSet[6*i+1] = 0;
			vertexSet[6*i+2] = 0;

			vertexSet[6*i+3] = 0;
			vertexSet[6*i+4] = 0;
			vertexSet[6*i+5] = 0;
		} else { */
			vertexSet[6*i] = m_pVertexArray[i+EYES].x;
			vertexSet[6*i+1] = m_pVertexArray[i+EYES].y;
			vertexSet[6*i+2] = m_pVertexArray[i+EYES].z;

			vertexSet[6*i+3] = m_pNormalArray[i+EYES].x;
			vertexSet[6*i+4] = m_pNormalArray[i+EYES].y;
			vertexSet[6*i+5] = m_pNormalArray[i+EYES].z;
		//}

		//if (i==17784 || i==17643 || i==17918) printf( " %f, %f, %f, ", vertexSet[3*i], vertexSet[3*i+1],vertexSet[3*i+2]);
		//vertexSet[i] = v;


	}
	/* for(int i = 0; i < 6*m_nNumPoint; i++){
		printf( " %f ", vertexSet[i]);
	} */
	printf( " ( %d %d ) ", numFace, m_nNumPoint);
	for(int i = 0; i < m_nNumPolygon*2; i++){
		if (m_pTriangles[i].v1>=EYES && m_pTriangles[i].v2>=EYES && m_pTriangles[i].v1>=EYES) {
			indices[3*m_nNumPolygon_Head] =  m_pTriangles[i].v1-EYES;
			indices[3*m_nNumPolygon_Head+1] =  m_pTriangles[i].v2-EYES;
			indices[3*m_nNumPolygon_Head+2] =  m_pTriangles[i].v3-EYES;
			m_nNumPolygon_Head++;
		}
	}
	m_nNumPolygon_Eyes=m_nNumPolygon*2 - m_nNumPolygon_Head;


	//printf( " %f, %f, %f, %f, %f, %f, %f, %f, %f  ", vertexSet[0], vertexSet[1],vertexSet[2], vertexSet[6], vertexSet[7],vertexSet[8], vertexSet[12], vertexSet[13],vertexSet[14]);
	printf( " %d %d %d ", indices[0], indices[1], indices[2]);


	//glGenVertexArrays(1, &VertexArrayID);
	//glBindVertexArray(VertexArrayID);

	// Generate 1 buffer, put the resulting identifier in vertexbuffer
	glGenBuffers(1, &vertexbuffer);

	// The following commands will talk about our 'vertexbuffer' buffer
	glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);

	// Give our vertices to OpenGL.
	//glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
	glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*(m_nNumPoint-EYES)*6 , vertexSet, GL_STATIC_DRAW); // sizeof(vertexSet)


	glGenBuffers(1, &indexbuffer);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexbuffer);
	//glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(g_index_buffer_data), g_index_buffer_data, GL_STATIC_DRAW);
	//glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*m_nNumPolygon*6, indices, GL_STATIC_DRAW); //  sizeof(indices)
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*m_nNumPolygon_Head*3, indices, GL_STATIC_DRAW); //  sizeof(indices)

	printf(" !!! %d !!! ", m_nNumPoint-EYES);

}
Example #13
0
TDSScene * Load3DSFile(const char * fileName)
{
  int i;
  unsigned short chunk, count;
  int fileSize, chunkLen;
  FILE * f;
  TDSScene * tds;
  TDSObject * obj;

  // Open file
  f = fopen(fileName, "rb");
  if(!f)
    return (TDSScene *) 0;

  // Get file size
  fseek(f, 0, SEEK_END);
  fileSize = ftell(f);
  fseek(f, 0, SEEK_SET);

  // Check file size (rough initial check)
  if(fileSize < 6)
  {
    fclose(f);
    return (TDSScene *) 0;
  }

  // Read & check file header identifier
  chunk = ReadInt16(f);
  chunkLen = ReadInt32(f);
  if((chunk != CHUNK_MAIN) || (chunkLen != fileSize))
  {
    fclose(f);
    return (TDSScene *) 0;
  }

  // Create new TDSScene instance
  tds = (TDSScene *) malloc(sizeof(TDSScene));
  if(!tds)
  {
    fclose(f);
    return (TDSScene *) 0;
  }
  memset(tds, 0, sizeof(TDSScene));

  // Parse chunks...
  obj = (TDSObject *) 0;
  while(ftell(f) < fileSize)
  {
    // Read next chunk
    chunk = ReadInt16(f);
    chunkLen = ReadInt32(f);

    // What chunk did we get?
    switch(chunk)
    {
      // 3D Edit -> Step into
      case CHUNK_3DEDIT:
        break;

      // Object -> Step into
      case CHUNK_OBJECT:
        // Skip object name (null terminated string)
        while((ftell(f) < fileSize) && fgetc(f));

        // Create a new object
        obj = malloc(sizeof(TDSObject));
        if(obj)
        {
          memset(obj, 0, sizeof(TDSObject));
          obj->vertices = (TDSVertex *) 0;
          obj->vertCount = 0;
          obj->indices = (unsigned short *) 0;
          obj->faceCount = 0;
          AddObject(tds, obj);
        }
        break;

      // Triangle mesh -> Step into
      case CHUNK_TRIMESH:
        break;

      // Vertex list (point coordinates)
      case CHUNK_VERTEXLIST:
        count = ReadInt16(f);
        if((!obj) || ((obj->vertCount > 0) && (obj->vertCount != count)))
        {
          fseek(f, count * 12, SEEK_CUR);
          break;
        }
        if(obj->vertCount == 0)
        {
          obj->vertCount = count;
          obj->vertices = (TDSVertex *) malloc(sizeof(TDSVertex) * count);
          memset(obj->vertices, 0, sizeof(TDSVertex) * count);
        }
        for(i = 0; i < count; ++ i)
          ReadVector3D(f, obj->vertices[i].point);
        break;

      // Texture map coordinates (UV coordinates)
      case CHUNK_MAPPINGCOORDS:
        count = ReadInt16(f);
        if((!obj) || ((obj->vertCount > 0) && (obj->vertCount != count)))
        {
          fseek(f, count * 8, SEEK_CUR);
          break;
        }
        if(obj->vertCount == 0)
        {
          obj->vertCount = count;
          obj->vertices = (TDSVertex *) malloc(sizeof(TDSVertex) * count);
          memset(obj->vertices, 0, sizeof(TDSVertex) * count);
        }
        for(i = 0; i < count; ++ i)
        {
          obj->vertices[i].u = ReadFloat32(f);
          obj->vertices[i].v = ReadFloat32(f);
        }
        break;

      // Face description (triangle indices)
      case CHUNK_FACES:
        count = ReadInt16(f);
        if(!obj)
        {
          fseek(f, count * 8, SEEK_CUR);
          break;
        }
        if(obj->faceCount == 0)
        {
          obj->faceCount = count;
          obj->indices = (unsigned short *) malloc(sizeof(unsigned short) * 3 * count);
        }
        for(i = 0; i < count; ++ i)
        {
          obj->indices[i * 3] = ReadInt16(f);
          obj->indices[i * 3 + 1] = ReadInt16(f);
          obj->indices[i * 3 + 2] = ReadInt16(f);
          ReadInt16(f); // Skip face flag
        }
        break;
        
      default:      // Unknown/ignored - skip past this one
        fseek(f, chunkLen - 6, SEEK_CUR);
    }
  }

  // Close file
  fclose(f);

  // Calculate normals for all the objects
  for(i = 0; i < tds->objectCount; ++ i)
    CalcNormals(tds->objects[i]);

  return tds;
}