instead of osgDB::Registry where it original lay. This has been done to allow fileName strings to be encode data rather than just file names, such as one requires when using PagedLOD along with plugins for doing dynamic tesselation.
973 lines
39 KiB
C++
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,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", 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() { 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;
|
|
|