OpenSceneGraph/src/osgPlugins/dw/ReaderWriterDW.cpp
2004-10-26 10:26:43 +00:00

973 lines
39 KiB
C++

#include <stdio.h>
#include <string.h>
// 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 <osg/CullFace>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Geometry>
#include <osg/Light>
#include <osg/LightSource>
#include <osg/Material>
#include <osg/Texture2D>
#include <osg/TexEnv>
#include <osg/StateSet>
#include <osg/Notify>
#include <osgDB/FileNameUtils>
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/FileUtils>
#include <osg/GLU>
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() {
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());
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() { // 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();
}
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<Vec3> 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-1) {
ic++;
i2=idx[ic];
}
int i3=idx[ic]; // third, must be non-coincident
while (ic<nv-1 && (i3==i2 || i3==i1)) {
ic++;
i3=idx[ic];
}
if(ic>=nv) {
printf("Invalid vertices %d of %d. I1-3 %d %d %d.\n", ic, nv, i1, i2, i3);
}
if(i1>=static_cast<int>(verts.size()) || i2>=static_cast<int>(verts.size()) || i3>=static_cast<int>(verts.size())) {
printf("Invalid indices %d, %d, %d max allowed %d.\n", i1,i2,i3,static_cast<int>(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<Vec3> 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<Vec3> 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; i<nop; i++) opening[i].move(&oldop[i]);
delete [] oldop;
opening[nop].setnv(n);
nop++;
return (nop-1);
}
void move(class _face *oldop) { *this=*oldop; oldop->idx=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<nop; i++) ntot+=opening[i].getnv();
return ntot;
}
void setnorm(const std::vector<Vec3> verts) { // set the face normal
getnorm(verts);
for (int i=0; i<nop; i++) {
opening[i].setnorm(verts);
if (nrm*opening[i].nrm > 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<Vec3> 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<Vec3> 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<Vec3> 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<Vec3> verts, const dwmaterial *themat, const _face *f2) const;
void reverse() { // reverse order of the vertices
for (int j=0; j<nv/2; j++) {
int it=idx[j];
idx[j]=idx[nv-j-1];
idx[nv-j-1]=it;
}
}
int nop; // number of openings so far
class _face *opening; // openings in this face. number of verts, vertex list for opening
int nv; // number of vertices in the face
int nset; // number read so far
int nVertStart; // start index of vertices in the grand Geometry
Vec3 nrm; // surface normal
int *idx; // indices into the vertex list for the object
};
// structure for generating triangles (and tstrips, tfans etc)
// from a design workshop object.
class prims {
public:
prims() { nbegin=0; // primlengs=NULL; gsidx=NULL;nrmidx=NULL;
// txidx=NULL;nrms=NULL;txcoords=NULL;
// nload=0; nff=0; curmode=0;
vertices = new osg::Vec3Array;
normals = new osg::Vec3Array;
txc = new osg::Vec3Array;
txcoords=new osg::Vec3Array; // new Vec2[6*nfnvf]; // one texture coord per vertex
tmat=NULL;
}
~prims() { /*delete [] primlengs; delete [] nrms;
delete [] gsidx; delete [] nrmidx; delete [] txcoords;*/
}
void addv(avertex *pos) { // tesselation callback
vertices->push_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<Vec3> 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<Vec3> 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<int>(errno),errm);//, errm
}
//==========
void _face::linkholes(const std::vector<Vec3> verts, const dwmaterial *themat, const _face *f2) const
{
int ipr[2];
ipr[0]=nv-1;
for (int i=0; i<nv; i++) { // pairs of vertices
ipr[1]=nVertStart+i;
prd->linkholes(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<Vec3> 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<nexpected) { // for each opening
if (dwfgets(buff, sizeof( buff ), fp )) {
if (strncmp(buff, "Opening:",8)==0) {
} else if (strncmp(buff, "faces:",6)==0) {
sscanf(buff, "faces: %hu %hu", fc1+nopens, fc2+nopens);
} else if (strncmp(buff, "numVerts:",9)==0) {
int nvtot=nverts; // total number of hole vertices read so far
nvop=atoi(buff+9);
openings[nopens*2]=faces[fc1[nopens]].setnvop(nvop/2); // prepare opening in face
openings[nopens*2+1]=faces[fc2[nopens]].setnvop(nvop/2);
readVerts(fp, nvop);
for (; nvtot<nverts; nvtot++) {
if (faces[fc1[nopens]].holecomplete()) {
if (!faces[fc2[nopens]].holecomplete()) {
faces[fc2[nopens]].addholevtx(nvtot);
} else { // error
}
} else {
faces[fc1[nopens]].addholevtx(nvtot);
}
}
if (faces[fc2[nopens]].holecomplete()) {
nopens++;
}
} else { // general line
}
}
}
return nopens;
}
int readEdges(FILE *fp, const int nexpected)
{ // read up to nexpected vertex pairs. These are currently ignored.
// will define crease edges in future.
edges=new _dwedge[nexpected];
nedges=0;
if (edges) {
char buff[256];
while (nedges<nexpected) {
if (dwfgets(buff, sizeof( buff ), fp )) {
int i1, i2;
sscanf(buff,"%d %d", &i1, &i2);
edges[nedges].set(i1,i2);
nedges++;
}
}
}
return nedges;
}
int readFaces(FILE *fp, const int nexpected)
{ // read up to nexpected faces
faces=new _face[nexpected];
char buff[256];
if (faces) {
while (nfaces<nexpected) {
if (dwfgets(buff, sizeof( buff ), fp )) {
if (strncmp(buff,"numVerts:",9)==0) {
int nv=atoi(buff+9);
faces[nfaces].setnv(nv);
} else {
int idx=atoi(buff);
faces[nfaces].addvtx(idx);
if (faces[nfaces].complete()) {
nfaceverts+=faces[nfaces].getnv();
nfaces++;
}
}
}
}
}
return nfaces;
}
void buildDrawable(Group *grp); // convert dwobj into osg geosets
void setcolour(const float rgb[3]) {
colour[0]=rgb[0]; colour[1]=rgb[1]; colour[2]=rgb[2];
}
void reset() { faces=NULL; //verts=NULL;
nverts=nfaces=nfaceverts=nopens=nedges=0;
}
void setmat(dwmaterial *mt) {
themat=mt;
}
int readVerts(FILE *fp, const int nexpected)
{ // read up to nexpected vertices
int ntot=nverts+nexpected;
char buff[256];
verts.reserve(ntot);
while (nverts<ntot) {
if (dwfgets(buff, sizeof( buff ), fp )) {
float x,y,z;
sscanf(buff,"%f %f %f", &x, &y, &z);
Vec3 pos(x,-y,z);
verts.push_back(pos);
}
nverts++;
}
// osg::notify(osg::NOTICE) << nverts<<" inp "<<verts[nverts-1].x()<<
// " "<<verts[nverts-1].y()<<" "<<verts[nverts-1].z()<<" "<<verts.size()<< std::endl;
return nverts;
}
int addvtx(float x, float y, float z) { // add a single vertex to the object
Vec3 pos(x,y,z);
verts.push_back(pos); //
nverts++;
return nverts-1;
}
void settmat(const Matrix& mx) {
tmat= new Matrix(mx);
}
void makeuv(Vec2 &uv, const double pos[]) {
Vec3 p;
Vec3 txc;
p.set(pos[0],pos[1],pos[2]);
txc = (*mx)*p;
uv[0]=txc[0];
uv[1]=txc[1];
}
inline void setmx(Matrix *m) { mx=m;}
private:
Vec4 colour;
std::vector<Vec3> 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<Vec3> 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; j<nv; j++) {
Vec3 uv;
uv=mx*verts[idx[j]];
setposes(poses[nused], j, verts);
poses[nused].uv[0]=uv[0];
poses[nused].uv[1]=uv[1];
gluTessVertex(ts, (double *)&(poses[nused]), (double *)(poses+nused));
nused++;
}
gluTessEndContour(ts);
for (int k=0; k<nop; k++) { // now holes in the face
gluTessBeginContour(ts);
for (int j=0; j<opening[k].nv; j++) {
Vec3 uv;
uv=mx*verts[opening[k].idx[j]];
opening[k].setposes(poses[nused], j, verts);
poses[nused].nrmv*=-1; // get to agree with base polygon
poses[nused].nrmv=nrm;
poses[nused].uv[0]=uv[0];
poses[nused].uv[1]=uv[1];
gluTessVertex(ts, (double *)&(poses[nused]), (double *)(poses+nused));
nused++;
}
gluTessEndContour(ts);/* */
}
gluTessEndPolygon(ts);
delete [] poses;
}
void prims::combine( GLdouble coords[3], avertex *d[4],
GLfloat w[4], avertex **dataOut , _dwobj *dwob) {
avertex *newv = new avertex(); // (avertex *)calloc(1, sizeof(avertex));
newv->pos[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)
{ // 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; i<nverts; i++) {
pos[0]+=verts[i].x();
pos[1]+=verts[i].y();
pos[2]+=verts[i].z();
}
pos/=nverts;
pos[3]=1.0f;
LightSource *ls=themat->makeLight(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; i<nfaces; i++) { // for each face
faces[i].setnorm(verts); // set its normal and any hole normals
nfnvf+=faces[i].getallverts(); // get total vertices in object, defines dimensions of NEW arrays
}
GLUtesselator* ts=gluNewTess();
gluTessCallback(ts, GLU_TESS_BEGIN, (void (CALLBACK *) ())myFaceBegin);
gluTessCallback(ts, GLU_TESS_VERTEX, (GLvoid (CALLBACK *) ())myVertex);
gluTessCallback(ts, GLU_TESS_END, (GLvoid (CALLBACK *) ())myFaceEnd);
gluTessCallback(ts, GLU_TESS_ERROR, (GLvoid (CALLBACK *) ())error);
gluTessCallback(ts, GLU_TESS_COMBINE_DATA, (GLvoid (CALLBACK *) ()) &combineCallback);
// for (int nvf=0; nvf<6; nvf++) { // for each length of face
// for Geometry we dont need to collect prim types individually
// prd.setmode(nvf , nfnvf); // filter out only this type of tesselated face
prd=new prims;
prd->settmat(tmat);
osg::Geometry *gset = new osg::Geometry;
prd->setGeometry(gset);
StateSet *dstate=themat->make();
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; i<nfaces; i++) { // for each face, collect up
prd->tesselate(faces[i],verts, themat, ts, this);
}
for (i=0; i<nopens; i++) { // for each hole, join up front & back with Quads
if (fc1 && fc2) {
faces[fc1[i]].link(openings[i*2], &faces[fc2[i]],openings[i*2+1],verts, themat);
}
} // for each opening
prd->buildGeometry();
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)
{
return osgDB::equalCaseInsensitive(extension,"dw");
}
virtual ReadResult readNode(const std::string& file,const osgDB::ReaderWriter::Options*)
{
std::string ext = osgDB::getLowerCaseFileExtension(file);
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
std::string fileName = osgDB::findDataFile( file );
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( "<<fileName.c_str()<<" )\n";
#ifdef _MSC_VER
notify(osg::NOTICE)<< "MS Visual C++ version "<<_MSC_VER<<"\n";
#endif
FILE *fp;
if( (fp = fopen( fileName.c_str(), "r" )) == (FILE *)0L )
{
return std::string("Unable to open file \""+fileName+"\"");
}
Group *grp = new Group;
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);
obj.reset();
} else if( strncmp(buff,"Mat:",4)==0) {
int mt=atoi(buff+4);
for (int j=0; j<nmn; j++) {
if (matpalet[j].getid() == mt)
obj.setmat(&(matpalet[j]));
}
} else if( strncmp(buff,"Color:",6)==0) {
float rgb[3];
if (rdg==MATERIAL) { // get material colour
sscanf(buff+6,"%f %f %f", &rgb[0], &rgb[1], &rgb[2]);
matpalet[nmat].setcolour(rgb);
} else if (rdg==OBJECT) {
float rgb[3];
sscanf(buff+6,"%f %f %f", &rgb[0], &rgb[1], &rgb[2]);
rgb[0]/=65536.0f; // convert to range 0-1
rgb[1]/=65536.0f; // convert to range 0-1
rgb[2]/=65536.0f; // convert to range 0-1
obj.setcolour(rgb);
}
} else if( strncmp(buff,"numVerts:",9)==0) {
nexpected=atoi(buff+9);
obj.readVerts(fp, nexpected);
nexpected=0;
} else if( strncmp(buff,"numFaces:",9)==0) {
nexpected=atoi(buff+9);
obj.readFaces(fp, nexpected);
nexpected=0;
} else if( strncmp(buff,"numEdges:",9)==0) {
nexpected=atoi(buff+9);
obj.readEdges(fp, nexpected);
//nrecs=0; // a numVerts is followed by nv vetex postiions
} else if( strncmp(buff,"numOpenings:",12)==0) {
nexpected=atoi(buff+12);
//nrecs=0; // a numVerts is followed by nv vetex postiions
if (nexpected>0) 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); // 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 (nread<max && c1!= 13 && c1!= 10 && feof( fin )== 0 );
if (nread>0) 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<ReaderWriterDW> g_readerWriter_DW_Proxy;