#ifndef _NMASK_H_ #define _NMASK_H_ //------------------------------------------------------------- // nmask.h // // Hansong Zhang // Department of Computer Science // UNC-Chapel // 1996 // // Refer to "Fast Backface Culling Using Normal Masks" in // ACM Symposium of Interactive 3D Graphics, 1997, for more // details on the algorithm //-------------------------------------------------------------- //-------------------------------------------------------------- // Defines 2 classes: NormalMask and PolygonNormalMask // // The NormalMask is used as the frontface mask in which every // normal cluster has a bit to indicate whether it is // front-facing (when the bit is 1) or backfacing (when // the bit is 0). // // Note that In my 1997 I3D paper I described the algorithm // in terms of a backface mask, which is the bitwise NOT of // the front-face mask used in this implementation - just to // test if you really understand it :^) // // The PolygonNormalMask is the per-polygon normal mask, // stored in the compact form (byteOffset, bitMask) since // only one bit can be ON. ByteOffset and bitMask are both // unsigned bytes. // // So the whole thing normally works this way: // // As a preprocessing (or during model loading) each polygon's // 2-byte normal code is computed and stored in // PolygonNormalMask; // Then for each frame, a FrontfaceMask (of type NormalMask) // is computed given the viewing direction and FOV, using // the FillBits() function in NormalMask (you can do // this multiple time, each for a sub-frustum... see the // paper); // For each polygon, FrontfaceMask[byteOffset] & bitMask tells // you whether the polygon's considered front-facing. The macro // FRONTFACING(VMSK, PMSK) is a shorthand for this. //-------------------------------------------------------------- // Replace with you own vector class class Vec3 { public: float x, y, z; float Length (void) const { return ((float)sqrt(x*x+y*y+z*z)); } void Normalize (void) { float L = Length(); x/=L; y/=L; z/=L; } float operator * (const Vec3& A) const { return x*A.x+y*A.y+z*A.z; } float operator /= (const Vec3& A) const { return x*A.x+y*A.y+z*A.z; } void operator /= (float s) { x/=s; y/=s; z/=s; } }; // // The backface test; used per polygon. // #define FRONTFACING(VMSK,PMSK) ((VMSK)[(PMSK).byteOffset]&(PMSK).bitMask) #define MAX_NUM_SUBDIV 20 static Masks[8] = {1, 2, 4, 8, 16, 32, 64, 128}; class NormalMask { public: int nSubdiv; int size; unsigned char *mask; NormalMask(int _nSubdiv); ~NormalMask() { delete mask; } // Called in the constructor void InitNormals(); // This is called for each frame to form the viewing masking // representing all the "active" normal clusters. The main thing. void NormalMask::FillBits(Vec3 &vDir, float FOV); void NormalMask::Print(); inline void TurnOn(int bitOffset) { mask[bitOffset>>3] |= Masks[bitOffset & 0x7]; } inline void TurnOff(int bitOffset) { mask[bitOffset>>3] &= ~Masks[bitOffset & 0x7]; } inline void TurnOn(int byteOffset, int bitOffset) { mask[byteOffset] |= Masks[bitOffset & 0x7]; } inline void TurnOff(int byteOffset, int bitOffset) { mask[byteOffset] &= ~Masks[bitOffset & 0x7]; } inline int IsOn(int bitOffset) { return mask[bitOffset>>3] & Masks[bitOffset & 0x7]; } inline unsigned char operator[] (int byteOffset) { return mask[byteOffset]; } void Draw(float x0, float y0, float width, int nWidth) ; }; typedef struct _PolygonNormalMask_ { unsigned char byteOffset, bitMask; int bitOffset; void Encode(Vec3 &normal, int nSubdiv); } PolygonNormalMask; #endif