Пример #1
0
/* _ase_load:
 *  loads a 3dsmax ase model file.
*/
static picoModel_t *_ase_load( PM_PARAMS_LOAD )
{
	picoModel_t    *model;
	picoParser_t   *p;
	char			lastNodeName[ 1024 ];

	aseVertex_t* vertices = NULL;
	aseTexCoord_t* texcoords = NULL;
	aseColor_t* colors = NULL;
	aseFace_t* faces = NULL;
	int numVertices = 0;
	int numFaces = 0;
	int numTextureVertices = 0;
	int numTextureVertexFaces = 0;
	int numColorVertices = 0;
	int numColorVertexFaces = 0;
	int vertexId = 0;
   int currentVertexFace=0;
   int currentVertexIndex=0;
   int counter=0;
   int submodel=0;

	aseMaterial_t* materials = NULL;

#ifdef DEBUG_PM_ASE
	clock_t start, finish;
	double elapsed;
	start = clock();
#endif

	/* helper */
	#define _ase_error_return(m) \
	{ \
		_pico_printf( PICO_ERROR,"%s in ASE, line %d.",m,p->curLine); \
		_pico_free_parser( p ); \
		PicoFreeModel( model ); \
		return NULL; \
	}
	/* create a new pico parser */
	p = _pico_new_parser( (picoByte_t *)buffer,bufSize );
	if (p == NULL) return NULL;

	/* create a new pico model */
	model = PicoNewModel();
	if (model == NULL)
	{
		_pico_free_parser( p );
		return NULL;
	}
	/* do model setup */
	PicoSetModelFrameNum( model, frameNum );
	PicoSetModelName( model, fileName );
	PicoSetModelFileName( model, fileName );

	/* initialize some stuff */
	memset( lastNodeName,0,sizeof(lastNodeName) );

	/* parse ase model file */
	while( 1 )
	{
		/* get first token on line */
		if (_pico_parse_first( p ) == NULL)
			break;

		/* we just skip empty lines */
		if (p->token == NULL || !strlen( p->token ))
			continue;

		/* we skip invalid ase statements */
		if (p->token[0] != '*' && p->token[0] != '{' && p->token[0] != '}')
		{
			_pico_parse_skip_rest( p );
			continue;
		}
		/* remember node name */
		if (!_pico_stricmp(p->token,"*node_name"))
		{
			/* read node name */
			char *ptr = _pico_parse( p,0 );
			if (ptr == NULL)
				_ase_error_return("Node name parse error");

			/* remember node name */
			strncpy( lastNodeName,ptr,sizeof(lastNodeName) );
		}
		/* model mesh (originally contained within geomobject) */
		else if (!_pico_stricmp(p->token,"*mesh"))
		{
			/* finish existing surface */
			_ase_submit_triangles(model, materials, vertices, texcoords, colors, faces, numFaces,numVertices,submodel++);
			_pico_free(faces);
			_pico_free(vertices);
			_pico_free(texcoords);
			_pico_free(colors);
		}
		else if (!_pico_stricmp(p->token,"*mesh_numvertex"))
		{
  			if (!_pico_parse_int( p, &numVertices) )
				_ase_error_return("Missing MESH_NUMVERTEX value");

			vertices = _pico_calloc(numVertices, sizeof(aseVertex_t));
         currentVertexIndex=0;   
		}
		else if (!_pico_stricmp(p->token,"*mesh_numfaces"))
		{
			if (!_pico_parse_int( p, &numFaces) )
				_ase_error_return("Missing MESH_NUMFACES value");

			faces = _pico_calloc(numFaces, sizeof(aseFace_t));

		}
		else if (!_pico_stricmp(p->token,"*mesh_numtvertex"))
		{
			if (!_pico_parse_int( p, &numTextureVertices) )
				_ase_error_return("Missing MESH_NUMTVERTEX value");

			texcoords = _pico_calloc(numTextureVertices, sizeof(aseTexCoord_t));
		}
		else if (!_pico_stricmp(p->token,"*mesh_numtvfaces"))
		{
			if (!_pico_parse_int( p, &numTextureVertexFaces) )
				_ase_error_return("Missing MESH_NUMTVFACES value");
		}
		else if (!_pico_stricmp(p->token,"*mesh_numcvertex"))
		{
			if (!_pico_parse_int( p, &numColorVertices) )
				_ase_error_return("Missing MESH_NUMCVERTEX value");

			colors = _pico_calloc(numColorVertices, sizeof(aseColor_t));
			memset( colors, 255, numColorVertices * sizeof( aseColor_t ) );	/* ydnar: force colors to white initially */
		}
		else if (!_pico_stricmp(p->token,"*mesh_numcvfaces"))
		{
			if (!_pico_parse_int( p, &numColorVertexFaces) )
				_ase_error_return("Missing MESH_NUMCVFACES value");
		}
		/* mesh material reference. this usually comes at the end of */
		/* geomobjects after the mesh blocks. we must assume that the */
		/* new mesh was already created so all we can do here is assign */
		/* the material reference id (shader index) now. */
		else if (!_pico_stricmp(p->token,"*material_ref"))
		{
			int mtlId;

			/* get the material ref (0..n) */
			if (!_pico_parse_int( p,&mtlId) )
				_ase_error_return("Missing material reference ID");

			{
				int i = 0;
				/* fix up all of the aseFaceList in the surface to point to the parent material */
				/* we've already saved off their subMtl */
				for(; i < numFaces; ++i)
				{
					faces[i].materialId = mtlId;
				}
			}
		}
		/* model mesh vertex */
		else if (!_pico_stricmp(p->token,"*mesh_vertex"))
		{
			int			index;

			if( numVertices == 0 )
				_ase_error_return("Vertex parse error");

			/* get vertex data (orig: index +y -x +z) */
			if (!_pico_parse_int( p,&index ))
				_ase_error_return("Vertex parse error");
			if (!_pico_parse_vec( p,vertices[index].xyz ))
				_ase_error_return("Vertex parse error");

			vertices[index].id = vertexId++;
		}
		else if (!_pico_stricmp(p->token,"*mesh_facenormal"))
		{
		   //Grab the faceindex for the next vertex normals.
         if( numVertices == 0 )
				_ase_error_return("Vertex parse error (facenormals)");

         if (!_pico_parse_int( p,&currentVertexFace ))
				_ase_error_return("Vertex parse error");

 			if (!_pico_parse_vec( p,faces[currentVertexFace].facenormal ))
				_ase_error_return("Vertex parse error");

      }
      /* model mesh vertex normal */
		else if (!_pico_stricmp(p->token,"*mesh_vertexnormal"))
		{
			int			index;

			if( numVertices == 0 )
				_ase_error_return("Vertex parse error");

			/* get vertex data (orig: index +y -x +z) */
			if (!_pico_parse_int( p,&index ))
				_ase_error_return("Vertex parse error");

         //^^ Index is 'wrong' in .ase models.  they reference the same vert index with multiple normals..
         // I've tried, this is a lost cause.  Use the SG's
         // 
			/*
         
         if (!_pico_parse_vec( p,vertices[counter].normal ))
				_ase_error_return("Vertex parse error");
         vertices[counter].faceid=index;
         counter++;
         */
		}
		/* model mesh face */
		else if (!_pico_stricmp(p->token,"*mesh_normals"))
      {
      //   counter=0; //part of the above vertex normals fix
      }
         
      /* model mesh face */
		else if (!_pico_stricmp(p->token,"*mesh_face"))
		{
			picoIndex_t indexes[3];
			int index;
			
			if( numFaces == 0 )
				_ase_error_return("Face parse error");

			/* get face index */
			if (!_pico_parse_int( p,&index ))
				_ase_error_return("Face parse error");

			/* get 1st vertex index */
			_pico_parse( p,0 );
			if (!_pico_parse_int( p,&indexes[0] ))
				_ase_error_return("Face parse error");

			/* get 2nd vertex index */
			_pico_parse( p,0 );
			if (!_pico_parse_int( p,&indexes[1] ))
				_ase_error_return("Face parse error");

			/* get 3rd vertex index */
			_pico_parse( p,0 );
			if (!_pico_parse_int( p,&indexes[2] ))
				_ase_error_return("Face parse error");
			
			/* parse to the subMaterial ID */
			while ( 1 )
			{
				if (!_pico_parse (p,0)) /* EOL */
				{
					break;
				}
				if (!_pico_stricmp (p->token,"*MESH_SMOOTHING" ))
				{
               int total=0;
               char* point;
               char* start;
               _pico_parse(p,0);

               point=p->token;
               start=point;
               faces[index].smoothingGroup=0;
              
               //Super dodgy comma delimited string parse
               while (*point<'A') 
               {
                  if (*point<=32 || *point==',')
                  {
                     total=atoi(start);
                     if (total!=0)
                     {
                        faces[index].smoothingGroup+=1<<total;
                     }
                     start=point+1;
                  }
                  
                  point++;
               }
               
               
			      
               
            }
				if (!_pico_stricmp (p->token,"*MESH_MTLID" ))
				{
					_pico_parse_int ( p , &faces[index].subMaterialId );
				}
			}
			
			faces[index].materialId = 0;
			faces[index].indices[0] = indexes[2];
			faces[index].indices[1] = indexes[1];
			faces[index].indices[2] = indexes[0];
		}
		/* model texture vertex */
		else if (!_pico_stricmp(p->token,"*mesh_tvert"))
		{
			int			index;

			if( numVertices == 0 )
				_ase_error_return("Vertex parse error");

			/* get uv vertex index */
			if (!_pico_parse_int( p,&index ))
				_ase_error_return("UV vertex parse error");

			/* get uv vertex s */
			if (!_pico_parse_float( p,&texcoords[index].texcoord[0] ))
				_ase_error_return("UV vertex parse error");

			/* get uv vertex t */
			if (!_pico_parse_float( p,&texcoords[index].texcoord[1] ))
				_ase_error_return("UV vertex parse error");
			
			/* ydnar: invert t */
			texcoords[index].texcoord[ 1 ] = 1.0f - texcoords[index].texcoord[ 1 ];
		}
		/* ydnar: model mesh texture face */
		else if( !_pico_stricmp( p->token, "*mesh_tface" ) )
		{
			picoIndex_t indexes[3];
			int			index;
			
			if( numFaces == 0 )
				_ase_error_return("Texture face parse error");
			
			/* get face index */
			if (!_pico_parse_int( p,&index ))
				_ase_error_return("Texture face parse error");
			
			/* get 1st vertex index */
			if (!_pico_parse_int( p,&indexes[0] ))
				_ase_error_return("Texture face parse error");
			
			/* get 2nd vertex index */
			if (!_pico_parse_int( p,&indexes[1] ))
				_ase_error_return("Texture face parse error");
			
			/* get 3rd vertex index */
			if (!_pico_parse_int( p,&indexes[2] ))
				_ase_error_return("Texture face parse error");

			faces[index].indices[3] = indexes[2];
			faces[index].indices[4] = indexes[1];
			faces[index].indices[5] = indexes[0];
		}
		/* model color vertex */
		else if (!_pico_stricmp(p->token,"*mesh_vertcol"))
		{
			int			index;
			float		colorInput;

			if( numVertices == 0 )
				_ase_error_return("Color Vertex parse error");

			/* get color vertex index */
			if (!_pico_parse_int( p,&index ))
				_ase_error_return("Color vertex parse error");

			/* get R component */
			if (!_pico_parse_float( p,&colorInput ))
				_ase_error_return("Color vertex parse error");
			colors[index].color[0] = (picoByte_t)(colorInput * 255);

			/* get G component */
			if (!_pico_parse_float( p,&colorInput ))
				_ase_error_return("Color vertex parse error");
			colors[index].color[1] = (picoByte_t)(colorInput * 255);

			/* get B component */
			if (!_pico_parse_float( p,&colorInput ))
				_ase_error_return("Color vertex parse error");
			colors[index].color[2] = (picoByte_t)(colorInput * 255);
			
			/* leave alpha alone since we don't get any data from the ASE format */
			colors[index].color[3] = 255;

         /* 27 hack, red as alpha */
         colors[index].color[3]=colors[index].color[0];
         colors[index].color[0]=255;
         colors[index].color[1]=255;
         colors[index].color[2]=255;

		}
		/* model color face */
		else if (!_pico_stricmp(p->token,"*mesh_cface"))
		{
			picoIndex_t indexes[3];
			int			index;

			if( numFaces == 0 )
				_ase_error_return("Face parse error");

			/* get face index */
			if (!_pico_parse_int( p,&index ))
				_ase_error_return("Face parse error");

			/* get 1st cvertex index */
			//			_pico_parse( p,0 );
			if (!_pico_parse_int( p,&indexes[0] ))
				_ase_error_return("Face parse error");

			/* get 2nd cvertex index */
			//			_pico_parse( p,0 );
			if (!_pico_parse_int( p,&indexes[1] ))
				_ase_error_return("Face parse error");

			/* get 3rd cvertex index */
			//			_pico_parse( p,0 );
			if (!_pico_parse_int( p,&indexes[2] ))
				_ase_error_return("Face parse error");

			faces[index].indices[6] = indexes[2];
			faces[index].indices[7] = indexes[1];
			faces[index].indices[8] = indexes[0];
		}
		/* model material */
		else if( !_pico_stricmp( p->token, "*material" ) )
		{
			aseSubMaterial_t*	subMaterial = NULL;
			picoShader_t		*shader;
			int					level = 1, index;
			char				materialName[ 1024 ];
			float				transValue = 0.0f, shineValue = 1.0f;
			picoColor_t			ambientColor, diffuseColor, specularColor;
			char				*mapname = NULL;
			int					subMtlId, subMaterialLevel = -1;
			
			
			/* get material index */
			_pico_parse_int( p,&index );
			
			/* check brace */
			if (!_pico_parse_check(p,1,"{"))
				_ase_error_return("Material missing opening brace");
			
			/* parse material block */
			while( 1 )
			{
				/* get next token */
				if (_pico_parse(p,1) == NULL) break;
				if (!strlen(p->token)) continue;

				/* handle levels */
				if (p->token[0] == '{') level++;
				if (p->token[0] == '}') level--;
				if (!level) break;

				if( level == subMaterialLevel )
				{
					/* set material name */
					_pico_first_token( materialName );
					PicoSetShaderName( shader, materialName);

					/* set shader's transparency */
					PicoSetShaderTransparency( shader,transValue );

					/* set shader's ambient color */
					PicoSetShaderAmbientColor( shader,ambientColor );

					/* set diffuse alpha to transparency */
					diffuseColor[3] = (picoByte_t)( transValue * 255.0 );

					/* set shader's diffuse color */
					PicoSetShaderDiffuseColor( shader,diffuseColor );

					/* set shader's specular color */
					PicoSetShaderSpecularColor( shader,specularColor );

					/* set shader's shininess */
					PicoSetShaderShininess( shader,shineValue );

					/* set material map name */
					PicoSetShaderMapName( shader, mapname );

					subMaterial = _ase_add_submaterial( &materials, index, subMtlId, shader );
					subMaterialLevel = -1;
				}

				/* parse submaterial index */
				if (!_pico_stricmp(p->token,"*submaterial"))
				{											
					/* allocate new pico shader */
					_pico_parse_int( p , &subMtlId );

					shader = PicoNewShader( model );
					if (shader == NULL)
					{
						PicoFreeModel( model );
						return NULL;
					}			
					subMaterialLevel = level;
				}
				/* parse material name */
				else if (!_pico_stricmp(p->token,"*material_name"))
				{
					char* name = _pico_parse(p,0);
					if ( name == NULL)
						_ase_error_return("Missing material name");
					
					strcpy ( materialName , name );
					/* skip rest and continue with next token */
					_pico_parse_skip_rest( p );
					continue;
				}
				/* parse material transparency */
				else if (!_pico_stricmp(p->token,"*material_transparency"))
				{
					/* get transparency value from ase */
					if (!_pico_parse_float( p,&transValue ))
						_ase_error_return("Material transparency parse error");

					/* skip rest and continue with next token */
					_pico_parse_skip_rest( p );
					continue;
				}
				/* parse material shininess */
				else if (!_pico_stricmp(p->token,"*material_shine"))
				{
					/* remark:
					 * - not sure but instead of '*material_shine' i might
					 *   need to use '*material_shinestrength' */

					/* get shine value from ase */
					if (!_pico_parse_float( p,&shineValue ))
						_ase_error_return("Material shine parse error");

					/* scale ase shine range 0..1 to pico range 0..127 */
					shineValue *= 128.0;

					/* skip rest and continue with next token */
					_pico_parse_skip_rest( p );
					continue;
				}
				/* parse ambient material color */
				else if (!_pico_stricmp(p->token,"*material_ambient"))
				{
					picoVec3_t  vec;
					/* get r,g,b float values from ase */
					if (!_pico_parse_vec( p,vec ))
						_ase_error_return("Material color parse error");

					/* setup 0..255 range color values */
					ambientColor[ 0 ] = (int)( vec[ 0 ] * 255.0 );
					ambientColor[ 1 ] = (int)( vec[ 1 ] * 255.0 );
					ambientColor[ 2 ] = (int)( vec[ 2 ] * 255.0 );
					ambientColor[ 3 ] = (int)( 255 );

					/* skip rest and continue with next token */
					_pico_parse_skip_rest( p );
					continue;
				}
				/* parse diffuse material color */
				else if (!_pico_stricmp(p->token,"*material_diffuse"))
				{
					picoVec3_t  vec;

					/* get r,g,b float values from ase */
					if (!_pico_parse_vec( p,vec ))
						_ase_error_return("Material color parse error");

					/* setup 0..255 range color */
					diffuseColor[ 0 ] = (int)( vec[ 0 ] * 255.0 );
					diffuseColor[ 1 ] = (int)( vec[ 1 ] * 255.0 );
					diffuseColor[ 2 ] = (int)( vec[ 2 ] * 255.0 );
					diffuseColor[ 3 ] = (int)( 255 );

					/* skip rest and continue with next token */
					_pico_parse_skip_rest( p );
					continue;
				}
				/* parse specular material color */
				else if (!_pico_stricmp(p->token,"*material_specular"))
				{
					picoVec3_t  vec;

					/* get r,g,b float values from ase */
					if (!_pico_parse_vec( p,vec ))
						_ase_error_return("Material color parse error");

					/* setup 0..255 range color */
					specularColor[ 0 ] = (int)( vec[ 0 ] * 255 );
					specularColor[ 1 ] = (int)( vec[ 1 ] * 255 );
					specularColor[ 2 ] = (int)( vec[ 2 ] * 255 );
					specularColor[ 3 ] = (int)( 255 );

					/* skip rest and continue with next token */
					_pico_parse_skip_rest( p );
					continue;
				}
				/* material diffuse map */
				else if (!_pico_stricmp(p->token,"*map_diffuse") )
				{
					int sublevel = 0;

					/* parse material block */
					while( 1 )
					{
						/* get next token */
						if (_pico_parse(p,1) == NULL) break;
						if (!strlen(p->token)) continue;
						
						/* handle levels */
						if (p->token[0] == '{') sublevel++;
						if (p->token[0] == '}') sublevel--;
						if (!sublevel) break;
						
						/* parse diffuse map bitmap */
						if (!_pico_stricmp(p->token,"*bitmap"))
						{
							char* name = _pico_parse(p,0);
							if (name == NULL)
								_ase_error_return("Missing material map bitmap name");
							mapname = _pico_alloc ( strlen ( name ) + 1 );
							strcpy ( mapname, name );
							/* skip rest and continue with next token */
							_pico_parse_skip_rest( p );
							continue;
						}
					}
				}
				/* end map_diffuse block */
			}
			/* end material block */

			if( subMaterial == NULL )
			{
				/* allocate new pico shader */
				shader = PicoNewShader( model );
				if (shader == NULL)
				{
					PicoFreeModel( model );
					return NULL;
				}

				/* set material name */
				PicoSetShaderName( shader,materialName );

				/* set shader's transparency */
				PicoSetShaderTransparency( shader,transValue );

				/* set shader's ambient color */
				PicoSetShaderAmbientColor( shader,ambientColor );

				/* set diffuse alpha to transparency */
				diffuseColor[3] = (picoByte_t)( transValue * 255.0 );

				/* set shader's diffuse color */
				PicoSetShaderDiffuseColor( shader,diffuseColor );

				/* set shader's specular color */
				PicoSetShaderSpecularColor( shader,specularColor );

				/* set shader's shininess */
				PicoSetShaderShininess( shader,shineValue );

				/* set material map name */
				PicoSetShaderMapName( shader, mapname );

        /* extract shadername from bitmap path */
        if(mapname != NULL)
        {
          char* p = mapname;

          /* convert to shader-name format */
          {
            /* unix-style path separators */
            char* s = mapname;
            for(; *s != '\0'; ++s)
            {
              if(*s == '\\')
              {
                *s = '/';
              }
            }
          }
          {
            /* remove extension */
            char* last_period = strrchr(p, '.');
            if(last_period != NULL)
            {
              *last_period = '\0';
            }
          }

          /* find game root */
          for(; *p != '\0'; ++p)
          {
            if(_pico_strnicmp(p, "quake", 5) == 0 || _pico_strnicmp(p, "doom", 4) == 0)
            {
              break;
            }
          }
          /* root-relative */
          for(; *p != '\0'; ++p)
          {
            if(*p == '/')
            {
              ++p;
              break;
            }
          }
          /* game-relative */
          for(; *p != '\0'; ++p)
          {
            if(*p == '/')
            {
              ++p;
              break;
            }
          }

          if(*p != '\0')
          {
				    /* set material name */
				    PicoSetShaderName( shader,p );
          }
        }

        /* this is just a material with 1 submaterial */
				subMaterial = _ase_add_submaterial( &materials, index, 0, shader );
			}
			
			/* ydnar: free mapname */
			if( mapname != NULL )
				_pico_free( mapname );
		}	// !_pico_stricmp ( "*material" )

		/* skip unparsed rest of line and continue */
		_pico_parse_skip_rest( p );
	}
	
	/* ydnar: finish existing surface */
	_ase_submit_triangles(model, materials, vertices, texcoords, colors, faces, numFaces,numVertices,submodel++);
	_pico_free(faces);
	_pico_free(vertices);
	_pico_free(texcoords);
	_pico_free(colors);

#ifdef DEBUG_PM_ASE
	_ase_print_materials(materials);
	finish = clock();
	elapsed = (double)(finish - start) / CLOCKS_PER_SEC;
	_pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s)\n", elapsed );
#endif //DEBUG_PM_ASE

	_ase_free_materials(&materials);

  _pico_free_parser( p );

	/* return allocated pico model */
	return model;
}
Пример #2
0
static picoModel_t *_terrain_load( PM_PARAMS_LOAD ){
	int i, j, v, pw[ 5 ], r;
	picoParser_t    *p;

	char            *shader, *heightmapFile, *colormapFile;
	picoVec3_t scale, origin;

	unsigned char   *imageBuffer;
	int imageBufSize, w, h, cw, ch;
	unsigned char   *heightmap, *colormap, *heightPixel, *colorPixel;

	picoModel_t     *picoModel;
	picoSurface_t   *picoSurface;
	picoShader_t    *picoShader;
	picoVec3_t xyz, normal;
	picoVec2_t st;
	picoColor_t color;


	/* create pico parser */
	p = _pico_new_parser( (const picoByte_t*) buffer, bufSize );
	if ( p == NULL ) {
		return NULL;
	}

	/* get first token */
	if ( _pico_parse_first( p ) == NULL ) {
		return NULL;
	}

	/* check first token */
	if ( _pico_stricmp( p->token, "picoterrain" ) ) {
		_pico_printf( PICO_ERROR, "Invalid PicoTerrain model" );
		_pico_free_parser( p );
		return NULL;
	}

	/* setup */
	shader = heightmapFile = colormapFile = NULL;
	_pico_set_vec( scale, 512, 512, 32 );

	/* parse ase model file */
	while ( 1 )
	{
		/* get first token on line */
		if ( !_pico_parse_first( p ) ) {
			break;
		}

		/* skip empty lines */
		if ( !p->token || !p->token[ 0 ] ) {
			continue;
		}

		/* shader */
		if ( !_pico_stricmp( p->token, "shader" ) ) {
			if ( _pico_parse( p, 0 ) && p->token[ 0 ] ) {
				if ( shader != NULL ) {
					_pico_free( shader );
				}
				shader = _pico_clone_alloc( p->token );
			}
		}

		/* heightmap */
		else if ( !_pico_stricmp( p->token, "heightmap" ) ) {
			if ( _pico_parse( p, 0 ) && p->token[ 0 ] ) {
				if ( heightmapFile != NULL ) {
					_pico_free( heightmapFile );
				}
				heightmapFile = _pico_clone_alloc( p->token );
			}
		}

		/* colormap */
		else if ( !_pico_stricmp( p->token, "colormap" ) ) {
			if ( _pico_parse( p, 0 ) && p->token[ 0 ] ) {
				if ( colormapFile != NULL ) {
					_pico_free( colormapFile );
				}
				colormapFile = _pico_clone_alloc( p->token );
			}
		}

		/* scale */
		else if ( !_pico_stricmp( p->token, "scale" ) ) {
			_pico_parse_vec( p, scale );
		}

		/* skip unparsed rest of line and continue */
		_pico_parse_skip_rest( p );
	}

	/* ----------------------------------------------------------------- */

	/* load heightmap */
	heightmap = imageBuffer = NULL;
	_pico_load_file( heightmapFile, &imageBuffer, &imageBufSize );
	_terrain_load_tga_buffer( imageBuffer, &heightmap, &w, &h );
	_pico_free( heightmapFile );
	_pico_free_file( imageBuffer );

	if ( heightmap == NULL || w < 2 || h < 2 ) {
		_pico_printf( PICO_ERROR, "PicoTerrain model with invalid heightmap" );
		if ( shader != NULL ) {
			_pico_free( shader );
		}
		if ( colormapFile != NULL ) {
			_pico_free( colormapFile );
		}
		_pico_free_parser( p );
		return NULL;
	}

	/* set origin (bottom lowest corner of terrain mesh) */
	_pico_set_vec( origin, ( w / -2 ) * scale[ 0 ], ( h / -2 ) * scale[ 1 ], -128 * scale[ 2 ] );

	/* load colormap */
	colormap = imageBuffer = NULL;
	_pico_load_file( colormapFile, &imageBuffer, &imageBufSize );
	_terrain_load_tga_buffer( imageBuffer, &colormap, &cw, &ch );
	_pico_free( colormapFile );
	_pico_free_file( imageBuffer );

	if ( cw != w || ch != h ) {
		_pico_printf( PICO_WARNING, "PicoTerrain colormap/heightmap size mismatch" );
		_pico_free( colormap );
		colormap = NULL;
	}

	/* ----------------------------------------------------------------- */

	/* create new pico model */
	picoModel = PicoNewModel();
	if ( picoModel == NULL ) {
		_pico_printf( PICO_ERROR, "Unable to allocate a new model" );
		return NULL;
	}

	/* do model setup */
	PicoSetModelFrameNum( picoModel, frameNum );
	PicoSetModelNumFrames( picoModel, 1 ); /* sea */
	PicoSetModelName( picoModel, fileName );
	PicoSetModelFileName( picoModel, fileName );

	/* allocate new pico surface */
	picoSurface = PicoNewSurface( picoModel );
	if ( picoSurface == NULL ) {
		_pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
		PicoFreeModel( picoModel ); /* sea */
		return NULL;
	}

	/* terrain surfaces are triangle meshes */
	PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );

	/* set surface name */
	PicoSetSurfaceName( picoSurface, "picoterrain" );

	/* create new pico shader */
	picoShader = PicoNewShader( picoModel );
	if ( picoShader == NULL ) {
		_pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
		PicoFreeModel( picoModel );
		_pico_free( shader );
		return NULL;
	}

	/* detox and set shader name */
	_pico_setfext( shader, "" );
	_pico_unixify( shader );
	PicoSetShaderName( picoShader, shader );
	_pico_free( shader );

	/* associate current surface with newly created shader */
	PicoSetSurfaceShader( picoSurface, picoShader );

	/* make bogus normal */
	_pico_set_vec( normal, 0.0f, 0.0f, 0.0f );

	/* create mesh */
	for ( j = 0; j < h; j++ )
	{
		for ( i = 0; i < w; i++ )
		{
			/* get pointers */
			v = i + ( j * w );
			heightPixel = heightmap + v * 4;
			colorPixel = colormap
						 ? colormap + v * 4
						 : NULL;

			/* set xyz */
			_pico_set_vec( xyz, origin[ 0 ] + scale[ 0 ] * i,
						   origin[ 1 ] + scale[ 1 ] * j,
						   origin[ 2 ] + scale[ 2 ] * heightPixel[ 0 ] );
			PicoSetSurfaceXYZ( picoSurface, v, xyz );

			/* set normal */
			PicoSetSurfaceNormal( picoSurface, v, normal );

			/* set st */
			st[ 0 ] = (float) i;
			st[ 1 ] = (float) j;
			PicoSetSurfaceST( picoSurface, 0, v, st );

			/* set color */
			if ( colorPixel != NULL ) {
				_pico_set_color( color, colorPixel[ 0 ], colorPixel[ 1 ], colorPixel[ 2 ], colorPixel[ 3 ] );
			}
			else{
				_pico_set_color( color, 255, 255, 255, 255 );
			}
			PicoSetSurfaceColor( picoSurface, 0, v, color );

			/* set triangles (zero alpha in heightmap suppresses this quad) */
			if ( i < ( w - 1 ) && j < ( h - 1 ) && heightPixel[ 3 ] >= 128 ) {
				/* set indexes */
				pw[ 0 ] = i + ( j * w );
				pw[ 1 ] = i + ( ( j + 1 ) * w );
				pw[ 2 ] = i + 1 + ( ( j + 1 ) * w );
				pw[ 3 ] = i + 1 + ( j * w );
				pw[ 4 ] = i + ( j * w );  /* same as pw[ 0 ] */

				/* set radix */
				r = ( i + j ) & 1;

				/* make first triangle */
				PicoSetSurfaceIndex( picoSurface, ( v * 6 + 0 ), (picoIndex_t) pw[ r + 0 ] );
				PicoSetSurfaceIndex( picoSurface, ( v * 6 + 1 ), (picoIndex_t) pw[ r + 1 ] );
				PicoSetSurfaceIndex( picoSurface, ( v * 6 + 2 ), (picoIndex_t) pw[ r + 2 ] );

				/* make second triangle */
				PicoSetSurfaceIndex( picoSurface, ( v * 6 + 3 ), (picoIndex_t) pw[ r + 0 ] );
				PicoSetSurfaceIndex( picoSurface, ( v * 6 + 4 ), (picoIndex_t) pw[ r + 2 ] );
				PicoSetSurfaceIndex( picoSurface, ( v * 6 + 5 ), (picoIndex_t) pw[ r + 3 ] );
			}
		}
	}

	/* free stuff */
	_pico_free_parser( p );
	_pico_free( heightmap );
	_pico_free( colormap );

	/* return the new pico model */
	return picoModel;
}
Пример #3
0
static int _obj_mtl_load (picoModel_t *model)
{
	picoParser_t *p;
	picoByte_t *mtlBuffer;
	int mtlBufSize;
	char *fileName;

	/* sanity checks */
	if (model == NULL || model->fileName == NULL)
	return 0;

	/* skip if we have a zero length model file name */
	if (!strlen(model->fileName))
	return 0;

	/* helper */
#define _obj_mtl_error_return \
	{ \
		_pico_free_parser( p ); \
		_pico_free_file( mtlBuffer ); \
		_pico_free( fileName ); \
		return 0; \
	}
	/* alloc copy of model file name */
	fileName = _pico_clone_alloc(model->fileName);
	if (fileName == NULL)
	return 0;

	/* change extension of model file to .mtl */
	_pico_setfext(fileName, "mtl");

	/* load .mtl file contents */
	_pico_load_file(fileName, &mtlBuffer, &mtlBufSize);

	/* check result */
	if (mtlBufSize == 0)
	return 1; /* file is empty: no error */
	if (mtlBufSize < 0)
	return 0; /* load failed: error */

	/* create a new pico parser */
	p = _pico_new_parser(mtlBuffer, mtlBufSize);
	if (p == NULL)
	_obj_mtl_error_return;

	/* doo teh .mtl parse */
	while (1) {
		/* get next token in material file */
		if (_pico_parse(p, 1) == NULL)
		break;
#if 0

		/* skip empty lines */
		if (p->token == NULL || !strlen(p->token))
		continue;

		/* skip comment lines */
		if (p->token[0] == '#') {
			_pico_parse_skip_rest(p);
			continue;
		}
		/* new material */
		if (!_pico_stricmp(p->token, "newmtl")) {
			picoShader_t *shader;
			char *name;

			/* get material name */
			name = _pico_parse(p, 0);

			/* validate material name */
			if (name == NULL || !strlen(name)) {
				_pico_printf(PICO_ERROR, "Missing material name in MTL, line %d.", p->curLine);
				_obj_mtl_error_return;
			}
			/* create a new pico shader */
			shader = PicoNewShader(model);
			if (shader == NULL)
			_obj_mtl_error_return;

			/* set shader name */
			PicoSetShaderName(shader, name);

			/* assign pointer to current shader */
			curShader = shader;
		}
		/* diffuse map name */
		else if (!_pico_stricmp(p->token, "map_kd")) {
			char *mapName;

			/* pointer to current shader must be valid */
			if (curShader == NULL)
			_obj_mtl_error_return;

			/* get material's diffuse map name */
			mapName = _pico_parse(p, 0);

			/* validate map name */
			if (mapName == NULL || !strlen(mapName)) {
				_pico_printf(PICO_ERROR, "Missing material map name in MTL, line %d.", p->curLine);
				_obj_mtl_error_return;
			}
			/* set shader map name */
			PicoSetShaderMapName(shader, mapName);
		}
		/* dissolve factor (pseudo transparency 0..1) */
		/* where 0 means 100% transparent and 1 means opaque */
		else if (!_pico_stricmp(p->token, "d")) {
			picoByte_t *diffuse;
			float value;

			/* get dissolve factor */
			if (!_pico_parse_float(p, &value))
			_obj_mtl_error_return;

			/* set shader transparency */
			PicoSetShaderTransparency(curShader, value);

			/* get shader's diffuse color */
			diffuse = PicoGetShaderDiffuseColor(curShader);

			/* set diffuse alpha to transparency */
			diffuse[3] = (picoByte_t) (value * 255.0);

			/* set shader's new diffuse color */
			PicoSetShaderDiffuseColor(curShader, diffuse);
		}
		/* shininess (phong specular component) */
		else if (!_pico_stricmp(p->token, "ns")) {
			/* remark:
			 * - well, this is some major obj spec fuckup once again. some
			 *   apps store this in 0..1 range, others use 0..100 range,
			 *   even others use 0..2048 range, and again others use the
			 *   range 0..128, some even use 0..1000, 0..200, 400..700,
			 *   honestly, what's up with the 3d app coders? happens when
			 *   you smoke too much weed i guess. -sea
			 */
			float value;

			/* pointer to current shader must be valid */
			if (curShader == NULL)
			_obj_mtl_error_return;

			/* get totally screwed up shininess (a random value in fact ;) */
			if (!_pico_parse_float(p, &value))
			_obj_mtl_error_return;

			/* okay, there is no way to set this correctly, so we simply */
			/* try to guess a few ranges (most common ones i have seen) */

			/* assume 0..2048 range */
			if (value > 1000)
			value = 128.0 * (value / 2048.0);
			/* assume 0..1000 range */
			else if (value > 200)
			value = 128.0 * (value / 1000.0);
			/* assume 0..200 range */
			else if (value > 100)
			value = 128.0 * (value / 200.0);
			/* assume 0..100 range */
			else if (value > 1)
			value = 128.0 * (value / 100.0);
			/* assume 0..1 range */
			else {
				value *= 128.0;
			}
			/* negative shininess is bad (yes, i have seen it...) */
			if (value < 0.0)
			value = 0.0;

			/* set the pico shininess value in range 0..127 */
			/* geez, .obj is such a mess... */
			PicoSetShaderShininess(curShader, value);
		}
		/* kol0r ambient (wut teh fuk does "ka" stand for?) */
		else if (!_pico_stricmp(p->token, "ka")) {
			picoColor_t color;
			picoVec3_t v;

			/* pointer to current shader must be valid */
			if (curShader == NULL)
			_obj_mtl_error_return;

			/* get color vector */
			if (!_pico_parse_vec(p, v))
			_obj_mtl_error_return;

			/* scale to byte range */
			color[0] = (picoByte_t) (v[0] * 255);
			color[1] = (picoByte_t) (v[1] * 255);
			color[2] = (picoByte_t) (v[2] * 255);
			color[3] = (picoByte_t) (255);

			/* set ambient color */
			PicoSetShaderAmbientColor(curShader, color);
		}
		/* kol0r diffuse */
		else if (!_pico_stricmp(p->token, "kd")) {
			picoColor_t color;
			picoVec3_t v;

			/* pointer to current shader must be valid */
			if (curShader == NULL)
			_obj_mtl_error_return;

			/* get color vector */
			if (!_pico_parse_vec(p, v))
			_obj_mtl_error_return;

			/* scale to byte range */
			color[0] = (picoByte_t) (v[0] * 255);
			color[1] = (picoByte_t) (v[1] * 255);
			color[2] = (picoByte_t) (v[2] * 255);
			color[3] = (picoByte_t) (255);

			/* set diffuse color */
			PicoSetShaderDiffuseColor(curShader, color);
		}
		/* kol0r specular */
		else if (!_pico_stricmp(p->token, "ks")) {
			picoColor_t color;
			picoVec3_t v;

			/* pointer to current shader must be valid */
			if (curShader == NULL)
			_obj_mtl_error_return;

			/* get color vector */
			if (!_pico_parse_vec(p, v))
			_obj_mtl_error_return;

			/* scale to byte range */
			color[0] = (picoByte_t) (v[0] * 255);
			color[1] = (picoByte_t) (v[1] * 255);
			color[2] = (picoByte_t) (v[2] * 255);
			color[3] = (picoByte_t) (255);

			/* set specular color */
			PicoSetShaderSpecularColor(curShader, color);
		}
#endif
		/* skip rest of line */
		_pico_parse_skip_rest(p);
	}

	/* free parser, file buffer, and file name */
	_pico_free_parser(p);
	_pico_free_file(mtlBuffer);
	_pico_free(fileName);

	/* return with success */
	return 1;
}
Пример #4
0
/**
 * @brief loads a wavefront obj model file.
 */
static picoModel_t *_obj_load (PM_PARAMS_LOAD)
{
	TObjVertexData *vertexData = NULL;
	picoModel_t *model;
	picoSurface_t *curSurface = NULL;
	picoParser_t *p;
	int allocated;
	int entries;
	int numVerts = 0;
	int numNormals = 0;
	int numUVs = 0;
	int curVertex = 0;
	int curFace = 0;
	picoShader_t *shader;

	/* helper */
#define _obj_error_return(m) \
	{ \
		_pico_printf( PICO_ERROR,"%s in OBJ, line %d.",m,p->curLine); \
		_pico_free_parser( p ); \
		FreeObjVertexData( vertexData ); \
		PicoFreeModel( model ); \
		return NULL; \
	}
	/* alllocate a new pico parser */
	p = _pico_new_parser((picoByte_t *) buffer, bufSize);
	if (p == NULL)
		return NULL;

	/* create a new pico model */
	model = PicoNewModel();
	if (model == NULL) {
		_pico_free_parser(p);
		return NULL;
	}
	/* do model setup */
	PicoSetModelFrameNum(model, frameNum);
	PicoSetModelName(model, fileName);
	PicoSetModelFileName(model, fileName);

	/* try loading the materials; we don't handle the result */
	shader = _obj_default_shader(model);
#if 0
	shader = _obj_mtl_load(model);
#endif

	/* parse obj line by line */
	while (1) {
		/* get first token on line */
		if (_pico_parse_first(p) == NULL)
			break;

		/* skip empty lines */
		if (p->token == NULL || !strlen(p->token))
			continue;

		/* skip comment lines */
		if (p->token[0] == '#') {
			_pico_parse_skip_rest(p);
			continue;
		}
		/* vertex */
		if (!_pico_stricmp(p->token, "v")) {
			TObjVertexData *data;
			picoVec3_t v;

			vertexData = SizeObjVertexData(vertexData, numVerts + 1, &entries, &allocated);
			if (vertexData == NULL)
				_obj_error_return("Realloc of vertex data failed (1)");

			data = &vertexData[numVerts++];

			/* get and copy vertex */
			if (!_pico_parse_vec(p, v))
				_obj_error_return("Vertex parse error");

			_pico_copy_vec(v, data->v);

#ifdef DEBUG_PM_OBJ_EX
			printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]);
#endif
		}
		/* uv coord */
		else if (!_pico_stricmp(p->token, "vt")) {
			TObjVertexData *data;
			picoVec2_t coord;

			vertexData = SizeObjVertexData(vertexData, numUVs + 1, &entries, &allocated);
			if (vertexData == NULL)
				_obj_error_return("Realloc of vertex data failed (2)");

			data = &vertexData[numUVs++];

			/* get and copy tex coord */
			if (!_pico_parse_vec2(p, coord))
				_obj_error_return("UV coord parse error");

			_pico_copy_vec2(coord, data->vt);

#ifdef DEBUG_PM_OBJ_EX
			printf("TexCoord: u: %f v: %f\n",coord[0],coord[1]);
#endif
		}
		/* vertex normal */
		else if (!_pico_stricmp(p->token, "vn")) {
			TObjVertexData *data;
			picoVec3_t n;

			vertexData = SizeObjVertexData(vertexData, numNormals + 1, &entries, &allocated);
			if (vertexData == NULL)
				_obj_error_return("Realloc of vertex data failed (3)");

			data = &vertexData[numNormals++];

			/* get and copy vertex normal */
			if (!_pico_parse_vec(p, n))
				_obj_error_return("Vertex normal parse error");

			_pico_copy_vec(n, data->vn);

#ifdef DEBUG_PM_OBJ_EX
			printf("Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2]);
#endif
		}
		/* new group (for us this means a new surface) */
		else if (!_pico_stricmp(p->token, "g")) {
			picoSurface_t *newSurface;
			char *groupName;

			/* get first group name (ignore 2nd,3rd,etc.) */
			groupName = _pico_parse(p, 0);
			if (groupName == NULL || !strlen(groupName)) {
				/* some obj exporters feel like they don't need to */
				/* supply a group name. so we gotta handle it here */
#if 1
				strcpy(p->token, "default");
				groupName = p->token;
#else
				_obj_error_return("Invalid or missing group name");
#endif
			}
			/* allocate a pico surface */
			newSurface = PicoNewSurface(model);
			if (newSurface == NULL)
				_obj_error_return("Error allocating surface");

			/* reset face index for surface */
			curFace = 0;

			/* set ptr to current surface */
			curSurface = newSurface;

			/* we use triangle meshes */
			PicoSetSurfaceType(newSurface, PICO_TRIANGLES);

			/* set surface name */
			PicoSetSurfaceName(newSurface, groupName);

			/* associate current surface with newly created shader */
			PicoSetSurfaceShader(newSurface, shader);

#ifdef DEBUG_PM_OBJ_EX
			printf("Group: '%s'\n",groupName);
#endif
		}
		/* face (oh jesus, hopefully this will do the job right ;) */
		else if (!_pico_stricmp(p->token, "f")) {
			/* okay, this is a mess. some 3d apps seem to try being unique, */
			/* hello cinema4d & 3d exploration, feel good today?, and save */
			/* this crap in tons of different formats. gah, those screwed */
			/* coders. tho the wavefront obj standard defines exactly two */
			/* ways of storing face information. so, i really won't support */
			/* such stupid extravaganza here! */

			picoVec3_t verts[4];
			picoVec3_t normals[4];
			picoVec2_t coords[4];

			int iv[4], has_v;
			int ivt[4], has_vt = 0;
			int ivn[4], has_vn = 0;
			int have_quad = 0;
			int slashcount;
			int doubleslash;
			int i;

			/* group defs *must* come before faces */
			if (curSurface == NULL)
				_obj_error_return("No group defined for faces");

#ifdef DEBUG_PM_OBJ_EX
			printf("Face: ");
#endif
			/* read vertex/uv/normal indices for the first three face */
			/* vertices (cause we only support triangles) into 'i*[]' */
			/* store the actual vertex/uv/normal data in three arrays */
			/* called 'verts','coords' and 'normals'. */
			for (i = 0; i < 4; i++) {
				char *str;

				/* get next vertex index string (different */
				/* formats are handled below) */
				str = _pico_parse(p, 0);
				if (str == NULL) {
					/* just break for quads */
					if (i == 3)
						break;

					/* error otherwise */
					_obj_error_return("Face parse error");
				}
				/* if this is the fourth index string we're */
				/* parsing we assume that we have a quad */
				if (i == 3)
					have_quad = 1;

				/* get slash count once */
				if (i == 0) {
					slashcount = _pico_strchcount(str, '/');
					doubleslash = strstr(str, "//") != NULL;
				}
				/* handle format 'v//vn' */
				if (doubleslash && (slashcount == 2)) {
					has_v = has_vn = 1;
					sscanf(str, "%d//%d", &iv[i], &ivn[i]);
				}
				/* handle format 'v/vt/vn' */
				else if (!doubleslash && (slashcount == 2)) {
					has_v = has_vt = has_vn = 1;
					sscanf(str, "%d/%d/%d", &iv[i], &ivt[i], &ivn[i]);
				}
				/* handle format 'v/vt' (non-standard fuckage) */
				else if (!doubleslash && (slashcount == 1)) {
					has_v = has_vt = 1;
					sscanf(str, "%d/%d", &iv[i], &ivt[i]);
				}
				/* else assume face format 'v' */
				/* (must have been invented by some bored granny) */
				else {
					/* get single vertex index */
					has_v = 1;
					iv[i] = atoi(str);

					/* either invalid face format or out of range */
					if (iv[i] == 0)
						_obj_error_return("Invalid face format");
				}
				/* fix useless back references */
				/* todo: check if this works as it is supposed to */

				/* assign new indices */
				if (iv[i] < 0)
					iv[i] = (numVerts - iv[i]);
				if (ivt[i] < 0)
					ivt[i] = (numUVs - ivt[i]);
				if (ivn[i] < 0)
					ivn[i] = (numNormals - ivn[i]);

				/* validate indices */
				/* - commented out. index range checks will trigger
				 if (iv [ i ] < 1) iv [ i ] = 1;
				 if (ivt[ i ] < 1) ivt[ i ] = 1;
				 if (ivn[ i ] < 1) ivn[ i ] = 1;
				 */
				/* set vertex origin */
				if (has_v) {
					/* check vertex index range */
					if (iv[i] < 1 || iv[i] > numVerts)
						_obj_error_return("Vertex index out of range");

					/* get vertex data */
					verts[i][0] = vertexData[iv[i] - 1].v[0];
					verts[i][1] = vertexData[iv[i] - 1].v[1];
					verts[i][2] = vertexData[iv[i] - 1].v[2];
				}
				/* set vertex normal */
				if (has_vn) {
					/* check normal index range */
					if (ivn[i] < 1 || ivn[i] > numNormals)
						_obj_error_return("Normal index out of range");

					/* get normal data */
					normals[i][0] = vertexData[ivn[i] - 1].vn[0];
					normals[i][1] = vertexData[ivn[i] - 1].vn[1];
					normals[i][2] = vertexData[ivn[i] - 1].vn[2];
				}
				/* set texture coordinate */
				if (has_vt) {
					/* check uv index range */
					if (ivt[i] < 1 || ivt[i] > numUVs)
						_obj_error_return("UV coord index out of range");

					/* get uv coord data */
					coords[i][0] = vertexData[ivt[i] - 1].vt[0];
					coords[i][1] = vertexData[ivt[i] - 1].vt[1];
					coords[i][1] = -coords[i][1];
				}
#ifdef DEBUG_PM_OBJ_EX
				printf("(%4d",iv[ i ]);
				if (has_vt) printf(" %4d",ivt[ i ]);
				if (has_vn) printf(" %4d",ivn[ i ]);
				printf(") ");
#endif
			}
#ifdef DEBUG_PM_OBJ_EX
			printf("\n");
#endif
			/* now that we have extracted all the indices and have
			 * read the actual data we need to assign all the crap
			 * to our current pico surface */
			if (has_v) {
				int max = 3;
				if (have_quad)
					max = 4;

				/* assign all surface information */
				for (i = 0; i < max; i++) {
					PicoSetSurfaceXYZ(curSurface, (curVertex + i), verts[i]);
					PicoSetSurfaceST(curSurface, 0, (curVertex + i), coords[i]);
					PicoSetSurfaceNormal(curSurface, (curVertex + i), normals[i]);
				}
				/* add our triangle (A B C) */
				PicoSetSurfaceIndex(curSurface, (curFace * 3 + 2), (picoIndex_t) (curVertex + 0));
				PicoSetSurfaceIndex(curSurface, (curFace * 3 + 1), (picoIndex_t) (curVertex + 1));
				PicoSetSurfaceIndex(curSurface, (curFace * 3 + 0), (picoIndex_t) (curVertex + 2));
				curFace++;

				/* if we don't have a simple triangle, but a quad... */
				if (have_quad) {
					/* we have to add another triangle (2nd half of quad which is A C D) */
					PicoSetSurfaceIndex(curSurface, (curFace * 3 + 2), (picoIndex_t) (curVertex + 0));
					PicoSetSurfaceIndex(curSurface, (curFace * 3 + 1), (picoIndex_t) (curVertex + 2));
					PicoSetSurfaceIndex(curSurface, (curFace * 3 + 0), (picoIndex_t) (curVertex + 3));
					curFace++;
				}

				/* associate current surface with newly created shader */
				PicoSetSurfaceShader(curSurface, shader);

				/* increase vertex count */
				curVertex += max;
			}
		}
		/* skip unparsed rest of line and continue */
		_pico_parse_skip_rest(p);
	}
	/* free memory used by temporary vertexdata */
	FreeObjVertexData(vertexData);

	/* return allocated pico model */
	return model;
}
Пример #5
0
/* _obj_load:
 *  loads a wavefront obj model file.
 */
static picoModel_t *_obj_load( PM_PARAMS_LOAD ){
	TObjVertexData *vertexData  = NULL;
	picoModel_t    *model;
	picoSurface_t  *curSurface  = NULL;
	picoParser_t   *p;
	int allocated = 0;
	int entries;
	int numVerts    = 0;
	int numNormals  = 0;
	int numUVs      = 0;
	int curVertex   = 0;
	int curFace     = 0;

	int autoGroupNumber = 0;
	char autoGroupNameBuf[64];

#define AUTO_GROUPNAME( namebuf ) \
	sprintf( namebuf, "__autogroup_%d", autoGroupNumber++ )
#define NEW_SURFACE( name )	\
	{ \
		picoSurface_t *newSurface; \
		/* allocate a pico surface */ \
		newSurface = PicoNewSurface( model ); \
		if ( newSurface == NULL ) {	\
			_obj_error_return( "Error allocating surface" ); } \
		/* reset face index for surface */ \
		curFace = 0; \
		curVertex = 0; \
		/* if we can, assign the previous shader to this surface */	\
		if ( curSurface ) {	\
			PicoSetSurfaceShader( newSurface, curSurface->shader ); } \
		/* set ptr to current surface */ \
		curSurface = newSurface; \
		/* we use triangle meshes */ \
		PicoSetSurfaceType( newSurface,PICO_TRIANGLES ); \
		/* set surface name */ \
		PicoSetSurfaceName( newSurface,name ); \
	}

	/* helper */
#define _obj_error_return( m ) \
	{ \
		_pico_printf( PICO_ERROR,"%s in OBJ, line %d.",m,p->curLine ); \
		_pico_free_parser( p );	\
		FreeObjVertexData( vertexData ); \
		PicoFreeModel( model );	\
		return NULL; \
	}
	/* allocate a new pico parser */
	p = _pico_new_parser( (const picoByte_t *)buffer,bufSize );
	if ( p == NULL ) {
		return NULL;
	}

	/* create a new pico model */
	model = PicoNewModel();
	if ( model == NULL ) {
		_pico_free_parser( p );
		return NULL;
	}
	/* do model setup */
	PicoSetModelFrameNum( model,frameNum );
	PicoSetModelName( model,fileName );
	PicoSetModelFileName( model,fileName );

	/* try loading the materials */
	_obj_mtl_load( model );

	/* parse obj line by line */
	while ( 1 )
	{
		/* get first token on line */
		if ( _pico_parse_first( p ) == NULL ) {
			break;
		}

		/* skip empty lines */
		if ( p->token == NULL || !strlen( p->token ) ) {
			continue;
		}

		/* skip comment lines */
		if ( p->token[0] == '#' ) {
			_pico_parse_skip_rest( p );
			continue;
		}
		/* vertex */
		if ( !_pico_stricmp( p->token,"v" ) ) {
			TObjVertexData *data;
			picoVec3_t v;

			vertexData = SizeObjVertexData( vertexData,numVerts + 1,&entries,&allocated );
			if ( vertexData == NULL ) {
				_obj_error_return( "Realloc of vertex data failed (1)" );
			}

			data = &vertexData[ numVerts++ ];

			/* get and copy vertex */
			if ( !_pico_parse_vec( p,v ) ) {
				_obj_error_return( "Vertex parse error" );
			}

			_pico_copy_vec( v,data->v );

#ifdef DEBUG_PM_OBJ_EX
			printf( "Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2] );
#endif
		}
		/* uv coord */
		else if ( !_pico_stricmp( p->token,"vt" ) ) {
			TObjVertexData *data;
			picoVec2_t coord;

			vertexData = SizeObjVertexData( vertexData,numUVs + 1,&entries,&allocated );
			if ( vertexData == NULL ) {
				_obj_error_return( "Realloc of vertex data failed (2)" );
			}

			data = &vertexData[ numUVs++ ];

			/* get and copy tex coord */
			if ( !_pico_parse_vec2( p,coord ) ) {
				_obj_error_return( "UV coord parse error" );
			}

			_pico_copy_vec2( coord,data->vt );

#ifdef DEBUG_PM_OBJ_EX
			printf( "TexCoord: u: %f v: %f\n",coord[0],coord[1] );
#endif
		}
		/* vertex normal */
		else if ( !_pico_stricmp( p->token,"vn" ) ) {
			TObjVertexData *data;
			picoVec3_t n;

			vertexData = SizeObjVertexData( vertexData,numNormals + 1,&entries,&allocated );
			if ( vertexData == NULL ) {
				_obj_error_return( "Realloc of vertex data failed (3)" );
			}

			data = &vertexData[ numNormals++ ];

			/* get and copy vertex normal */
			if ( !_pico_parse_vec( p,n ) ) {
				_obj_error_return( "Vertex normal parse error" );
			}

			_pico_copy_vec( n,data->vn );

#ifdef DEBUG_PM_OBJ_EX
			printf( "Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2] );
#endif
		}
		/* new group (for us this means a new surface) */
		else if ( !_pico_stricmp( p->token,"g" ) ) {
			char *groupName;

			/* get first group name (ignore 2nd,3rd,etc.) */
			groupName = _pico_parse( p,0 );
			if ( groupName == NULL || !strlen( groupName ) ) {
				/* some obj exporters feel like they don't need to */
				/* supply a group name. so we gotta handle it here */
#if 1
				strcpy( p->token,"default" );
				groupName = p->token;
#else
				_obj_error_return( "Invalid or missing group name" );
#endif
			}

			if ( curFace == 0 && curSurface != NULL ) {
				PicoSetSurfaceName( curSurface,groupName );
			}
			else
			{
				NEW_SURFACE( groupName );
			}

#ifdef DEBUG_PM_OBJ_EX
			printf( "Group: '%s'\n",groupName );
#endif
		}
		/* face (oh jesus, hopefully this will do the job right ;) */
		else if ( !_pico_stricmp( p->token,"f" ) ) {
			/* okay, this is a mess. some 3d apps seem to try being unique, */
			/* hello cinema4d & 3d exploration, feel good today?, and save */
			/* this crap in tons of different formats. gah, those screwed */
			/* coders. tho the wavefront obj standard defines exactly two */
			/* ways of storing face information. so, i really won't support */
			/* such stupid extravaganza here! */
			const int numPointsMax = 128;
			picoVec3_t verts  [ numPointsMax ];
			picoVec3_t normals[ numPointsMax ];
			picoVec2_t coords [ numPointsMax ];

			int iv [ numPointsMax ], has_v;
			int ivt[ numPointsMax ], has_vt = 0;
			int ivn[ numPointsMax ], has_vn = 0;
			int slashcount = 0;
			int doubleslash = 0;
			int i;

			if ( curSurface == NULL ) {
				_pico_printf( PICO_WARNING,"No group defined for faces, so creating an autoSurface in OBJ, line %d.",p->curLine );
				AUTO_GROUPNAME( autoGroupNameBuf );
				NEW_SURFACE( autoGroupNameBuf );
			}

			/* group defs *must* come before faces */
			if ( curSurface == NULL ) {
				_obj_error_return( "No group defined for faces" );
			}

#ifdef DEBUG_PM_OBJ_EX
			printf( "Face: " );
#endif
			/* read vertex/uv/normal indices for the first three face */
			/* vertices (cause we only support triangles) into 'i*[]' */
			/* store the actual vertex/uv/normal data in three arrays */
			/* called 'verts','coords' and 'normals'. */
			for ( i = 0; i < numPointsMax; i++ )
			{
				char *str;

				/* get next vertex index string (different */
				/* formats are handled below) */
				str = _pico_parse( p,0 );
				if ( str == NULL ) {
					/* got nuff points */
					if ( i >= 3 ) {
						break;
					}

					/* error otherwise */
					_obj_error_return( "Face parse error" );
				}

				/* get slash count once */
				if ( i == 0 ) {
					slashcount  = _pico_strchcount( str,'/' );
					doubleslash =  strstr( str,"//" ) != NULL;
				}
				/* handle format 'v//vn' */
				if ( doubleslash && ( slashcount == 2 ) ) {
					has_v = has_vn = 1;
					sscanf( str,"%d//%d",&iv[ i ],&ivn[ i ] );
				}
				/* handle format 'v/vt/vn' */
				else if ( !doubleslash && ( slashcount == 2 ) ) {
					has_v = has_vt = has_vn = 1;
					sscanf( str,"%d/%d/%d",&iv[ i ],&ivt[ i ],&ivn[ i ] );
				}
				/* handle format 'v/vt' (non-standard fuckage) */
				else if ( !doubleslash && ( slashcount == 1 ) ) {
					has_v = has_vt = 1;
					sscanf( str,"%d/%d",&iv[ i ],&ivt[ i ] );
				}
				/* else assume face format 'v' */
				/* (must have been invented by some bored granny) */
				else {
					/* get single vertex index */
					has_v = 1;
					iv[ i ] = atoi( str );

					/* either invalid face format or out of range */
					if ( iv[ i ] == 0 ) {
						_obj_error_return( "Invalid face format" );
					}
				}
				/* fix useless back references */
				/* assign new indices */
				if ( iv [ i ] < 0 ) {
					iv [ i ] = ( numVerts   + iv [ i ] + 1 );
				}
				if ( ivt[ i ] < 0 ) {
					ivt[ i ] = ( numUVs     + ivt[ i ] + 1 );
				}
				if ( ivn[ i ] < 0 ) {
					ivn[ i ] = ( numNormals + ivn[ i ] + 1 );
				}

				/* validate indices */
				/* - commented out. index range checks will trigger
				   if (iv [ i ] < 1) iv [ i ] = 1;
				   if (ivt[ i ] < 1) ivt[ i ] = 1;
				   if (ivn[ i ] < 1) ivn[ i ] = 1;
				 */
				/* set vertex origin */
				if ( has_v ) {
					/* check vertex index range */
					if ( iv[ i ] < 1 || iv[ i ] > numVerts ) {
						_obj_error_return( "Vertex index out of range" );
					}

					/* get vertex data */
					verts[ i ][ 0 ] =  vertexData[ iv[ i ] - 1 ].v[ 0 ];
					verts[ i ][ 1 ] = -vertexData[ iv[ i ] - 1 ].v[ 2 ];
					verts[ i ][ 2 ] =  vertexData[ iv[ i ] - 1 ].v[ 1 ];
				}
				/* set vertex normal */
				if ( has_vn ) {
					/* check normal index range */
					if ( ivn[ i ] < 1 || ivn[ i ] > numNormals ) {
						_obj_error_return( "Normal index out of range" );
					}

					/* get normal data */
					normals[ i ][ 0 ] =  vertexData[ ivn[ i ] - 1 ].vn[ 0 ];
					normals[ i ][ 1 ] = -vertexData[ ivn[ i ] - 1 ].vn[ 2 ];
					normals[ i ][ 2 ] =  vertexData[ ivn[ i ] - 1 ].vn[ 1 ];
				}
				/* set texture coordinate */
				if ( has_vt ) {
					/* check uv index range */
					if ( ivt[ i ] < 1 || ivt[ i ] > numUVs ) {
						_obj_error_return( "UV coord index out of range" );
					}

					/* get uv coord data */
					coords[ i ][ 0 ] =  vertexData[ ivt[ i ] - 1 ].vt[ 0 ];
					coords[ i ][ 1 ] = -vertexData[ ivt[ i ] - 1 ].vt[ 1 ];
				}
#ifdef DEBUG_PM_OBJ_EX
				printf( "(%4d",iv[ i ] );
				if ( has_vt ) {
					printf( " %4d",ivt[ i ] );
				}
				if ( has_vn ) {
					printf( " %4d",ivn[ i ] );
				}
				printf( ") " );
#endif
			}
#ifdef DEBUG_PM_OBJ_EX
			printf( "\n" );
#endif
			/* now that we have extracted all the indices and have */
			/* read the actual data we need to assign all the crap */
			/* to our current pico surface */
			if ( has_v ) {
				const int numPoints = i;

				/* assign all surface information */
				for ( i = 0; i < numPoints; i++ )
				{
					/*if( has_v  )*/ PicoSetSurfaceXYZ( curSurface,  ( curVertex + i ), verts  [ i ] );
					/*if( has_vt )*/ PicoSetSurfaceST( curSurface, 0, ( curVertex + i ), coords [ i ] );
					/*if( has_vn )*/ PicoSetSurfaceNormal( curSurface,  ( curVertex + i ), normals[ i ] );
					if( curSurface && curSurface->shader )
						PicoSetSurfaceColor( curSurface, 0, ( curVertex + i ), curSurface->shader->diffuseColor );
				}
				/* add triangles */
				for ( i = 1; i < numPoints - 1; ++i )
				{
					PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
					PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + i ) );
					PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + i + 1 ) );
					curFace++;
				}
				/* increase vertex count */
				curVertex += numPoints;
			}
		}
		else if ( !_pico_stricmp( p->token,"usemtl" ) ) {
			picoShader_t *shader;
			char *name;

			/* get material name */
			name = _pico_parse( p,0 );

			if ( curFace != 0 || curSurface == NULL ) {
				_pico_printf( PICO_WARNING,"No group defined for usemtl, so creating an autoSurface in OBJ, line %d.",p->curLine );
				AUTO_GROUPNAME( autoGroupNameBuf );
				NEW_SURFACE( autoGroupNameBuf );
			}

			/* validate material name */
			if ( name == NULL || !strlen( name ) ) {
				_pico_printf( PICO_ERROR,"Missing material name in OBJ, line %d.",p->curLine );
			}
			else
			{
				shader = PicoFindShader( model, name, 1 );
				if ( shader == NULL ) {
					_pico_printf( PICO_WARNING, "Undefined material name \"%s\" in OBJ, line %d. Making a default shader.", name, p->curLine );

					/* create a new pico shader */
					shader = PicoNewShader( model );
					if ( shader != NULL ) {
						PicoSetShaderName( shader,name );
						PicoSetShaderMapName( shader,name );
						PicoSetSurfaceShader( curSurface, shader );
					}
				}
				else
				{
					PicoSetSurfaceShader( curSurface, shader );
				}
			}
		}
		/* skip unparsed rest of line and continue */
		_pico_parse_skip_rest( p );
	}

	/* free memory used by temporary vertexdata */
	FreeObjVertexData( vertexData );

	/* return allocated pico model */
	return model;
//	return NULL;
}