#include #include // reading a design workshop file utility // (c) GW Michel, 2001-2003. // (c) 2003 - modified to use Geometry rather than old GeoSet. // Design Workshop format files can be downloaded from www.artifice.com // Design Workshop editor can be downloaded from www.artifice.com = Mac & Win95/98/NT versions are available. // DW Lite is completely free, produces textured 3D models // aimed mostly at the architectural world. Flat polygons are generally produced // No ability to produce smooth shading, unfortunately. // But it is the best bangs per buck. (Anything/nothing = infinite value, and this is quite a lot/nothing) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace osg; #ifndef WIN32 #define CALLBACK #endif class _dwobj; // predefine for later call int dwfgets(char *clin, int max, FILE *fin); // , end of line= 13 as well as Creturn=10 class dwmaterial {// design workshop material, to be translated to OGL public: typedef enum {Properties,TiledTexture,FullFace, SpotLight,PointLight} mttype; dwmaterial() { type=Properties; opacity=1; specular=0; specexp=0; fname="";TextureWidth=1; TextureHeight=1; ctx=NULL; tx=NULL; id=0; dstate=NULL;colour[0]=colour[1]=colour[2]=colour[3]=1; bright=halfIn=halfOut=falloff=0;atyp=NONE; _lightnum=1; } ~dwmaterial() { } void settexture(const osgDB::ReaderWriter::Options *options) { if (!dstate) dstate = new StateSet; if (isTextured()) { // shares common textures if (!ctx || !tx) { // new texture needed if (fname.length()>0) { ctx=osgDB::readImageFile(fname.c_str(),options); if (ctx) { ctx->setFileName(fname); tx=new Texture2D; tx->setImage(ctx); tx->setWrap(Texture2D::WRAP_S, Texture2D::REPEAT); tx->setWrap(Texture2D::WRAP_T, Texture2D::REPEAT); } osg::TexEnv* texenv = new osg::TexEnv; texenv->setMode(osg::TexEnv::MODULATE); dstate->setTextureAttribute(0, texenv ); } } if (ctx && tx) { // texture exists dstate->setTextureAttributeAndModes(0,tx,osg::StateAttribute::ON); } } } StateSet *make(const osgDB::ReaderWriter::Options *options) { // returns the OSG material if (!dstate) { // if it does not exist, then make it dstate = new StateSet; osg::Material* osgMaterial = new osg::Material; dstate->setAttribute(osgMaterial); if (opacity<0.99) { osgMaterial->setTransparency(Material::FRONT_AND_BACK, opacity); dstate->setMode(GL_BLEND,StateAttribute::ON); dstate->setRenderingHint(StateSet::TRANSPARENT_BIN); colour[3]=opacity; } osgMaterial->setAmbient(Material::FRONT_AND_BACK,colour); osgMaterial->setDiffuse(Material::FRONT_AND_BACK,colour); Vec4 colspec=colour*specular; colspec[3]=colour[3]; osgMaterial->setSpecular(Material::FRONT_AND_BACK,colspec); osgMaterial->setShininess(Material::FRONT_AND_BACK,specexp); dstate->setMode( GL_LIGHTING, StateAttribute::ON ); dstate->setMode( GL_CULL_FACE, StateAttribute::ON ); osg::CullFace *cf = new osg::CullFace; // to define non-default culling cf->setMode(osg::CullFace::BACK); dstate->setAttribute(cf); dstate->setTextureMode(0,GL_TEXTURE_2D,StateAttribute::OFF); settexture(options); } return dstate; } inline int isType(mttype t1) const {return (type==t1 ); } inline int isTextured() {return (type==TiledTexture || type==FullFace ); } void setcolour(const float rgb[3]) { colour[0]=rgb[0]; colour[1]=rgb[1]; colour[2]=rgb[2]; } void settxrep(const float repx, const float repy) { TextureWidth=repx; TextureHeight=repy; } inline float getRepWid() const { return TextureWidth;} inline float getRepHt() const { return TextureHeight;} inline int isFullFace() const { return type==FullFace;} inline int getid() const { return id;} inline void setid(const int i) { id=i;} inline void setopacity(float o) { opacity=o;} inline void setspecular(float o) { specular=o;} inline void setspecexp(float o) { specexp=o;} void setType(const char *buff) { if (strncmp(buff,"Tiled_Texture",13)==0) type=dwmaterial::TiledTexture; else if (strncmp(buff,"Spot_Light",11)==0) type=dwmaterial::SpotLight; else if (strncmp(buff,"Point_Light",11)==0) type=dwmaterial::PointLight; else if (strncmp(buff,"Properties",11)==0) type=dwmaterial::Properties; else if (strncmp(buff,"Full_Face_Texture",16)==0) type=dwmaterial::FullFace; } void setfname(const char *buff) { //fname=new char[strlen(buff+13)+5]; fname= (buff+13); fname+= ".tga"; } LightSource *makeLight(const Vec4 pos) { Light *lt= new Light; Vec4 cdef; cdef[0]=cdef[1]=cdef[2]=0.0f; cdef[3]=0.0f; lt->setLightNum(_lightnum++); lt->setSpecular(colour*bright/2.0f); lt->setDiffuse(colour*bright/4.0f); lt->setAmbient(cdef); if (atyp==NONE) ; else if (atyp==INVERSE_DIST) { lt->setLinearAttenuation(1.0f); lt->setConstantAttenuation(0.01f); } lt->setPosition(pos); LightSource *ls=new LightSource(); ls->setLight(lt); return ls; } void setAtten(const char *buff) { if (strstr(buff,"kQ3AttenuationTypeNone")) atyp=NONE; else if (strstr(buff,"kQ3AttenuationTypeInverseDistance")) atyp=INVERSE_DIST; // else if (strstr(buff,"kQ3AttenuationTypeNone")) ; } void setBright(const float br) { bright=br;} void setHalfAngleIn(const float ha) { halfIn=ha;} void setHalfAngleOut(const float ha) { halfOut=ha;} void setFallOff(const float fo) { falloff=fo;} const Vec4 getcolour() { return colour;} private: int id; Vec4 colour; // the ambient/diffuse+alpha colour mttype type; float opacity, specular, specexp; // transp, specularity properties float TextureWidth, TextureHeight; std::string fname; // picture file enum atten {NONE, INVERSE_DIST, INVERSE_SQUARE} atyp; float bright,halfIn,halfOut,falloff; // light brightness Image *ctx; Texture2D *tx; int _lightnum; StateSet *dstate; // used to represent the dw material in OSG }; // structure to use as data for tesselation typedef struct { double pos[3]; // must be double for the tessellator to detect vertices Vec2 uv; // texture coordainte - may not be used? Vec3 nrmv; // surface normal int idx; // index in the verts[] array } avertex; class _face { public: _face() { nVertStart=0; opening=NULL; idx=NULL; nv=0; nop=0; nset=0; nrm[0]=nrm[1]=nrm[2]=0;} ~_face() { delete [] idx;} void setnv(const int n){ nv=n; idx=new int[n];} void addvtx(const int n){ if (nset < nv) { idx[nset]=n; nset++; } } void addholevtx(const int nvtot) { if (opening) { opening[nop-1].addvtx(nvtot); } } void setNBegin(int n1) {nVertStart=n1;} void norm(Vec3 &n, const Vec3 side, const Vec3 s2) const { n=s2^side; // perpendicular n.normalize(); // unit norm } const Vec3 getnorm(void) const { return nrm; } // use the predefined normal void getside12(Vec3 &s1, Vec3 &s2, const std::vector verts) const { int ic=0; // counter for later vertices to ensure not coincident int i1=idx[0]; // first vertex of face int i2=idx[1]; // second, must be non-coincident while (i2==i1 && ic=nv) { printf("Invalid vertices %d of %d. I1-3 %d %d %d.\n", ic, nv, i1, i2, i3); } if(i1>=static_cast(verts.size()) || i2>=static_cast(verts.size()) || i3>=static_cast(verts.size())) { printf("Invalid indices %d, %d, %d max allowed %d.\n", i1,i2,i3,static_cast(verts.size()));//, errm } s1=(verts[i2]-verts[i1]); // side 1 of face s2=(verts[i3]-verts[i2]); // side 2 of face } void getnorm(const std::vector verts) { Vec3 side, s2; // used in cross product to find normal getside12(side,s2, verts); norm(nrm, s2, side); } void settrans(Matrix &mx, const Vec3 nrm, const std::vector verts, const dwmaterial *mat) const { // define the matrix perpendcular to normal for mapping textures float wid=mat->getRepWid(); float ht=mat->getRepHt(); Vec3 r1, r2,r3; // 3 rows of rotation matrix if (mat->isFullFace()) { // set wid, ht from polygon Vec3 s2; // want transformed u coordinate parallel to 'r1' getside12(r1,s2, verts); // r1 = edge of first side // printf("fullface s2 %f %f %f\n", s2.x(),s2.y(),s2.z());//, errm r3=nrm; float len=r1.length(); r1=r1/len; r2=r3^r1; r1=r1/len; r2=r2/s2.length(); } else { // mat.nrm= (0,0,1) AND mat (0,1,0) => (0,a,b) // the transformation is unitary - preserves lengths; and // converts points on a plane into (s,t, constant) coords for use with texturing // Rinv.(0,0,1) = (nrm) implies R since Rinv=R(transpose) r3=nrm; // already a unit vector // mat.(010) = (0ab) -> Minv.(0ab) = (010); and this row DOT nrm=0 if (r3.z() < 0.99f && r3.z() > -0.99f) { // not face parallel to ground - choose r1 perpendicular to nrm & 001 r2.set(0,0,1); // therefore r1 is in plane of face. r1=r2^r3; r1.normalize(); } else { // parallel to ground - make perpendicular to edge 1 of face r1=verts[idx[1]]-verts[idx[0]]; r1.normalize(); } r2=r3^r1; } for (int j=0; j<3; j++) { // and create the transpose matrix (inverse of rotation matrix) mx(0,j)=r1[j]; mx(1,j)=r2[j]; mx(2,j)=r3[j]; } // mx.postTrans(mx,0.5f,0.5f,0.0f); if (mat->isFullFace()) { // set offset such that mx*verts[idx[0]] -> uv=(0,0) Vec3 pos; pos=mx*verts[idx[0]]; mx(0,3)=-pos.x(); mx(1,3)=-pos.y(); mx(2,3)=-pos.z(); } else { // scale inversely to the texture preferred repeat size mx(0,0)*=1.0f/wid; mx(1,0)*=1.0f/wid; mx(0,1)*=1.0f/ht; mx(1,1)*=1.0f/ht; mx(0,3)=0.5f/wid; mx(1,3)=0.5f/ht; } // mx.postScale(mx,1.0f/themat->TextureWidth, 1.0f/themat->TextureHeight,1); } inline int setnvop(const unsigned short n) { // add a new hole in this face with n vertices _face *oldop=opening; opening=new _face[nop+1]; for (int i=0; iidx=NULL;} inline int getnv() { return nv;} inline int getvert(const int j) { return idx[j];} inline int complete() { return (idx && nv>0 && nset==nv);} // face has all defined inline int holecomplete() { if (!opening) return 1; // no hole, so it is complete return opening[nop-1].complete();} // latest opening in face has all vertices defined int getallverts(void) const { int ntot=nv; for (int i=0; i verts) { // set the face normal getnorm(verts); for (int i=0; i 0.0f) { // normals are parallel - reverse order of vertices opening[i].reverse(); opening[i].setnorm(verts); } } } void setposes(avertex &poses, const int j, const std::vector verts) const { poses.pos[0]=verts[idx[j]].x(); poses.pos[1]=verts[idx[j]].y(); poses.pos[2]=verts[idx[j]].z(); poses.nrmv=nrm; poses.idx=idx[j]; } void tesselate(const std::vector verts, const dwmaterial *themat, GLUtesselator *ts, _dwobj *dwob, const Matrix *tmat) const; void link(const int idop, const _face *f2, const int idop2,const std::vector verts, const dwmaterial *themat) const; // to join up opposed faces of a hole inline const int getidx(int i) const { return idx[i];} private: void linkholes(const std::vector verts, const dwmaterial *themat, const _face *f2) const; void reverse() { // reverse order of the vertices for (int j=0; jpush_back(osg::Vec3(pos->pos[0],pos->pos[1],pos->pos[2])); normals->push_back(pos->nrmv); txcoords->push_back(osg::Vec3(pos->uv[0],pos->uv[1],0.0f)); } void End() { // tesselation is done int nverts=vertices->size()-nbegin; osg::DrawArrays *drw=NULL; switch (primType) { case GL_TRIANGLES: //gset->setPrimType( osg::GeoSet::TRIANGLES ); //gset->setNumPrims( nload/3 ); drw=new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,nbegin,nverts); gset->addPrimitiveSet(drw); break; case GL_TRIANGLE_STRIP: //gset->setPrimType( osg::GeoSet::TRIANGLE_STRIP ); //gset->setPrimLengths( nuprimlengs ); drw=new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP,nbegin,nverts); gset->addPrimitiveSet(drw); break; case GL_TRIANGLE_FAN: //gset->setPrimType( osg::GeoSet::TRIANGLE_FAN ); //gset->setPrimLengths( nuprimlengs ); drw=new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_FAN,nbegin,nverts); gset->addPrimitiveSet(drw); break; case GL_QUADS: //gset->setPrimType( osg::GeoSet::QUADS ); //gset->setNumPrims( nload/4 ); drw=new osg::DrawArrays(osg::PrimitiveSet::QUADS,nbegin,nverts); gset->addPrimitiveSet(drw); break; case GL_QUAD_STRIP: //gset->setPrimType( osg::GeoSet::QUAD_STRIP ); drw=new osg::DrawArrays(osg::PrimitiveSet::QUAD_STRIP,nbegin,nverts); gset->addPrimitiveSet(drw); break; case GL_POLYGON: //gset->setPrimType( osg::GeoSet::POLYGON ); drw=new osg::DrawArrays(osg::PrimitiveSet::POLYGON,nbegin,nverts); gset->addPrimitiveSet(drw); break; } } void begin(GLenum op) { // part of a tesselator callback - starts a new primitive of type op primType=op; nbegin=vertices->size(); } void combine( GLdouble coords[3], avertex *d[4], GLfloat w[4], avertex **dataOut , _dwobj *dwob); void linkholes(const std::vector verts, const dwmaterial *themat, const _face *f1, const _face *f2, const int ipr[2], const int nv) { int gsidx[4]; gsidx[0]=f1->getidx(ipr[1]); // vertex position index gsidx[1]=f1->getidx(ipr[0]); // vertex position index gsidx[2]=f2->getidx(nv-ipr[0]-1); // vertex position index gsidx[3]=f2->getidx(nv-ipr[1]-1); // vertex position index Matrix mx; // texture matrix transform to plane Vec3 s1,s2; Vec3 nrm; // calculated normal to face s1=verts[gsidx[1]]-verts[gsidx[0]]; s2=verts[gsidx[2]]-verts[gsidx[1]]; f1->norm(nrm, s2, s1); f1->settrans(mx, nrm, verts,themat); int n1=vertices->size(); for (int j=0; j<4; j++) { Vec3 uv; Vec3 coord=(verts[gsidx[j]]); vertices->push_back( coord ); uv=mx*verts[gsidx[j]]; txcoords->push_back(uv); normals->push_back(nrm); } osg::DrawArrays *drw=NULL; drw=new osg::DrawArrays(osg::PrimitiveSet::QUADS,n1,4); gset->addPrimitiveSet(drw); } void tesselate(_face &fc, const std::vector verts, const dwmaterial *themat,GLUtesselator* ts, _dwobj *dwob) { // generates a set of primitives all of one type (eg tris, qstrip trifan...) fc.setNBegin(vertices->size()); fc.tesselate(verts, themat, ts, dwob, tmat); } void buildGeometry() { // at end of all faces, add collection of vertices to geometry gset->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); //BIND_PERPRIM); // gset->setNormalArray(normals); gset->setTexCoordArray(0,txcoords); gset->setVertexArray(vertices); // setCoords( vts, nusidx ); } void setGeometry(osg::Geometry *gs) { gset=gs; } void settmat(const Matrix *mx) { tmat= mx; } private: osg::Geometry *gset; osg::Vec3Array* vertices; osg::Vec3Array* normals; osg::Vec3Array* txc; osg::Vec3Array* txcoords; GLenum primType; int nbegin; // vertex indices for current primitive const Matrix *tmat; // local texture matrix, or may be NULL for default mapping }; static prims *prd=NULL; // OK not nice to have a static but the OpenGL tesselator etc wants to be able to refer // to things that are not available via an argument // tesselation subroutines - have 'C' prototypes, not a member of any class... // But I want ot use the prims class to contain useful information such as texture matrix etc. void CALLBACK myFaceBegin(GLenum op) {// tess 'primitive begins' call back prd->begin(op); } void CALLBACK myFaceEnd() {// tess primiitve ends call back prd->End(); } void CALLBACK myVertex(void *pv) {// tess vertex call back with texture coord == void *pv1, prd->addv((avertex *)pv); } void CALLBACK combineCallback( GLdouble coords[3], avertex *d[4], GLfloat w[4], avertex **dataOut , _dwobj *dwob) { // dwob needed if there is a combine callback to add the new vertex to group prd->combine(coords, d, w, dataOut,dwob); } void CALLBACK error (GLenum errno) { // tess error code const unsigned char *errm=gluErrorString(errno); printf("tesselator error %d %s\n", static_cast(errno),errm);//, errm } //========== void _face::linkholes(const std::vector verts, const dwmaterial *themat, const _face *f2) const { int ipr[2]; ipr[0]=nv-1; for (int i=0; ilinkholes(verts, themat, this, f2, ipr, nv); ipr[0]=ipr[1]; } } void _face::link(const int idop, const _face *f2, const int idop2,const std::vector verts, const dwmaterial *themat) const { // to join up opposed faces of a hole; starts using hole[idop] in THIS, ands at f2.Hole[idop2] opening[idop].linkholes(verts, themat, &f2->opening[idop2]); } //======== Edges link 2 vertices; indicate where a sharp crease can be found ========== class _dwedge { public: _dwedge(){;} ~_dwedge(){;} void set(int i, int j) { e1=i; e2=j; } private: int e1,e2; // ends of the edge - it joins verts[e1] to verts[e2] }; //=================== class _dwobj { // class for design workshop read of a single object public: _dwobj() { nverts=nfaces=0; openings=NULL;faces=NULL; tmat=NULL; edges=NULL; nopens=nfaceverts=0; fc1=fc2=NULL; colour[0]=colour[1]=colour[2]=colour[3]=1; } ~_dwobj() {/*delete verts; delete faces;delete openings;*/ delete fc1;delete fc2; } int readOpenings(FILE *fp, const int nexpected) { // read up to nexpected openings, each opening may have a number of vertices char buff[256]; openings=new int[nexpected*2]; fc1=new unsigned short[nexpected]; fc2=new unsigned short[nexpected]; nopens=0; int nvop=0; // current number of vertices in hole in object while (nopens verts; dwmaterial *themat; unsigned short nverts,nfaces,nedges; unsigned short nfaceverts; unsigned short nopens; _face *faces; _dwedge *edges; int *openings; unsigned short *fc1, *fc2; // openings[i] is in faces[fc1[i]] to faces[fc2[i]] Matrix *tmat; Matrix *mx; // current uvw transform for currently tessealting face }; void _face::tesselate(const std::vector verts, const dwmaterial *themat, GLUtesselator *ts, _dwobj *dwob, const Matrix * /*tmat*/) const { int nvall=getallverts(); int nused=0; avertex *poses=new avertex[2*nvall]; // passed to tesselator to redraw Matrix mx; // texture matrix transform to plane settrans(mx, nrm, verts,themat); dwob->setmx(&mx); // may be used by combine callback to define txcoord gluTessBeginPolygon(ts, dwob); gluTessBeginContour(ts); /**/ for (int j=0; jpos[0] = coords[0]; newv->pos[1] = coords[1]; newv->pos[2] = coords[2]; newv->uv[0] = newv->uv[1] =0; newv->nrmv[0] = newv->nrmv[1] = newv->nrmv[2] =0; for (int i=0; i<4; i++) { if (d[i]) { newv->uv[0] = w[i]*d[i]->uv[0]; newv->uv[1] = w[i]*d[i]->uv[1]; newv->nrmv[0] = w[i]*d[i]->nrmv[0]; newv->nrmv[1] = w[i]*d[i]->nrmv[1]; newv->nrmv[2] = w[i]*d[i]->nrmv[2]; } } dwob->makeuv(newv->uv, newv->pos); newv->idx=dwob->addvtx(coords[0], coords[1], coords[2]); *dataOut = newv; } void _dwobj::buildDrawable(Group *grp, const osgDB::ReaderWriter::Options *options) { // current DWobject complete; make a drawable, and add it to a osg::Group if (nfaces>0) { if (themat->isType(dwmaterial::PointLight) || themat->isType(dwmaterial::SpotLight)) { Vec4 pos; pos.set(0.0f,0.0f,0.0f,0.0f); for (int i=0; imakeLight(pos); grp->addChild(ls); } else { Geode *geode = new Geode; int nfnvf=0; // number of vertices for faces plus holes int i; // a general counter for (i=0; isettmat(tmat); osg::Geometry *gset = new osg::Geometry; prd->setGeometry(gset); StateSet *dstate=themat->make(options); gset->setStateSet( dstate ); grp->addChild( geode ); // add to the world outside geode->addDrawable(gset); // each face adds a primitive to the geometry, after it is tesselated for (i=0; itesselate(faces[i],verts, themat, ts, this); } for (i=0; ibuildGeometry(); gluDeleteTess(ts); delete prd; } } // nfaces>0 verts.clear(); } ////////// tesselation complete class ReaderWriterDW : public osgDB::ReaderWriter { public: virtual const char* className() const { return "Design Workshop Database Reader"; } virtual bool acceptsExtension(const std::string& extension) const { return osgDB::equalCaseInsensitive(extension,"dw"); } virtual ReadResult readNode(const std::string& file,const osgDB::ReaderWriter::Options* options) const { std::string ext = osgDB::getLowerCaseFileExtension(file); if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; std::string fileName = osgDB::findDataFile( file, options ); if (fileName.empty()) return ReadResult::FILE_NOT_FOUND; _dwobj obj; enum reading {NONE, MATERIAL, OBJECT}; //unsigned short nrecs=0; // number of records read after a divider (numVerts, numFaces, numOpenings...) int nexpected=0; // number of records to be read in a block dwmaterial *matpalet=NULL; int nmat=-1; // current element of matpalet being modified int nmn=0; // number of materials found reading rdg=NONE; char buff[256]; notify(INFO)<< "ReaderWriterDW::readNode( "< local_opt = options ? static_cast(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options; local_opt->setDatabasePath(osgDB::getFilePath(fileName)); while( !feof( fp ) ) { // reads the Design Workshop format in ASCII if (dwfgets( buff, sizeof( buff ), fp )) { if( buff[0] == '#' ) continue; else if( strncmp(buff,"numMaterials:",13)==0) { // No of materials nmn=atoi(buff+14); matpalet=new dwmaterial[nmn]; } else if( strncmp(buff,"Material:",9)==0) { // No of materials dwfgets(buff, sizeof( buff ), fp ); nmat++; rdg=MATERIAL; int id=atoi(buff); matpalet[nmat].setid(id); // current material to be modified } else if (strncmp(buff, "Phase:",6)==0) { } else if (strncmp(buff, "CurrPhase:",10)==0) { } else if (strncmp(buff, "numPhases:",10)==0) { } else if (strncmp(buff, "Opacity:",8)==0) { float opacity=atof(buff+8); matpalet[nmat].setopacity(opacity); } else if (strncmp(buff, "FallOff:",8)==0) { float fo=atof(buff+8); matpalet[nmat].setFallOff(fo); } else if (strncmp(buff, "InnerHalfAngle:",15)==0) { float ha=atof(buff+15); matpalet[nmat].setHalfAngleIn(ha); } else if (strncmp(buff, "OuterHalfAngle:",15)==0) { float ha=atof(buff+15); matpalet[nmat].setHalfAngleOut(ha); } else if (strncmp(buff, "Brightness:",11)==0) { float br=atof(buff+11); matpalet[nmat].setBright(br); } else if (strncmp(buff, "Attentuation:",13)==0) { // oops - they can't spell matpalet[nmat].setAtten(buff+13); } else if (strncmp(buff, "MaterialType:",13)==0) { matpalet[nmat].setType(buff+14); } else if (strncmp(buff, "SpecularReflectivity:",21)==0) { float spec=atof(buff+21); matpalet[nmat].setspecular(spec); } else if (strncmp(buff, "SmoothnessExponent:",19)==0) { float spec=atof(buff+19); matpalet[nmat].setspecexp(spec*128.0f/100.0f); // convert to 0-128 range from percent } else if (strncmp(buff, "TextureWidthAndHeight:",22)==0) { char *ct=strchr(buff+22,','); float repx=atof(buff+23), repy=atof(ct+1); matpalet[nmat].settxrep(repx, repy); } else if (strncmp(buff, "PictureFile:",12)==0) { char *end=strchr(buff,'\n'); // end of line if (end) *end='\0'; // removed matpalet[nmat].setfname(buff); } else if( strncmp(buff,"Extrusion:",10)==0 || strncmp(buff,"Cube:",5)==0 || strncmp(buff,"Polyline:",9)==0 || strncmp(buff,"Polyhedron:",11)==0) { rdg=OBJECT; obj.buildDrawable(grp, options); obj.reset(); } else if( strncmp(buff,"Mat:",4)==0) { int mt=atoi(buff+4); for (int j=0; j0) obj.readOpenings(fp, nexpected); } else if( strncmp(buff,"UVW:",4)==0) { // texture application matrix double mx[3][2]; sscanf(buff+4,"%lf %lf %lf %lf %lf %lf %lf %lf %lf", &mx[0][0], &mx[0][1], &mx[0][2], &mx[1][0], &mx[1][1], &mx[1][2], &mx[2][0], &mx[2][1], &mx[2][2]); obj.settmat(Matrix(mx[0][0],mx[0][1],mx[0][2],0.0, mx[1][0],mx[1][1],mx[1][2],0.0, mx[2][0],mx[2][1],mx[2][2],0.0, 0.0 ,0.0 ,0.0 ,1.0)); } } } fclose( fp ); obj.buildDrawable(grp, options); // tidy up any remaining objects return grp; } private: }; int dwfgets(char *clin, int max, FILE *fin) { // replace fgets to detect EOL = char 13 as well as Creturn=10 GWM 111100 // Macintosh produced files (such as those obtainable //from the great buildings site at www.Artifice.com) use 13 format, PC models use 10. int nread=0; char c1=1; do { if (!feof( fin )) { clin[nread]=c1=fgetc(fin); nread++; } } while (nread0) clin[nread-1]='\0'; // null terminate and remove training blank return nread; } // now register with osg::Registry to instantiate the above // reader/writer. osgDB::RegisterReaderWriterProxy g_readerWriter_DW_Proxy;