root/OpenSceneGraph/trunk/src/osgPlugins/dw/ReaderWriterDW.cpp @ 9527

Revision 9527, 40.5 kB (checked in by robert, 5 years ago)

Replaced readImageFile() usage with readRefImageFile() to prevent threading issues with caching of imagery in the osgDB::Registry cache.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4// reading a design workshop file utility
5// (c) GW Michel, 2001-2003.
6// (c) 2003 - modified to use Geometry rather than old GeoSet.
7// Design Workshop format files can be downloaded from www.artifice.com
8// Design Workshop editor can be downloaded from www.artifice.com = Mac & Win95/98/NT versions are available.
9// DW Lite is completely free, produces textured 3D models
10// aimed mostly at the architectural world.  Flat polygons are generally produced
11// No ability to produce smooth shading, unfortunately. 
12// But it is the best bangs per buck. (Anything/nothing = infinite value, and this is quite a lot/nothing)
13
14#include <osg/CullFace>
15#include <osg/Geode>
16#include <osg/Group>
17#include <osg/Geometry>
18#include <osg/Light>
19#include <osg/LightSource>
20#include <osg/Material>
21#include <osg/Texture2D>
22#include <osg/TexEnv>
23#include <osg/StateSet>
24#include <osg/Notify>
25
26#include <osgDB/FileNameUtils>
27#include <osgDB/Registry>
28#include <osgDB/ReadFile>
29#include <osgDB/FileUtils>
30
31#include <osg/GLU>
32
33using namespace osg;
34
35#ifndef WIN32
36    #define CALLBACK
37#endif
38
39class _dwobj; // predefine for later call
40int dwfgets(char *clin, int max, FILE *fin); // , end of line= 13 as well as Creturn=10
41
42class dwmaterial {// design workshop material, to be translated to OGL
43public:
44    typedef enum {Properties,TiledTexture,FullFace, SpotLight,PointLight} mttype;
45    dwmaterial() { type=Properties;
46        opacity=1; specular=0; specexp=0; fname="";TextureWidth=1; TextureHeight=1;
47        ctx=NULL; tx=NULL; id=0; dstate=NULL;colour[0]=colour[1]=colour[2]=colour[3]=1;
48        bright=halfIn=halfOut=falloff=0;atyp=NONE;
49        _lightnum=1;
50    }
51    ~dwmaterial() { }
52    void settexture(const osgDB::ReaderWriter::Options *options) {
53        if (!dstate) dstate = new StateSet;
54        if (isTextured()) { // shares common textures
55            if (!ctx || !tx) { // new texture needed
56                if (fname.length()>0) {
57                    ctx=osgDB::readRefImageFile(fname.c_str(),options);
58                    if (ctx.valid()) {
59                        ctx->setFileName(fname);
60                        tx=new Texture2D(ctx.get());
61                        tx->setWrap(Texture2D::WRAP_S, Texture2D::REPEAT);
62                        tx->setWrap(Texture2D::WRAP_T, Texture2D::REPEAT);
63                    }
64                    osg::TexEnv* texenv = new osg::TexEnv;
65                    texenv->setMode(osg::TexEnv::MODULATE);
66                    dstate->setTextureAttribute(0, texenv );
67                }
68            }
69            if (ctx.valid() && tx.valid()) { // texture exists
70                dstate->setTextureAttributeAndModes(0,tx.get(),osg::StateAttribute::ON);
71            }
72        }
73    }
74    StateSet *make(const osgDB::ReaderWriter::Options *options) { // returns the OSG material
75        if (!dstate) { // if it does not exist, then make it
76            dstate = new StateSet;
77            osg::Material* osgMaterial = new osg::Material;
78            dstate->setAttribute(osgMaterial);
79            if (opacity<0.99) {
80                osgMaterial->setTransparency(Material::FRONT_AND_BACK, opacity);
81                dstate->setMode(GL_BLEND,StateAttribute::ON);
82                dstate->setRenderingHint(StateSet::TRANSPARENT_BIN);
83                colour[3]=opacity;
84            }
85            osgMaterial->setAmbient(Material::FRONT_AND_BACK,colour);
86            osgMaterial->setDiffuse(Material::FRONT_AND_BACK,colour);
87
88            Vec4 colspec=colour*specular;
89            colspec[3]=colour[3];
90            osgMaterial->setSpecular(Material::FRONT_AND_BACK,colspec);
91            osgMaterial->setShininess(Material::FRONT_AND_BACK,specexp);
92
93            dstate->setMode( GL_LIGHTING, StateAttribute::ON );
94            dstate->setMode( GL_CULL_FACE, StateAttribute::ON );
95
96            osg::CullFace *cf = new osg::CullFace; // to define non-default culling
97            cf->setMode(osg::CullFace::BACK);
98            dstate->setAttribute(cf);
99
100            dstate->setTextureMode(0,GL_TEXTURE_2D,StateAttribute::OFF);
101            settexture(options);
102        }
103        return dstate;
104    }
105    inline int isType(mttype t1) const {return     (type==t1 ); }
106    inline int isTextured() {return     (type==TiledTexture || type==FullFace ); }
107    void setcolour(const float rgb[3]) {
108        colour[0]=rgb[0]; colour[1]=rgb[1]; colour[2]=rgb[2];
109    }
110    void settxrep(const float repx, const float repy) {
111        TextureWidth=repx;
112        TextureHeight=repy;
113    }
114    inline float getRepWid() const { return TextureWidth;}
115    inline float getRepHt() const { return TextureHeight;}
116    inline int isFullFace() const { return  type==FullFace;}
117    inline int getid() const { return  id;}
118    inline void setid(const int i) { id=i;}
119    inline void setopacity(float o) { opacity=o;}
120    inline void setspecular(float o) { specular=o;}
121    inline void setspecexp(float o) { specexp=o;}
122    void setType(const char *buff) {
123        if (strncmp(buff,"Tiled_Texture",13)==0)
124            type=dwmaterial::TiledTexture;
125        else if (strncmp(buff,"Spot_Light",11)==0)
126            type=dwmaterial::SpotLight;
127        else if (strncmp(buff,"Point_Light",11)==0)
128            type=dwmaterial::PointLight;
129        else if (strncmp(buff,"Properties",11)==0)
130            type=dwmaterial::Properties;
131        else if (strncmp(buff,"Full_Face_Texture",16)==0)
132            type=dwmaterial::FullFace;
133    }
134    void setfname(const char *buff) {
135        //fname=new char[strlen(buff+13)+5];
136        fname= (buff+13);
137        fname+= ".tga";
138    }
139    LightSource *makeLight(const Vec4 pos)
140    {
141        Light *lt= new Light;
142        Vec4 cdef;
143        cdef[0]=cdef[1]=cdef[2]=0.0f; cdef[3]=0.0f;
144        lt->setLightNum(_lightnum++);
145        lt->setSpecular(colour*bright/2.0f);
146        lt->setDiffuse(colour*bright/4.0f);
147        lt->setAmbient(cdef);
148        if (atyp==NONE) ;
149        else if (atyp==INVERSE_DIST) {
150            lt->setLinearAttenuation(1.0f);
151            lt->setConstantAttenuation(0.01f);
152        }
153        lt->setPosition(pos);
154        LightSource *ls=new LightSource();
155        ls->setLight(lt);
156        return ls;
157    }
158    void setAtten(const char *buff) {
159        if (strstr(buff,"kQ3AttenuationTypeNone")) atyp=NONE;
160        else if (strstr(buff,"kQ3AttenuationTypeInverseDistance")) atyp=INVERSE_DIST;
161        //    else if (strstr(buff,"kQ3AttenuationTypeNone")) ;
162    }
163    void setBright(const float br) { bright=br;}
164    void setHalfAngleIn(const float ha) { halfIn=ha;}
165    void setHalfAngleOut(const float ha) { halfOut=ha;}
166    void setFallOff(const float fo) { falloff=fo;}
167    const Vec4 getcolour() { return colour;}
168private:
169    int id;
170    Vec4 colour; // the ambient/diffuse+alpha colour
171    mttype type;
172    float opacity, specular, specexp; // transp, specularity properties
173    float TextureWidth, TextureHeight;
174    std::string fname; // picture file
175    enum atten {NONE, INVERSE_DIST, INVERSE_SQUARE} atyp;
176    float bright,halfIn,halfOut,falloff; // light brightness
177    osg::ref_ptr<osg::Image> ctx;
178    osg::ref_ptr<osg::Texture2D> tx;
179    int _lightnum;
180    StateSet *dstate; // used to represent the dw material in OSG
181};
182// structure to use as data for tessellation
183
184typedef struct {
185    double pos[3]; // must be double for the tessellator to detect vertices
186    Vec2 uv; // texture coordainte - may not be used?
187    Vec3 nrmv; // surface normal
188    int idx; // index in the verts[] array
189} avertex;
190
191class _face {
192public:
193    _face() { nVertStart=0; opening=NULL; idx=NULL; nv=0; nop=0; nset=0; nrm[0]=nrm[1]=nrm[2]=0;}
194    ~_face() { delete [] idx;}
195    void setnv(const int n){ nv=n; idx=new int[n];}
196    void addvtx(const int n){
197        if (nset < nv) {
198            idx[nset]=n;
199            nset++;
200        }
201    }
202    void addholevtx(const int nvtot) {
203        if (opening) {
204            opening[nop-1].addvtx(nvtot);
205        }
206    }
207    void setNBegin(int n1) {nVertStart=n1;}
208    void norm(Vec3 &n, const Vec3 side, const Vec3 s2) const {
209        n=s2^side; // perpendicular
210        n.normalize(); // unit norm
211    }
212    const Vec3 getnorm(void) const { return nrm; } // use the predefined normal
213    void getside12(Vec3 &s1, Vec3 &s2, const std::vector<Vec3> verts) const {
214        int ic=0; // counter for later vertices to ensure not coincident
215        int i1=idx[0]; // first vertex of face
216        int i2=idx[1]; // second, must be non-coincident
217        while (i2==i1 && ic<nv-1) {
218            ic++;
219            i2=idx[ic];
220        }
221        int i3=idx[ic]; // third, must be non-coincident
222        while (ic<nv-1 && (i3==i2 || i3==i1)) {
223            ic++;
224            i3=idx[ic];
225        }
226        if(ic>=nv) {
227            printf("Invalid vertices %d of %d. I1-3 %d %d %d.\n", ic, nv, i1, i2, i3);
228        }
229        if(i1>=static_cast<int>(verts.size()) || i2>=static_cast<int>(verts.size()) || i3>=static_cast<int>(verts.size())) {
230            printf("Invalid indices %d, %d, %d max allowed %d.\n", i1,i2,i3,static_cast<int>(verts.size()));//, errm
231        }
232        s1=(verts[i2]-verts[i1]); // side 1 of face
233        s2=(verts[i3]-verts[i2]); // side 2 of face
234    }
235    void getnorm(const std::vector<Vec3> verts) {
236        Vec3 side, s2; // used in cross product to find normal
237        getside12(side,s2, verts);
238        norm(nrm, s2, side);
239    }
240    void settrans(Matrix &mx, const Vec3 nrm, const std::vector<Vec3> verts, const dwmaterial *mat) const {
241        // define the matrix perpendcular to normal for mapping textures
242        float wid=mat->getRepWid();
243        float ht=mat->getRepHt();
244        Vec3 r1, r2,r3; // 3 rows of rotation matrix
245        if (mat->isFullFace()) { // set wid, ht from polygon
246            Vec3 s2; // want transformed u coordinate parallel to 'r1'
247            getside12(r1,s2, verts); // r1 = edge of first side
248//         printf("fullface s2 %f %f %f\n", s2.x(),s2.y(),s2.z());//, errm
249            r3=nrm;
250            float len=r1.length();
251            r1=r1/len;
252            r2=r3^r1;
253            r1=r1/len;
254            r2=r2/s2.length();
255       } else {
256            // mat.nrm= (0,0,1)  AND mat (0,1,0) => (0,a,b)
257            // the transformation is unitary - preserves lengths; and
258            // converts points on a plane into (s,t, constant) coords for use with texturing
259            // Rinv.(0,0,1) = (nrm) implies R since Rinv=R(transpose)
260            r3=nrm; // already a unit vector
261            // mat.(010) = (0ab) -> Minv.(0ab) = (010); and this row DOT nrm=0
262            if (r3.z() < 0.99f && r3.z() > -0.99f) { // not face parallel to ground - choose r1 perpendicular to nrm & 001
263                r2.set(0,0,1); // therefore r1 is in plane of face.
264                r1=r2^r3;
265                r1.normalize();
266            } else { // parallel to ground - make perpendicular to edge 1 of face
267                r1=verts[idx[1]]-verts[idx[0]];
268                r1.normalize();
269            }
270            r2=r3^r1;
271        }
272        for (int j=0; j<3; j++) { // and create the transpose matrix (inverse of rotation matrix)
273            mx(0,j)=r1[j];
274            mx(1,j)=r2[j];
275            mx(2,j)=r3[j];
276        }       
277        //        mx.postTrans(mx,0.5f,0.5f,0.0f);
278        if (mat->isFullFace()) { // set offset such that mx*verts[idx[0]] -> uv=(0,0)
279            Vec3 pos;
280            pos=mx*verts[idx[0]];
281            mx(0,3)=-pos.x();
282            mx(1,3)=-pos.y();
283            mx(2,3)=-pos.z();
284        } else { // scale inversely to the texture preferred repeat size
285            mx(0,0)*=1.0f/wid;
286            mx(1,0)*=1.0f/wid;
287            mx(0,1)*=1.0f/ht;
288            mx(1,1)*=1.0f/ht;
289            mx(0,3)=0.5f/wid;
290            mx(1,3)=0.5f/ht;
291        }
292        //        mx.postScale(mx,1.0f/themat->TextureWidth, 1.0f/themat->TextureHeight,1);
293    }
294    inline int setnvop(const unsigned short n) { // add a new hole in this face with n vertices
295        _face *oldop=opening;
296        opening=new _face[nop+1];
297        for (int i=0; i<nop; i++) opening[i].move(&oldop[i]);
298        delete [] oldop;
299        opening[nop].setnv(n);
300        nop++;
301        return (nop-1);
302    }
303    void move(class _face *oldop) { *this=*oldop; oldop->idx=NULL;}
304    inline int getnv() { return nv;}
305    inline int getvert(const int j) { return idx[j];}
306    inline int complete() { return (idx && nv>0 && nset==nv);} // face has all defined
307    inline int holecomplete() { if (!opening) return 1; // no hole, so it is complete
308        return opening[nop-1].complete();} // latest opening in face has all vertices defined
309    int getallverts(void) const { int ntot=nv;
310        for (int i=0; i<nop; i++) ntot+=opening[i].getnv();
311        return ntot;
312    }
313    void setnorm(const std::vector<Vec3> verts) { // set the face normal
314        getnorm(verts);
315        for (int i=0; i<nop; i++) {
316            opening[i].setnorm(verts);
317            if (nrm*opening[i].nrm > 0.0f) { // normals are parallel - reverse order of vertices
318                opening[i].reverse();
319                opening[i].setnorm(verts);
320            }
321        }
322    }
323    void setposes(avertex &poses, const int j, const std::vector<Vec3> verts) const {
324        poses.pos[0]=verts[idx[j]].x();
325        poses.pos[1]=verts[idx[j]].y();
326        poses.pos[2]=verts[idx[j]].z();
327        poses.nrmv=nrm;
328        poses.idx=idx[j];
329    }
330    void tessellate(const std::vector<Vec3> verts, const dwmaterial *themat,
331          GLUtesselator *ts, _dwobj *dwob, const Matrix *tmat) const;
332    void link(const int idop, const _face *f2, const int idop2,const std::vector<Vec3> verts, const dwmaterial *themat) const; // to join up opposed faces of a hole
333    inline const int getidx(int i) const { return idx[i];}
334private:
335    void linkholes(const std::vector<Vec3> verts, const dwmaterial *themat, const _face *f2) const;
336    void reverse() { // reverse order of the vertices
337        for (int j=0; j<nv/2; j++) {
338            int it=idx[j];
339            idx[j]=idx[nv-j-1];
340            idx[nv-j-1]=it;
341        }
342    }
343    int nop; // number of openings so far
344    class _face *opening; // openings in this face. number of verts, vertex list for opening
345    int nv; // number of vertices in the face
346    int nset; // number read so far
347    int nVertStart; // start index of vertices in the grand Geometry
348    Vec3 nrm; // surface normal
349    int *idx; // indices into the vertex list for the object
350};
351
352// structure for generating triangles (and tstrips, tfans etc)
353// from a design workshop object.
354
355class prims {
356public:
357    prims() { nbegin=0; // primlengs=NULL; gsidx=NULL;nrmidx=NULL;
358     //   txidx=NULL;nrms=NULL;txcoords=NULL;
359     //   nload=0; nff=0; curmode=0;
360        vertices = new osg::Vec3Array;
361        normals = new osg::Vec3Array;
362        txc = new osg::Vec3Array;
363        txcoords=new osg::Vec3Array; // new Vec2[6*nfnvf]; // one texture coord per vertex
364        tmat=NULL;
365    }
366    ~prims() {    /*delete [] primlengs; delete [] nrms;
367        delete [] gsidx; delete [] nrmidx; delete [] txcoords;*/
368    }
369    void addv(avertex *pos) { // tessellation callback
370        vertices->push_back(osg::Vec3(pos->pos[0],pos->pos[1],pos->pos[2]));
371        normals->push_back(pos->nrmv);
372        txcoords->push_back(osg::Vec3(pos->uv[0],pos->uv[1],0.0f));
373    }
374    void End() { // tessellation is done
375        int nverts=vertices->size()-nbegin;
376        osg::DrawArrays *drw=NULL;                               
377            switch (primType) {
378            case GL_TRIANGLES: //gset->setPrimType( osg::GeoSet::TRIANGLES );
379                //gset->setNumPrims( nload/3 );
380                drw=new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,nbegin,nverts);                               
381                gset->addPrimitiveSet(drw);
382                break;
383            case GL_TRIANGLE_STRIP: //gset->setPrimType( osg::GeoSet::TRIANGLE_STRIP );
384                //gset->setPrimLengths( nuprimlengs );
385                drw=new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP,nbegin,nverts);                               
386                gset->addPrimitiveSet(drw);
387                break;
388            case GL_TRIANGLE_FAN: //gset->setPrimType( osg::GeoSet::TRIANGLE_FAN );
389                //gset->setPrimLengths( nuprimlengs );
390                drw=new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_FAN,nbegin,nverts);                               
391                gset->addPrimitiveSet(drw);
392                break;
393            case GL_QUADS: //gset->setPrimType( osg::GeoSet::QUADS );
394                //gset->setNumPrims( nload/4 );
395                drw=new osg::DrawArrays(osg::PrimitiveSet::QUADS,nbegin,nverts);                               
396                gset->addPrimitiveSet(drw);
397                break;
398            case GL_QUAD_STRIP: //gset->setPrimType( osg::GeoSet::QUAD_STRIP );
399                drw=new osg::DrawArrays(osg::PrimitiveSet::QUAD_STRIP,nbegin,nverts);                               
400                gset->addPrimitiveSet(drw);
401                break;
402            case GL_POLYGON: //gset->setPrimType( osg::GeoSet::POLYGON );
403                drw=new osg::DrawArrays(osg::PrimitiveSet::POLYGON,nbegin,nverts);                               
404                gset->addPrimitiveSet(drw);
405                break;
406            }
407    }
408    void begin(GLenum op) { // part of a Tessellator callback - starts a new primitive of type op
409        primType=op;
410        nbegin=vertices->size();
411    }
412    void combine( GLdouble coords[3], avertex *d[4],
413        GLfloat w[4], avertex **dataOut , _dwobj *dwob);
414    void linkholes(const std::vector<Vec3> verts, const dwmaterial *themat,
415        const _face *f1, const _face *f2,
416        const int ipr[2], const int nv) {
417        int gsidx[4];
418        gsidx[0]=f1->getidx(ipr[1]); // vertex position index
419        gsidx[1]=f1->getidx(ipr[0]); // vertex position index
420        gsidx[2]=f2->getidx(nv-ipr[0]-1); // vertex position index
421        gsidx[3]=f2->getidx(nv-ipr[1]-1); // vertex position index
422       
423        Matrix mx; // texture matrix transform to plane
424        Vec3 s1,s2;
425        Vec3 nrm; // calculated normal to face
426        s1=verts[gsidx[1]]-verts[gsidx[0]];
427        s2=verts[gsidx[2]]-verts[gsidx[1]];
428        f1->norm(nrm, s2, s1);
429        f1->settrans(mx, nrm, verts,themat);
430        int n1=vertices->size();
431        for (int j=0; j<4; j++) {
432            Vec3 uv;
433            Vec3 coord=(verts[gsidx[j]]);
434            vertices->push_back( coord );
435            uv=mx*verts[gsidx[j]];
436            txcoords->push_back(uv);
437            normals->push_back(nrm);
438        }
439        osg::DrawArrays *drw=NULL;                               
440        drw=new osg::DrawArrays(osg::PrimitiveSet::QUADS,n1,4);
441        gset->addPrimitiveSet(drw);
442    }
443    void tessellate(_face &fc, const std::vector<Vec3> verts, const dwmaterial *themat,GLUtesselator* ts, _dwobj *dwob)
444    {    // generates a set of primitives all of one type (eg tris, qstrip trifan...)
445        fc.setNBegin(vertices->size());
446        fc.tessellate(verts, themat, ts, dwob, tmat);
447    }
448    void buildGeometry() { // at end of all faces, add collection of vertices to geometry
449        gset->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); //BIND_PERPRIM); //
450        gset->setNormalArray(normals);
451        gset->setTexCoordArray(0,txcoords);
452        gset->setVertexArray(vertices); // setCoords( vts, nusidx );
453    }
454    void setGeometry(osg::Geometry *gs) {
455        gset=gs;
456    }
457    void settmat(const Matrix *mx) {
458        tmat= mx;
459    }
460private:
461    osg::Geometry *gset;
462    osg::Vec3Array* vertices;
463    osg::Vec3Array* normals;
464    osg::Vec3Array* txc;
465    osg::Vec3Array* txcoords;
466    GLenum primType;
467    int nbegin; // vertex indices for current primitive
468    const Matrix *tmat; // local texture matrix, or may be NULL for default mapping
469};
470
471static prims *prd=NULL; // OK not nice to have a static but the OpenGL Tessellator etc wants to be able to refer
472// to things that are not available via an argument
473// tessellation subroutines - have 'C' prototypes, not a member of any class...
474// But I want ot use the prims class to contain useful information such as texture matrix etc.
475void CALLBACK myFaceBegin(GLenum op)
476{// tess 'primitive begins' call back
477    prd->begin(op);
478}
479void CALLBACK myFaceEnd()
480{// tess primiitve ends call back
481    prd->End();
482}
483void CALLBACK myVertex(void *pv)
484{// tess vertex call back with texture coord == void *pv1,
485        prd->addv((avertex *)pv);
486}
487void CALLBACK combineCallback( GLdouble coords[3], avertex *d[4],
488                         GLfloat w[4], avertex **dataOut , _dwobj *dwob)
489{
490    // dwob needed if there is a combine callback to add the new vertex to group
491    prd->combine(coords, d, w, dataOut,dwob);
492}
493void CALLBACK error (GLenum errno)
494{ // tess error code
495    const unsigned char *errm=gluErrorString(errno);
496    printf("Tessellator error %d %s\n", static_cast<int>(errno),errm);//, errm
497}
498    //==========
499void _face::linkholes(const std::vector<Vec3> verts, const dwmaterial *themat, const _face *f2) const
500{
501    int ipr[2];
502    ipr[0]=nv-1;
503    for (int i=0; i<nv; i++) { // pairs of vertices
504        ipr[1]=nVertStart+i;
505        prd->linkholes(verts, themat, this, f2, ipr, nv);
506        ipr[0]=ipr[1];
507    }
508}
509void _face::link(const int idop, const _face *f2, const int idop2,const std::vector<Vec3> verts, const dwmaterial *themat) const
510{ // to join up opposed faces of a hole; starts using hole[idop] in THIS, ands at f2.Hole[idop2]
511    opening[idop].linkholes(verts, themat, &f2->opening[idop2]);
512}
513//======== Edges link 2 vertices; indicate where a sharp crease can be found ==========
514class _dwedge {
515public:
516    _dwedge(){;}
517    ~_dwedge(){;}
518    void set(int i, int j) { e1=i; e2=j; }
519private:
520    int e1,e2; // ends of the edge - it joins verts[e1] to verts[e2]
521};
522//===================
523class _dwobj {  // class for design workshop read of a single object
524public:
525    _dwobj() { nverts=nfaces=0; openings=NULL;faces=NULL; tmat=NULL; edges=NULL;
526        nopens=nfaceverts=0; fc1=fc2=NULL; colour[0]=colour[1]=colour[2]=colour[3]=1;
527    }
528    ~_dwobj() {/*delete verts; delete faces;delete openings;*/
529        delete fc1;delete fc2;
530    }
531    int readOpenings(FILE *fp, const int nexpected)
532    { // read up to nexpected openings, each opening may have a number of vertices
533        char buff[256];
534        openings=new int[nexpected*2];
535        fc1=new unsigned short[nexpected];
536        fc2=new unsigned short[nexpected];
537        nopens=0;
538        int nvop=0; // current number of vertices in hole in object
539        while (nopens<nexpected) { // for each opening
540            if (dwfgets(buff, sizeof( buff ), fp )) {
541                if (strncmp(buff, "Opening:",8)==0) {
542                } else if (strncmp(buff, "faces:",6)==0) {
543                    sscanf(buff, "faces: %hu %hu", fc1+nopens, fc2+nopens);
544                } else if (strncmp(buff, "numVerts:",9)==0) {
545                    int nvtot=nverts; // total number of hole vertices read so far
546                    nvop=atoi(buff+9);
547                    openings[nopens*2]=faces[fc1[nopens]].setnvop(nvop/2); // prepare opening in face
548                    openings[nopens*2+1]=faces[fc2[nopens]].setnvop(nvop/2);
549                    readVerts(fp, nvop);
550                    for (; nvtot<nverts; nvtot++) {
551                        if (faces[fc1[nopens]].holecomplete()) {
552                            if (!faces[fc2[nopens]].holecomplete()) {
553                                faces[fc2[nopens]].addholevtx(nvtot);
554                            } else { // error
555                            }
556                        } else {
557                            faces[fc1[nopens]].addholevtx(nvtot);
558                        }
559                    }
560                    if (faces[fc2[nopens]].holecomplete()) {
561                        nopens++;
562                    }
563                } else { // general line
564                }
565            }
566        }
567        return nopens;
568    }
569    int readEdges(FILE *fp, const int nexpected)
570    { // read up to nexpected vertex pairs.  These are currently ignored.
571        // will define crease edges in future.
572        edges=new _dwedge[nexpected];
573        nedges=0;
574        if (edges) {
575            char buff[256];
576            while (nedges<nexpected) {
577                if (dwfgets(buff, sizeof( buff ), fp )) {
578                    int i1, i2;
579                    sscanf(buff,"%d %d", &i1, &i2);
580                    edges[nedges].set(i1,i2);
581                    nedges++;
582                }
583            }
584        }
585        return nedges;
586    }
587    int readFaces(FILE *fp, const int nexpected)
588    { // read up to nexpected faces
589        faces=new _face[nexpected];
590        char buff[256];
591        if (faces) {
592            while (nfaces<nexpected) {
593                if (dwfgets(buff, sizeof( buff ), fp )) {
594                    if (strncmp(buff,"numVerts:",9)==0) {
595                        int nv=atoi(buff+9);
596                        faces[nfaces].setnv(nv);
597                    } else {
598                        int idx=atoi(buff);
599                        faces[nfaces].addvtx(idx);
600                        if (faces[nfaces].complete()) {
601                            nfaceverts+=faces[nfaces].getnv();
602                            nfaces++;
603                        }
604                    }
605                }
606            }
607        }
608        return nfaces;
609    }
610    void buildDrawable(Group *grp, const osgDB::ReaderWriter::Options *options);  // convert dwobj into osg geosets
611    void setcolour(const float rgb[3]) {
612        colour[0]=rgb[0]; colour[1]=rgb[1]; colour[2]=rgb[2];
613    }
614    void reset() {    faces=NULL; //verts=NULL;
615        nverts=nfaces=nfaceverts=nopens=nedges=0;
616    }
617    void setmat(dwmaterial *mt) {
618        themat=mt;
619    }
620    int readVerts(FILE *fp, const int nexpected)
621    { // read up to nexpected vertices
622        int ntot=nverts+nexpected;
623        char buff[256];
624        verts.reserve(ntot);
625        while (nverts<ntot) {
626            if (dwfgets(buff, sizeof( buff ), fp )) {
627                float x,y,z;
628                sscanf(buff,"%f %f %f", &x, &y, &z);
629                Vec3 pos(x,-y,z);
630                verts.push_back(pos);
631            }
632            nverts++;
633        }
634//    osg::notify(osg::NOTICE) << nverts<<" inp "<<verts[nverts-1].x()<<
635//        " "<<verts[nverts-1].y()<<" "<<verts[nverts-1].z()<<" "<<verts.size()<< std::endl;
636
637        return nverts;
638    }
639    int addvtx(float x, float y, float z) { // add a single vertex to the object
640        Vec3 pos(x,y,z);
641        verts.push_back(pos); //
642        nverts++;
643        return nverts-1;
644    }
645    void settmat(const Matrix& mx) {
646        tmat= new Matrix(mx);
647    }
648    void makeuv(Vec2 &uv, const double pos[]) {
649        Vec3 p;
650        Vec3 txc;
651        p.set(pos[0],pos[1],pos[2]);
652        txc = (*mx)*p;
653        uv[0]=txc[0];
654        uv[1]=txc[1];
655    }
656    inline void setmx(Matrix *m) { mx=m;}
657private:
658    Vec4 colour;
659    std::vector<Vec3> verts;
660    dwmaterial *themat;
661    unsigned short nverts,nfaces,nedges;
662    unsigned short nfaceverts;
663    unsigned short nopens;
664    _face *faces;
665    _dwedge *edges;
666    int *openings;
667    unsigned short *fc1, *fc2; // openings[i] is in faces[fc1[i]] to faces[fc2[i]]
668    Matrix *tmat;
669    Matrix *mx; // current uvw transform for currently tessealting face
670};
671
672void _face::tessellate(const std::vector<Vec3> verts, const dwmaterial *themat,
673               GLUtesselator *ts, _dwobj *dwob, const Matrix * /*tmat*/) const {
674    int nvall=getallverts();
675    int nused=0;
676    avertex *poses=new avertex[2*nvall]; // passed to Tessellator to redraw
677    Matrix mx; // texture matrix transform to plane
678    settrans(mx, nrm, verts,themat);
679    dwob->setmx(&mx); // may be used by combine callback to define txcoord
680    gluTessBeginPolygon(ts, dwob);
681    gluTessBeginContour(ts); /**/
682    for (int j=0; j<nv; j++) {
683        Vec3 uv;
684        uv=mx*verts[idx[j]];
685        setposes(poses[nused], j, verts);
686        poses[nused].uv[0]=uv[0];
687        poses[nused].uv[1]=uv[1];
688        gluTessVertex(ts, (double *)&(poses[nused]), (double *)(poses+nused));
689        nused++;
690    }
691    gluTessEndContour(ts);
692    for (int k=0; k<nop; k++) { // now holes in the face
693        gluTessBeginContour(ts);
694        for (int j=0; j<opening[k].nv; j++) {
695            Vec3 uv;
696            uv=mx*verts[opening[k].idx[j]];
697            opening[k].setposes(poses[nused], j, verts);
698            poses[nused].nrmv*=-1; // get to agree with base polygon
699            poses[nused].nrmv=nrm;
700            poses[nused].uv[0]=uv[0];
701            poses[nused].uv[1]=uv[1];
702            gluTessVertex(ts, (double *)&(poses[nused]), (double *)(poses+nused));
703            nused++;
704        }
705        gluTessEndContour(ts);/* */
706    }
707    gluTessEndPolygon(ts);
708    delete [] poses;
709}
710void prims::combine( GLdouble coords[3], avertex *d[4],
711                    GLfloat w[4], avertex **dataOut , _dwobj *dwob) {
712    avertex *newv = new avertex(); // (avertex *)calloc(1, sizeof(avertex));
713    newv->pos[0] = coords[0];
714    newv->pos[1] = coords[1];
715    newv->pos[2] = coords[2];
716    newv->uv[0] = newv->uv[1] =0;
717    newv->nrmv[0] = newv->nrmv[1] = newv->nrmv[2] =0;
718    for (int i=0; i<4; i++) {
719        if (d[i]) {
720            newv->uv[0] = w[i]*d[i]->uv[0];
721            newv->uv[1] = w[i]*d[i]->uv[1];
722            newv->nrmv[0] = w[i]*d[i]->nrmv[0];
723            newv->nrmv[1] = w[i]*d[i]->nrmv[1];
724            newv->nrmv[2] = w[i]*d[i]->nrmv[2];
725        }
726    }
727    dwob->makeuv(newv->uv, newv->pos);
728    newv->idx=dwob->addvtx(coords[0], coords[1], coords[2]);
729    *dataOut = newv;
730}
731void _dwobj::buildDrawable(Group *grp, const osgDB::ReaderWriter::Options *options)
732// current DWobject complete; make a drawable, and add it to a osg::Group
733    if (nfaces>0) {
734        if (themat->isType(dwmaterial::PointLight) || themat->isType(dwmaterial::SpotLight)) {
735            Vec4 pos;
736            pos.set(0.0f,0.0f,0.0f,0.0f);
737            for (int i=0; i<nverts; i++) {
738                pos[0]+=verts[i].x();
739                pos[1]+=verts[i].y();
740                pos[2]+=verts[i].z();
741            }
742            pos/=nverts;
743            pos[3]=1.0f;
744            LightSource *ls=themat->makeLight(pos);
745            grp->addChild(ls);
746        } else {
747            Geode *geode = new Geode;
748            int nfnvf=0; // number of vertices for faces plus holes
749            int i; // a general counter
750            for (i=0; i<nfaces; i++) { // for each face
751                faces[i].setnorm(verts); // set its normal and any hole normals
752                nfnvf+=faces[i].getallverts(); // get total vertices in object, defines dimensions of NEW arrays
753            }
754           
755           
756            GLUtesselator* ts=gluNewTess();
757            gluTessCallback(ts, GLU_TESS_BEGIN, (GLU_TESS_CALLBACK) myFaceBegin); 
758            gluTessCallback(ts, GLU_TESS_VERTEX, (GLU_TESS_CALLBACK) myVertex); 
759            gluTessCallback(ts, GLU_TESS_END, (GLU_TESS_CALLBACK) myFaceEnd); 
760            gluTessCallback(ts, GLU_TESS_ERROR, (GLU_TESS_CALLBACK) error); 
761            gluTessCallback(ts, GLU_TESS_COMBINE_DATA, (GLU_TESS_CALLBACK) combineCallback);
762            //  for (int nvf=0; nvf<6; nvf++) { // for each length of face
763            // for Geometry we dont need to collect prim types individually
764            //     prd.setmode(nvf , nfnvf); // filter out only this type of tessellated face
765            prd=new prims;
766            prd->settmat(tmat);
767            osg::Geometry *gset = new osg::Geometry;
768            prd->setGeometry(gset);
769            StateSet *dstate=themat->make(options);           
770            gset->setStateSet( dstate );
771            grp->addChild( geode ); // add to the world outside
772            geode->addDrawable(gset);
773           
774            // each face adds a primitive to the geometry, after it is tessellated
775            for (i=0; i<nfaces; i++) { // for each face, collect up
776                prd->tessellate(faces[i],verts, themat, ts, this);
777            }
778            for (i=0; i<nopens; i++) { // for each hole, join up front & back with Quads
779                if (fc1 && fc2) {
780                    faces[fc1[i]].link(openings[i*2], &faces[fc2[i]],openings[i*2+1],verts, themat);
781                }
782            } // for each opening
783            prd->buildGeometry();
784        gluDeleteTess(ts);
785        delete prd;
786        }
787    } // nfaces>0
788    verts.clear();
789}
790////////// tessellation complete
791
792class ReaderWriterDW : public osgDB::ReaderWriter
793{
794    public:
795   
796        ReaderWriterDW()
797        {
798            supportsExtension("dw","Designer Workbench model format");
799        }
800   
801        virtual const char* className() const { return "Design Workshop Database Reader"; }
802
803        virtual ReadResult readNode(const std::string& file,const osgDB::ReaderWriter::Options* options) const
804        {
805
806            std::string ext = osgDB::getLowerCaseFileExtension(file);
807            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
808
809            std::string fileName = osgDB::findDataFile( file, options );
810            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
811
812
813            _dwobj obj;
814            enum reading {NONE, MATERIAL, OBJECT};
815            //unsigned short nrecs=0; // number of records read after a divider (numVerts, numFaces, numOpenings...)
816            int nexpected=0; // number of records to be read in a block
817            dwmaterial *matpalet=NULL;
818            int nmat=-1; // current element of matpalet being modified
819            int nmn=0; // number of materials found
820            reading rdg=NONE;
821
822            char buff[256];
823
824            notify(INFO)<<   "ReaderWriterDW::readNode( "<<fileName.c_str()<<" )\n";
825#ifdef _MSC_VER
826            notify(osg::NOTICE)<<   "MS Visual C++ version "<<_MSC_VER<<"\n";
827#endif
828
829            FILE *fp;
830
831            if( (fp = osgDB::fopen( fileName.c_str(), "r" )) == (FILE *)0L )
832            {
833                return std::string("Unable to open file \""+fileName+"\"");
834            }
835            Group *grp = new Group;
836
837            // code for setting up the database path so that internally referenced file are searched for on relative paths.
838            osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
839            local_opt->setDatabasePath(osgDB::getFilePath(fileName));
840
841
842            while( !feof( fp ) )
843            { // reads the Design Workshop format in ASCII
844                if (dwfgets( buff, sizeof( buff ), fp )) {
845                    if( buff[0] == '#' )
846                        continue;
847                    else if( strncmp(buff,"numMaterials:",13)==0) { // No of materials
848                        nmn=atoi(buff+14);
849                        matpalet=new dwmaterial[nmn];
850                    } else if( strncmp(buff,"Material:",9)==0) { // No of materials
851                        dwfgets(buff, sizeof( buff ), fp );
852                        nmat++;
853                        rdg=MATERIAL;
854                        int id=atoi(buff);
855                        matpalet[nmat].setid(id); // current material to be modified
856                       
857                    } else if (strncmp(buff, "Phase:",6)==0) {
858                    } else if (strncmp(buff, "CurrPhase:",10)==0) {
859                    } else if (strncmp(buff, "numPhases:",10)==0) {
860                    } else if (strncmp(buff, "Opacity:",8)==0) {
861                        float opacity=atof(buff+8);
862                        matpalet[nmat].setopacity(opacity);
863                    } else if (strncmp(buff, "FallOff:",8)==0) {
864                        float fo=atof(buff+8);
865                        matpalet[nmat].setFallOff(fo);
866                    } else if (strncmp(buff, "InnerHalfAngle:",15)==0) {
867                        float ha=atof(buff+15);
868                        matpalet[nmat].setHalfAngleIn(ha);
869                    } else if (strncmp(buff, "OuterHalfAngle:",15)==0) {
870                        float ha=atof(buff+15);
871                        matpalet[nmat].setHalfAngleOut(ha);
872                    } else if (strncmp(buff, "Brightness:",11)==0) {
873                        float br=atof(buff+11);
874                        matpalet[nmat].setBright(br);
875                    } else if (strncmp(buff, "Attentuation:",13)==0) { // oops - they can't spell
876                        matpalet[nmat].setAtten(buff+13);
877                    } else if (strncmp(buff, "MaterialType:",13)==0) {
878                        matpalet[nmat].setType(buff+14);
879                    } else if (strncmp(buff, "SpecularReflectivity:",21)==0) {
880                        float spec=atof(buff+21);
881                        matpalet[nmat].setspecular(spec);
882                    } else if (strncmp(buff, "SmoothnessExponent:",19)==0) {
883                        float spec=atof(buff+19);
884                        matpalet[nmat].setspecexp(spec*128.0f/100.0f); // convert to 0-128 range from percent
885                    } else if (strncmp(buff, "TextureWidthAndHeight:",22)==0) {
886                        char *ct=strchr(buff+22,',');
887                        float repx=atof(buff+23), repy=atof(ct+1);
888                        matpalet[nmat].settxrep(repx, repy);
889                    } else if (strncmp(buff, "PictureFile:",12)==0) {
890                        char *end=strchr(buff,'\n'); // end of line
891                        if (end) *end='\0'; // removed
892                        matpalet[nmat].setfname(buff);
893                    } else if( strncmp(buff,"Extrusion:",10)==0 ||
894                        strncmp(buff,"Cube:",5)==0 ||
895                        strncmp(buff,"Polyline:",9)==0 ||
896                        strncmp(buff,"Polyhedron:",11)==0) {
897                        rdg=OBJECT;
898                        obj.buildDrawable(grp, options);
899                        obj.reset();
900                    } else if( strncmp(buff,"Mat:",4)==0) {
901                        int mt=atoi(buff+4);
902                        for (int j=0; j<nmn; j++) {
903                            if (matpalet[j].getid() == mt)
904                                obj.setmat(&(matpalet[j]));
905                        }
906                    } else if( strncmp(buff,"Color:",6)==0) {
907                        float rgb[3];
908                        if (rdg==MATERIAL) { // get material colour
909                            sscanf(buff+6,"%f %f %f", &rgb[0], &rgb[1], &rgb[2]);
910                            matpalet[nmat].setcolour(rgb);
911                        } else if (rdg==OBJECT) {
912                            float rgb[3];
913                            sscanf(buff+6,"%f %f %f", &rgb[0], &rgb[1], &rgb[2]);
914                            rgb[0]/=65536.0f; // convert to range 0-1
915                            rgb[1]/=65536.0f; // convert to range 0-1
916                            rgb[2]/=65536.0f; // convert to range 0-1
917                            obj.setcolour(rgb);
918                        }
919                    } else if( strncmp(buff,"numVerts:",9)==0) {
920                        nexpected=atoi(buff+9);
921                        obj.readVerts(fp, nexpected);
922                        nexpected=0;
923                    } else if( strncmp(buff,"numFaces:",9)==0) {
924                        nexpected=atoi(buff+9);
925                        obj.readFaces(fp, nexpected);
926                        nexpected=0;
927                    } else if( strncmp(buff,"numEdges:",9)==0) {
928                        nexpected=atoi(buff+9);
929                        obj.readEdges(fp, nexpected);
930                        //nrecs=0; // a numVerts is followed by nv vetex postiions
931                    } else if( strncmp(buff,"numOpenings:",12)==0) {
932                        nexpected=atoi(buff+12);
933                        //nrecs=0; // a numVerts is followed by nv vetex postiions
934                        if (nexpected>0) obj.readOpenings(fp, nexpected);
935                    } else if( strncmp(buff,"UVW:",4)==0) { // texture application matrix
936                        double mx[3][3];
937                        sscanf(buff+4,"%lf %lf %lf %lf %lf %lf %lf %lf %lf",
938                            &mx[0][0], &mx[0][1], &mx[0][2],
939                            &mx[1][0], &mx[1][1], &mx[1][2],
940                            &mx[2][0], &mx[2][1], &mx[2][2]);
941                       
942                        obj.settmat(Matrix(mx[0][0],mx[0][1],mx[0][2],0.0,
943                                           mx[1][0],mx[1][1],mx[1][2],0.0,
944                                           mx[2][0],mx[2][1],mx[2][2],0.0,
945                                           0.0     ,0.0     ,0.0     ,1.0));
946                    }
947                }
948
949            }
950            fclose( fp );
951            obj.buildDrawable(grp, options); // tidy up any remaining objects
952
953            return grp;
954
955        }
956private:
957};
958
959int dwfgets(char *clin, int max, FILE *fin)
960{ // replace fgets to detect EOL = char 13 as well as Creturn=10 GWM 111100
961    // Macintosh produced files (such as those obtainable
962    //from the great buildings site at www.Artifice.com) use 13 format, PC models use 10.
963    int nread=0;
964    char c1=1;
965    do {
966        if (!feof( fin )) {
967            clin[nread]=c1=fgetc(fin);
968            nread++;
969        }
970    } while (nread<max && c1!= 13 && c1!= 10 &&  feof( fin )== 0 );
971    if (nread>0) clin[nread-1]='\0'; // null terminate and remove training blank
972    return nread;
973}
974
975// now register with osg::Registry to instantiate the above
976// reader/writer.
977REGISTER_OSGPLUGIN(dw, ReaderWriterDW)
Note: See TracBrowser for help on using the browser.