全部博文(135)
2010年(135)
分类: LINUX
2010-07-15 16:11:24
这个程序在PC上工作比较合适,试过打开一个300万个三角形的模型,吃掉了大约800M内存。这个Viewer需要预先把纹理图片转成
PVRTC格式,并且对此不做检查,如果没对应的.pvr,直接出Accessing
Violation。这个转换可能得分两步,先缩放成正方形,然后用PVRTextureTool压缩。对于前一步我是用Java imageio
kit写了个小程序来处理并保存成未压缩24bit BMP的。
Parser的主要代码如下:
//
//
// @ Project : Wavefront Obj Loader
// @ File Name : ObjConverter.cpp
// @ Date : 2007-9-4
// @ Author : pinxue
//
// @ Copyright : 2007-9 Example source license.
// By keep this header comment,
// you may use this source file in your project for non-commicial or commicial purpose.
//
#include "ObjConverter.h"
#include
#include
#include "gles/gl.h"
#include "fixmath.h"
#include "Obj.h"
#include "Vertex.h"
#include "UV.h"
#include "Normal.h"
#include "Mesh.h"
#include "Face.h"
#include "MatLib.h"
#include "Mat.h"
#include "Vector3i.h"
#include "Buf.h"
bool debugging = true;
void debug(const char * msg)
{
if ( debugging )
printf("%s", msg);
}
#define CLEAN_FGETS(buf) buf[strlen(buf)-2]=0;
bool ObjConverter::loadWavefrontMtl(const char * src)
{
char buf[1024];
Mat curMat;
BufmatBuf(128);
matLib->fileName = _strdup(src);
long matCount = 0;
FILE *fp = fopen(src, "rb");
if ( fp == NULL ) {
printf("File %s cannot open!\n", src);
return false;
}
bool lineReaded = false;
bool firstMat = true;
bool readFailed = false;
while(!feof(fp))
{
if ( ! lineReaded ){readFailed = NULL==fgets(buf, 1024, fp); CLEAN_FGETS(buf);}
else lineReaded = false;
if ( ! readFailed )
{
if ( buf[0] == '#' )
{
continue;
}else if ( 0 == strncmp("newmtl", buf, 6) )
{
// finish privious mat
if ( !firstMat ) { matBuf << curMat; }
else firstMat = false;
memset(&curMat, 0, sizeof(Mat));
// start new mat
curMat.name = _strdup(& buf[7]);// skip "newmtl "
// parse mat parameters: Ka, Kd, Ks, illum, Ns
float r,g,b,a=1.0f;
while(!feof(fp))
{
readFailed = NULL==fgets(buf, 1024, fp); CLEAN_FGETS(buf);
if ( ! readFailed )
{
// skip tab and space
char * bp = buf;
while( (*bp == ' ') || (*bp == '\t') ) ++bp;
if ( 0 == strncmp("Ka ", bp, 3) )
{
sscanf(bp + 3, "%f %f %f", &r, &g, &b );
curMat.ambient.r = Float2Fixed(r);
curMat.ambient.g = Float2Fixed(g);
curMat.ambient.b = Float2Fixed(b);
curMat.ambient.a = Float2Fixed(a);
}else if ( 0 == strncmp("Kd ", bp, 3) )
{
sscanf(bp + 3, "%f %f %f", &r, &g, &b );
curMat.diffuse.r = Float2Fixed(r);
curMat.diffuse.g = Float2Fixed(g);
curMat.diffuse.b = Float2Fixed(b);
curMat.diffuse.a = Float2Fixed(a);
}else if ( 0 == strncmp("Ks ", bp, 3) )
{
curMat.specular.r = Float2Fixed(r);
curMat.specular.g = Float2Fixed(g);
curMat.specular.b = Float2Fixed(b);
curMat.specular.a = Float2Fixed(a);
}else if ( 0 == strncmp("Ns ", bp, 3) )
{
float f;
sscanf(bp + 3, "%f", & f);
curMat.specularExponent = Float2Fixed(f);
}else if ( 0 == strncmp("illum ", bp, 6) )
{
// not full support yet: illumination mode
// 0 : Color and Ambient off { constant color illumination model, color = Kd }
// 1 : Color and Ambient on { diffuse illMod with lambertian shading,
// color = Ka*Ia + Kd * [SUM j=1..ls, (N * Lj)Ij] }
// 2 : Highlight on { GL matching! diffuse and specular illMod
// using lambertian shading and Blinn-Phone specular illMod.
// color = Ka*Ia + Kd * {SUM j, (N*Lj)Ij} + Ks * {SUM j, ((H*Hj)^Ns)Ij}
// following is too complex if there is no shader support,
// i.e. GLES 1.x, refer .mtl spec for details
// 3 : Reflection on and Raytrace on
// 4 : Transparency: Glass on; Reflection: Raytrace on
// 5 : Reflection: Fresnel on and Raytrace on
// 6 : Transparentcy: Refraction on; Reflection: Fresnel off and Raytrace on
// 7 : Transparency: Refraction on; Reflection: Fresnel on and Raytrace on
// 8 : Reflection on and Raytrace off
// 9 : Transparenc: Glass on; Reflection: Raytrace off
//10 : Cast shadows onto invisible surfaces
sscanf(bp + 6, "%d", & curMat.illumMode);
}else if ( 0 == strncmp("d ", bp, 2) )
{
// not support yet: dissolve for current mat
}else{
// a lot of capability are not supported: sharpness, Ni, Tf and options for the tag supported
lineReaded = true;
break;
}
}else{
if ( ! feof(fp) )
{
debug("get next line failed in parsing mat");
debug(matBuf.last().name);
debug("\n");
}
break;
}
}
}
// texture maps (apply to Ka/Kd/Ks/Ns/d
else if ( 0 == strncmp("map_bump", buf, 8) || 0 == strncmp("map_Bump", buf, 8) )
{
// this is not standard tag, but it does used by some exporter instead of 'bump'
// and we will use the first one met.
if ( curMat.bumpMap == 0 )
curMat.bumpMap = _strdup(&buf[9]);
}else if ( 0 == strncmp("map_Ka", buf, 6) || 0 == strncmp("map_kA", buf, 6) )
{
curMat.ambientMap = _strdup(&buf[7]);
}else if ( 0 == strncmp("map_Kd", buf, 6) || 0 == strncmp("map_kD", buf, 6) )
{
curMat.diffuseMap = _strdup(&buf[7]);
}else if ( 0 == strncmp("map_Ks", buf, 6) || 0 == strncmp("map_kS", buf, 6) )
{
curMat.specularMap = _strdup(&buf[7]);
}else if ( 0 == strncmp("map_Ns", buf, 6) || 0 == strncmp("map_nS", buf, 6))
{
// not support yet : apply to Ns, specular exponent
}else if ( 0 == strncmp("bump", buf, 4) )
{
if ( curMat.bumpMap == 0 )
curMat.bumpMap = _strdup(&buf[5]);
}else{
// not support yet: map_d, map_aat on, decal{ tex_color(tv)*decal(tv)+mtl_color*(1.0-decal(tv) },
// disp, refl and options
debug("unknown type");
debug(buf); debug("\n");
}
}else{
if ( ! feof(fp) )
debug("read next line failed in mat lib\n");
}
}
// handle last mat
matBuf << curMat;
// to avoid curMat free the file names of last mat
memset(&curMat, 0, sizeof(Mat));
matLib->matCount = matBuf.count;
matLib->mats = matBuf.expose();
return true;
}
#define UNKNOWN_TYPE 1
#define UNKNOWN_LINE 2
#define BUF_DELTA 1024
bool ObjConverter::loadWavefrontObj(const char * src)
{
char buf[1024];
Vertex vertex;
BufvertexBuf(4096);
UV uv;
BufuvBuf(1024);
Normal normal;
BufnormalBuf(4096);
Vector3i tri;
BuftriBuf(1024);
Mesh mesh;
BufmeshBuf(4096);
Face face;
BufmeshFaceBuf(4096);
Vertex meshVertex;
BufmeshVertexBuf(4096);
UV meshUV;
BufmeshUVBuf(4096);
Normal meshNormal;
BufmeshNormalBuf(4096);
bool firstMesh = true;
int error = 0;
// assume there are enough free memory
// skip null check for all memory allocation.
FILE * fp = fopen(src, "rb");
if ( fp == NULL ) {
printf("File %s cannot open!\n", src);
return false;
}
bool readFailed = false;
while(!feof(fp))
{
readFailed = NULL == fgets(buf, 1024, fp); CLEAN_FGETS(buf);
if ( ! readFailed )
{
switch(buf[0])
{
case '#'://ignore
break;
case 'm'://parse matlib
if ( 0 == strncmp("mtllib", buf, 6) )
{
matLib = new MatLib();
matLib->fileName = _strdup(&buf[7]);
loadWavefrontMtl( matLib->fileName );
}else{
error = UNKNOWN_TYPE;
}
break;
case 'g'://start of obj, get obj name
if ( buf[1] == ' ' )
{
if ( obj == 0 )
{
obj = new Obj();
obj->name = _strdup( &buf[2] );
} // todo : support more than one obj, and handle group of non-object like faces
// (houdini exports a lot group without name)
else {
printf("warning: multiple g tag, we don't support yet!\n");
}
}else{
error = UNKNOWN_TYPE;
}
break;
case 'v':
//'v ' vertex
if ( buf[1] == ' ' )
{
float x,y,z;
sscanf( &buf[2], "%f %f %f", &x, &y, &z );
vertex.x = Float2Fixed(x);
vertex.y = Float2Fixed(y);
vertex.z = Float2Fixed(z);
vertexBuf << vertex;
}else
//'vt ' vertex texcoord
if ( buf[1] == 't' && buf[2] == ' ' )
{
float x,y;
sscanf( &buf[2], "%f %f", &x, &y );
uv.x = Float2Fixed(x);
uv.y = Float2Fixed(y);
uvBuf << uv;
}else
//'vn ' vertex normal
if ( buf[1] == 'n' && buf[2] == ' ' )
{
float x,y,z;
sscanf( &buf[2], "%f %f %f", &x, &y, &z );
normal.x = Float2Fixed(x);
normal.y = Float2Fixed(y);
normal.z = Float2Fixed(z);
normalBuf << normal;
}else{
error = UNKNOWN_TYPE;
}
break;
case 'u'://parse usemtl and mat name
// this means a new mesh too
if ( 0 == strncmp("usemtl", buf, 6) )
{
char * matName = & buf[7];
Mat * mat = matLib->get( matName );
// finish privous mesh
if ( ! firstMesh ) // if this is NOT the first mesh then we have privious mesh
{
mesh.vertexCount = meshVertexBuf.count;
mesh.vertexs = meshVertexBuf.expose();
// In current design, uvs and normals have same number of elements as vertexs
mesh.uvs = meshUVBuf.expose();
mesh.normals = meshNormalBuf.expose();
mesh.faceCount = meshFaceBuf.count;
mesh.faces = meshFaceBuf.expose();
if ( mesh.faceCount > 65536 )
printf("warning: mesh with %d faces\n", mesh.faceCount);
else { if (debugging) printf("mesh with %d faces\n", mesh.faceCount); }
meshBuf << mesh;
} else { firstMesh = false; }
// create new mesh
// by use new Buf wrapper, we just reuse the temporary mesh.
// and as all buffers are exposed, we even need not clean up it (it is reset internally)
mesh.mat = mat;
}
break;
case 'f'://face
if ( buf[1] == ' ' )
{
// face may contains a series of index
// each index contains v/vt/vn
// num/num/num num/num/num '\'
#define MAX_FACE_LINE_SIZE 4096
char line[MAX_FACE_LINE_SIZE];
char *p = line; int num=0, idx=0;
strncpy(line, &buf[3], MAX_FACE_LINE_SIZE);
bool startNewSet = true;
// skip leading space in the line, this is important because we use space to separate set of index
// in fact, we can counting idx to avoid the dependent, but anyway,
// the set of index is separated by space.
while( *p == ' ' || *p == '\t') ++p;
while( *p != 0 )
{
switch( *p )
{
case ' ': // next triple index set
idx = 0; num = 0; startNewSet = true;
++p;
break;
case '/': // next index of current set
startNewSet = false; // we're handling it.
num = 0; ++idx; ++p;
break;
case '\\':// a new new and a new set starting
startNewSet = true; num = 0; idx = 0;
readFailed = NULL==fgets( line, 1024, fp); CLEAN_FGETS(line);
p=line;
break;
default:
startNewSet = false; // we're handling it.
if ( *p == '-' )
num = -num;
else if ( isdigit(*p) )
{
num = num * 10 + (*p - '0');
switch(idx){
case 0:
tri.a = num;
break;
case 1:
tri.b = num;
break;
case 2:
tri.c = num;
break;
default: debug("Warning: face point with more than 3 index!\n");
}
}else{
debug("invalid char in face line\n");
}
++p;
}
if ( startNewSet ) {
triBuf << tri;
} // what about last set?
}
// the last tri
triBuf << tri;
if ( triBuf.count > 3 ) // triangular(triBuf);
{
// p0, [1, 2] [2,3] [3,4] ..., we will gen triCount - 2 triangles as FAN
// todo : generate TRIANGLE STRIP instead of FAN
BuftmpTriBuf(1024);
for ( long i = 0; i < triBuf.count-2; ++i )
{
// point 0
tmpTriBuf << triBuf[0];
// point +1
tmpTriBuf << triBuf[i+1];
// point +2
tmpTriBuf << triBuf[i+2];
}
triBuf.exchange(tmpTriBuf);
}
// put the face into mesh including append v/vt/vn pointed by face into mesh
bool existed=false;
long j = 0;
Vertex * cv;
for ( long i = 0; i < triBuf.count; ++i )
{
cv = &vertexBuf[triBuf[i].a -1]; // Wavefront Obj face index start from 1
existed=false;
for ( j = 0 ; j < meshVertexBuf.count; ++j )
{
if ( meshVertexBuf[j].x == cv->x
&& meshVertexBuf[j].y == cv->y
&& meshVertexBuf[j].z == cv->z )
{ existed = true; break; }
}
if ( existed )
{
// todo : check normal and uv at j is same
}else{
// append it
meshVertexBuf << *cv;
meshUVBuf << uvBuf[triBuf[i].b-1]; // Wavefront Obj face index start from 1
meshNormalBuf << normalBuf[triBuf[i].c-1]; // Wavefront Obj face index start from 1
}
switch(i % 3)
{
case 0: face.a = j; break;
case 1: face.b = j; break;
case 2: face.c = j;
// record the face
meshFaceBuf << face;
break;
}
}
triBuf.cleanup();
//++ faceGroup;
}
break;
default:
// ignore
debug("\nignore unknown line: ");
debug(buf); debug("\n");
;
}
}else{
if ( ! feof(fp) ){
printf( "failed to read a line!\n");
return false;
}
}
}
// finish last mesh
mesh.vertexCount = meshVertexBuf.count;
mesh.vertexs = meshVertexBuf.expose();
mesh.uvs = meshUVBuf.expose();
mesh.normals = meshNormalBuf.expose();
mesh.faceCount = meshFaceBuf.count;
mesh.faces = meshFaceBuf.expose();
meshBuf << mesh;
if ( mesh.faceCount > 65536 )
printf( "warning: mesh with %d faces!", mesh.faceCount);
else { if (debugging) printf("mesh with %d faces.\n"); }
// we got it!
obj->meshCount = meshBuf.count;
obj->meshs = meshBuf.expose();
return true;
}
这里面大量使用的Buf是个短小但实用的小容器,其实你也完全可以用STL库的,不过eVC4好象没带STL的实现。
//
//
// @ Project : Wavefront Obj Loader
// @ File Name : Main.cpp
// @ Date : 2007-9-12
// @ Author : pinxue
//
// @ Copyright : 2007-8 Example source license.
By keep this header comment,
you may use this source file in your project for non-commicial or commicial purpose.
//
#ifndef BUF_H
#define BUF_H
#include
#include
template <typename T>
class Buf
{
private:
T * _buf;
long cap;
public:
long count;
private:
void enlargeBuf() { cap += 1024; _buf = (T *) realloc( _buf, cap * sizeof(T) ); }
void reset(long initCap){cap=initCap; count=0; _buf = (T *) malloc( cap * sizeof(T) ); memset( _buf, 0, cap * sizeof(T)); }
public:
Buf(long initCap){ reset(initCap); }
~Buf(){ if ( _buf != 0 ) free( _buf ); }
void cleanup(void){ count = 0; };
const Buf* append(const T & t)
{
//_buf[count] = t;
memcpy( & _buf[count], &t , sizeof(T) );
++count;
if ( count >= cap ) enlargeBuf();
return this;
}
const Buf* operator <<(const T& t){ return append(t); }
T& operator [](long idx) { return _buf[idx]; }
const T* getBuf(void){ return _buf; }
T& firstFree(void){ return _buf[count]; }
/** User promise there has at least one element by calling this function */
T& first(void){ return _buf[0]; }
/** User promise there has at least one element by calling this function */
T& last(void){ return _buf[count-1]; }
T* expose(void){
T* tmp = (T*) realloc(_buf, count * sizeof(T)) ;
reset(cap);
return tmp;
}
void exchange(Buf & b){
long t; T * p;
p = _buf; _buf = b._buf; b._buf = p;
t = count; count = b.count; b.count = t;
t = cap; cap = b.cap; b.cap = t;
}
};
#endif //ifndef BUF_H