parallel rendering

The Render Box

    A. First Edition

    This is what I have been doing for more than one year!

    B.The problem

Environment setup for WMPI:

1. Setup Linux working environment:

setup file sharing between linux by NFS:

a) create /etc/exports file to allow all other machine to access:

/ ipadress-of-machine1(rw)

/ ipadress-of-machine2(rw)

¡

b) create mounting point for all machines under /mnt

mkdir /mnt/machine1

mkdir /mnt/machine2

¡

c) start NFS mounting by a simple script:

#!/bin/sh

service iptables stop

service nfs start

mount -t nfs 132.205.221.3:/ /mnt/varsha

mount -t nfs 132.205.221.4:/ /mnt/paush

mount -t nfs 132.205.221.5:/ /mnt/falgun

mount -t nfs 132.205.221.24:/ /mnt/vividh

2. Setup WMPI

You can just setup environment manually.

a) download linux version gcc-4.0 from www.criticalsoftware.com for a free evaluation version of 40 days. Every time you need a new trial license, you need to register with a different email address. The license-key will be mailed after downloading up to 24 hours.

b) Choose a license key server of linux and place the license key file under /etc/wmpi2/

Also in all machines including license key server, under /etc/wmpi2 create a file named wmpi2ls.conf in which you give the machine name of license key server.

c) In all machine, copy unzipped directory ¡°wmpi2¡± under /usr/local/

This is not mandatory but recommended.

d) Create all soft link for library and header file. This can be done by

a script. (The script also does part of b) for you.)

#!/bin/sh

cd /usr/lib

ln -sf /usr/local/wmpi2/lib/libpwmpi2.a libpwmpi2.a

ln -sf /usr/local/wmpi2/lib/libwmpi2.so.2.4.1 libwmpi2.so.2.4.1

ln -sf /usr/local/wmpi2/lib/libwmpi2.so.2.4.1 libwmpi2.so.2.4

ln -sf /usr/local/wmpi2/lib/libwmpi2.so.2.4.1 libwmpi2.so.2

ln -sf /usr/local/wmpi2/lib/libwmpi2.so.2.4.1 libwmpi2.so

cd /usr/include

ln -sf /usr/local/wmpi2/include/mpi.h mpi.h

ln -sf /usr/local/wmpi2/include/mpi2.h mpi2.h

ln -sf /usr/local/wmpi2/include/mpi_errno.h mpi_errno.h

ln -sf /usr/local/wmpi2/include/mpi++.h mpi++.h

ln -sf /usr/local/wmpi2/include/mpi++_inline.h mpi++_inline.h

cd /usr/bin

ln -sf /usr/local/wmpi2/bin/mpiexec mpiexec

ln -sf /usr/local/wmpi2/bin/wmpi2doc wmpi2doc

ln -sf /usr/local/wmpi2/bin/wmpi2testconf wmpi2testconf

ln -sf /usr/local/wmpi2/bin/wmpi2reguser wmpi2reguser

ln -sf /usr/local/wmpi2/bin/wmpi2testconf wmpi2testconf

cd /usr/sbin

ln -sf /usr/local/wmpi2/sbin/wmpi2licensed wmpi2licensed

ln -sf /usr/local/wmpi2/sbin/wmpi2publisherd wmpi2publisherd

ln -sf /usr/local/wmpi2/sbin/wmpi2serviced wmpi2serviced

echo paush.encs.concordia.ca > /etc/wmpi2/wmpi2ls.conf

d) Better to register your user name by running ¡°wmpi2reguser ¨Ca none root¡±

e) To eliminate fire wall interference, shut down it by ¡°service iptables stop¡±. This is also necessary for NFS setup.

f) For each wmpi program, you need two setting file at its directory. wmpi2.conf which lists all running machines INCLUDING YOUR OWN STARTING MACHINE! And another file is yourprgramwithoutextension.pg2 which use xml format to specify running process and running machines EXCEPT YOUR OWN STARTING MACHINE. It also specifies the location of running program in all machines. Of course this implies that you need to place them in identical path in all machines.

3. running wmpi program in linux

It is almost as simple as running in windows by first copy program into running machine and start from starting machine by running the program without using mpiexec.

    preprocessing:

1. octreeTool is a utility tool for data preprocessing, data testing and walk-through for single machineCompilation:
2. Compilation:

It is already written in both Windows and Linux compatible. To compile in Linux, just comment out the macro LINUX_VERSION at header file ¡°syscall.h¡±.

a) In windows: use the shipped project file ¡°octreeTool¡± to compile in VC++6. You need to make sure ¡°LINUX_VERSION¡± is undefined in ¡°syscall.h¡±.

In Linux: use the make file ¡°single.make¡± by run ¡°make ¨Cf single.make¡±. And of course you need to ¡°#define LINUX_VERSION 1¡± at ¡°syscall.h¡±.

3. Preprocessing:

a) The output file name is hard coded in source ¡°octree.cpp¡± for following variables:

char* InfoStruct::vertexFileName="vertex.data";

char* InfoStruct::faceFileName="face.data";

char* InfoStruct::fakeColorFileName="fakeColor.data";

char* InfoStruct::infoFileName="info.data";

char* InfoStruct::octreeFileName="octree.data";

b) At command line type ¡°octreeTool.exe path [number]¡± will start preprocess data. ¡°path¡± is the target input raw data file. Currently it is the file from ¡°powerplant format¡± which is downloaded and transformed into little-endian. Continue run preprocess will incrementally establish octree. ¡°number¡± is optional for ¡°refining data mesh¡±. Basically it divides each triangle into four smaller ones. By default it is 0, meaning no refining. But don¡¯t use more than 1 unless each individual input raw data file is not very big, say 10M +.

c) After preprocessing, you need to generate ¡°fakeColor.data¡± from original ¡°vertex.data¡±. So, MAKE A COPY OF ORIGINAL ¡°vertex.data¡±. And at same directory run ¡°octreeTool.exe fake¡± which will modify internal color of ¡°vertex.data¡± to ¡°fakeColor¡±. Then change name of ¡°vertex.data¡± to ¡°fakeColor.data¡± and change your backup ¡°vertex.data¡± to its original ¡°vertex.data¡±. (For single machine, this process is not necessary.)

4. Running:

To run, just type ¡°octreeTool.exe¡± without parameter.

a) hot key:

¡®e¡¯: toggle lighting on/off

¡®n¡¯: toggle smooth shading on/off

¡®i¡¯: forward

¡®j¡¯: turning left

¡®l¡¯: turning move

¡®k¡¯: backward

¡®w¡¯: view-angle up

¡®s¡¯: view-angle down

¡®u¡¯: move up

¡®U¡¯: move down

¡®+¡¯: double rendering budget (increase detail)

¡®-¡®: reduce by half of rendering budget (reduce detail)

¡®h¡¯: hierarchical visibility search mode/¡±flooding¡± visibility search mode

¡®m¡¯: showing rendering details

¡®esc¡¯: exit

5. Possible future improvement

a) When preprocessing large input raw data file, there is possible running out of virtual memory. i.e. memory using more than 2G. I have tried many ways to over come this issue by carefully passing temporary file handle as recursive function parameter instead of directly passing data. Still the problem remains when expansion number is bigger or equal to ¡°2¡± (the command line parameter ¡°number¡±.) . In fact, 2 means square of four. So, I might need to develop a 64bit version.

b) The ¡°flooding¡± searching model has a big drawback that it will stop searching whenever there is a gap between octree leaf nodes. The possible improvement is to insert ¡°thunk link¡± between gaps.

    ¡¡

    manual for parallel rendering:

¡°renderBox.exe¡± is the executable program for parallel rendering.

1. data setup: In non-NFS system including windows platform, each running node is expected to have identical data files copied to identical path. The file name and path are hard coded in source ¡°octree.cpp¡±. There are five data files.

a) info.data: This gives the basic information for loader to read and write data file.

b) vertex.data: This file contains all vertex data which includes coordinates, normal and rgba color for each point. The data stream in file is not continuous, but separated according to octree lead nodes. There are two major reason for this arrangement. First, all ¡°file mapping¡± API requires starting address in file to be multiple times of ¡°page size¡±(64k in windows, 4k or other in linux). Second, triangle files uses local offset as index to refer to vertex so that there is no limit in this relative indexing.

c) face.data: This file contains all triangle index data which are also separated according to each octree leaf node with similar reason of vertex data, namely ¡°file mapping¡± requirement. One side effect is that this arrangement may allow some data insertion easily in some cases without relocate each segment.

d) octree.data: This file contains the hierarchical octree data like bounding box, parent, sons, starting address of vertex and triangle data in files and their sizes etc. See the structure definition of source ¡°octree.h¡± for details. All octree are represented in array-based structure and all pointers are index-based. Therefore the loading and storing data in disk and memory is fast and with no modification at all.

e) fakeColor.data: This file is only generated from original ¡°vertex.data¡± by ¡°octreeTool.exe¡± with parameter ¡°fake¡±. (See explanation of ¡°octreeTool¡± for details.) Basically it has identical data as ¡°vertex.data¡± except that the rgba color for each vertex is replaced with 32bit integer which is the index of octree lead node it belongs to.

2. running setup: Basically you need to setup the number of ¡°renderer¡± and ¡°tester¡± in program in source ¡°config.h¡±. There are two constant number ¡°TesterCount¡± and ¡°RenderCount¡± in this file. Also the two constants ¡°TesterHostStartIndex¡± and ¡°RenderHostStartIndex¡± must be setup properly such that all tester indices are not overlapped by other nodes and continuous. So does render nodes. For example, if we plan to run 4 renderers and 3 testers and we want ¡°tester node¡± running from index 0. Then we can set as following: MasterIndex=3; TesterHostStartIndex=0; RenderHostStartIndex=4; TesterCount=3; RenderCount=4;

But usually we want master running at index 0 which maybe a requirement in some system.

3. screen tile setup: In Hierarchical Depth Buffer algorithm, we divide screen into tiles. This setup is at ¡°dataStructure.h¡± where constant ¡°DepthBufferAtomWidth¡± is set. This number must be set such that ¡°Screen size¡± is multiple times of it.
4. In order to compile in different platform, a macro ¡°LINUX_VERSION¡± must be properly defined or undefined in ¡°syscall.h¡±. In linux, make file is ¡°multiple.make¡± and in windows you can use project file ¡°octree¡±. Also, in different platform the MPI compiler is different and the ¡°makefile¡± should be modified properly. If using WMPI, generic c++ compiler is used like VC++6 and g++ in windows and linux respectively. If using HP-MPI, the compiler is mpiCC.

¡¡

C.The idea of program

    D.The major functions

    E.Further improvement

    F.File listing

    Header Files

    1. syscall.h

    2. config.h

    3. dataStructure.h

    4. frustum.h

    5. plyModel.h

    6. viewCollector.h

    7. octree.h

    8. master.h

    9. tester.h

    10. render.h

    11. octreeLoader.h

    12. renderEngine.h

    CPP Files

    1. syscallLinux.cpp

    2. syscallWindow.cpp

    3. frustum.cpp

    4. dataStructure.cpp

    5. viewCollector.cpp

    6. octree.cpp

    7. octreeLoader.cpp

    8. master.cpp

    9. tester.cpp

    10. render.cpp

    11. renderBox.cpp(main)

    ¡¡

    file name: syscall.h

    /******************************************************
    This module is a wrapper of system call of windows and linux
    ***************************************************************/
    #pragma once
    #define LINUX_VERSION 1
    #define USING_COMPRESSION 1
    //#define USING_DUMMY_RENDER 1
    //#define USING_NAIVE_DEPTH_BUFFER_EXCHANGE 1
    //#define DEBUGGING 1
    #define USING_BROADCAST 1
    #ifdef LINUX_VERSION
    #define USING_FORCE_REFRESH 1
    //this is only meaningful in linux HPMPI
    #define PROFILING 1
    #endif

    #include <stdio.h>
    #include <vector>
    #ifndef LINUX_VERSION
    #include <windows.h>
    #else
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>

    #endif

    using namespace std;

    static int result;

    #ifdef LINUX_VERSION
    #define HANDLE int

    #define MAX_TEMP_NAME_LENGTH L_tmpnam
    #define MAX_PATH 256
    #define DWORD unsigned long int

    #define LINUX_SYSCALL(x, y) if (x==-1){perror(y); exit(123);}
    //#define LINUX_SYSCALL(x, y) if (x==-1){perror(y); }

    #define Int32x32To64(a,b) ((long long)((long)(a))*(long long)((long)(b)))
    //long int GetTickCount();
    #else
    #define MAX_TEMP_NAME_LENGTH MAX_PATH

    typedef struct
    {
    	time_t tv_sec;
    	time_t tv_usec;
    }timeval;

    #define WINAPI_CALL(x, y) if (x==0) {printf("%s: #%d\n", y, GetLastError()); exit(124);}

    #endif

    long int GenericGetTime();

    HANDLE GenericOpenFile(char* fileName, bool readOnly);

    int GenericWriteFile(HANDLE fileHandle, void* buffer, size_t length);

    int GenericReadFile(HANDLE fileHandle, void* buffer, size_t length);

    HANDLE GenericOpenTempFile(const char* nameString, char* buf);

    void GenericCloseTempFile(HANDLE handle, const char* fileName);

    void GenericCloseFile(HANDLE fileHandle);

    void GenericSetFilePointer(HANDLE fileHandle, long int low, long int high);

    HANDLE GenericCreateFileMapping(HANDLE fileHandle, bool readOnly);

    void* GenericMapViewOfFile(HANDLE fileMappingHandle, bool readOnly, int fileAddHigh, int fileAddLow, int length);

    void GenericUnmapViewOfFile(void* address, int length);

    void GenericRetrieveAllFile(char* dirName, vector<string>& fileNameVector);

    void GenericCloseFileMapping(HANDLE fileMappingHandle);

    //HANDLE GenericOpenLog(char* logName);

    ¡¡

    file name: config.h

    /*************************************************************************************
    file name: config.h
    author: qingzhe huang
    date: Jul. 21, 2007

    purpose: This is used to define all those configuration other than system API.
    1. Geometry data structure type definition.
    2. MPI running configure:
     TesterCount+RenderCount+1 = "size" of total nodes running for MPI
    [TesterHostStartIndex, TesterHostStartIndex+TesterCount]: Tester Index Range
    [RenderHostStartIndex, RenderHosterStartIndex+RenderCount]: Render Index Range

    a)
    MasterIndex can be any index between 0 to size-1 as long as it doesn't fall within either
    Tester index or Render index.
    b)
    Tester Index Range and Render Index Range cannot overlap.

    3. octreeLoader configuration:
    a) Different system may have different physical memory and when physical memory is
    not enough "file mapping" will fail. So, constant "ThresholdRate" setup a threshold
    limit for "unmap" starts to free unused mapping by checking if it appears in past
    "MaxCoherentFrameNumber" frames. If you have large physical memory and you want better
    performance, you can adjust both these number to bigger.

    4. Linux user should best check "PageSize" by "sysconf" because I just hardcoded 4k.
    5. Originally I thought it might help to improve "depth-buffer-precision" by using
    double-precision floating number instead of default single-precision. Later I think
    OpenGL might internally use single-precision. This is not a sure thing, but for performance
    purpose I stick to single. This is why I have macro of "USE_DOUBLE_PRECISION".
    6. OpenGL maximumly support 8 lights, and the "MaxLightNumber" should not exceed 8. Of
    course to speed up, you can set it smaller. However, if you want to change this number, you
    may need to modify ininitialized light color, position manually in "renderEngine.cpp".
    *************************************************************************************/

    #ifndef CONFIG_H
    #define CONFIG_H
    /////////////////////////////////////
    //define various constants, datatypes, macros etc
    #include "syscall.h"

    #ifndef LINUX_VERSION

    #pragma comment(lib, "glu32.lib")
    #pragma comment(lib, "OpenGL32.lib")
    //#pragma comment(lib, "wmpi2.lib")
    //#pragma comment(lib, "wmpi2cppbindings.lib")
    const unsigned int PageSize=64*1024;

    #else
    //this must be checked by "sysconf", but I just hardcoded for the time being.
    const unsigned int PageSize=4*1024;
    #endif

    /////////////////////////////////////////
    //octree data types
    #define CHECK_NEW(x) if (x==NULL){printf("memory alloc failed\n"); exit(1);}

    typedef unsigned char ubyte;
    typedef unsigned short ushort;

    //#define USE_DOUBLE_PRECISION 1

    typedef float FVector[3];
    typedef double DVector[3];

    //always use VerVector
    #ifdef USE_DOUBLE_PRECISION
    	#define VVector DVector
    	#define VType double
    #else
    	#define VVector FVector
    	#define VType float
    #endif

    /////////////////////////////////////////////
    //octree constants

    const VType Epsilon=0.001;
    const VType MinimumEdgeLength=0.000001;

    const unsigned int DefaultMaxLeafNodeVertexNumber=1024*4;
    const unsigned int MaxPossibleDepth=30;

    const unsigned int MaxNeighbourCount=26;

    /////////////////////////////////////////////////
    //constants for octreeloader

    const unsigned int MaxVertexCapacity=64*1024;
    const unsigned int MaxFaceCapacity=64*1024;
    const unsigned int MaxCapacity=2*1024*1024*1024/MaxFaceCapacity;
    const VType ThresholdRate=200;
    const unsigned int MaxCoherentFrameNumber=2;

    //broadcast requires fixed buffer size, so make it smaller
    #ifdef USING_BROADCAST
    const unsigned int MaxPossibleNodeCollectable=8*1024;
    #else
    const unsigned int MaxPossibleNodeCollectable=64*1024;
    #endif

    //////////////////////////////////////////////////////
    //master's data structure

    const int MasterIndex=7;
    const int RenderHostStartIndex=0;
    const int TesterHostStartIndex=3;
    const int RenderHostCount=3;
    const int RenderCount=RenderHostCount;
    const int TesterCount=4;//default number
    const int TesterHostCount=TesterCount;

    ////////////////////////////////////////
    const int ScreenSize = 800;
    const int ColorBufferSize=ScreenSize*ScreenSize;
    const int ColorBufferByteSize=ColorBufferSize*4;
    const int DepthBufferSize=ColorBufferSize;
    const int DepthBufferByteSize=sizeof(float)*DepthBufferSize;

    //this is used to make sure compression will not overbust buffer
    const int VisibleTableBufferRate=2;
    const int VisibleTableSize=ColorBufferSize*VisibleTableBufferRate;

    ///////////////////////////////////////////////////////
    //render engine
    const int scrW=ScreenSize;
    const int scrH=ScreenSize;
    const int MaxLightNumber=8;

    #endif

    ¡¡

    file name: frustum.h

    #ifndef _FRUSTUM_H
    #define _FRUSTUM_H

    #include "plyModel.h"

    // This will allow us to create an object to keep track of our frustum
    class CFrustum {

    public:

    	bool cohenSutherland(unsigned char first, unsigned char second);
    	bool pointInsideFrustum(VType coord[3], unsigned char& codess);
    	bool canCullCube(VType center[3], VType halfWidth);
    	// Call this every time the camera moves to update the frustum
    	void CalculateFrustum();

    	// This takes a 3D point and returns TRUE if it's inside of the frustum
    	bool PointInFrustum(VType x, VType y, VType z);

    	// This takes a 3D point and a radius and returns TRUE if the sphere is inside of the frustum
    	bool SphereInFrustum(VType x, VType y, VType z, VType radius);

    	// This takes the center and half the length of the cube.
    	int CubeInFrustum( VType x, VType y, VType z, VType size );

    	int cubeInsideFrustum(VType x, VType y, VType z, VType size );

    	bool canCullCube(VType x, VType y, VType z, VType halfWidth);

    private:

    	// This holds the A B C and D values for each side of our frustum.
    	VType m_Frustum[6][4];
    };

    #endif //_FRUSTUM_H

    //
    // Ben Humphrey (DigiBen)
    // Game Programmer
    // DigiBen@GameTutorials.com
    // Co-Web Host of www.GameTutorials.com
    //

    ¡¡

    file name: plyModel.h

    #ifndef PLYMODEL_H
    #define PLYMODEL_H

    #include "octree.h"
    #include <vector>
    #include <string>

    #ifndef LINUX_VERSION
    #include <windows.h>
    #endif

    using namespace std;

    struct PlyModel
    {
    	int vNumber;//internal usage only
    	int fNumber;//internal usage only
    	PlyVertex* vertex;
    	UIVector* triangle;
    	//int vertexCount;
    	//int faceCount;
    	VVector maxCoord;
    	VVector minCoord;
    	VType maxEdge;

    	//void retrievePlyData(char* dirName, Octree* octree, int expandNumber, bool toConvert);

    	bool isValidTriangle(UIVector& myFace);

    	void addToOctree(char* dirName, int expandNumber);

    	void doAddToOctree(const char* fileName, int expandNumber);

    	void addToOctree(char* dirName, VType width, int expandNumber=1);

    	void doAddToOctree(const char* fileName, VType width, int expandNumber);

    	void divideFileByFour(HANDLE vertexHandles[4], HANDLE faceHandles[4]);

    	void getTriangleCenter(PlyVertex triangle[3], PlyVertex& center);

    	void getMiddleVertex(PlyVertex* old1, PlyVertex* old2, PlyVertex* result);

    	//void getVertexFromIndex(HANDLE vertexHandle, int index, PlyVertex& plyVertex);
    	void expandModel();
    	void calcLongestEdge(UIVector tri);
    	bool analysis(char* buf);
    	//int readFile(const char* fileName, int expandNumber);
    	int doReadFile(const char* fileName);
    	int convertFile(const char* fileName);
    	//void readDirectory(char* dir, int expandNumber, bool toConvert=false);

    	//void outputTriangle(int triangleIndex);
    	//bool equalVertex(int index1, int index2);
    	//bool validTriangle(int triangleIndex);

    };

    void mergeData(char* dirName, int expandTimes=0);
    void mergeData(char* dirName, VType width, int expandNumber=1);
    void sortTriangle(UIVector triangle);
    void sortTriangle(FaceStruct& triangle);
    void retrievePlyData(char* dirName, Octree* octree, int expandNumber, bool toConvert);

    #endif

    ¡¡

    file name: dataStructure.h

    /*****************************************************************************************
    author: qingzhe huang
    date: Jul.31, 2007
    1. The trick of "MPIContext" structure is that it is a structure with variable length. i.e.
    For each frame, you don't know how many "octree node index" are used to represent total model.
    This is a bit inconvenient in networking cause you always expect to send something fixed size.
    (Maybe this is not the appropriate term. Anyway it is inconvenient for other modules.)
    So, within MPIContext structure I set another structure to give information of variable length
    index set array. It is "RenderContext" structure.
    2.DepthBufferAtomSize should be checked such that ScreenSize is multiple times of its value.
    Otherwise screen is not properly divided into "tiles".

    ****************************************************************************************/

    #ifndef DATASTRUCTURE_H
    #define DATASTRUCTURE_H

    #include "config.h"

    #include <mpi.h>

    void getLastErrorMsg();

    #ifdef DEBUGGING
    #define DEBUG_INFO(x) printf(x);
    #else
    //this is a tricky question, if people use lazy "if (x) DEBUG(y) z;"
    //instead of safer "if (x) { DEBUG(y) } z;"
    #define DEBUG_INFO(x)   ;
    #endif

    static int MPI_error_code=MPI_SUCCESS;
    #ifdef DEBUGGING
    #define SAFE_CALL(x) if ((MPI_error_code=x)!=MPI_SUCCESS) getLastErrorMsg();
    #else
    #define SAFE_CALL(x) x;
    #endif

    const int DepthBufferAtomWidth=100;
    const int DepthBufferAtomSize=DepthBufferAtomWidth*DepthBufferAtomWidth;
    //const int DepthBufferSnapShotUpperSize=15;
    const int DepthBufferSnapShotWidth=ScreenSize/DepthBufferAtomWidth;
    //const int DepthBufferSnapShotNumber=DepthBufferSnapShotUpperSize+32;//let's show the running result
    const int DepthBufferSnapShotNumber=DepthBufferSnapShotWidth*DepthBufferSnapShotWidth;//let's show the running result

    //const int DepthBufferTileRequestsNumber=DepthBufferSnapShotNumber*TesterCount;

    const int RENDER_CONTEXT=13000;
    const int VISIBLE_PIXEL_INFO=14000;
    const int PIXEL_DATA=15000;
    const int DEPTH_BUFFER_SNAP_SHOT=16000;

    struct RenderContext
    {
    	unsigned char key;
    	unsigned char reserved0;
    	unsigned char reserved1;
    	unsigned char reserved2;
    	int frameNumber;
    	//int menuChoice;

    	//VType fMat[16];
    	int indexCount;
    	int renderShare[RenderCount];
    	int testerShare[TesterCount];
    };

    const int MaxRenderContextBufferSize=sizeof(RenderContext)+MaxPossibleNodeCollectable*2*sizeof(int);

    enum NodeStatus
    {
    	MASTER, RENDER, TESTER, DISPLAYER
    };

    //common for every node
    //keyboard should not modify directly the "renderContext->startMPI" flag.
    //Instead modify "keyboardStartMPIFlag".
    //similarly keyboard should not modify "
    struct MPIContext
    {
    	int myRank;
    	int mySize;
    	NodeStatus myNodeStatus;
    	bool startMPI;
    	unsigned char pendingKey; //

    	static char* renderContextBuffer;

    	MPI_Request* contextRequests;
    	RenderContext* renderContext;
    	int renderContextSize;
    	int* renderShareArray;
    	int* testerShareArray;

    	void recvRenderContext();
    	void testRenderContextSending();
    	void sendRenderContext();
    	void testRenderContextRecving();

    	void quit();
    	void init();
    };

    //static MPIContext mpiContext;
    int decompressBuffer(ubyte* source, int length, ubyte* target, int maxBufferSize);
    int compressBuffer(ubyte* source, int length, ubyte* target, int maxBufferSize);

    #endif

    ¡¡

    file name: octree.h

    /**********************************************************************
    author: qingzhe huang
    date: Jul. 31, 2007
    *********************************************************************/
    #ifndef OCTREE_H
    #define OCTREE_H

    #include <cstdio>
    #include <cstdlib>
    #include <vector>
    #include "syscall.h"
    #include "config.h"

    #ifndef LINUX_VERSION
    #include <windows.h>
    #else
    #define LPVOID void*
    #define INVALID_HANDLE_VALUE 0xFFFFFFFF
    #define LONGLONG long long
    #endif

    using namespace std;

    const VType MinOctreeNodeWidth=1.0;
    const int NodeArrayLengthIncrement=1024;

    #define NVector FVector

    typedef unsigned char UCVector[4];
    typedef unsigned int UIVector[3];

    struct PlyVertex
    {
    	VVector coord;
    	UCVector color;
    	NVector normal;
    	void display()
    	{
    		printf("coord[%f,%f,%f]\n", coord[0], coord[1], coord[2]);
    	}
    };

    static bool equalCoord(VVector left, VVector right)
    {
    	return left[0]==right[0]&&left[1]==right[1]&&left[2]==right[2];
    }

    struct PlyFace
    {
    	unsigned char number;
    	int* indexList;
    	UCVector color;
    };

    struct VertexInfo
    {
    	int sonPos;
    	int offset;
    };

    typedef vector<VertexInfo> HashTableNode;
    typedef vector<PlyVertex> VertexVector;

    struct FaceStruct
    {
    	UIVector face;
    	unsigned int& operator[](int index)
    	{
    		return face[index];
    	}
    	void operator=(UIVector triangle)
    	{
    		memcpy(face, triangle, sizeof(UIVector));
    	}
    	void display()
    	{
    		printf("face[%d,%d,%d]\n", face[0], face[1], face[2]);
    	}
    };

    typedef vector<FaceStruct> FaceVector;

    void sortTriangleIndex(unsigned int triangle[3]);

    struct InfoStruct
    {
    	int nodeCount;
    	int totalVPageCount;
    	int totalFPageCount;
    	int nodeSize;
    	int maxLeafNodeVertexNumber;
    	VType maxEdge;
    	VType minEdge;
    	VType minX, maxX, minZ, maxZ;
    	VVector origin;
    	int totalNeighbourCount;
    	int maxDepth;
    	unsigned int totalRepeatFace;
    	unsigned int totalRepeatVertex;
    	int leafNodeNumber;
    	int maxNodeArrayLength;
    	unsigned long int totalVertex;
    	unsigned long int totalFace;
    	InfoStruct();

    	static char vertexFileName[MAX_PATH];
    	static char faceFileName[MAX_PATH];
    	static char fakeColorFileName[MAX_PATH];
    	static char infoFileName[MAX_PATH];
    	static char octreeFileName[MAX_PATH];
    	void display();
    	void display(FILE* stream);
    };

    struct Octree
    {

    	////////////////////////////////////
    	//general configuration data

    	static InfoStruct infoStruct;

    	static VType operations[8][3];

    	enum OctreeNodeEnum
    	{
    		TOP_LEFT_FRONT =2, TOP_LEFT_BACK =3,
    		TOP_RIGHT_BACK =7, TOP_RIGHT_FRONT =6,
    		BOTTOM_LEFT_FRONT =0, BOTTOM_LEFT_BACK =1,
    		BOTTOM_RIGHT_BACK =5, BOTTOM_RIGHT_FRONT =4
    	};

    	////////////////////////////////////////////
    	//utility temporary data
    	static Octree* root;
    	static HANDLE vertexHandle, faceHandle,	octreeHandle, infoHandle;
    	//static HANDLE pseudoVertexHandle, pseudoFaceHandle;
    	/////////////////////////////////////////////////////////////////////

    	/////////////////////////////////////////////////////////////////////
    	//these are user method
    	static void uninitialize(bool readOnly);
    	static void initialize(bool readOnly, bool useFake=false);
    	static void addToOctree(char* inVertexFileName, char* inFaceFileName);

    	static void readInfo(bool readOnly);

    	static void addToOctree(VertexVector& inVertexVector, FaceVector& inFaceVector);

    	///////////////////////////////////////////////////////////////////////////////
    	//utility methods and can be used by user
    	static int relativePosition(VVector vPoint, VVector vCtr);
    	static void setSonNodeCentre(int parentIndex, int sonIndex, int nodeID);
    	static int createNewOctreeNode(int myOwnIndex, int nodeID);

    	static void initOctreeNode(int nodeIndex);
    	static bool isCoordInsideBox(int nodeIndex, VVector coord);
    	static void setFilePointerF(int nodeIndex, int offset);
    	static void setFilePointerV(int nodeIndex, int offset);
    	static void calculateFileOffsetF(int startPageIndex, int offset, long& low, long& high);
    	static void calculateFileOffsetV(int startPageIndex, int offset, long& low, long& high);
    	static void calculateEdges(FaceStruct& tri, VertexVector& vertexVector);
    	static VType calcEdge(VVector v1, VVector v2);
    	////////////////////////////////////////////////////////////////////////////////////////////
    	//internal methods and should never be called by user
    	static void readDataFromFile(HANDLE vertexHandle, VertexVector& vertexVector);
    	static void readDataFromFile(HANDLE faceHandle, FaceVector& vertexVector);
    	static void addVertexToOctree(HANDLE outVertexHandle, VertexVector& vertexVector, int nodeIndex);
    	static void addFaceToOctree(HANDLE outFaceHandle, FaceVector& faceVector, int nodeIndex);
    	static void addFaceToSonFaceVector(HashTableNode* hashTable, FaceStruct& triangle,
    		VertexVector& vertexVector, VertexVector& repeatVertexVector,
    		VertexVector* sonVertexVector[8], VertexVector* sonRepeatVertexVector[8],
    		FaceVector* sonFaceVector[8], FaceVector* sonRepeatFaceVector[8]);

    	static void readOriginalFace(int nodeIndex, FaceVector& faceVector,
    			FaceVector& repeatFaceVector, int insertVertexCount, int insertRepeatVertexCount);
    	static void readOriginalVertex(int nodeIndex, VertexVector& vertexVector,
    								VertexVector& repeatVertexVector, FaceVector& repeatFaceVector);
    	static void expandRootNodeBounding(VVector coord, bool freeExpand);
    	static void doAddToOctree(int nodeIndex, VertexVector& vertexVector,
    		VertexVector& repeatVertexVector, FaceVector& faceVector, FaceVector& repeatFaceVector);

    	static void calculateNeighbour();
    	static void doCalculateNeighbour(int nodeIndex, int neighbourIndex);
    	static bool addVertexInfoToHashTable(HashTableNode* hashTable, int vIndex, int sonPos,
    									  int& offset);
    	static void doCreateLeafNode(int nodeIndex, VertexVector& vertexVector,
    		VertexVector& repeatVertexVector, FaceVector& faceVector, FaceVector& repeatFaceVector);
    	static void distributeVertex(int nodeIndex, HashTableNode* hashTable, VertexVector& vertexVector,
    				VertexVector& repeatVertexVector, VertexVector* sonVertexVector[8]);

    	static void doAddToOctreeNodePreparation(HashTableNode*& hashTable, int hashTableSize, VertexVector* sonVertexVector[8],
    		VertexVector* sonRepeatVertexVector[8], FaceVector* sonFaceVector[8], FaceVector* sonRepeatFaceVector[8]);

    	static void doAddToOctreeWrapper(int nodeIndex, int vertexCount, int repeatVertexCount, int faceCount,
    		int repeatFaceCount, HANDLE vertexHandle, HANDLE faceHandle);
    	//internal methods end
    	/////////////////////////////////////////////////////////////////////////////

    	static void sortTriangle(FaceStruct& triangle);

    	////////////////////////////////////////////////////
    	//the following are all data member of octree node, some are only meaningful for leaf node
    	VVector center;//0
    	VType width;//The full width//3
    	int vNumber;//4
    	int vRepeatNumber;//5
    	int fNumber;      //6
    	int fRepeatNumber;//7
    	int myNodeIndex;  //8
    	int parentIndex;  //9
    	int sons[8];      //10
    	int vStartPageIndex;//vertex//18
    	int vPageCount;	         //19
    	int fStartPageIndex;//face //20
    	int fPageCount;        //21
    	long vFileAddLow;      //22
    	long vFileAddHigh;		//23
    	long fFileAddLow;		//24
    	long fFileAddHigh;		//25
    	int vMapSize;			//26
    	int fMapSize;			//27
    	LPVOID vAddress;		//28
    	LPVOID fAddress;		//29
    	unsigned char sonCount;
    	unsigned char level;
    	unsigned char neighbourCount;
    	int neighbours[MaxNeighbourCount];//maximum 26 neighbours

    };

    #endif

    file name: octreeLoader.h

    #ifndef OCTREELOADER_H
    #define OCTREELOADER_H

    #include "octree.h"
    #include "config.h"
    #include "frustum.h"
    #include <cstdio>
    #include <set>

    using namespace std;

    typedef set<int> OctreeNodeSet;
    //const int MaxVertexCapacity=512*1024;
    //const int MaxFaceCapacity=512*1024;

    ////////////////////////////////////////////////////////////////////////////
    //begin of expand by 8

    struct OctreeShiftDescriptor
    {
    	int levelOffset;
    	VVector newCenter;
    	VVector relativeOps;
    	int indexOffset;
    	int vPageOffset;
    	int fPageOffset;
    };
    void getLastError();

    void vectorMultiply(VVector v1, VVector v2, VVector result);
    void vectorScalor(VVector v1, VType scalor);

    void vectorScalor(VVector v1, VType scalor, VVector result);

    void vectorMinus(VVector v1, VVector v2, VVector result);
    void vectorPlus(VVector v1, VVector v2, VVector result);

    struct OctreeLoader
    {
    	//static HANDLE loadingThread;
    	//static HANDLE mutex;
    	//static HANDLE event;
    	//static CRITICAL_SECTION criticalSection;
    	//static bool canTerminate;
    	//static int ignoredLevel;
    	static HANDLE vertexMapHandle, faceMapHandle;
    	//PageManager vertexManager, faceManager;
    	static int currentIndex, nextIndex;
    	static OctreeNodeSet nodeSet[MaxCoherentFrameNumber];
    	//OctreeNodeQueue nodeQueue[2];
    	static OctreeNodeSet loadedNodeSet;
    	//static OctreeNodeQueue loadingQueue;
    	//static bool usingThread;

    	static unsigned int loadedVertexCounter, loadedFaceCounter;
    	static int capacityFactor;

    	void addCandidate(int index);
    	void addCandidates(int count, int* array);

    	//void printPriority();

    	////////////////////////////////////////////////////////////////////////
    	//this is a utility for shift-expanding of octree
    	static void copyOctreeLeafNode(Octree* refOctree, Octree* targetOctree, OctreeShiftDescriptor& octreeShift);
    	static void calcOctreeShiftDescriptor(OctreeShiftDescriptor& octreeShift, int i);
    	static void calcNewOctreeRoot(Octree& newRoot);

    	//This is a bit tricky,
    	//bool leafNodeContainPoint(int index, FVector origin, OctreeNodeQueue& result);

    	//void waitLoadingThread();

    	static void unmapUnusedNodes();
    	static void mapOctreeNode(int index);
    	static bool doMapOctreeNode(int index, bool readOnly=true);

    	static void paintPseudoColor();

    	static void expandBy8();

    	static void readInfo();

    	void displayOctreeLeafNode(int index);

    	//OctreeLoader();
    	static void initialize(bool readOnly=true, bool fakeColor=false);
    	static void uninitialize();
    	static Octree* root;
    	//~OctreeLoader();
    	//void calculateOctree(int nodeIndex);

    	void swapBuffer();
    };

    #endif

    ¡¡

    file name: renderEngine.h

    #ifndef RENDERENGINE_H
    #define RENDERENGINE_H
    #include "config.h"
    #include "octreeLoader.h"

    #define PI 3.1415926535897932384626433832795

    const double DefaultNearPlane=0.001;
    //static int frameCounter=0;
    //static int lastFrameCounter=0;
    //static unsigned long lastTickCounter=0;

    //float pos_x=0.0, pos_y=0.0, pos_z=0.0;

    extern void convertPlyFile(char* dirName, bool toExpand, bool toConvert=false);

    //void updateDrawingContext();

    void createFiles(char* dirName, int expandNumber, bool toConvert=false);
    //inline VType deg2rad(VType deg);
    //inline VType rad2deg(VType rad);

    //const VType AngleOffset=5;
    // VType PositionOffset=10.1235;
    //const GLfloat PositionOffset=0.01;
    //const VType MovementOffset=10.1235;
    //const GLfloat MovementOffset=0.01;

    //void calcCoord(VType distance, const Movement& position, VType &x, VType &y, VType &z);
    //void drawOctree();

    //static Movement movement;

    //static PlyVertex* vertexArray=NULL;
    //static UIVector* faceArray=NULL;
    //static PlyModel myPly;

    //void simpleInit();
    //void simpleDisplay();

    //static FVector StartingPos={-0.037830, 0.127940, -0.995525};
    //static

    struct DrawingContext
    {
    	VVector eye;
    	VVector center;
    	VVector up;
    	VType	fovy;
    	VType	aspect;
    	VType	zNear;
    	VType	zFar;
    };

    struct Movement
    {
    	VType x,y,z;
    	VType yaw, pitch, roll;
    	void display();
    };

    struct Lighting
    {
    	enum ColorName
    	{
    		BLACK, RED, GREEN, BLUE, YELLOW, MAGENTA, PINK, WHITE
    	};

    	int currentLightNumber;//=8;
    	bool lightEnabled;//=false;
    	void init();
    	void toggleLighting();
    	void update(const Movement& movement);
    };

    struct RenderEngine
    {
    	Movement movement;
    	DrawingContext drawingContext;
    	Lighting lighting;

    	VVector startingPos;//={-100, 25500, 100};
    	VType positionOffset;//=10.1235;
    	VType angleOffset;//=5;
    	VType movementOffset;//=10.1235;
    	bool flatShading;//=true;
    	bool wireFrame;

    	void setupPerspective(OctreeLoader* octreeLoader);
    	void updatePosition();

    	void initializeMovement(VVector origin);

    	void showMenu();
    	inline VType deg2rad(VType deg);
    	inline VType rad2deg(VType rad);
    	void calcCoord(VType distance, const Movement& position, VType &x, VType &y, VType &z);
    	//void initLights();

    	void init(OctreeLoader* octreeLoader);

    	void keyboardCallback(unsigned char key, int x, int y);
    	void specialCallback(int key, int x, int y);

    };

    #endif

    ¡¡

    file name: master.h

    /*************************************************************************************
    file name: master.h
    author: qingzhe huang
    date: Jul. 21, 2007
    This is a combination of "Master" and "Displayer" module which maybe needs to be separated
    in other situation. i.e. It runs a too heavy job as a role of two.

    *************************************************************************************/
    #pragma once

    #include "config.h"
    #include "dataStructure.h"
    #include "viewCollector.h"
    #include "renderEngine.h"
    #include <vector>

    using namespace std;

    struct MasterContext
    {
    	RenderEngine renderEngine;
    	OctreeLoader octreeLoader;
    	ViewCollector viewCollector;
    	static char* windowTitle;
    	int renderPixelCount[RenderCount+1];
    	ubyte* renderPixelPtr[RenderCount];

    	ubyte* colorBuffer;
    	ubyte* pixelDataBuffer;
    	ubyte* visibleTable;
    	//////////////////////////////////////
    	//use this as receiving buffer for compressed visible table
    	ubyte* compressedVisibleTable;
    	////////////////////////////////////

    	MPI_Request* visibleTableRequests;
    	MPI_Request* pixelDataRequests;

    	MPI_Request naiveVisibleTableRequest;

    	vector<int>* renderVector;
    	vector<int>* testerVector;
    	int* renderShareCount;
    	int* testerShareCount;
    	void init();
    	void uninit();
    	void doKnapsack(int count, int* array, NodeStatus nodeStatus);
    	void doKnapsack(int count, int* array, int partyNumber, int* shareArray, int* shareCount,
    				vector<int>* shareVector, int* output);
    	void knapsack(int count, int* array, bool doSimple=true);
    	void keyboardCallback(unsigned char key, int x, int y);

    	void displayCallback();

    	void recvVisiblePixelInfo();
    	void testVisiblePixelInfoRecving();

    	void displayerPrepareRecvPixelData();
    	void recvPixelData();
    	void testPixelDataRecving();

    	void outputColorBuffer();
    	void recvNaiveVisiblePixelInfo();
    };

    static MasterContext masterContext;

    ¡¡

    file name: tester.h

    #pragma once

    #include "config.h"
    #include "dataStructure.h"
    #include "octreeLoader.h"
    #include "renderEngine.h"
    #include <mpi.h>

    struct TesterContext
    {
    	static char* windowTitle;
    	int myTesterIndex;
    	RenderEngine renderEngine;
    	OctreeLoader octreeLoader;
    	MPI_Request** visibleTableRenderRequests;
    	MPI_Request* visibleTableDisplayerRequests;
    	MPI_Request* snapShotSendingRequests;
    	MPI_Request* snapShotRecvingRequests;
    	MPI_Request** depthBufferExchangeRequests;
    	MPI_Request** visibleTableExchangeRequests;

    	int* snapShotCandidate;
    	float** snapShots;
    	float** depthBuffer;
    	ubyte** visibleTable;
    	ubyte* colorBuffer;
    	ubyte* renderIndexHashTable;
    	///////////////////////////////
    	//add an compression buffer for visible table
    	ubyte* compressedVisibleTable;

    	//return the length of compressed run-length
    	int compressVisibleTable(ubyte*source, int tileIndex);
    	void decompressVisibleTable(ubyte*target, int tileIndex, int count);
    	/////////////////////////////////
    	void init();

    	void keyboardCallback(unsigned char key, int x, int y);

    	void displayCallback();

    	void readColorBuffer();
    	void readDepthBuffer();
    	void createRenderIndexHashTable();

    	void processDepthBuffer();
    	void exchangeDepthBufferSnapShot();
    	void exchangeDepthBuffer();
    	void testDepthBufferExchanging();

    	void sendVisiblePixelInfo();
    	void testVisiblePixelInfoSending();
    	void doSendExchangeDepthBuffer(int snapShotIndex, int targetTester);
    	void doRecvExchangeDepthBuffer(int snapShotIndex, int sourceTester);
    };

    static TesterContext testerContext;

    ¡¡

    file name: render.h

    #pragma once

    #include "config.h"
    #include "dataStructure.h"
    #include "octreeLoader.h"
    #include "renderEngine.h"
    #include <mpi.h>
    #include <GL/glut.h>

    struct RendererContext
    {
    	static char* windowTitle;
    	int myRenderIndex;

    	OctreeLoader octreeLoader;
    	RenderEngine renderEngine;

    	ubyte* colorBuffer;
    	ubyte* pixelDataBuffer;
    	ubyte* visibleTable;
    	//////////////////////////////////////
    	//compressed visible table
    	ubyte* compressedVisibleTable;
    	/////////////////////////////////////

    	MPI_Request* visibleTableRequests;
    	MPI_Request* pixelDataRequests;
    	MPI_Request naiveVisibleTableRequest;

    	void init();

    	void displayCallback();

    	void readColorBuffer();

    	void recvVisiblePixelInfo();
    	void testVisiblePixelInfoRecving();

    	void testPixelDataSending();
    	void sendPixelData();
    	void recvNaiveVisiblePixelInfo();

    };

    static RendererContext rendererContext;

    ¡¡

    ¡¡

    file name: viewCollector.h

    #ifndef VIEWCOLLECTOR_H
    #define VIEWCOLLECTOR_H

    #include "octree.h"
    #include "octreeLoader.h"
    #include "config.h"
    #include "frustum.h"
    #include <vector>
    #include <queue>

    typedef vector<int> OctreeNodeVector;

    struct ViewCollector
    {
    	typedef deque<int> OctreeNodeQueue;
    	static Octree*root;

    	CFrustum frustum;

    	OctreeNodeVector octreeNodeVector;

    	static int vertexCounter, faceCounter;
    	OctreeNodeSet searchSet;
    	OctreeNodeQueue searchQueue;
    	//static bool conservativeMode;
    	static bool fullRenderMode;
    	static bool hierRenderMode;

    	void doCollectLeafNode(int index);//a BFS

    	void collectHierLeafNode(int index);
    	void collectLeafNode(VVector origin);//collect leaf nodes from near to far within capacity
    	bool getStartingNodes(int index, VVector origin, OctreeNodeQueue& result);
    	bool testAndAdd(int index);

    	bool checkCapacity();
    	void addCandidate(int index);
    	void startCollecting(VVector origin);

    	void initialize(Octree* theRoot){root=theRoot;}

    };

    #endif

    ¡¡

    ¡¡

    file name: syscallLinxu.cpp

    #include "syscall.h"

    #ifdef LINUX_VERSION
    #define __USE_FILE_OFFSET64 1
    #include <cstdio>
    #include <cstdlib>
    #include <dirent.h>
    #include <string>
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <sys/time.h>

    using namespace std;

    long int GenericGetTime()
    {
    	struct timeval tv;
    	long int result;
    	LINUX_SYSCALL(gettimeofday(&tv, NULL), "gettimeofday")
    	result=tv.tv_sec*1000+tv.tv_usec/1000;
    	return result;
    }

    HANDLE GenericOpenFile(char* fileName, bool readOnly)
    {
    	int flag=S_IWUSR|S_IRUSR|S_IROTH|S_IWOTH;
    	int accessRight=readOnly?O_RDONLY|O_LARGEFILE:O_RDWR|O_CREAT;
    	HANDLE result;
    	if (readOnly)
    	{
    		result=open(fileName, accessRight);
    	}
    	else
    	{
    		result=open(fileName, accessRight, flag);
    	}
    	LINUX_SYSCALL(result, "open file error");
    	return result;
    }

    int GenericWriteFile(HANDLE fileHandle, void* buffer, size_t length)
    {
    	int result=write(fileHandle, buffer, length);
    	LINUX_SYSCALL(result, "write file error");
    	return result;
    }

    int GenericReadFile(HANDLE fileHandle, void* buffer, size_t length)
    {
    	int result;
    	result=read(fileHandle, buffer, length);
    	LINUX_SYSCALL(result, "read file error");
    	return result;
    }

    const char* Pattern="temp.XXXXXX";

    HANDLE GenericOpenTempFile(const char* nameString, char* buf)
    {
    /*
    	HANDLE result;
    	if (!tmpnam(buf+2))
    	{
    		perror("create temp file name");
    		exit(123);
    	}
    	buf[0]='.';
    	buf[1]='/';
    	result=open(buf, O_RDWR|O_CREAT|O_LARGEFILE);
    	LINUX_SYSCALL(result, "open temp file");
    	return result;
    */
    	HANDLE result;
    	strcpy(buf, Pattern);
    	result=mkstemp(buf);
    	LINUX_SYSCALL(result, "create temp file");
    	return result;
    }

    void GenericCloseFile(HANDLE fileHandle)
    {
    	LINUX_SYSCALL(close(fileHandle), "close file");
    }

    void GenericCloseTempFile(HANDLE fileHandle, const char* fileName)
    {
    	LINUX_SYSCALL(close(fileHandle), "close file");
    	LINUX_SYSCALL(unlink(fileName), "unlink temp file");
    }

    void GenericSetFilePointer(HANDLE fileHandle, long int low, long int high)
    {
    	long long offset=0;
    	offset=high;
    	offset<<=32;
    	offset+=low;
    	LINUX_SYSCALL(lseek(fileHandle, offset, SEEK_SET), "set file pointer");
    }

    HANDLE GenericCreateFileMapping(HANDLE fileHandle, bool readOnly)
    {
    	return fileHandle;
    }

    void* GenericMapViewOfFile(HANDLE fileMappingHandle, bool readOnly, int fileAddHigh, int fileAddLow, int length)
    {
    	void* result;
    	int accessRight=readOnly?PROT_READ:PROT_READ|PROT_WRITE;
    	long long offset=0;
    	offset=fileAddHigh;
    	offset<<=32;
    	offset+=fileAddLow;
    	//offset>>=12;//this is because mmap2 uses 4k as offset to represent large file.
    	//this mmap2 must be Unix instead of linux
    	result=mmap(0, length, accessRight, MAP_SHARED, fileMappingHandle, offset);
    	//LINUX_SYSCALL(result, "map view of file");
    	if (result==NULL)
    	{
    		perror("map view of file");
    		exit(1234);
    	}
    	return result;
    }

    void GenericUnmapViewOfFile(void* address, int length)
    {
    	LINUX_SYSCALL(munmap(address, length), "unmap view of file");
    }

    void GenericRetrieveAllFile(char* dirName, vector<string>& fileNameVector)
    {
    	struct stat buf;
    	char curFileName[256];
    	int nameLen;

    	DIR* dirPtr;
    	struct dirent* direntPtr;

    	LINUX_SYSCALL(lstat(dirName, &buf), "read stat");

    	if (S_ISREG(buf.st_mode))
    	{
    		fileNameVector.push_back(string(dirName));
    	}
    	else
    	{
    		if (S_ISDIR(buf.st_mode))
    		{
    			if ((dirPtr=opendir(dirName))==NULL)
    			{
    				perror("read dir");
    				exit(1234);
    			}
    			nameLen=strlen(dirName);
    			strcpy(curFileName, dirName);
    			while ((direntPtr=readdir(dirPtr))!=NULL)
    			{
    				if (strcmp(direntPtr->d_name, ".")!=0&&strcmp(direntPtr->d_name, "..")!=0)
    				{
    					curFileName[nameLen]='/';
    					strcpy(curFileName+nameLen+1, direntPtr->d_name);
    					GenericRetrieveAllFile(curFileName, fileNameVector);
    				}
    			}
    		}
    	}
    }

    void GenericCloseFileMapping(HANDLE fileMappingHandle)
    {
    	//do nothing, actually this is only to suit windows api.
    }

    #endif

    ¡¡

    file name: syscallWindow.cpp

    #include "syscall.h"
    #include <windows.h>

    #ifndef LINUX_VERSION

    const int MaxTempNameStringLength=8+4+1;

    unsigned long int temp;

    char tempName[MaxTempNameStringLength]=

    {

    	'A','A','A','A','A','A','A','A','.','t','m','p', '\0'
    };

    long int GenericGetTime()
    {
    	return GetTickCount();
    }

    HANDLE GenericOpenFile(char* fileName, bool readOnly)
    {
    	DWORD accessRight=readOnly?GENERIC_READ:GENERIC_READ|GENERIC_WRITE;
    	DWORD createDisposition=readOnly?OPEN_EXISTING:OPEN_ALWAYS;
    	HANDLE result;

    	result=CreateFile(fileName, accessRight, FILE_SHARE_READ, NULL, createDisposition,
    		FILE_ATTRIBUTE_NORMAL, NULL);
    	WINAPI_CALL(result, "open file");

    	return result;
    }

    int GenericWriteFile(HANDLE fileHandle, void* buffer, size_t length)
    {
    	WINAPI_CALL(WriteFile(fileHandle, buffer, length, &temp, NULL), "write file error");
    	return temp;
    }

    //Linux "read" function is more sensible in indicating end of file by returning 0
    //and -1 for error. windows is non-sense in this context.
    int GenericReadFile(HANDLE fileHandle, void* buffer, size_t length)
    {
    	int result=ReadFile(fileHandle, buffer, length, &temp, NULL);
    	WINAPI_CALL(result, "read file");

    	return temp;
    }

    //this is dirty because windows API "GetTempFileName" is written by a naive guy who
    //believe people only generate temp file of total number of 65535
    //while in Linux, they are better, because they all 26^6 which is roughly hundreds of million
    HANDLE GenericOpenTempFile(const char* nameString, char* buf)
    {
    	HANDLE result;
    	int pos=0;
    	while (true)
    	{
    		if (tempName[pos]=='Z')
    		{
    			tempName[pos]='A';
    			pos++;
    		}
    		else
    		{
    			tempName[pos]++;
    			break;
    		}
    	}
    	strcpy(buf, nameString);
    	strcat(buf, tempName);

    	//WINAPI_CALL(GetTempFileName(".", nameString, 0, buf), "create temp file name");

    	//result=CreateFile(buf, GENERIC_READ|GENERIC_WRITE, 0, NULL,
    	//			OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    	result=CreateFile(buf, GENERIC_READ|GENERIC_WRITE, 0, NULL,
    				CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
    	WINAPI_CALL(result, "create temp file");
    	return result;
    }

    void GenericCloseFile(HANDLE fileHandle)
    {
    	WINAPI_CALL(CloseHandle(fileHandle), "close file");

    }

    void GenericCloseTempFile(HANDLE fileHandle, const char* fileName)
    {
    	WINAPI_CALL(CloseHandle(fileHandle), "close temp file");
    	WINAPI_CALL(DeleteFile(fileName), "delete temp file");
    }

    void GenericSetFilePointer(HANDLE fileHandle, long int low, long int high)
    {
    	if (SetFilePointer(fileHandle, low, &high, FILE_BEGIN)==0xFFFFFFFF)
    	{
    		printf("error of %s #%d\n", "set file pointer", GetLastError());
    		exit(123);
    	}
    }

    HANDLE GenericCreateFileMapping(HANDLE fileHandle, bool readOnly)
    {
    	DWORD accessRight=readOnly?PAGE_READONLY:PAGE_READWRITE;
    	HANDLE result;
    	result=CreateFileMapping(fileHandle, NULL, accessRight, 0, 0, NULL);

    	WINAPI_CALL(result, "create file mapping");
    	return result;
    }

    void GenericCloseFileMapping(HANDLE fileMappingHandle)
    {
    	WINAPI_CALL(CloseHandle(fileMappingHandle), "close file mapping");
    }

    void* GenericMapViewOfFile(HANDLE fileMappingHandle, bool readOnly, int fileAddHigh, int fileAddLow, int length)
    {
    	DWORD accessRight=readOnly?FILE_MAP_READ:FILE_MAP_WRITE;
    	void*result;
    	result=MapViewOfFile(fileMappingHandle, accessRight, fileAddHigh, fileAddLow, length);
    	WINAPI_CALL(result, "map file");
    	return result;
    }

    void GenericUnmapViewOfFile(void* address, int length)
    {
    	WINAPI_CALL(UnmapViewOfFile(address), "unmap view of file");
    }

    void GenericRetrieveAllFile(char* dir, vector<string>& fileNameVector)
    {
    	HANDLE handle;
    	char curFileName[256];
    	char wildFileName[256];

    	WIN32_FIND_DATA ffd;

    	sprintf(wildFileName, "%s\\*.*", dir);
    	handle=FindFirstFile(wildFileName, &ffd);

    	if (handle==INVALID_HANDLE_VALUE)
    	{
    		printf("findfirst failed of error code =%d\n", GetLastError());
    		exit(19);
    	}

    	while (FindNextFile(handle, &ffd))
    	{
    		if (strcmp(ffd.cFileName, "..")!=0)
    		{
    			sprintf(curFileName, "%s\\%s", dir, ffd.cFileName);
    			if  (GetFileAttributes(curFileName)&FILE_ATTRIBUTE_DIRECTORY)
    			{
    				GenericRetrieveAllFile(curFileName, fileNameVector);
    			}
    			else
    			{
    				fileNameVector.push_back(string(curFileName));
    			}
    		}
    	}
    	FindClose(handle);
    }

    #endif

    ¡¡

    file name: dataStructure.cpp

    #include <mpi.h>
    #include "config.h"
    #include "dataStructure.h"
    #include <cstdio>
    #include <cstdlib>
    #include <GL/glut.h>
    #include "except.h"
    #include <assert.h>

    extern FILE* dumpPtr;

    char* MPIContext::renderContextBuffer=NULL;

    MPIContext mpiContext;

    #ifdef PROFILING
    bool startTracing=false;
    #endif

    void MPIContext::init()
    {
    	int i;
    	MPI_Comm_size(MPI_COMM_WORLD, &mySize);
    	MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
    	renderContextBuffer=new char[MaxRenderContextBufferSize];
    	CHECK_NEW(renderContextBuffer);
    	renderContext=(RenderContext*)renderContextBuffer;

    	pendingKey=0;
    	startMPI=false;

    	renderContext->indexCount=0;
    	renderContext->frameNumber=0;
    	renderContext->key=0;//default key

    	renderShareArray=testerShareArray=NULL;
    	if (myRank==MasterIndex)
    	{
    		myNodeStatus=MASTER;
    		contextRequests=new MPI_Request[mySize];
    		CHECK_NEW(contextRequests);
    		for (i=0; i<mySize; i++)
    		{
    			contextRequests[i]=MPI_REQUEST_NULL;
    		}
    	}

    	if (myRank>=TesterHostStartIndex&&myRank<TesterHostStartIndex+TesterHostCount)
    	{
    		myNodeStatus=TESTER;
    		contextRequests=new MPI_Request;
    		CHECK_NEW(contextRequests);
    		contextRequests[0]=MPI_REQUEST_NULL;
    	}
    	if (myRank>=RenderHostStartIndex&&myRank<RenderHostStartIndex+RenderHostCount)
    	{
    		myNodeStatus=RENDER;
    		contextRequests=new MPI_Request;
    		CHECK_NEW(contextRequests);
    		contextRequests[0]=MPI_REQUEST_NULL;
    	}
    }

    #ifdef	USING_BROADCAST
    //masterContext will guarantee renderContext->startMpi be modified to be true BEFORE "sendRenderContext"
    //is called.
    void MPIContext::sendRenderContext()
    {
    	//to make sure the "very" first render context is still sending by "synchronized"
    	//so that the receiving party can receive it immediately

    	if (pendingKey!=0)
    	{
    		renderContext->key=pendingKey;
    	}
    	if (startMPI)
    	{
    //////////////////////////////////////////////
    #ifdef PROFILING
    		if (!startTracing)
    		{
    			startTracing=true;
    			MPIHP_Trace_on();
    		}
    #endif
    /////////////////////////////////////////////
    	SAFE_CALL(MPI_Bcast(renderContext, MaxRenderContextBufferSize, MPI_UNSIGNED_CHAR,
    		MasterIndex, MPI_COMM_WORLD));

    	//this will guarantee "master" send first notifying message with "synchoronized method"
    	if (renderContext->key=='B'||renderContext->key=='b')
    	{
    		startMPI=true;
    	}

    	if (renderContext->key=='q'||renderContext->key=='Q')
    	{
    /////////////////////////////////////////////
    #ifdef PROFILING
    		if (startTracing)
    		{
    			MPIHP_Trace_off();
    		}
    #endif
    /////////////////////////////////////////////
    		quit();
    	}
    }
    #else
    //masterContext will guarantee renderContext->startMpi be modified to be true BEFORE "sendRenderContext"
    //is called.
    void MPIContext::sendRenderContext()
    {
    	int i;
    	//to make sure the "very" first render context is still sending by "synchronized"
    	//so that the receiving party can receive it immediately

    	if (pendingKey!=0)
    	{
    		renderContext->key=pendingKey;
    	}

    	if (startMPI)
    	{
    //////////////////////////////////////////////
    #ifdef PROFILING
    		if (!startTracing)
    		{
    			startTracing=true;
    			MPIHP_Trace_on();
    		}
    #endif
    /////////////////////////////////////////////
    		for (i=0; i<mySize; i++)
    		{
    			if (i!=MasterIndex)
    			{
    				//printf("master is about to send render context no. %d to host %d\n", renderContext->frameNumber, i);
    				//i is the rank
    				SAFE_CALL(MPI_Issend(renderContext, renderContextSize, MPI_UNSIGNED_CHAR, i, RENDER_CONTEXT,
    					MPI_COMM_WORLD, contextRequests+i));
    			}
    			//else index remain the same
    		}
    	}
    	else
    	{
    		for (i=0; i<mySize; i++)
    		{
    			if (i!=MasterIndex)
    			{
    				//printf("master is about to send render context no. %d to host %d\n", renderContext->frameNumber, i);
    				//i is the rank
    				SAFE_CALL(MPI_Ssend(renderContext, renderContextSize, MPI_UNSIGNED_CHAR, i, RENDER_CONTEXT,
    					MPI_COMM_WORLD));
    			}
    			//else index remain the same
    		}
    		//printf("master finished sending render context of frame number %d\n", renderContext->frameNumber);
    	}

    	//this will guarantee "master" send first notifying message with "synchoronized method"
    	if (renderContext->key=='B'||renderContext->key=='b')
    	{
    		startMPI=true;
    	}

    	if (renderContext->key=='q'||renderContext->key=='Q')
    	{
    		SAFE_CALL(MPI_Waitall(mySize, contextRequests, MPI_STATUSES_IGNORE));
    		//masterTestRenderContextSending();
    		//masterTestDepthBufferRecving();
    		//masterTestVisiblePixelInfoSending();
    		//masterTestPixelDataRecving();
    		//SAFE_CALL(MPI_Waitall(RenderHostCount, pixelDataRequests, MPI_STATUSES_IGNORE))

    		//SAFE_CALL(MPI_Waitall(TesterCount*2, depthBufferSnapShotRequests, MPI_STATUSES_IGNORE))
    /////////////////////////////////////////////
    #ifdef PROFILING
    		if (startTracing)
    		{
    			MPIHP_Trace_off();
    		}
    #endif
    /////////////////////////////////////////////
    		quit();
    	}
    }
    #endif

    void MPIContext::quit()
    {
    	printf("about to abort on frame number of %d\n", renderContext->frameNumber);
    	if (dumpPtr!=NULL)
    	{
    		fclose(dumpPtr);
    		dumpPtr=NULL;
    	}
    	MPI_Finalize();
    	exit(0);
    }

    #ifdef USING_BROADCAST
    //we have to reset the key value, otherwise it is not an event, but a status!!!!
    void MPIContext::testRenderContextSending()
    {

    	renderContext->frameNumber++;
    	if (pendingKey==renderContext->key)
    	{
    		//this measn the data is already sent
    		pendingKey=renderContext->key=0;
    	}
    }
    #else
    //we have to reset the key value, otherwise it is not an event, but a status!!!!
    void MPIContext::testRenderContextSending()
    {
    	if (startMPI)
    	{
    		SAFE_CALL(MPI_Waitall(mySize, contextRequests, MPI_STATUSES_IGNORE));
    	}
    	//printf("master finished sending render context of frame number %d\n", renderContext->frameNumber);
    	renderContext->frameNumber++;

    	if (pendingKey==renderContext->key)
    	{
    		//this measn the data is already sent
    		pendingKey=renderContext->key=0;
    	}
    }
    #endif

    #ifdef USING_BROADCAST
    void MPIContext::testRenderContextRecving()
    {

    	//printf("host receives rendercontext of frame no. %d\n", renderContext->frameNumber);
    	if (renderContext->key=='q'||renderContext->key=='Q')
    	{
    /////////////////////////////////////////////
    #ifdef PROFILING
    		if (startTracing)
    		{
    			MPIHP_Trace_off();
    		}
    #endif
    /////////////////////////////////////////////
    		//renderTestVisiblePixelInfoRecving();
    		//renderTestPixelDataSending();
    		quit();
    	}
    	renderShareArray=(int*)(renderContextBuffer+sizeof(RenderContext));
    	testerShareArray=(int*)(renderContextBuffer+sizeof(RenderContext)+sizeof(int)*renderContext->indexCount);
    	if (renderContext->key=='b'||renderContext->key=='B')
    	{
    //////////////////////////////////////////////
    #ifdef PROFILING
    		if (!startTracing)
    		{
    			startTracing=true;
    			MPIHP_Trace_on();
    		}
    #endif
    /////////////////////////////////////////////
    		startMPI=true;
    	}
    }
    #else
    void MPIContext::testRenderContextRecving()
    {
    	if (startMPI)
    	{
    		SAFE_CALL(MPI_Wait(contextRequests, MPI_STATUS_IGNORE));
    	}
    	//printf("host receives rendercontext of frame no. %d\n", renderContext->frameNumber);
    	if (renderContext->key=='q'||renderContext->key=='Q')
    	{
    /////////////////////////////////////////////
    #ifdef PROFILING
    		if (startTracing)
    		{
    			MPIHP_Trace_off();
    		}
    #endif
    /////////////////////////////////////////////
    		//renderTestVisiblePixelInfoRecving();
    		//renderTestPixelDataSending();
    		quit();
    	}
    	renderShareArray=(int*)(renderContextBuffer+sizeof(RenderContext));
    	testerShareArray=(int*)(renderContextBuffer+sizeof(RenderContext)+sizeof(int)*renderContext->indexCount);
    	if (renderContext->key=='b'||renderContext->key=='B')
    	{
    //////////////////////////////////////////////
    #ifdef PROFILING
    		if (!startTracing)
    		{
    			startTracing=true;
    			MPIHP_Trace_on();
    		}
    #endif
    /////////////////////////////////////////////
    		startMPI=true;
    	}
    }
    #endif

    #ifdef USING_BROADCAST
    void MPIContext::recvRenderContext()
    {
    	SAFE_CALL(MPI_Bcast(renderContext, MaxRenderContextBufferSize, MPI_UNSIGNED_CHAR,
    		MasterIndex, RENDER_CONTEXT, MPI_COMM_WORLD));
    }
    #else
    void MPIContext::recvRenderContext()
    {
    	if (startMPI)
    	{
    		SAFE_CALL(MPI_Irecv(renderContext, MaxRenderContextBufferSize, MPI_UNSIGNED_CHAR, MasterIndex, RENDER_CONTEXT,
    			MPI_COMM_WORLD, contextRequests))
    	}
    	else
    	{
    		SAFE_CALL(MPI_Recv(renderContext, MaxRenderContextBufferSize, MPI_UNSIGNED_CHAR, MasterIndex, RENDER_CONTEXT,
    			MPI_COMM_WORLD, MPI_STATUS_IGNORE))
    	}
    }
    #endif

    void getLastErrorMsg()
    {
    	char msgBuf[256];
    	int length;
    	if (MPI_error_code!=MPI_SUCCESS)
    	{
    		MPI_Error_string(MPI_error_code, msgBuf, &length);
    		fprintf(dumpPtr, "the mpi error is:%s\n", msgBuf);

    	}
    }

    //a simple run-length compression, return the byte size of compressed buffer, -1 indicating buffer overflow
    int compressBuffer(ubyte* source, int length, ubyte* target, int maxBufferSize)
    {
            int i;
    	int count=0;
            ubyte current[2];//treat ushort as if ubyte[2];
            current[0]=source[0];
            current[1]=1;
            for (i=1; i<length; i++)
            {
                    if (current[0]==source[i])
                    {
                            if (current[1]<255)
                            {
                                    current[1]++;
                            }
                            else
                            {
                                    //the counter is full, we need to write
                                   	memcpy(target+2*count, current, 2);
                           		count++;

    				current[1]=1;//restart
                                    //we don't change current[0]
                            }
                    }
                    else
                    {
                            //start new one after we write
                            memcpy(target+2*count, current, 2);
                           	count++;
                            current[0]=source[i];
                            current[1]=1;
                    }
            }
            //finally we still have to write the last one
    	//lazy checking, only do it once at last, so I don't guarantee buffer is NOT overwritten.
    	assert(count*2<maxBufferSize);
           	memcpy(target+2*count, current, 2);
           	count++;
    	//printf("compressed size %u, rate %f\%\n", count*2, (float)count*2.0*100.0/(float)maxBufferSize);
            return count*2;
    }

    int decompressBuffer(ubyte* source, int length, ubyte* target, int maxBufferSize)
    {
            int i, offset=0;
            for (i=0; i<length; i+=2)
            {
                    memset(target+offset, source[i], source[i+1]);
                    offset+=source[i+1];
            }
    	assert(offset<=maxBufferSize);

    	//lazy checking, so, maybe the buffer is already overwritten. no duty for me.
    	return offset;
    }

    file name: frustum.cpp

    //***********************************************************************//
    //																		 //
    //		- "Talk to me like I'm a 3 year old!" Programming Lessons -		 //
    //                                                                       //
    //		$Author:		DigiBen		digiben@gametutorials.com			 //
    //																		 //
    //		$Program:		Frustum Culling									 //
    //																		 //
    //		$Description:	Demonstrates checking if shapes are in view		 //
    //																		 //
    //		$Date:			8/28/01											 //
    //																		 //
    //***********************************************************************//

    										// Header File For The OpenGL32 Library

    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include <GL/glut.h>
    #include "frustum.h"

    /*****************************************************************
    The Cohen-Sutherland algorithm
    seeing from Z-Axis: horizontal is X-Axis, vertical is Y-Axis

    X: L, R
    Y: U, D
    Z: F, B
    12 DIMENSIONS: THESE WON'T INTERSECT AND SHOULD KEEP MUTUAL EXCLUSIVE
    {LF,	LB,		LU,		LD,		RF,		RD,		RU,		RD,		UF,		UB,		DF,		DB}
    [-X,+Z],[-X,-Z],[-X,+Y],[-X,-Y],[+X,+Z],[+X,-Z],[+X,+Y],[+X,-Y],[+Y,+Z],[+Y,-Z],[-Y,+Z],[-Y,-Z]
    //////////////////////////////////////////////////////////////////////////////////////////////
    PLUS 6 EXTRA DIMENSIONS:
    {LC, RC, UC, DC, FC, BC} THEY WON'T INTERSECT EITHER.

    LUF		|		UF				| RUF
    LUC		|		UC				| RUC
    LUB		|		UB				| RUB
    --------|-----------------------|---------------------------
    		|						|
    		|						|
    		|						|
    LCF		|		CF				|  RCF
    LC		|		C				|  RC
    LCB		|		CB				|  RCB
    		|						|
    		|						|
    		|						|
    		|						|
    --------|-----------------------|-----------------------
    		|						|
    LDF		|		DCF				| RDF
    LDC		|		DC				| RDC
    LDB		|		DCB				| RDB

    /////////////////////////////////////////////////////////////////////
    Seeing from X-Axis: horizontal is Z-Axis, vertical is Y-Axis

    FU		|		U				| BU
    		|						|
    --------|-----------------------|---------------------------
    		|						|
    		|						|
    		|						|
    		|						|
    F		|		C				|  B
    		|						|
    		|						|
    		|						|
    		|						|
    		|						|
    --------|-----------------------|-----------------------
    		|						|
    FD		|		D				| BD
    		|						|

    Seeing from Y-Axis: horizontal is X-Axis, vertical is Z-Axis

    LF		|		F				| RF
    		|						|
    --------|-----------------------|---------------------------
    		|						|
    		|						|
    		|						|
    		|						|
    L		|		C				|  R
    		|						|
    		|						|
    		|						|
    		|						|
    		|						|
    --------|-----------------------|-----------------------
    		|						|
    LB		|		B				| RB
    		|						|

    *****************************************************************/

    // We create an enum of the sides so we don't have to call each side 0 or 1.
    // This way it makes it more understandable and readable when dealing with frustum sides.
    enum FrustumSide
    {
    	RIGHT	= 0,		// The RIGHT side of the frustum
    	LEFT	= 1,		// The LEFT	 side of the frustum
    	BOTTOM	= 2,		// The BOTTOM side of the frustum
    	TOP		= 3,		// The TOP side of the frustum
    	BACK	= 4,		// The BACK	side of the frustum
    	FRONT	= 5			// The FRONT side of the frustum
    };

    // Like above, instead of saying a number for the ABC and D of the plane, we
    // want to be more descriptive.
    enum PlaneData
    {
    	A = 0,				// The X value of the plane's normal
    	B = 1,				// The Y value of the plane's normal
    	C = 2,				// The Z value of the plane's normal
    	D = 3				// The distance the plane is from the origin
    };

    unsigned char CSMasks[6]=
    {
    	0x01, 0x02, 0x04, 0x08, 0x10, 0x20
    };

    //true means the point is outside frustum, false means inside of course
    bool CFrustum::pointInsideFrustum(VType coord[3], unsigned char& code)
    {
    	VType temp;
    	bool result=true;
    	for(int i = 0; i < 6; i++ )
    	{
    		// Calculate the plane equation and check if the point is behind a side of the frustum
    		temp=m_Frustum[i][A] * coord[0] + m_Frustum[i][B] * coord[1] +
    			m_Frustum[i][C] * coord[2] + m_Frustum[i][D];
    		if (temp<-Epsilon)
    		{
    			// The point was behind a side, so it ISN'T in the frustum
    			code|=CSMasks[i];
    			result=false;
    		}
    	}
    	return result;//true means outside
    }

    bool CFrustum::cohenSutherland(unsigned char first, unsigned char second)
    {
    	return (first&second)==0;
    }

    bool CFrustum::canCullCube(VType center[3], VType halfWidth)
    {
    	bool hasPointInside=false;
    	int factors[3]={-1, -1, -1};
    	VType coord[3];
    	int i, j, k, l;
    	unsigned char codes[8]={0};

    	for (i=0; i<2; i++)
    	{
    		factors[0]*=-1;
    		for (j=0; j<2; j++)
    		{
    			factors[1]*=-1;
    			for (k=0; k<2; k++)
    			{
    				factors[2]*=-1;
    				for (l=0; l<3; l++)
    				{
    					coord[l]=center[l]+factors[l]*halfWidth;
    				}
    				//true means we can cull, or at least it has point outside
    				if (pointInsideFrustum(coord, codes[i*4+j*2+k]))
    				{
    					hasPointInside=true;
    				}
    			}
    		}
    	}
    	//at least one point is inside
    	if (hasPointInside)
    	{
    		return false;
    	}
    	else
    	{
    		for (i=0; i<8; i++)
    		{
    			for (j=i+1; j<8; j++)
    			{
    				//any of this test show suspected candidate, we consider negative for culling
    				if ((codes[i]&codes[j]) ==0)
    				{
    					return false;
    				}
    			}
    		}
    	}
    	return true;

    }

    ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    /////	This normalizes a plane (A side) from a given frustum.
    /////
    ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

    void NormalizePlane(VType frustum[6][4], int side)
    {
    	// Here we calculate the magnitude of the normal to the plane (point A B C)
    	// Remember that (A, B, C) is that same thing as the normal's (X, Y, Z).
    	// To calculate magnitude you use the equation:  magnitude = sqrt( x^2 + y^2 + z^2)
    	VType magnitude = (VType)sqrt( frustum[side][A] * frustum[side][A] +
    								   frustum[side][B] * frustum[side][B] +
    								   frustum[side][C] * frustum[side][C] );

    	// Then we divide the plane's values by it's magnitude.
    	// This makes it easier to work with.
    	frustum[side][A] /= magnitude;
    	frustum[side][B] /= magnitude;
    	frustum[side][C] /= magnitude;
    	frustum[side][D] /= magnitude;
    }

    ///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    /////	This extracts our frustum from the projection and modelview matrix.
    /////
    ///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

    void CFrustum::CalculateFrustum()
    {
    	VType   proj[16];								// This will hold our projection matrix
    	VType   modl[16];								// This will hold our modelview matrix
    	VType   clip[16];								// This will hold the clipping planes

    	// glGetVTypev() is used to extract information about our OpenGL world.
    	// Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix.
    	// It then stores the matrix into an array of [16].
    #ifdef USE_DOUBLE_PRECISION
    	glGetDoublev(GL_PROJECTION_MATRIX, proj );
    	glGetDoublev( GL_MODELVIEW_MATRIX, modl );
    #else
    	glGetFloatv( GL_PROJECTION_MATRIX, proj );

    	// By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix.
    	// This also stores it in an array of [16].
    	glGetFloatv( GL_MODELVIEW_MATRIX, modl );
    #endif
    	// Now that we have our modelview and projection matrix, if we combine these 2 matrices,
    	// it will give us our clipping planes.  To combine 2 matrices, we multiply them.

    	clip[ 0] = modl[ 0] * proj[ 0] + modl[ 1] * proj[ 4] + modl[ 2] * proj[ 8] + modl[ 3] * proj[12];
    	clip[ 1] = modl[ 0] * proj[ 1] + modl[ 1] * proj[ 5] + modl[ 2] * proj[ 9] + modl[ 3] * proj[13];
    	clip[ 2] = modl[ 0] * proj[ 2] + modl[ 1] * proj[ 6] + modl[ 2] * proj[10] + modl[ 3] * proj[14];
    	clip[ 3] = modl[ 0] * proj[ 3] + modl[ 1] * proj[ 7] + modl[ 2] * proj[11] + modl[ 3] * proj[15];

    	clip[ 4] = modl[ 4] * proj[ 0] + modl[ 5] * proj[ 4] + modl[ 6] * proj[ 8] + modl[ 7] * proj[12];
    	clip[ 5] = modl[ 4] * proj[ 1] + modl[ 5] * proj[ 5] + modl[ 6] * proj[ 9] + modl[ 7] * proj[13];
    	clip[ 6] = modl[ 4] * proj[ 2] + modl[ 5] * proj[ 6] + modl[ 6] * proj[10] + modl[ 7] * proj[14];
    	clip[ 7] = modl[ 4] * proj[ 3] + modl[ 5] * proj[ 7] + modl[ 6] * proj[11] + modl[ 7] * proj[15];

    	clip[ 8] = modl[ 8] * proj[ 0] + modl[ 9] * proj[ 4] + modl[10] * proj[ 8] + modl[11] * proj[12];
    	clip[ 9] = modl[ 8] * proj[ 1] + modl[ 9] * proj[ 5] + modl[10] * proj[ 9] + modl[11] * proj[13];
    	clip[10] = modl[ 8] * proj[ 2] + modl[ 9] * proj[ 6] + modl[10] * proj[10] + modl[11] * proj[14];
    	clip[11] = modl[ 8] * proj[ 3] + modl[ 9] * proj[ 7] + modl[10] * proj[11] + modl[11] * proj[15];

    	clip[12] = modl[12] * proj[ 0] + modl[13] * proj[ 4] + modl[14] * proj[ 8] + modl[15] * proj[12];
    	clip[13] = modl[12] * proj[ 1] + modl[13] * proj[ 5] + modl[14] * proj[ 9] + modl[15] * proj[13];
    	clip[14] = modl[12] * proj[ 2] + modl[13] * proj[ 6] + modl[14] * proj[10] + modl[15] * proj[14];
    	clip[15] = modl[12] * proj[ 3] + modl[13] * proj[ 7] + modl[14] * proj[11] + modl[15] * proj[15];

    	// Now we actually want to get the sides of the frustum.  To do this we take
    	// the clipping planes we received above and extract the sides from them.

    	// This will extract the RIGHT side of the frustum
    	m_Frustum[RIGHT][A] = clip[ 3] - clip[ 0];
    	m_Frustum[RIGHT][B] = clip[ 7] - clip[ 4];
    	m_Frustum[RIGHT][C] = clip[11] - clip[ 8];
    	m_Frustum[RIGHT][D] = clip[15] - clip[12];

    	// Now that we have a normal (A,B,C) and a distance (D) to the plane,
    	// we want to normalize that normal and distance.

    	// Normalize the RIGHT side
    	NormalizePlane(m_Frustum, RIGHT);

    	// This will extract the LEFT side of the frustum
    	m_Frustum[LEFT][A] = clip[ 3] + clip[ 0];
    	m_Frustum[LEFT][B] = clip[ 7] + clip[ 4];
    	m_Frustum[LEFT][C] = clip[11] + clip[ 8];
    	m_Frustum[LEFT][D] = clip[15] + clip[12];

    	// Normalize the LEFT side
    	NormalizePlane(m_Frustum, LEFT);

    	// This will extract the BOTTOM side of the frustum
    	m_Frustum[BOTTOM][A] = clip[ 3] + clip[ 1];
    	m_Frustum[BOTTOM][B] = clip[ 7] + clip[ 5];
    	m_Frustum[BOTTOM][C] = clip[11] + clip[ 9];
    	m_Frustum[BOTTOM][D] = clip[15] + clip[13];

    	// Normalize the BOTTOM side
    	NormalizePlane(m_Frustum, BOTTOM);

    	// This will extract the TOP side of the frustum
    	m_Frustum[TOP][A] = clip[ 3] - clip[ 1];
    	m_Frustum[TOP][B] = clip[ 7] - clip[ 5];
    	m_Frustum[TOP][C] = clip[11] - clip[ 9];
    	m_Frustum[TOP][D] = clip[15] - clip[13];

    	// Normalize the TOP side
    	NormalizePlane(m_Frustum, TOP);

    	// This will extract the BACK side of the frustum
    	m_Frustum[BACK][A] = clip[ 3] - clip[ 2];
    	m_Frustum[BACK][B] = clip[ 7] - clip[ 6];
    	m_Frustum[BACK][C] = clip[11] - clip[10];
    	m_Frustum[BACK][D] = clip[15] - clip[14];

    	// Normalize the BACK side
    	NormalizePlane(m_Frustum, BACK);

    	// This will extract the FRONT side of the frustum
    	m_Frustum[FRONT][A] = clip[ 3] + clip[ 2];
    	m_Frustum[FRONT][B] = clip[ 7] + clip[ 6];
    	m_Frustum[FRONT][C] = clip[11] + clip[10];
    	m_Frustum[FRONT][D] = clip[15] + clip[14];

    	// Normalize the FRONT side
    	NormalizePlane(m_Frustum, FRONT);
    }

    // The code below will allow us to make checks within the frustum.  For example,
    // if we want to see if a point, a sphere, or a cube lies inside of the frustum.
    // Because all of our planes point INWARDS (The normals are all pointing inside the frustum)
    // we then can assume that if a point is in FRONT of all of the planes, it's inside.

    ///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    /////	This determines if a point is inside of the frustum
    /////
    ///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

    bool CFrustum::PointInFrustum( VType x, VType y, VType z )
    {
    	// If you remember the plane equation (A*x + B*y + C*z + D = 0), then the rest
    	// of this code should be quite obvious and easy to figure out yourself.
    	// In case don't know the plane equation, it might be a good idea to look
    	// at our Plane Collision tutorial at www.GameTutorials.com in OpenGL Tutorials.
    	// I will briefly go over it here.  (A,B,C) is the (X,Y,Z) of the normal to the plane.
    	// They are the same thing... but just called ABC because you don't want to say:
    	// (x*x + y*y + z*z + d = 0).  That would be wrong, so they substitute them.
    	// the (x, y, z) in the equation is the point that you are testing.  The D is
    	// The distance the plane is from the origin.  The equation ends with "= 0" because
    	// that is true when the point (x, y, z) is ON the plane.  When the point is NOT on
    	// the plane, it is either a negative number (the point is behind the plane) or a
    	// positive number (the point is in front of the plane).  We want to check if the point
    	// is in front of the plane, so all we have to do is go through each point and make
    	// sure the plane equation goes out to a positive number on each side of the frustum.
    	// The result (be it positive or negative) is the distance the point is front the plane.

    	// Go through all the sides of the frustum
    	VType temp;
    	for(int i = 0; i < 6; i++ )
    	{
    		// Calculate the plane equation and check if the point is behind a side of the frustum
    		temp=m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D];
    		if (temp<-Epsilon)
    		{
    			// The point was behind a side, so it ISN'T in the frustum
    			return false;
    		}
    	}

    	// The point was inside of the frustum (In front of ALL the sides of the frustum)
    	return true;
    }

    ///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    /////	This determines if a sphere is inside of our frustum by it's center and radius.
    /////
    ///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

    bool CFrustum::SphereInFrustum( VType x, VType y, VType z, VType radius )
    {
    	// Now this function is almost identical to the PointInFrustum(), except we
    	// now have to deal with a radius around the point.  The point is the center of
    	// the radius.  So, the point might be outside of the frustum, but it doesn't
    	// mean that the rest of the sphere is.  It could be half and half.  So instead of
    	// checking if it's less than 0, we need to add on the radius to that.  Say the
    	// equation produced -2, which means the center of the sphere is the distance of
    	// 2 behind the plane.  Well, what if the radius was 5?  The sphere is still inside,
    	// so we would say, if(-2 < -5) then we are outside.  In that case it's false,
    	// so we are inside of the frustum, but a distance of 3.  This is reflected below.

    	// Go through all the sides of the frustum
    	for(int i = 0; i < 6; i++ )
    	{
    		// If the center of the sphere is farther away from the plane than the radius
    		if( m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= -radius )
    		{
    			// The distance was greater than the radius so the sphere is outside of the frustum
    			return false;
    		}
    	}

    	// The sphere was inside of the frustum!
    	return true;
    }

    int CFrustum::cubeInsideFrustum(VType x, VType y, VType z, VType halfWidth)
    {
    	int factorX=1, factorY=1, factorZ=1;
    	bool isInside=false, isOutside=false, result;
    	for (int i=0; i<2; i++)
    	{
    		factorX*=-1;
    		for (int j=0; j<2; j++)
    		{
    			factorY*=-1;
    			for (int k=0; k<2; k++)
    			{
    				factorZ*=-1;
    				result= PointInFrustum(x+factorX*halfWidth, y+factorY*halfWidth,
    					z+factorZ*halfWidth);

    				//initialize first!!!
    				isInside=result?true:isInside;
    				isOutside=!result?true:isOutside;

    				if (isInside&&!result || isOutside&&result)
    				{
    					return 0;//intersection
    				}
    			}
    		}
    	}
    	if (isInside&&!isOutside)
    	{
    		return 1;
    	}
    	if (isOutside&&!isInside)
    	{
    		return -1;
    	}

    	printf("cannot come here\n");

    	exit(8);

    }

    bool CFrustum::canCullCube(VType x, VType y, VType z, VType halfWidth)
    {
    	int factorX=1, factorY=1, factorZ=1;
    	for (int i=0; i<2; i++)
    	{
    		factorX*=-1;
    		for (int j=0; j<2; j++)
    		{
    			factorY*=-1;
    			for (int k=0; k<2; k++)
    			{
    				factorZ*=-1;
    				if (PointInFrustum(x+factorX*halfWidth, y+factorY*halfWidth,
    					z+factorZ*halfWidth))
    				{
    					return false;
    				}
    			}
    		}
    	}
    	return true;
    }

    ///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    /////
    /////	This determines if a cube is in or around our frustum by it's center and 1/2 it's length
    /////
    ///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
    ///////////////////////////////////////////////////////////////////////////////////
    //Let's define the return value like this:
    // 1 means the cube is completely inside frustum
    // 0 means the cube is intersects with frustum
    // -1 means it is completely outside frustum
    //the code below seems a bit clumnsy, but I think it should be the better way of coding.

    int CFrustum::CubeInFrustum( VType x, VType y, VType z, VType size )
    {
    	// This test is a bit more work, but not too much more complicated.
    	// Basically, what is going on is, that we are given the center of the cube,
    	// and half the length.  Think of it like a radius.  Then we checking each point
    	// in the cube and seeing if it is inside the frustum.  If a point is found in front
    	// of a side, then we skip to the next side.  If we get to a plane that does NOT have
    	// a point in front of it, then it will return false.

    	// *Note* - This will sometimes say that a cube is inside the frustum when it isn't.
    	// This happens when all the corners of the bounding box are not behind any one plane.
    	// This is rare and shouldn't effect the overall rendering speed.
    	bool isInside=false, isOutside=false;
    	for(int i = 0; i < 6; i++ )
    		// A violation of all the six conditions means its completely inside the frustum. @RG (I hope so :))
    	{
    		if(m_Frustum[i][A]*(x-size)+m_Frustum[i][B]*(y-size)+
    			m_Frustum[i][C]*(z-size)+m_Frustum[i][D]> 0)
    		{
    			continue;
    		}
    			//////////////////////////////////////////////////////

    		if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
    		{
    			continue;
    		}

    		if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
    		{
    			continue;
    		}

    		if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
    		{
    			continue;
    		}

    		if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
    		{
    			continue;
    		}

    		if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
    		{
    			continue;
    		}

    		if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
    		{
    			continue;
    		}

    		if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
    		{
    			continue;
    		}
    	}

    	//if we reach here it means they are not same
    	if (isInside)
    	{
    		return 1;
    	}

    	if (isOutside)
    	{
    		return -1;
    	}

    }

    /////////////////////////////////////////////////////////////////////////////////
    //
    // * QUICK NOTES *
    //
    // WOZZERS!  That seemed like an incredible amount to look at, but if you break it
    // down, it's not.  Frustum culling is a VERY useful thing when it comes to 3D.
    // If you want a large world, there is no way you are going to send it down the
    // 3D pipeline every frame and let OpenGL take care of it for you.  That would
    // give you a 0.001 frame rate.  If you hit '+' and bring the sphere count up to
    // 1000, then take off culling, you will see the HUGE difference it makes.
    // Also, you wouldn't really be rendering 1000 spheres.  You would most likely
    // use the sphere code for larger objects.  Let me explain.  Say you have a bunch
    // of objects, well... all you need to do is give the objects a radius, and then
    // test that radius against the frustum.  If that sphere is in the frustum, then you
    // render that object.  Also, you won't be rendering a high poly sphere so it won't
    // be so slow.  This goes for bounding box's too (Cubes).  If you don't want to
    // do a cube, it is really easy to convert the code for rectangles.  Just pass in
    // a width and height, instead of just a length.  Remember, it's HALF the length of
    // the cube, not the full length.  So it would be half the width and height for a rect.
    //
    // This is a perfect starter for an octree tutorial.  Wrap you head around the concepts
    // here and then see if you can apply this to making an octree.  Hopefully we will have
    // a tutorial up and running for this subject soon.  Once you have frustum culling,
    // the next step is getting space partitioning.  Either it being a BSP tree of an Octree.
    //
    // Let's go over a brief overview of the things we learned here:
    //
    // 1) First we need to abstract the frustum from OpenGL.  To do that we need the
    //    projection and modelview matrix.  To get the projection matrix we use:
    //
    //			glGetVTypev( GL_PROJECTION_MATRIX, /* An Array of 16 VTypes */ );
    //    Then, to get the modelview matrix we use:
    //
    //			glGetFloatv( GL_MODELVIEW_MATRIX, /* An Array of 16 floats */ );
    //
    //	  These 2 functions gives us an array of 16 floats (The matrix).
    //
    // 2) Next, we need to combine these 2 matrices.  We do that by matrix multiplication.
    //
    // 3) Now that we have the 2 matrixes combined, we can abstract the sides of the frustum.
    //    This will give us the normal and the distance from the plane to the origin (ABC and D).
    //
    // 4) After abstracting a side, we want to normalize the plane data.  (A B C and D).
    //
    // 5) Now we have our frustum, and we can check points against it using the plane equation.
    //    Once again, the plane equation (A*x + B*y + C*z + D = 0) says that if, point (X,Y,Z)
    //    times the normal of the plane (A,B,C), plus the distance of the plane from origin,
    //    will equal 0 if the point (X, Y, Z) lies on that plane.  If it is behind the plane
    //    it will be a negative distance, if it's in front of the plane (the way the normal is facing)
    //    it will be a positive number.
    //
    //
    // If you need more help on the plane equation and why this works, download our
    // Ray Plane Intersection Tutorial at www.GameTutorials.com.
    //
    // That's pretty much it with frustums.  There is a lot more we could talk about, but
    // I don't want to complicate this tutorial more than I already have.
    //
    // I want to thank Mark Morley for his tutorial on frustum culling.  Most of everything I got
    // here comes from his teaching.  If you want more in-depth, visit his tutorial at:
    //
    // http://www.markmorley.com/opengl/frustumculling.html
    //
    // Good luck!
    //
    //
    // Ben Humphrey (DigiBen)
    // Game Programmer
    // DigiBen@GameTutorials.com
    // Co-Web Host of www.GameTutorials.com
    //
    //

    ¡¡

    file name: viewCollector.cpp

    #include "viewCollector.h"

    bool ViewCollector::fullRenderMode=false;
    bool ViewCollector::hierRenderMode=true;
    int ViewCollector::vertexCounter=0;
    int ViewCollector::faceCounter=0;
    Octree* ViewCollector::root=NULL;

    void ViewCollector::startCollecting(VVector origin)
    {
    	frustum.CalculateFrustum();
    	vertexCounter=faceCounter=0;
    	octreeNodeVector.clear();
    	if (hierRenderMode)
    	{
    		collectHierLeafNode(0);
    	}
    	else
    	{
    		collectLeafNode(origin);
    	}
    }

    void ViewCollector::addCandidate(int index)
    {
    	vertexCounter+=root[index].vNumber+root[index].vRepeatNumber;
    	faceCounter+=root[index].fNumber+root[index].fRepeatNumber;
    	//to do:
    	//put into result set
    	octreeNodeVector.push_back(index);
    }

    void ViewCollector::doCollectLeafNode(int index)
    {
    	Octree* me=root+index;
    	int i;
    	pair<OctreeNodeSet::iterator, bool> result;
    	//we insert the searched path in the first place

    	//no matter it is leaf node or not
    	result=searchSet.insert(index);
    	if (!result.second)
    	{
    		return;
    	}

    	if (!frustum.canCullCube(root[index].center[0], root[index].center[1],
    		root[index].center[2], root[index].width/2.0))
    	{
    		for (i=0; i<root[index].neighbourCount; i++)
    		{
    			searchQueue.push_back(root[index].neighbours[i]);
    		}

    		if (root[index].sonCount>0)
    		{
    			for (i=0; i<8; i++)
    			{
    				if (root[index].sons[i]!=-1)
    				{
    					doCollectLeafNode(root[index].sons[i]);
    				}
    			}
    		}
    		else
    		{
    			//now they are leaves and we test and add
    			//and they must already be added into searchSet!!!!!!
    			//and  they must have already been added into nodeSet by testAndAdd
    			//now we handle its neighbour immediately
    			testAndAdd(index);
    		}
    	}
    }

    //a typical BFS because we want to search from near to far within capacity
    void ViewCollector::collectLeafNode(VVector origin)
    {
    	int i;
    	vector<int> result;

    	//nodeQueue[nextIndex].clear();
    	searchSet.clear();
    	searchQueue.clear();

    	if(!getStartingNodes(0, origin, searchQueue))
    	{
    		//we just add all and this will be a problem when viewer is outside octree boundbox
    		for (i=0; i<root->infoStruct.nodeCount; i++)
    		{
    			if (root[i].sonCount==0)
    			{
    				//none of them has been tested
    				searchQueue.push_back(i);
    			}
    		}
    	}

    	//printf("searchQueue.size=%d\n", searchQueue.size());
    	//let node decide whether to put itself or not
    	while (!searchQueue.empty())
    	{
    		if (checkCapacity())
    		{
    			//don't put
    			break;
    		}
    		int index=searchQueue.front();
    		searchQueue.pop_front();

    		//so this queue is used as a searching clue, if the element itself
    		//is inside frustum, we add it into our nodeSet[nextInt].
    		//and also test its neighbours, provided they are not searched
    		//if not, we don't even care about its neighbours
    		//so, whatever is inside searchqueue are all our candidates for testing.
    		doCollectLeafNode(index);//add more neighbours
    	}

    }

    //this is a bit tricky, because it is possible that there is no leaf nodes contain
    //the point and the internal node contains the point, in this case we still need
    //those leaf nodes which belongs to lowest internal node
    bool ViewCollector::getStartingNodes(int index, VVector origin, OctreeNodeQueue& result)
    {
    	Octree* ptr;
    	int i;
    	ptr=root+index;

    	for (i=0; i<3; i++)
    	{
    		if (origin[i]<ptr->center[i]-ptr->width/2.0-Epsilon
    			||origin[i]>ptr->center[i]+ptr->width/2.0+Epsilon)
    		{
    			return false;//no condition
    		}
    	}

    	if (!frustum.canCullCube(ptr->center[0], ptr->center[1], ptr->center[2], ptr->width/2.0))
    	{
    		result.push_back(index);
    	}
    	else
    	{
    		//this is tricky, it means the point is contained in the cube
    		//but our cube is not contained inside frustum or intersects with.
    		//the only explanation is that the cube contains frustum, so go down

    		for (i=0; i<8; i++)
    		{
    			if (ptr->sons[i]!=-1)
    			{
    				getStartingNodes(ptr->sons[i], origin, result);
    			}
    		}
    	}
    	//we must report adding successful
    	return result.size()!=0;//no condition

    }

    bool ViewCollector::checkCapacity()
    {
    	return vertexCounter>OctreeLoader::capacityFactor*MaxVertexCapacity||
    		faceCounter>OctreeLoader::capacityFactor*MaxFaceCapacity||
    		octreeNodeVector.size()>MaxPossibleNodeCollectable;
    }

    void ViewCollector::collectHierLeafNode(int index)
    {
    	if (checkCapacity())
    	{
    		return;
    	}
    	if (!frustum.canCullCube(root[index].center, root[index].width/2.0))
    	{
    		if (root[index].sonCount==0)
    		{
    			addCandidate(index);
    		}
    		else
    		{
    			for (int i=0; i<8; i++)
    			{
    				if (root[index].sons[i]!=-1)
    				{
    					collectHierLeafNode(root[index].sons[i]);
    				}
    			}
    		}
    	}
    }

    bool ViewCollector::testAndAdd(int nodeIndex)
    {
    	Octree*ptr=root+nodeIndex;

    	if (!frustum.canCullCube(ptr->center[0], ptr->center[1], ptr->center[2], ptr->width/2.0))
    	{
    		//we only counts the leaf node data
    		addCandidate(nodeIndex);
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }

    ¡¡

    file name: octree.cpp

    ///////////////////////////////////////////////////////////////
    //The molton code scheme is as simple as following:
    //1. using an unsigned int (32) for code.
    //2. The first 4 bits represents level, , root node starting from 0x0 (this limits our deepest level from 16
    //3. each level using 3 bits representing son index.
    // I forget this after only two weeks
    //
    ////////////////////////////////////////////////////////////////
    ////we use the largest radius for radius of octree node
    //
    //////////////////////////////////////////////////////////////

    #include <stdio.h>
    #include <assert.h>
    #include <math.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <algorithm>
    #include "octree.h"

    #ifndef LINUX_VERSION
    #include <windows.h>
    #include <winbase.h>
    #include "except.h"
    const char* DataFilePath=".\\";
    #else
    const char* DataFilePath="./";
    //const char* DataFilePath="/sfs/nobackup/all/qingz_hu/octree/single/expand1/";
    #endif
    //////////////////////////////////////////////

    //application programmer is responsible to change this path to its application path
    //const char* DataFilePath="/sfs/backup/all/qingz_hu/octree/bin/single/bin";

    const char* VertexFileName="vertex.data";
    const char* FaceFileName="face.data";
    const char* FakeColorFileName="fakeColor.data";
    const char* InfoFileName="info.data";
    const char* OctreeFileName="octree.data";

    //////////////////////////////////////////////////////////

    char InfoStruct::vertexFileName[MAX_PATH];
    char InfoStruct::faceFileName[MAX_PATH];
    char InfoStruct::fakeColorFileName[MAX_PATH];
    char InfoStruct::infoFileName[MAX_PATH];
    char InfoStruct::octreeFileName[MAX_PATH];

    Octree* Octree::root=NULL;

    //////////////////////////////////////////////////////////
    HANDLE Octree::vertexHandle=INVALID_HANDLE_VALUE;
    HANDLE Octree::faceHandle=INVALID_HANDLE_VALUE;
    HANDLE Octree::octreeHandle=INVALID_HANDLE_VALUE;
    HANDLE Octree::infoHandle=INVALID_HANDLE_VALUE;

    InfoStruct Octree::infoStruct;

    VType Octree::operations[8][3]=
    {
    	{-1,-1,1},//0
    	{-1,-1,-1},//1
    	{-1, 1,1},//2
    	{-1,1,-1},//3
    	{1,-1,-1},//5
    	{1,-1,1},//4
    	{1,1,-1},//7
    	{1,1,1},//6
    };

    void sortTriangleIndex(unsigned int triangle[3])
    {
    	int swap;
    	if (triangle[0]>triangle[1])
    	{
    		swap=triangle[0];
    		triangle[0]=triangle[1];
    		triangle[1]=swap;
    	}
    	if (triangle[0]>triangle[2])
    	{
    		swap=triangle[0];
    		triangle[0]=triangle[2];
    		triangle[2]=swap;
    	}
    	if (triangle[1]>triangle[2])
    	{
    		swap=triangle[1];
    		triangle[1]=triangle[2];
    		triangle[2]=swap;
    	}
    }

    InfoStruct::InfoStruct()
    {
    #ifdef LINUX_VERSION
    	char delimiter='/';
    #else
    	char delimiter='\\';
    #endif

    	int length=strlen(DataFilePath);
    	strcpy(vertexFileName, DataFilePath);
    	strcpy(faceFileName, DataFilePath);
    	strcpy(fakeColorFileName, DataFilePath);
    	strcpy(infoFileName, DataFilePath);
    	strcpy(octreeFileName, DataFilePath);

    	if (DataFilePath[length-1]!=delimiter)
    	{
    		vertexFileName[length]=delimiter;
    		faceFileName[length]=delimiter;
    		fakeColorFileName[length]=delimiter;
    		infoFileName[length]=delimiter;
    		octreeFileName[length]=delimiter;

    		vertexFileName[length+1]='\0';
    		faceFileName[length+1]='\0';
    		fakeColorFileName[length+1]='\0';
    		infoFileName[length+1]='\0';
    		octreeFileName[length+1]='\0';
    	}

    	strcat(vertexFileName, VertexFileName);
    	strcat(faceFileName, FaceFileName);
    	strcat(fakeColorFileName, FakeColorFileName);
    	strcat(infoFileName, InfoFileName);
    	strcat(octreeFileName, OctreeFileName);
    }

    void InfoStruct::display()
    {

    	printf("general configure information:\n");

    	printf("node size: %d, max leaf node vertex number: %d\n", nodeSize, maxLeafNodeVertexNumber);

    	printf("\n*************information for this particular octree*************\n");
    	printf("node count: %d, leafNodeNumber:%d, max depth: %d\n", nodeCount, leafNodeNumber, maxDepth);
    	printf("totalVPageCount: %d, totalFPageCount: %d\n", totalVPageCount, totalFPageCount);

    	printf("max edge: %f, min edge: %f \n", maxEdge, minEdge);

    	printf("max x=%f, min x=%f, max z=%f min z=%f\n", maxX, minX, maxZ, minZ);

    	printf("origin[%f,%f,%f]\n", origin[0], origin[1], origin[2]);

    	printf("total repeat vertex: %d, total repeat face: %d\n", totalRepeatVertex, totalRepeatFace);

    	printf("total neighbour count: %d\n", totalNeighbourCount);
    	printf("total vertex number:%d, total face number:%d\n", totalVertex, totalFace);
    	printf("file path %s\n", DataFilePath);

    }

    void InfoStruct::display(FILE* stream)
    {
    	fprintf(stream, "general configure information:\n");

    	fprintf(stream, "node size: %d, max leaf node vertex number: %d\n", nodeSize, maxLeafNodeVertexNumber);

    	fprintf(stream, "information for this particular octree:\n");
    	fprintf(stream, "node count: %d, leafNodeNumber:%d, max depth: %d\n", nodeCount, leafNodeNumber, maxDepth);
    	fprintf(stream, "totalVPageCount: %d, totalFPageCount: %d\n", totalVPageCount, totalFPageCount);

    	fprintf(stream, "max edge: %f, min edge: %f\n", maxEdge, minEdge);

    	fprintf(stream, "origin[%f,%f,%f]\n", origin[0], origin[1], origin[2]);

    	fprintf(stream, "total repeat vertex: %d, total repeat face: %d\n", totalRepeatVertex, totalRepeatFace);

    	fprintf(stream, "total neighbour count: %d\n", totalNeighbourCount);
    	fprintf(stream, "total vertex number:%d, total face number:%d\n", totalVertex, totalFace);
    	fprintf(stream, "file path %s\n", DataFilePath);
    }

    void Octree::readInfo(bool readOnly)
    {
    	int length;

    	infoHandle=GenericOpenFile(InfoStruct::infoFileName, readOnly);
    	length=GenericReadFile(infoHandle, &infoStruct, sizeof(InfoStruct));

    	if (length==0)
    	{
    		//this must be the very first
    		infoStruct.totalFPageCount=infoStruct.totalVPageCount=0;
    		infoStruct.leafNodeNumber=infoStruct.totalNeighbourCount=0;
    		infoStruct.totalRepeatFace=infoStruct.totalRepeatVertex=0;
    		infoStruct.maxDepth=infoStruct.nodeCount=0;
    		infoStruct.minEdge=infoStruct.maxEdge=0.0;

    		infoStruct.nodeSize=sizeof(Octree);
    		memset(infoStruct.origin, 0, sizeof(VVector));
    		infoStruct.maxNodeArrayLength=NodeArrayLengthIncrement;
    		infoStruct.maxLeafNodeVertexNumber=DefaultMaxLeafNodeVertexNumber;
    	}
    	else
    	{
    		if (length!=sizeof(InfoStruct))
    		{
    			printf("read info file error\n");
    			exit(1234);
    		}
    		//infoStruct.display();
    	}

    }

    void Octree::initialize(bool readOnly, bool useFake)
    {

    /*
    	unsigned long int length;
    	DWORD accessRight;

    	if (readOnly)
    	{
    		accessRight=GENERIC_READ;
    	}
    	else
    	{
    		accessRight=GENERIC_READ|GENERIC_WRITE;
    	}

    	if ((infoHandle=CreateFile(InfoStruct::infoFileName, accessRight, FILE_SHARE_READ, NULL,
    		OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL))==INVALID_HANDLE_VALUE)
    	{
    		printf("open info file failed\n");
    		exit(123);
    	}
    	if (ReadFile(infoHandle, &infoStruct, sizeof(InfoStruct), &length, NULL)==FALSE)
    	{
    		printf("read info file failed\n");
    		exit(1234);
    	}
    */

    	readInfo(readOnly);
    	//now let's open all files

    	/*
    	vertexHandle=CreateFile(InfoStruct::vertexFileName, accessRight,
    		FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL , NULL);
    	faceHandle=CreateFile(InfoStruct::faceFileName, accessRight, FILE_SHARE_READ,
    		NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    	octreeHandle=CreateFile(InfoStruct::octreeFileName, accessRight,
    		FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    	if (vertexHandle==INVALID_HANDLE_VALUE||faceHandle==INVALID_HANDLE_VALUE
    		||octreeHandle==INVALID_HANDLE_VALUE)
    	{
    		printf("open data file failed\n");
    		exit(12);
    	}
    	*/
    	if (useFake)
    	{
    		vertexHandle=GenericOpenFile(InfoStruct::fakeColorFileName, readOnly);
    	}
    	else
    	{
    		vertexHandle=GenericOpenFile(InfoStruct::vertexFileName, readOnly);
    	}
    	faceHandle=GenericOpenFile(InfoStruct::faceFileName, readOnly);
    	octreeHandle=GenericOpenFile(InfoStruct::octreeFileName, readOnly);

    	root=new Octree[infoStruct.maxNodeArrayLength];
    	CHECK_NEW(root);

    	/*
    	if (ReadFile(octreeHandle, root, sizeof(Octree)*infoStruct.nodeCount, &length, NULL)==FALSE)
    	{
    		printf("read octree data failed\n");
    		exit(78);
    	}
    	if (length!=sizeof(Octree)*infoStruct.nodeCount)
    	{
    		printf("read octree data error\n");
    		exit(9);
    	}
    	*/
    	GenericReadFile(octreeHandle, root, sizeof(Octree)*infoStruct.nodeCount);
    }

    void Octree::uninitialize(bool readOnly)
    {
    	if (!readOnly)
    	{
    		GenericSetFilePointer(infoHandle, 0, 0);
    		GenericWriteFile(infoHandle,  &infoStruct, sizeof(InfoStruct));
    		GenericSetFilePointer(octreeHandle, 0, 0);
    		GenericWriteFile(octreeHandle, root, sizeof(Octree)*infoStruct.nodeCount);
    	}
    	delete[] root;
    	GenericCloseFile(infoHandle);
    	GenericCloseFile(octreeHandle);
    	GenericCloseFile(vertexHandle);
    	GenericCloseFile(faceHandle);
    	/*
    	unsigned long int length;
    	if (!readOnly)
    	{
    		if (SetFilePointer(infoHandle, 0, NULL, FILE_BEGIN)==0xFFFFFFFF)
    		{
    			printf("set file pointer failed\n");
    			exit(1234);
    		}
    		if (WriteFile(infoHandle, &infoStruct, sizeof(InfoStruct), &length, NULL)==FALSE)
    		{
    			printf("write info file failed\n");
    			exit(123);
    		}
    		if (SetFilePointer(octreeHandle, 0, NULL, FILE_BEGIN)==0xFFFFFFFF)
    		{
    			printf("set file pointer failed\n");
    			exit(345);
    		}
    		if (WriteFile(octreeHandle, root, sizeof(Octree)*infoStruct.nodeCount, &length, NULL)==FALSE)
    		{
    			printf("write octree file failed\n");
    			exit(234);
    		}
    	}
    	delete[] root;
    	CloseHandle(infoHandle);
    	CloseHandle(octreeHandle);
    	CloseHandle(vertexHandle);
    	CloseHandle(faceHandle);
    	*/
    }

    //return the new node index
    int Octree::createNewOctreeNode(int myOwnIndex, int nodePos)
    {
    	//Octree *ptr;
    	Octree* newRoot;
    	int result;
    	//lazy
    	//ptr=root+nodeCount;
    	//parent

    	if (root[myOwnIndex].sons[nodePos]!=-1)
    	{
    		return root[myOwnIndex].sons[nodePos];
    	}

    	result=infoStruct.nodeCount;
    	initOctreeNode(result);

    	root[myOwnIndex].sonCount++;
    	root[myOwnIndex].sons[nodePos]=result;

    	setSonNodeCentre(myOwnIndex, result, nodePos);
    	////////////////////////////////
    	//son
    	root[result].width=root[myOwnIndex].width/2.0;
    	root[result].myNodeIndex=result;//this will not change
    	root[result].level=root[myOwnIndex].level+1;
    	root[result].parentIndex=root[myOwnIndex].myNodeIndex;

    	if (root[result].level>infoStruct.maxDepth)
    	{
    		infoStruct.maxDepth=root[result].level;
    		if (infoStruct.maxDepth>MaxPossibleDepth)
    		{
    			printf("oh my god! The depth is %d\n", infoStruct.maxDepth);
    		}
    	}

    	infoStruct.nodeCount++;

    	//beyond this line, all are safe because we haven't moved octree array yet.
    	//do we have any recursion call problem???
    	if (infoStruct.nodeCount==infoStruct.maxNodeArrayLength)//even this line is safe
    	{
    		//printf("max node array length reached!\n");
    		//exit(78);

    		infoStruct.maxNodeArrayLength+=NodeArrayLengthIncrement;
    		newRoot=new Octree[infoStruct.maxNodeArrayLength];
    		if (newRoot==NULL)
    		{
    			printf("allocate new octree node array failed\n");
    			exit(1);
    		}
    		memcpy(newRoot, root, sizeof(Octree)*infoStruct.nodeCount);
    		memset(root, 0, sizeof(Octree)*infoStruct.nodeCount);//for safety testing
    		delete[] root;
    		root=newRoot;
    	}
    	//this is dangerous because "root" has just changed and "this" is also changed
    	return 	result;
    }

    /////////////////////////////////////////

    void Octree::setSonNodeCentre(int parentIndex, int sonIndex, int nodeID)
    {
    	for (int i=0; i<3; i++)
    	{
    		root[sonIndex].center[i]=root[parentIndex].center[i]+
    			operations[nodeID][i]*root[parentIndex].width/4.0;
    	}
    }

    int Octree::relativePosition(VVector vPoint, VVector vCtr)
    {
    	if( (vPoint[0] <= vCtr[0]) && (vPoint[1] >= vCtr[1]) && (vPoint[2] >= vCtr[2]) )
    	{
    		return TOP_LEFT_FRONT;
    	}

    	if( (vPoint[0] <= vCtr[0]) && (vPoint[1] >= vCtr[1]) && (vPoint[2] <= vCtr[2]) )
    	{
    		return TOP_LEFT_BACK;
    	}
    	if( (vPoint[0] >= vCtr[0]) && (vPoint[1] >= vCtr[1]) && (vPoint[2] <= vCtr[2]) )
    	{
    		return TOP_RIGHT_BACK;
    	}

    	if( (vPoint[0] >= vCtr[0]) && (vPoint[1] >= vCtr[1]) && (vPoint[2] >= vCtr[2]) )
    	{
    		return TOP_RIGHT_FRONT;
    	}
    	if( (vPoint[0] <= vCtr[0]) && (vPoint[1] <= vCtr[1]) && (vPoint[2] >= vCtr[2]) )
    	{
    		return BOTTOM_LEFT_FRONT;
    	}

    	if( (vPoint[0] <= vCtr[0]) && (vPoint[1] <= vCtr[1]) && (vPoint[2] <= vCtr[2]) )
    	{
    		return BOTTOM_LEFT_BACK;
    	}

    	if( (vPoint[0] >= vCtr[0]) && (vPoint[1] <= vCtr[1]) && (vPoint[2] <= vCtr[2]) )
    	{
    		return BOTTOM_RIGHT_BACK;
    	}

    	if( (vPoint[0] >= vCtr[0]) && (vPoint[1] <= vCtr[1]) && (vPoint[2] >= vCtr[2]) )
    	{
    		return BOTTOM_RIGHT_FRONT;
    	}
    	//we should never reach this
    	printf("relative position error\n");
    	exit(234);
    	return -1;
    }

    void Octree::doCalculateNeighbour(int nodeIndex, int neighbourIndex)
    {
    	int i;
    	VType distance;
    	if (nodeIndex==neighbourIndex)
    	{
    		return;
    	}
    	if (root[neighbourIndex].level<root[nodeIndex].level)
    	{
    		for (i=0; i<8; i++)
    		{
    			if (root[neighbourIndex].sons[i]!=-1)
    			{
    				doCalculateNeighbour(nodeIndex, root[neighbourIndex].sons[i]);
    			}
    		}
    	}
    	else
    	{
    		if (root[nodeIndex].level==root[neighbourIndex].level)
    		{
    			//check all sides to see if they colinear
    			for (i=0; i<3; i++)
    			{
    				//all coord must be exact one width away or colinear
    				distance=fabs(root[nodeIndex].center[i]-root[neighbourIndex].center[i]);
    				if (distance>Epsilon)
    				{
    					distance=fabs(distance-root[nodeIndex].width);
    					if (distance>Epsilon)
    					{
    						//no! this is far away
    						return;
    					}
    				}
    			}
    			//got it
    			root[nodeIndex].neighbours[root[nodeIndex].neighbourCount]=neighbourIndex;
    			root[nodeIndex].neighbourCount++;
    			infoStruct.totalNeighbourCount++;
    			if (root[nodeIndex].neighbourCount>MaxNeighbourCount)
    			{
    				printf("corrupted data or neighbour calculation mistake\n");
    				exit(77);
    			}
    		}
    	}
    	//in all case we end
    }

    void Octree::calculateNeighbour()
    {
    	infoStruct.leafNodeNumber=infoStruct.totalNeighbourCount=0;
    	infoStruct.totalRepeatVertex=infoStruct.totalRepeatFace=0;
    	infoStruct.totalFace=infoStruct.totalVertex=0;
    	for (int i=0; i<infoStruct.nodeCount; i++)
    	{
    		root[i].neighbourCount=0;
    		doCalculateNeighbour(i, 0);
    		if (root[i].sonCount==0)
    		{
    			infoStruct.leafNodeNumber++;
    			infoStruct.totalRepeatVertex+=root[i].vRepeatNumber;
    			infoStruct.totalRepeatFace+=root[i].fRepeatNumber;
    			infoStruct.totalNeighbourCount+=root[i].neighbourCount;
    			infoStruct.totalVertex+=root[i].vNumber+root[i].vRepeatNumber;
    			infoStruct.totalFace+=root[i].fNumber+root[i].fRepeatNumber;
    		}
    	}
    }

    void Octree::initOctreeNode(int nodeIndex)
    {
    	int i;
    	root[nodeIndex].width=0.0;
    	root[nodeIndex].parentIndex=-1;
    	root[nodeIndex].level=-1;
    	memset(root[nodeIndex].center, 0, sizeof(VVector));
    	//root[nodeIndex].fStartIndex=0;
    	root[nodeIndex].fNumber=root[nodeIndex].vNumber
    		=root[nodeIndex].vRepeatNumber=root[nodeIndex].fRepeatNumber=0;
    	root[nodeIndex].neighbourCount=0;
    	for(i=0; i<8; i++)
    	{
    		root[nodeIndex].sons[i]=-1;
    	}
    	root[nodeIndex].sonCount=0;
    	//hard coding this;
    	root[nodeIndex].myNodeIndex=nodeIndex;
    	root[nodeIndex].vAddress=root[nodeIndex].fAddress=NULL;
    	root[nodeIndex].vFileAddHigh=root[nodeIndex].vFileAddLow
    		=root[nodeIndex].fFileAddLow=root[nodeIndex].fFileAddHigh=-1;
    	root[nodeIndex].vMapSize=root[nodeIndex].fMapSize=0;
    	root[nodeIndex].vPageCount=root[nodeIndex].fPageCount=0;
    	root[nodeIndex].vStartPageIndex=root[nodeIndex].fStartPageIndex=-1;

    }

    //we may need consider the floating point error.
    bool Octree::isCoordInsideBox(int nodeIndex, VVector coord)
    {

    	for (int i=0; i<3; i++)
    	{
    		if (coord[i]>root[nodeIndex].center[i]+root[nodeIndex].width/2.0||
    			coord[i]<root[nodeIndex].center[i]-root[nodeIndex].width/2.0)
    		{
    			return false;
    		}
    	}
    	return true;
    }

    //return existed offset if it has, if none find, use the "suggested"
    bool Octree::addVertexInfoToHashTable(HashTableNode* hashTable, int vIndex, int sonPos,
    									  int& offset)
    {
    	VertexInfo vertexInfo;

    	vertexInfo.sonPos=sonPos;
    	vertexInfo.offset=offset;
    	for (int i=0; i<hashTable[vIndex].size(); i++)
    	{
    		if (hashTable[vIndex][i].sonPos==sonPos)
    		{
    			offset=hashTable[vIndex][i].offset;
    			return true;
    		}
    	}
    	//no finding
    	//error checking, assuming input is initialized to -1 for repeat vertex
    	if (offset==-1)
    	{
    		printf("data corrupted\n");
    		exit(234);
    	}
    	hashTable[vIndex].push_back(vertexInfo);
    	return false;//not find it

    }

    void Octree::calculateFileOffsetV(int startPageIndex, int offset, long& low, long& high)
    {
    	LONGLONG li;
    	li=Int32x32To64(startPageIndex, PageSize) + Int32x32To64(offset, sizeof(PlyVertex));
    	low=li;
    	high=li>>32;
    }

    void Octree::calculateFileOffsetF(int startPageIndex, int offset, long& low, long& high)
    {
    	LONGLONG li;
    	li=Int32x32To64(startPageIndex, PageSize) + Int32x32To64(offset, sizeof(UIVector));
    	low=li;
    	high=li>>32;
    }

    void Octree::setFilePointerV(int nodeIndex, int offset)
    {
    	long low, high;
    	calculateFileOffsetV(root[nodeIndex].vStartPageIndex, offset, low, high);
    	/*
    	if (SetFilePointer(vertexHandle, low, &high, FILE_BEGIN)==0XFFFFFFFF)
    	{
    		printf("set file pointer failed %d\n", GetLastError());
    		exit(123);
    	}
    	*/
    	GenericSetFilePointer(vertexHandle, low, high);
    }

    void Octree::setFilePointerF(int nodeIndex, int offset)
    {
    	long low, high;
    	calculateFileOffsetF(root[nodeIndex].fStartPageIndex, offset, low, high);
    	/*
    	if (SetFilePointer(faceHandle, low, &high, FILE_BEGIN)==0XFFFFFFFF)
    	{
    		printf("set file pointer failed %d\n", GetLastError());
    		exit(123);
    	}
    	*/
    	GenericSetFilePointer(faceHandle, low, high);
    }

    bool findCoord(VertexVector& vertexVector, VVector coord)
    {
    	for (int i=0; i<vertexVector.size(); i++)
    	{
    		if (equalCoord(vertexVector[i].coord, coord))
    		{
    			return true;
    		}
    	}
    	return false;
    }

    void Octree::readOriginalVertex(int nodeIndex, VertexVector& vertexVector,
    				VertexVector& repeatVertexVector, FaceVector& repeatFaceVector)
    {
    	PlyVertex plyVertex;
    	int i, j, originalVertexVectorSize;
    	//unsigned long length;

    	setFilePointerV(nodeIndex, 0);
    	originalVertexVectorSize=vertexVector.size();

    	for (i=0; i<root[nodeIndex].vNumber; i++)
    	{
    		/*
    		if (ReadFile(vertexHandle, &plyVertex, sizeof(PlyVertex), &length, NULL)==FALSE)
    		{
    			printf("read vertex failed %d\n", GetLastError());
    			exit(89);
    		}
    		*/
    		GenericReadFile(vertexHandle, &plyVertex, sizeof(PlyVertex));
    		vertexVector.push_back(plyVertex);
    	}

    	for (i=0; i<repeatFaceVector.size(); i++)
    	{
    		for (j=0; j<3; j++)
    		{
    			if (repeatFaceVector[i][j]>=originalVertexVectorSize)
    			{
    				repeatFaceVector[i][j]+=root[nodeIndex].vNumber;
    			}
    		}
    	}
    	for (i=0; i<root[nodeIndex].vRepeatNumber; i++)
    	{
    		/*
    		if (ReadFile(vertexHandle, &plyVertex, sizeof(PlyVertex), &length, NULL)==FALSE)
    		{
    			printf("read vertex failed %d\n", GetLastError());
    			exit(89);
    		}
    		*/
    		GenericReadFile(vertexHandle, &plyVertex, sizeof(PlyVertex));
    		repeatVertexVector.push_back(plyVertex);
    	}

    }

    void Octree::readOriginalFace(int nodeIndex, FaceVector& faceVector,
    			FaceVector& repeatFaceVector, int insertVertexCount, int insertRepeatVertexCount)
    {
    	FaceStruct triangle;
    	int i, j;

    	setFilePointerF(nodeIndex, 0);
    	for (i=0; i<root[nodeIndex].fNumber; i++)
    	{
    		/*
    		if (ReadFile(faceHandle, &triangle, sizeof(FaceStruct), &length, NULL)==FALSE)
    		{
    			printf("read face failed %d\n", GetLastError());
    			exit(189);
    		}
    		*/
    		GenericReadFile(faceHandle, &triangle, sizeof(FaceStruct));
    		for (j=0; j<3; j++)
    		{
    			triangle[j]+=insertVertexCount;
    		}
    		//we need to sort it here so that ...
    		sortTriangle(triangle);
    		faceVector.push_back(triangle);
    	}
    	for (i=0; i<root[nodeIndex].fRepeatNumber; i++)
    	{
    		/*
    		if (ReadFile(faceHandle, &triangle, sizeof(UIVector), &length, NULL)==FALSE)
    		{
    			printf("read face failed %d\n", GetLastError());
    			exit(289);
    		}
    		*/
    		GenericReadFile(faceHandle, &triangle, sizeof(UIVector));
    		for (j=0; j<3; j++)
    		{
    			if (triangle[j]<root[nodeIndex].vNumber)
    			{
    				triangle[j]+=insertVertexCount;
    			}
    			else
    			{
    				triangle[j]+=insertVertexCount+insertRepeatVertexCount;
    			}
    		}
    		sortTriangle(triangle);
    		////////////////////////////////////
    		//test
    		repeatFaceVector.push_back(triangle);
    	}

    }

    //this is exaggerating! How come we have such complicated parameter to pass?
    //However, this maybe the "key" function of whole upgrading of project which I try to
    //solve after a couple of days thinking
    void Octree::addFaceToSonFaceVector(HashTableNode* hashTable, FaceStruct& triangle,
    	VertexVector& vertexVector, VertexVector& repeatVertexVector,
    	VertexVector* sonVertexVector[8], VertexVector* sonRepeatVertexVector[8],
    	FaceVector* sonFaceVector[8], FaceVector* sonRepeatFaceVector[8])
    {
    	int i, j, sonIndex, suggestIndex;
    	FaceStruct result;
    	bool isRepeatFace;
    	VertexInfo vertexInfo[3];

    	//printf("at beginning of function: triangle[%d,%d,%d]\n", triangle[0], triangle[1], triangle[2]);

    	//assume triangle is sorted
    	for (i=0; i<3; i++)
    	{
    		if (triangle[i]<vertexVector.size())
    		{
    			if (hashTable[triangle[i]].size()==0)
    			{
    				printf("hash table error, triangle[%d,%d,%d]\n", triangle[0], triangle[1],
    					triangle[2]);
    				exit(890);
    			}
    			vertexInfo[i]=hashTable[triangle[i]][0];
    		}
    		else
    		{
    			vertexInfo[i].sonPos=-1;
    		}
    	}

    	//now let's see how many differenct choice we have
    	if (vertexInfo[0].sonPos==-1)
    	{
    		printf("hash table error 2, triangle[%d,%d,%d]\n", triangle[0], triangle[1], triangle[2]);
    		exit(389);
    	}

    	for (i=0; i<3; i++)
    	{
    		if (vertexInfo[i].sonPos!=-1)
    		{
    			sonIndex=vertexInfo[i].sonPos;
    		}
    		else
    		{
    			continue;
    		}
    		//still we must make sure it is not repeating
    		isRepeatFace=false;
    		for (j=0; j<i; j++)
    		{
    			if (vertexInfo[j].sonPos==sonIndex)
    			{
    				isRepeatFace=true;
    				break;
    			}
    		}

    		if (isRepeatFace)
    		{
    			continue;
    		}
    		isRepeatFace=false;
    		for (j=0; j<3; j++)
    		{
    			if (j==i)
    			{
    				result[j]=vertexInfo[j].offset;
    			}
    			else
    			{
    				suggestIndex=sonVertexVector[sonIndex]->size()+
    					sonRepeatVertexVector[sonIndex]->size();
    				if (vertexInfo[j].sonPos==-1)
    				{
    					//so it is  a repeated vertex,

    					if (!addVertexInfoToHashTable(hashTable, triangle[j], sonIndex, suggestIndex))
    					{
    						sonRepeatVertexVector[sonIndex]->push_back(
    							repeatVertexVector[triangle[j]-vertexVector.size()]);
    					}

    				}
    				else
    				{
    					if (!addVertexInfoToHashTable(hashTable, triangle[j], sonIndex, suggestIndex))
    					{
    						sonRepeatVertexVector[sonIndex]->push_back(vertexVector[triangle[j]]);
    					}
    				}
    				if (vertexInfo[j].sonPos!=sonIndex)
    				{
    					isRepeatFace=true;
    				}
    				//here it is...

    				result[j]=suggestIndex;
    			}
    		}
    		sortTriangle(result);
    		//write face

    		if (isRepeatFace)
    		{
    			sonRepeatFaceVector[sonIndex]->push_back(result);
    		}
    		else
    		{
    			////////////////////////////////
    			sonFaceVector[sonIndex]->push_back(result);
    		}
    	}

    }

    void Octree::doCreateLeafNode(int nodeIndex, VertexVector& vertexVector,
    		VertexVector& repeatVertexVector, FaceVector& faceVector, FaceVector& repeatFaceVector)
    {
    	//first we check if nodeIndex originally has storage
    	int i;

    	root[nodeIndex].vMapSize=sizeof(PlyVertex)*(vertexVector.size()+repeatVertexVector.size());
    	//either it is a new node or the old storage is too small
    	if (root[nodeIndex].vStartPageIndex==-1||(root[nodeIndex].vStartPageIndex!=-1&&
    		root[nodeIndex].vPageCount*PageSize<root[nodeIndex].vMapSize))
    	{
    		//we need to relocate at end of file, and lazy
    		root[nodeIndex].vPageCount=root[nodeIndex].vMapSize/PageSize +1;//lazy

    		root[nodeIndex].vStartPageIndex=infoStruct.totalVPageCount;
    		infoStruct.totalVPageCount+=root[nodeIndex].vPageCount;
    		//now let's calculate the file pointer to store
    		calculateFileOffsetV(root[nodeIndex].vStartPageIndex, 0, root[nodeIndex].vFileAddLow,
    			root[nodeIndex].vFileAddHigh);

    	}
    	//otherwise we could use the old file storage
    	//otherwise we use the original file storage and file address
    	setFilePointerV(nodeIndex, 0);
    	for (i=0; i<vertexVector.size(); i++)
    	{
    		/*
    		if (WriteFile(vertexHandle, &vertexVector[i], sizeof(PlyVertex), &length, NULL)
    			==FALSE)
    		{
    			printf("write vertex failed %d\n", GetLastError());
    			exit(889);
    		}
    		*/
    		GenericWriteFile(vertexHandle, &vertexVector[i], sizeof(PlyVertex));
    	}
    	for (i=0; i<repeatVertexVector.size(); i++)
    	{
    		/*
    		if ((WriteFile(vertexHandle, &repeatVertexVector[i], sizeof(PlyVertex), &length,
    			NULL))==FALSE)
    		{
    			printf("write vertex failed %d\n", GetLastError());
    			exit(789);
    		}
    		*/
    		GenericWriteFile(vertexHandle, &repeatVertexVector[i], sizeof(PlyVertex));
    	}

    	//now let's write face into file
    	root[nodeIndex].fMapSize=(faceVector.size()+repeatFaceVector.size())*sizeof(UIVector);

    	if (root[nodeIndex].fStartPageIndex==-1||(root[nodeIndex].fStartPageIndex!=-1
    		&&root[nodeIndex].fPageCount*PageSize<root[nodeIndex].fMapSize))
    	{
    		//we need to relocate at end of file
    		root[nodeIndex].fPageCount=root[nodeIndex].fMapSize/PageSize + 1;

    		root[nodeIndex].fStartPageIndex=infoStruct.totalFPageCount;
    		infoStruct.totalFPageCount+=root[nodeIndex].fPageCount;
    		//now let's calculate the file pointer to store
    		calculateFileOffsetF(root[nodeIndex].fStartPageIndex, 0, root[nodeIndex].fFileAddLow,
    			root[nodeIndex].fFileAddHigh);

    	}
    	//otherwise we could use the old file storage
    	//otherwise we use the original file storage and file address
    	setFilePointerF(nodeIndex, 0);
    	for (i=0; i<faceVector.size(); i++)
    	{
    		/*
    		if (WriteFile(faceHandle, &faceVector[i], sizeof(UIVector), &length, NULL)==FALSE)
    		{
    			printf("write vertex failed %d\n", GetLastError());
    			exit(3289);
    		}
    		*/
    		GenericWriteFile(faceHandle, &faceVector[i], sizeof(UIVector));
    	}
    	for (i=0; i<repeatFaceVector.size(); i++)
    	{
    		/*
    		if (WriteFile(faceHandle, &repeatFaceVector[i], sizeof(UIVector), &length,
    			NULL)==FALSE)
    		{
    			printf("write vertex failed %d\n", GetLastError());
    			exit(1289);
    		}
    		*/
    		GenericWriteFile(faceHandle, &repeatFaceVector[i], sizeof(UIVector));

    	}
    	root[nodeIndex].vNumber=vertexVector.size();
    	root[nodeIndex].vRepeatNumber=repeatVertexVector.size();
    	root[nodeIndex].fNumber=faceVector.size();
    	root[nodeIndex].fRepeatNumber=repeatFaceVector.size();

    }

    void Octree::expandRootNodeBounding(VVector coord, bool freeExpand)
    {
    	int i, oldSonIndex, newSonIndex, sonPos;
    	VType temp;

    	if (freeExpand)
    	{
    		for (i=0; i<3; i++)
    		{
    			temp=fabs((root[0].center[i]-coord[i])*2.0);
    			if (root[0].width<temp)
    			{
    				root[0].width=temp+Epsilon;
    			}
    		}
    	}
    	else
    	{
    		while (true)
    		{
    			root[0].width=root[0].width*2.0;
    			for (i=0; i<8; i++)
    			{
    				if (root[0].sons[i]!=-1)
    				{
    					//inserting a new son
    					//remove old temporarily
    					oldSonIndex=root[0].sons[i];
    					root[0].sons[i]=-1;
    					root[0].sonCount--;
    					//create new
    					newSonIndex=createNewOctreeNode(0, i);
    					sonPos=relativePosition(root[oldSonIndex].center, root[newSonIndex].center);
    					root[newSonIndex].sons[sonPos]=oldSonIndex;
    					root[newSonIndex].sonCount++;
    					root[oldSonIndex].parentIndex=newSonIndex;
    				}
    			}
    			if (isCoordInsideBox(0, coord))
    			{
    				break;
    			}
    		}
    	}
    }

    void outputFaceVector(char* fileName, FaceVector& faceVector)
    {
    	FILE* stream;
    	stream=fopen(fileName, "w");
    	for (int i=0; i<faceVector.size(); i++)
    	{
    		fprintf(stream, "no#%d: [%d, %d, %d]\n", i,  faceVector[i][0], faceVector[i][1],
    			faceVector[i][2]);
    	}
    	fclose(stream);
    }

    void outputVertexVector(char* fileName, VertexVector& vertexVector)
    {
    	FILE* stream;
    	stream=fopen(fileName, "w");
    	for (int i=0; i<vertexVector.size(); i++)
    	{
    		fprintf(stream, "no#%d: [%f, %f, %f]\n", i,  vertexVector[i].coord[0],
    			vertexVector[i].coord[1], vertexVector[i].coord[2]);
    	}
    	fclose(stream);
    }

    void outputRepeatTriangle(char* fileName, VertexVector& vertexVector,
    	VertexVector& repeatVertexVector, FaceVector& faceVector)
    {
    	FILE* stream;
    	int index;
    	stream=fopen(fileName, "w");
    	for (int i=0; i<faceVector.size(); i++)
    	{
    		fprintf(stream, "no#%d triangle:\n", i);
    		//printf("face[%d,%d,%d]\n", faceVector[i][0], faceVector[i][1], faceVector[i][2]);
    		if (faceVector[i][2]>=vertexVector.size()+repeatVertexVector.size())
    		{
    			fprintf(stream, "the error is face%d out of bound\n", i);
    			fprintf(stream, "face[%d,%d,%d]\n", faceVector[i][0], faceVector[i][1],
    				faceVector[i][2]);
    		}
    		for (int j=0; j<3; j++)
    		{
    			//printf("[%f,%f,%f],", vertexVector[faceVector[i][j]].coord[0],
    			//vertexVector[faceVector[i][j]].coord[1], vertexVector[faceVector[i][j]].coord[2]);
    			if (faceVector[i][j]>vertexVector.size())
    			{
    				index=faceVector[i][j]-vertexVector.size();
    				fprintf(stream, "[%f,%f,%f],", repeatVertexVector[index].coord[0],
    				repeatVertexVector[index].coord[1], repeatVertexVector[index].coord[2]);
    			}
    			else
    			{
    				fprintf(stream, "[%f,%f,%f],", vertexVector[faceVector[i][j]].coord[0],
    				vertexVector[faceVector[i][j]].coord[1], vertexVector[faceVector[i][j]].coord[2]);
    			}
    		}
    		//printf("\n");
    		fprintf(stream, "\n");
    	}
    	fclose(stream);
    }

    void outputTriangle(char* fileName, VertexVector& vertexVector, FaceVector& faceVector)
    {
    	FILE* stream;
    	stream=fopen(fileName, "w");
    	for (int i=0; i<faceVector.size(); i++)
    	{
    		fprintf(stream, "no#%d triangle:\n", i);
    		//printf("face[%d,%d,%d]\n", faceVector[i][0], faceVector[i][1], faceVector[i][2]);
    		if (faceVector[i][2]>=vertexVector.size())
    		{
    			fprintf(stream, "the error is face%d out of bound\n", i);
    			fprintf(stream, "face[%d,%d,%d]\n", faceVector[i].face[0], faceVector[i].face[1],
    				faceVector[i].face[2]);
    		}
    		fprintf(stream, "face[%d,%d,%d]\n", faceVector[i][0],
    				faceVector[i][1], faceVector[i][2]);
    		for (int j=0; j<3; j++)
    		{
    			//printf("[%f,%f,%f],", vertexVector[faceVector[i][j]].coord[0],
    			fprintf(stream, "[%f,%f,%f],", vertexVector[faceVector[i][j]].coord[0],
    			vertexVector[faceVector[i][j]].coord[1], vertexVector[faceVector[i][j]].coord[2]);
    		}
    		//printf("\n");
    		fprintf(stream, "\n");
    	}
    	fclose(stream);
    }

    void validateVertex(VertexVector& vertexVector, VertexVector sonVertexVector[8])
    {
    	int i, j, k;
    	//////////////////////
    	//testing
    	bool bFound;
    	for (i=0; i<8; i++)
    	{
    		for (j=0; j<sonVertexVector[i].size(); j++)
    		{
    			bFound=false;
    			for (k=0; k<vertexVector.size(); k++)
    			{
    				if (equalCoord(sonVertexVector[i][j].coord, vertexVector[k].coord))
    				{
    					bFound=true;
    					break;
    				}
    			}
    			if (!bFound)
    			{
    				printf("data corrupted!!!\n");
    				exit(89999);
    			}
    		}
    	}
    	/////////////////////////////////////////////
    }

    bool equalFace(VertexVector& firstVertexVector, FaceStruct& firstFace,
    				  VertexVector& secondVertexVector, FaceStruct& secondFace)
    {
    	int i, j;
    	vector<int> pattern;
    	bool findIt;

    	findIt=true;
    	for (i=0; i<3; i++)
    	{
    		pattern.push_back(secondFace[i]);
    		if (!equalCoord(firstVertexVector[firstFace[i]].coord,
    				secondVertexVector[secondFace[i]].coord))
    		{
    			findIt=false;
    		}
    	}
    	if (findIt)
    	{
    		return true;
    	}

    	while (next_permutation(pattern.begin(), pattern.end()))
    	{
    		findIt=true;
    		for (i=0; i<3; i++)
    		{
    			j=pattern[i];
    			if (!equalCoord(firstVertexVector[firstFace[i]].coord,
    				secondVertexVector[j].coord))
    			{
    				findIt=false;
    				break;
    			}
    		}
    		if (findIt)
    		{
    			return true;
    		}
    	}
    	return false;
    }

    bool findFace(VertexVector& targetVertex, FaceVector& targetFace, VertexVector& sourceVertex,
    			  FaceStruct& faceStruct)
    {
    	for (int i=0; i<targetFace.size(); i++)
    	{
    		if (equalFace(targetVertex, targetFace[i], sourceVertex, faceStruct))
    		{
    			return true;
    		}
    	}
    	return false;
    }

    void compareVertex(char* fileName, VertexVector& sourceVertex, FaceVector& sourceFace,
    				   VertexVector& targetVertex, FaceVector& targetFace)
    {
    	//bool bFound;
    	FILE* stream;
    	stream=fopen(fileName, "w");

    	for (int i=0; i<sourceFace.size(); i++)
    	{
    		if (!findFace(targetVertex, targetFace, sourceVertex, sourceFace[i]))
    		{
    			fprintf(stream, "cannot find this face:#%d\n", i);
    			fprintf(stream, "face%d[%d,%d,%d]\n", i, sourceFace[i].face[0],
    				sourceFace[i].face[1], sourceFace[i].face[2]);

    			fprintf(stream, "the three vertex are:\n");
    			for (int j=0; j<3; j++)
    			{
    				fprintf(stream, "v[%f,%f,%f]\n", sourceVertex[sourceFace[i][j]].coord[0],
    				sourceVertex[sourceFace[i][j]].coord[1], sourceVertex[sourceFace[i][j]].coord[2]);
    			}
    			fprintf(stream, "***************\n");
    		}
    	}
    	fclose(stream);
    }

    void compareRepeatVertex(char* fileName, VertexVector& sonVertexVector,
    	VertexVector& sonRepeatVertexVector, FaceVector& sonRepeatFaceVector,
    	VertexVector& vertexVector, FaceVector& faceVector)
    {
    	VertexVector sourceVertex;
    	FaceStruct faceStruct;
    	int i, j;
    	FILE* stream;
    	stream=fopen(fileName, "w");

    	for (i=0; i<sonVertexVector.size(); i++)
    	{
    		sourceVertex.push_back(sonVertexVector[i]);
    	}
    	for (i=0; i<sonRepeatVertexVector.size(); i++)
    	{
    		sourceVertex.push_back(sonRepeatVertexVector[i]);
    	}
    	for (i=0; i<sonRepeatFaceVector.size(); i++)
    	{
    		for (j=0; j<3; j++)
    		{
    			faceStruct[j]=sonRepeatFaceVector[i][j];
    		}
    		if (!findFace(vertexVector, faceVector, sourceVertex, faceStruct))
    		{
    			fprintf(stream, "cannot find this face:#%d\n", i);
    			fprintf(stream, "face%d[%d,%d,%d]\n", i, sonRepeatFaceVector[i][0],
    				sonRepeatFaceVector[i][1], sonRepeatFaceVector[i][2]);

    			fprintf(stream, "the three vertex are:\n");
    			for (int j=0; j<3; j++)
    			{
    				fprintf(stream, "v[%f,%f,%f]\n", sourceVertex[sonRepeatFaceVector[i][j]].coord[0],
    				sourceVertex[sonRepeatFaceVector[i][j]].coord[1],
    				sourceVertex[sonRepeatFaceVector[i][j]].coord[2]);
    			}
    			fprintf(stream, "***************\n");
    		}
    	}
    	fclose(stream);
    }

    void copyVector(VertexVector& source, VertexVector& target)
    {
    	target.assign(source.begin(), source.end());
    }

    void copyVector(FaceVector& source, FaceVector& target)
    {
    	target.assign(source.begin(), source.end());
    }

    void validateRepeat(char* fileName, VertexVector& vertexVector, FaceVector& faceVector)
    {
    	FILE* stream=fopen(fileName, "w");
    	int i, j;
    	bool findIt;
    	for (i=0; i<faceVector.size(); i++)
    	{
    		findIt=false;
    		for (j=0; j<faceVector.size(); j++)
    		{
    			if (i!=j)
    			{
    				if (equalFace(vertexVector, faceVector[i], vertexVector, faceVector[j]))
    				{
    					findIt=true;
    					break;
    				}
    			}
    		}
    		if (!findIt)
    		{
    			fprintf(stream, "missing face%d[%d,%d,%d]\n", i, faceVector[i][0],
    				faceVector[i][1], faceVector[i][2]);
    			for (j=0; j<3; j++)
    			{
    				fprintf(stream, "coord[%f,%f,%f]\n", vertexVector[faceVector[i][j]].coord[0],
    					vertexVector[faceVector[i][j]].coord[1],
    					vertexVector[faceVector[i][j]].coord[2]);
    			}
    		}
    	}
    	fclose(stream);
    }

    void compareTwoVertex(int nodeIndex, VertexVector& vertexVector, VertexVector& repeatVertexVector,
    				FaceVector& faceVector, FaceVector& repeatFaceVector)
    {
    	char buf[32];
    	VertexVector sourceVertex;
    	int i;

    	for (i=0; i<vertexVector.size(); i++)
    	{
    		sourceVertex.push_back(vertexVector[i]);
    	}
    	for (i=0; i<repeatVertexVector.size(); i++)
    	{
    		sourceVertex.push_back(repeatVertexVector[i]);
    	}
    	sprintf(buf, "CheckVertex%d.txt", nodeIndex);
    	validateRepeat(buf, sourceVertex, faceVector);
    	sprintf(buf, "CheckVertexRepeat%d.txt", nodeIndex);
    	validateRepeat(buf, sourceVertex, repeatFaceVector);
    }

    void Octree::distributeVertex(int nodeIndex, HashTableNode* hashTable, VertexVector& vertexVector,
    					VertexVector& repeatVertexVector, VertexVector* sonVertexVector[8])
    {
    	bool needRestart;
    	int counter;
    	int i, j, sonPos, suggestOffset;

    	counter=0;
    	do
    	{
    		needRestart=false;
    		counter++;
    		for (i=0; i<vertexVector.size(); i++)
    		{
    			//later we can optimize it, but just leave it for safety checking.
    			if (nodeIndex==0)
    			{
    				if (!isCoordInsideBox(nodeIndex, vertexVector[i].coord))
    				{
    					if (root[0].sonCount>0)
    					{
    						//let's expand and insert and re-do everything from scratch
    						expandRootNodeBounding(vertexVector[i].coord, false);

    						//here we need to restart everything.

    						for (j=0; j<8; j++)
    						{
    							sonVertexVector[j]->clear();
    						}
    						for (j=0; j<vertexVector.size()+repeatVertexVector.size(); j++)
    						{
    							hashTable[j].clear();
    						}
    						needRestart=true;
    						break;
    					}
    					else
    					{
    						//or we simply update the width of //simply
    						expandRootNodeBounding(vertexVector[i].coord, true);
    						//then we continue to insert
    					}
    				}
    			}

    			sonPos=relativePosition(vertexVector[i].coord, root[nodeIndex].center);

    			suggestOffset=sonVertexVector[sonPos]->size();
    			sonVertexVector[sonPos]->push_back(vertexVector[i]);
    			addVertexInfoToHashTable(hashTable, i, sonPos, suggestOffset);

    		}
    		if (counter%100==0)
    		{
    			printf("repeat in distributeVertex of %d in nodeIndex %d\n", counter, nodeIndex);
    		}
    	}
    	while (needRestart);
    	//now that we check the number of vertex to see if it exceeds limit
    }

    void Octree::doAddToOctreeNodePreparation(HashTableNode*& hashTable, int hashTableSize, VertexVector* sonVertexVector[8],
    		VertexVector* sonRepeatVertexVector[8], FaceVector* sonFaceVector[8], FaceVector* sonRepeatFaceVector[8])
    {

    	hashTable=new HashTableNode[hashTableSize];

    	CHECK_NEW(hashTable);
    	for (int i=0; i<8; i++)
    	{
    		sonVertexVector[i]= new VertexVector;
    		CHECK_NEW(sonVertexVector[i]);
    		sonRepeatVertexVector[i]=new VertexVector;
    		CHECK_NEW(sonRepeatVertexVector[i]);
    		sonFaceVector[i]=new FaceVector;
    		CHECK_NEW(sonFaceVector[i]);
    		sonRepeatFaceVector[i]=new FaceVector;
    		CHECK_NEW(sonRepeatFaceVector[i]);
    	}
    }

    //this function must be "static" because possible expansion happens...?? not really expansion
    void Octree::doAddToOctree(int nodeIndex, VertexVector& vertexVector,
    	VertexVector& repeatVertexVector, FaceVector& faceVector, FaceVector& repeatFaceVector)
    {
    	int i, j;
    	int vNumberCounter=0, vRepeatCounter=0, fNumberCounter=0, fRepeatCounter=0;
    	VertexVector* sonVertexVector[8];
    	VertexVector* sonRepeatVertexVector[8];
    	FaceVector* sonFaceVector[8];
    	FaceVector* sonRepeatFaceVector[8];
    	//bool needRestart;
    	int insertVertexCount, insertRepeatVertexCount;
    	HashTableNode* hashTable;
    	HANDLE tempVertexFileHandle;
    	HANDLE tempFaceFileHandle;
    	char tempVertexFileName[MAX_PATH];
    	char tempFaceFileName[MAX_PATH];

    	int vertexCount, repeatVertexCount, faceCount, repeatFaceCount;

    	if (root[nodeIndex].sonCount==0 )
    	{
    		//read in all vertex from file
    		insertVertexCount=vertexVector.size();
    		insertRepeatVertexCount=repeatVertexVector.size();
    		if (root[nodeIndex].vNumber>0||root[nodeIndex].vRepeatNumber>0)
    		{
    			readOriginalVertex(nodeIndex, vertexVector, repeatVertexVector, repeatFaceVector);
    		}
    		//assuming sorting and updating the "repeat-face-index"
    		if (root[nodeIndex].fNumber>0||root[nodeIndex].fRepeatNumber>0)
    		{
    			readOriginalFace(nodeIndex, faceVector, repeatFaceVector, insertVertexCount,
    				insertRepeatVertexCount);
    		}
    	}

    	//compareVertex(oldVertex, oldFace, vertexVector, faceVector);
    	//compareVertex( vertexVector, faceVector, oldVertex, oldFace);

    	//no need for "prelocated"
    	if (vertexVector.size()<infoStruct.maxLeafNodeVertexNumber)
    	{
    		//create leaf node and return
    		//compareTwoVertex(nodeIndex, vertexVector, repeatVertexVector, faceVector,
    		//	repeatFaceVector);
    		doCreateLeafNode(nodeIndex, vertexVector, repeatVertexVector, faceVector,
    			repeatFaceVector);

    		return;
    	}

    	doAddToOctreeNodePreparation(hashTable, vertexVector.size()+repeatVertexVector.size(), sonVertexVector,
    		sonRepeatVertexVector, sonFaceVector, sonRepeatFaceVector);

    	distributeVertex(nodeIndex, hashTable, vertexVector, repeatVertexVector, sonVertexVector);

    	//////////////////////////////////
    	//testing
    	//for (i=0; i<8; i++)
    	//{
    	//	compareVertex(sonVertexVector[i], sonFaceVector[i], vertexVector, faceVector);
    	//}

    	/*
    	if (nodeIndex==0)
    	{
    		outputVertexVector("beforeVertex.txt", vertexVector);
    		outputVertexVector("beforerepeatvertex.txt", repeatVertexVector);
    		outputFaceVector("beforeface.txt", faceVector);
    		outputFaceVector("beforerepeatface.txt", repeatFaceVector);
    		outputTriangle("beforetriangle.txt", vertexVector, faceVector);
    	}
    	*/

    	////////////////////////////
    	//now let's handle the face update
    	for (i=0; i<faceVector.size(); i++)
    	{
    		//printf("faceVector.size()=%d\n", faceVector.size());
    		//printf("triangle%d[%d,%d,%d]\n",i, faceVector[i].face[0],
    		//	faceVector[i].face[1], faceVector[i].face[2]);
    		addFaceToSonFaceVector(hashTable, faceVector[i], vertexVector, repeatVertexVector,
    			sonVertexVector, sonRepeatVertexVector, sonFaceVector, sonRepeatFaceVector);
    	}

    	for (i=0; i<repeatFaceVector.size(); i++)
    	{
    		//printf("repeatfaceVector.size()=%d\n", repeatFaceVector.size());
    		//printf("triangle%d[%d,%d,%d]\n",i, repeatFaceVector[i].face[0],
    		//	repeatFaceVector[i].face[1], repeatFaceVector[i].face[2]);
    		/////////////////
    		addFaceToSonFaceVector(hashTable, repeatFaceVector[i], vertexVector, repeatVertexVector,
    			sonVertexVector, sonRepeatVertexVector, sonFaceVector, sonRepeatFaceVector);
    	}

    	//before recursion, let's release memory
    	delete[]hashTable;

    	for (i=0; i<8; i++)
    	{
    		int sonNodeIndex;
    		if (sonVertexVector[i]->size()>0)
    		{
    			/////////////////////////////////////////////
    			sonNodeIndex=createNewOctreeNode(nodeIndex, i);

    			/////////////////////////////////////////////////
    			tempVertexFileHandle=GenericOpenTempFile("vertex", tempVertexFileName);
    			tempFaceFileHandle=GenericOpenTempFile("face", tempFaceFileName);

    			/*generic
    			if (GetTempFileName(".", "vertex", 0, tempVertexFileName)==0
    				||GetTempFileName(".", "face", 0, tempFaceFileName)==0)
    			{
    				printf("create temp file name failed%d\n", GetLastError());
    				exit(234);
    			}

    			tempVertexFileHandle=CreateFile(tempVertexFileName, GENERIC_READ|GENERIC_WRITE, 0, NULL,
    				OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    			tempFaceFileHandle=CreateFile(tempFaceFileName, GENERIC_READ|GENERIC_WRITE, 0, NULL,
    				OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    			if (tempVertexFileHandle==NULL||tempFaceFileHandle==NULL)
    			{
    				printf("create temp file failed\n");
    				exit(89);
    			}
    			*/

    			vertexCount=sonVertexVector[i]->size();
    			repeatVertexCount=sonRepeatVertexVector[i]->size();
    			faceCount=sonFaceVector[i]->size();
    			repeatFaceCount=sonRepeatFaceVector[i]->size();

    			for (j=0; j<vertexCount; j++)
    			{
    				GenericWriteFile(tempVertexFileHandle, &(sonVertexVector[i]->at(j)), sizeof(PlyVertex));
    				/*generic
    				if (WriteFile(tempVertexFileHandle, &(sonVertexVector[i]->at(j)), sizeof(PlyVertex),
    					&length, NULL)==0)
    				{
    					printf("write temp vertex file failed %d\n", GetLastError());
    					exit(123);
    				}
    				*/

    			}
    			for (j=0; j<repeatVertexCount; j++)
    			{
    				GenericWriteFile(tempVertexFileHandle, &(sonRepeatVertexVector[i]->at(j)), sizeof(PlyVertex));
    				/*
    				if (WriteFile(tempVertexFileHandle, &(sonRepeatVertexVector[i]->at(j)), sizeof(PlyVertex),
    					&length, NULL)==0)
    				{
    					printf("write temp repeat vertex file failed %d\n", GetLastError());
    					exit(123);
    				}
    				*/
    			}
    			for (j=0; j<faceCount; j++)
    			{
    				GenericWriteFile(tempFaceFileHandle, sonFaceVector[i]->at(j).face, sizeof(FaceStruct));
    				/*
    				if (WriteFile(tempFaceFileHandle, sonFaceVector[i]->at(j).face, sizeof(FaceStruct),
    					&length, NULL)==0)
    				{
    					printf("write temp face file failed %d\n", GetLastError());
    					exit(123);
    				}
    				*/

    			}
    			for (j=0; j<repeatFaceCount; j++)
    			{
    				GenericWriteFile(tempFaceFileHandle, sonRepeatFaceVector[i]->at(j).face, sizeof(FaceStruct));

    				/*
    				if (WriteFile(tempFaceFileHandle, sonRepeatFaceVector[i]->at(j).face, sizeof(FaceStruct),
    					&length, NULL)==0)
    				{
    					printf("write temp repeat face file failed %d\n", GetLastError());
    					exit(123);
    				}
    				*/
    			}
    			//I would rather delete it before recursion
    			delete sonVertexVector[i];
    			delete sonRepeatVertexVector[i];
    			delete sonFaceVector[i];
    			delete sonRepeatFaceVector[i];

    			doAddToOctreeWrapper(sonNodeIndex, vertexCount, repeatVertexCount, faceCount, repeatFaceCount,
    				tempVertexFileHandle, tempFaceFileHandle);

    			/*
    			if (CloseHandle(tempVertexFileHandle)==0||CloseHandle(tempFaceFileHandle)==0)
    			{
    				printf("close temp files failed %d\n", GetLastError());
    				exit(1223);
    			}
    			if (DeleteFile(tempVertexFileName)==0||DeleteFile(tempFaceFileName)==0)
    			{
    				printf("delete temp files failed %d\n", GetLastError());
    				exit(1223);
    			}
    			*/
    			GenericCloseTempFile(tempVertexFileHandle, tempVertexFileName);
    			GenericCloseTempFile(tempFaceFileHandle, tempFaceFileName);

    			//doAddToOctree(sonNodeIndex, *sonVertexVector[i], *sonRepeatVertexVector[i],
    			//	*sonFaceVector[i], *sonRepeatFaceVector[i]);
    			//////////////////////////////////

    			//here is just counting, we don't move file pointers
    			//after this recursion, the temp file will not be used any more
    		}
    		else
    		{
    			delete sonVertexVector[i];
    			delete sonRepeatVertexVector[i];
    			delete sonFaceVector[i];
    			delete sonRepeatFaceVector[i];
    		}

    		if (root[nodeIndex].sons[i]!=-1)
    		{
    			sonNodeIndex=root[nodeIndex].sons[i];
    			vNumberCounter+=root[sonNodeIndex].vNumber;
    			vRepeatCounter+=root[sonNodeIndex].vRepeatNumber;
    			fNumberCounter+=root[sonNodeIndex].fNumber;
    			fRepeatCounter+=root[sonNodeIndex].fRepeatNumber;
    		}
    	}

    	root[nodeIndex].vNumber=vNumberCounter;
    	root[nodeIndex].vRepeatNumber=vRepeatCounter;
    	root[nodeIndex].fNumber=fNumberCounter;
    	root[nodeIndex].fRepeatNumber=fRepeatCounter;
    }

    void Octree::doAddToOctreeWrapper(int nodeIndex, int vertexCount, int repeatVertexCount, int faceCount,
    		int repeatFaceCount, HANDLE vertexHandle, HANDLE faceHandle)
    {
    	int i;
    	//unsigned long length;
    	PlyVertex plyVertex;
    	FaceStruct faceStruct;
    	VertexVector* vertexVector;
    	VertexVector* repeatVertexVector;
    	FaceVector* faceVector;
    	FaceVector* repeatFaceVector;

    	vertexVector=new VertexVector;
    	CHECK_NEW(vertexVector);
    	repeatVertexVector=new VertexVector;
    	CHECK_NEW(repeatVertexVector);
    	faceVector=new FaceVector;
    	CHECK_NEW(faceVector);
    	repeatFaceVector=new FaceVector;
    	CHECK_NEW(repeatFaceVector);

    	//SetFilePointer(vertexHandle, 0, NULL, FILE_BEGIN);
    	GenericSetFilePointer(vertexHandle, 0, 0);

    	for (i=0; i<vertexCount; i++)
    	{
    		GenericReadFile(vertexHandle, &plyVertex, sizeof(PlyVertex));
    		//WINAPI_CALL(ReadFile(vertexHandle, &plyVertex, sizeof(PlyVertex), &length, NULL), "read vertex file");

    		vertexVector->push_back(plyVertex);
    	}
    	for (i=0; i<repeatVertexCount; i++)
    	{
    		GenericReadFile(vertexHandle, &plyVertex, sizeof(PlyVertex));
    		//WINAPI_CALL(ReadFile(vertexHandle, &plyVertex, sizeof(PlyVertex), &length, NULL), "read repeat vertex");
    		repeatVertexVector->push_back(plyVertex);
    	}

    	//SetFilePointer(faceHandle, 0, NULL, FILE_BEGIN);
    	GenericSetFilePointer(faceHandle, 0, 0);

    	for (i=0; i<faceCount; i++)
    	{
    		GenericReadFile(faceHandle, faceStruct.face, sizeof(FaceStruct));
    		//WINAPI_CALL(ReadFile(faceHandle, faceStruct.face, sizeof(FaceStruct), &length, NULL), "read face");
    		faceVector->push_back(faceStruct);
    	}

    	for (i=0; i<repeatFaceCount; i++)
    	{
    		GenericReadFile(faceHandle, faceStruct.face, sizeof(FaceStruct));
    		//WINAPI_CALL(ReadFile(faceHandle, faceStruct.face, sizeof(FaceStruct), &length, NULL), "read repeat face");
    		repeatFaceVector->push_back(faceStruct);
    	}

    	doAddToOctree(nodeIndex, *vertexVector, *repeatVertexVector, *faceVector, *repeatFaceVector);

    	delete vertexVector;
    	delete repeatVertexVector;
    	delete faceVector;
    	delete repeatFaceVector;
    }

    void Octree::readDataFromFile(HANDLE inVertexHandle, VertexVector& vertexVector)
    {
    	PlyVertex plyVertex;
    	//unsigned long length;
    	int length;
    	GenericSetFilePointer(inVertexHandle, 0, 0);
    	do
    	{
    		//WINAPI_CALL(ReadFile(inVertexHandle, &plyVertex, sizeof(PlyVertex), &length, NULL), "read vertex file");
    		length=GenericReadFile(inVertexHandle, &plyVertex, sizeof(PlyVertex));
    		if (length==0)
    		{
    			break;
    		}
    		else
    		{
    			assert(length==sizeof(PlyVertex));
    		}
    		vertexVector.push_back(plyVertex);
    	}
    	while (true);

    	if (vertexVector.size()==0)
    	{
    		printf("empty file error\n");
    	}

    }

    void Octree::readDataFromFile(HANDLE inFaceHandle, FaceVector& faceVector)
    {
    	FaceStruct triangle;
    	//unsigned long length;
    	int length;
    	GenericSetFilePointer(inFaceHandle, 0, 0);
    	do
    	{
    		//WINAPI_CALL(ReadFile(inFaceHandle, &triangle, sizeof(FaceStruct), &length, NULL), "read vertex file");
    		length=GenericReadFile(inFaceHandle, &triangle, sizeof(FaceStruct));

    		if (length==0)
    		{
    			break;
    		}
    		else
    		{
    			assert(length==sizeof(FaceStruct));
    		}
    		/////////////////////////////////////
    		faceVector.push_back(triangle);
    	}
    	while (true);
    	//error here
    	assert(faceVector.size()!=0);
    }

    void Octree::addToOctree(VertexVector& inVertexVector, FaceVector& inFaceVector)
    {
    	int i;
    	VertexVector repeatVertexVector;
    	FaceVector repeatFaceVector;
    	if (infoStruct.nodeCount==0)
    	{
    		//create from scratch
    		//to do
    		initOctreeNode(0);
    		root[0].level=0;
    		infoStruct.nodeCount=1;
    		memcpy(root[0].center, inVertexVector[0].coord, sizeof(VVector));
    		memcpy(infoStruct.origin, root[0].center, sizeof(VVector));
    		root[0].width=MinOctreeNodeWidth;
    		infoStruct.maxX=infoStruct.minX=inVertexVector[0].coord[0];
    		infoStruct.maxZ=infoStruct.minZ=inVertexVector[0].coord[2];
    		//create octree array and initialize
    	}
    	//here let's calculate the max-edge
    	for (i=0; i<inFaceVector.size(); i++)
    	{
    		calculateEdges(inFaceVector[i], inVertexVector);
    	}

    	for (i=0; i<inVertexVector.size(); i++)
    	{
    		if (inVertexVector[i].coord[0]>infoStruct.maxX)
    		{
    			infoStruct.maxX=inVertexVector[i].coord[0];
    		}
    		if (inVertexVector[i].coord[0]<infoStruct.minX)
    		{
    			infoStruct.minX=inVertexVector[i].coord[0];
    		}

    		if (inVertexVector[i].coord[2]>infoStruct.maxZ)
    		{
    			infoStruct.maxZ=inVertexVector[i].coord[2];
    		}
    		if (inVertexVector[i].coord[2]<infoStruct.minZ)
    		{
    			infoStruct.minZ=inVertexVector[i].coord[2];
    		}
    	}

    	doAddToOctree(0, inVertexVector, repeatVertexVector, inFaceVector, repeatFaceVector);
    	calculateNeighbour();

    }

    void Octree::addToOctree(char* inVertexFileName, char* inFaceFileName)
    {
    	VertexVector vertexVector;
    	FaceVector faceVector;
    	HANDLE inVertexHandle, inFaceHandle;
    	//to do

    	/*
    	inVertexHandle=CreateFile(inVertexFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING,
    		FILE_ATTRIBUTE_NORMAL , NULL);
    	inFaceHandle=CreateFile(inFaceFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING,
    		FILE_ATTRIBUTE_NORMAL , NULL);
    	if (inVertexHandle==INVALID_HANDLE_VALUE||inFaceHandle==INVALID_HANDLE_VALUE)
    	{
    		printf("cannot create output vertex file\n");
    		exit(9);
    	}
    	*/

    	inVertexHandle=GenericOpenFile(inVertexFileName, true);
    	inFaceHandle=GenericOpenFile(inFaceFileName, true);

    	//open file handle
    	readDataFromFile(inVertexHandle, vertexVector);
    	readDataFromFile(inFaceHandle, faceVector);

    	addToOctree(vertexVector, faceVector);

    	/*
    	CloseHandle(inVertexHandle);
    	CloseHandle(inFaceHandle);
    	*/
    	GenericCloseFile(inVertexHandle);
    	GenericCloseFile(inFaceHandle);

    }

    VType Octree::calcEdge(VVector v1, VVector v2)
    {
    	VType result=0;
    	for (int i=0; i<3; i++)
    	{
    		result+=(v1[i]-v2[i])*(v1[i]-v2[i]);
    	}
    	return sqrt(result);
    }

    void Octree::calculateEdges(FaceStruct& tri, VertexVector& vertexVector)
    {
    	VType result;
    	//float edge[3];
    	//int first, second;
    	for (int i=0; i<3; i++)
    	{
    		result=calcEdge(vertexVector[tri[i]].coord, vertexVector[tri[(i+1)%3]].coord);
    		if (result==0)
    		{
    			printf("data corrupted! edge cannot be 0 %d,%d,%d\n", tri.face[0], tri.face[1], tri.face[2]);
    			for (int j=0; j<3; j++)
    			{
    				printf("[%d]%f, [%d]%f\n", j, vertexVector[tri[i]].coord[j], j, vertexVector[tri[(i+1)%3]].coord[j]);
    			}
    			//exit(890);
    		}

    		if (infoStruct.maxEdge==0||infoStruct.maxEdge<result)
    		{
    			infoStruct.maxEdge=result;
    		}
    		if (infoStruct.minEdge==0||infoStruct.minEdge>result)
    		{
    			infoStruct.minEdge=result;
    		}
    	}
    }

    void Octree::sortTriangle(FaceStruct& triangle)
    {
    	sortTriangleIndex(triangle.face);
    }

    ¡¡

    file name: octreeLoader.cpp

    #include <GL/glut.h>
    #include "octree.h"
    #include "octreeLoader.h"
    #include "frustum.h"
    #include <cstdio>
    #include <vector>
    #include <set>
    #include <deque>
    #include <stack>

    using namespace std;

    void getLastError();
    //bool OctreeLoader::conservativeMode=false;

    unsigned int OctreeLoader::loadedVertexCounter=0;
    unsigned int OctreeLoader::loadedFaceCounter=0;
    OctreeNodeSet OctreeLoader::loadedNodeSet;
    //HANDLE OctreeLoader::loadingThread=NULL;
    //HANDLE OctreeLoader::mutex=NULL;
    //HANDLE OctreeLoader::event=NULL;
    HANDLE OctreeLoader::vertexMapHandle=NULL;
    HANDLE OctreeLoader::faceMapHandle=NULL;
    //OctreeNodeQueue OctreeLoader::loadingQueue;

    //CRITICAL_SECTION OctreeLoader::criticalSection;

    //bool OctreeLoader::canTerminate=false;

    int OctreeLoader::currentIndex=0;
    int OctreeLoader::nextIndex=1;
    Octree* OctreeLoader::root=NULL;
    OctreeNodeSet OctreeLoader::nodeSet[MaxCoherentFrameNumber];

    //int OctreeLoader::ignoredLevel=12;

    int OctreeLoader::capacityFactor=4096;

    /*
    DWORD WINAPI loadingProc(LPVOID param)
    {
    	while (!OctreeLoader::canTerminate)
    	{
    		if (WaitForSingleObject(OctreeLoader::mutex, INFINITE)!=WAIT_OBJECT_0)
    		{
    			printf("wait error %d\n", GetLastError());
    			exit(8);
    		}
    		if (OctreeLoader::loadingQueue.empty())
    		{
    			if (!ReleaseMutex(OctreeLoader::mutex))
    			{
    				printf("release error %d\n", GetLastError());
    				exit(8);
    			}
    			continue;
    		}
    		OctreeLoader::mapOctreeNode(OctreeLoader::loadingQueue.front());
    		OctreeLoader::loadingQueue.pop_front();
    		if (!ReleaseMutex(OctreeLoader::mutex))
    		{
    			printf("release error %d\n", GetLastError());
    			exit(8);
    		}

    	}
    	return 0;
    }
    */

    void vectorMultiply(VVector v1, VVector v2, VVector result)
    {
    	for (int i=0; i<3; i++)
    	{
    		result[i]=v1[i]*v2[i];
    	}
    }

    void vectorScalor(VVector v1, VType scalor)
    {
    	for (int i=0; i<3; i++)
    	{
    		v1[i]*=scalor;
    	}
    }

    void vectorScalor(VVector v1, VType scalor, VVector result)
    {
    	for (int i=0; i<3; i++)
    	{
    		result[i]=v1[i]*scalor;
    	}
    }

    void vectorMinus(VVector v1, VVector v2, VVector result)
    {
    	for (int i=0; i<3; i++)
    	{
    		result[i]=v1[i]-v2[i];
    	}
    }

    void vectorPlus(VVector v1, VVector v2, VVector result)
    {
    	for (int i=0; i<3; i++)
    	{
    		result[i]=v1[i]+v2[i];
    	}
    }

    void vectorAssign(VVector source, VVector result)
    {
    	for (int i=0; i<3; i++)
    	{
    		result[i]=source[i];
    	}
    }

    //v1=v0 + ops*offset
    //v0=v1-ops*offset
    void reverseCalcCenter(VVector subCenter, VType width, VVector operations, VVector result)
    {
    	vectorScalor(operations, (-1.0)*width, result);
    	vectorPlus(subCenter, result, result);
    }

    void calcRelativeOperation(VVector refOps, VVector oldOps, VVector relativeOps)
    {
    	vectorMultiply(refOps, oldOps, relativeOps);
    }

    //calc image coord of 'coord' with respect to center
    //let me explain as following:
    //  v1= v0 + op1 * offset
    //	v2= v0 + op2 * offset
    //  op1* op1= op2*op2 = IdentityVector
    //  v2=v0+ op2*( op1*op1*offset)=v0+op2*(op1*(v1-v0))= v0 + (v1-v0)*op2*op1
    //so relative operation =op1*op2
    void calcCenterImage(VVector center, VVector coord, VVector relativeOps, VVector result)
    {
    	VVector delta;
    	vectorMinus(coord, center, delta);
    	vectorMultiply(delta, relativeOps, result);
    	vectorPlus(center, result, result);
    }

    void calcShiftedLeafNode(Octree* refOctree, OctreeShiftDescriptor& octreeShift, Octree* result)
    {
    	int i;
    	memcpy(result, refOctree, sizeof(Octree));
    	result->level=refOctree->level+octreeShift.levelOffset;

    	calcCenterImage(octreeShift.newCenter, refOctree->center, octreeShift.relativeOps, result->center);

    	result->myNodeIndex=refOctree->myNodeIndex+octreeShift.indexOffset;
    	result->parentIndex=refOctree->parentIndex+octreeShift.indexOffset;

    	result->vStartPageIndex=refOctree->vStartPageIndex+octreeShift.vPageOffset;
    	result->fStartPageIndex=refOctree->fStartPageIndex+octreeShift.fPageOffset;

    	//surely you have noticed that fileAddLow and fileAddHigh are redundent data since
    	//we already have the startpageIndex, but the whole point is to use space for speed.
    	result->calculateFileOffsetV(result->vStartPageIndex, 0, result->vFileAddLow, result->vFileAddHigh);
    	result->calculateFileOffsetF(result->fStartPageIndex, 0, result->fFileAddLow, result->fFileAddHigh);

    	for (i=0; i<8; i++)
    	{
    		if (refOctree->sons[i]!=-1)
    		{
    			result->sons[i]=refOctree->sons[i]+octreeShift.indexOffset;
    		}
    	}

    	for (i=0; i<refOctree->neighbourCount; i++)
    	{
    		result->neighbours[i]=refOctree->neighbours[i]+octreeShift.indexOffset;
    	}
    }

    //raw data just needs to be copied after it is transformed
    void OctreeLoader::copyOctreeLeafNode(Octree* refOctree, Octree* targetOctree, OctreeShiftDescriptor& octreeShift)
    {
    	void* source;
    	int i;
    	PlyVertex* refVertex;
    	PlyVertex  targetVertex;
    	//FaceStruct* refFace;
    	//FaceStruct targetFace;
    	source=GenericMapViewOfFile(vertexMapHandle, true, refOctree->vFileAddHigh, refOctree->vFileAddLow,
    		refOctree->vMapSize);

    	GenericSetFilePointer(Octree::vertexHandle, targetOctree->vFileAddLow, targetOctree->vFileAddHigh);
    	refVertex=(PlyVertex*) source;
    	for (i=0; i<refOctree->vNumber+refOctree->vRepeatNumber; i++)
    	{
    		memcpy(targetVertex.color, refVertex[i].color, sizeof(UCVector));
    		memcpy(targetVertex.normal, refVertex[i].normal, sizeof(VVector));
    		calcCenterImage(octreeShift.newCenter, refVertex[i].coord, octreeShift.relativeOps, targetVertex.coord);
    		GenericWriteFile(Octree::vertexHandle, &targetVertex, sizeof(PlyVertex));
    	}
    	GenericUnmapViewOfFile(source, refOctree->vMapSize);

    	source=GenericMapViewOfFile(faceMapHandle, true, refOctree->fFileAddHigh, refOctree->fFileAddLow,
    		refOctree->fMapSize);
    	GenericSetFilePointer(Octree::faceHandle, targetOctree->fFileAddLow, targetOctree->fFileAddHigh);
    	GenericWriteFile(Octree::faceHandle, source, refOctree->fMapSize);

    	GenericUnmapViewOfFile(source, refOctree->fMapSize);

    }

    void OctreeLoader::calcOctreeShiftDescriptor(OctreeShiftDescriptor& octreeShift, int i)
    {
    	octreeShift.levelOffset=1;
    	calcRelativeOperation(root->operations[0], root->operations[i], octreeShift.relativeOps);
    	octreeShift.indexOffset=root->infoStruct.nodeCount*i +1;
    	octreeShift.vPageOffset=root->infoStruct.totalVPageCount*i;
    	octreeShift.fPageOffset=root->infoStruct.totalFPageCount*i;
    }

    void OctreeLoader::calcNewOctreeRoot(Octree& newRoot)
    {
    	memcpy(&newRoot, &root[0], sizeof(Octree));
    	reverseCalcCenter(root[0].center, root[0].width, root->operations[0], newRoot.center);
    	//in order to make data dense, we use half width. maybe 1/4?
    	//reverseCalcCenter(root[0].center, root[0].width/4.0, root->operations[0], newRoot.center);
    	newRoot.width=root[0].width*2.0;
    	newRoot.vNumber=root[0].vNumber*8;
    	newRoot.fNumber=root[0].fNumber*8;
    	newRoot.vRepeatNumber=root[0].vRepeatNumber*8;
    	newRoot.fRepeatNumber=root[0].fRepeatNumber*8;
    	newRoot.parentIndex=-1;
    	newRoot.sonCount=8;
    	newRoot.level=0;
    	newRoot.myNodeIndex=0;
    	newRoot.vAddress=newRoot.fAddress=NULL;
    	newRoot.vFileAddLow=newRoot.vFileAddHigh=newRoot.fFileAddLow=newRoot.fFileAddHigh=-1;

    	for (int i=0; i<8; i++)
    	{
    		newRoot.sons[i]=root->infoStruct.nodeCount*i+1;
    	}
    }

    void OctreeLoader::expandBy8()
    {
    	Octree* octreeBuffer;
    	Octree newRoot;

    	OctreeShiftDescriptor octreeShift;
    	int i, j;

    	initialize(false);
    	octreeBuffer=new Octree[root->infoStruct.nodeCount];
    	CHECK_NEW(octreeBuffer);
    	//considering the original cube is the first, we only need shift 7

    	calcNewOctreeRoot(newRoot);
    	vectorAssign(newRoot.center, octreeShift.newCenter);

    	GenericSetFilePointer(root->octreeHandle, 0, 0);
    	GenericWriteFile(root->octreeHandle, &newRoot, sizeof(Octree));

    	for (i=0; i<8; i++)
    	{
    		calcOctreeShiftDescriptor(octreeShift, i);

    		for (j=0; j<root->infoStruct.nodeCount; j++)
    		{
    			calcShiftedLeafNode(root+j, octreeShift, octreeBuffer+j);
    			if (i!=0&&root[j].sonCount==0)
    			{
    				//i==0 no need copy
    				copyOctreeLeafNode(root+j, octreeBuffer+j, octreeShift);
    			}
    			if (j==0)
    			{
    				octreeBuffer[0].parentIndex=0;
    			}
    		}
    		GenericWriteFile(root->octreeHandle, octreeBuffer, sizeof(Octree)*root->infoStruct.nodeCount);
    		printf("shift-expand finishes %d ...\n", i);
    	}
    	delete[]octreeBuffer;
    	GenericCloseFileMapping(vertexMapHandle);
    	GenericCloseFileMapping(faceMapHandle);

    	root->infoStruct.leafNodeNumber*=8;
    	root->infoStruct.nodeCount=root->infoStruct.nodeCount*8+1;
    	memcpy(root->infoStruct.origin, root[0].center, sizeof(VVector));
    	root->infoStruct.totalVPageCount*=8;
    	root->infoStruct.totalFPageCount*=8;
    	root->infoStruct.totalFace*=8;
    	root->infoStruct.totalVertex*=8;
    	root->infoStruct.totalNeighbourCount*=8;
    	root->infoStruct.totalRepeatVertex*=8;
    	root->infoStruct.totalRepeatFace*=8;
    	root->infoStruct.maxDepth+=1;

    	while (root->infoStruct.maxNodeArrayLength<=root->infoStruct.nodeCount)
    	{
    		root->infoStruct.maxNodeArrayLength+=NodeArrayLengthIncrement;
    	}
    	GenericSetFilePointer(root->infoHandle, 0, 0);
    	GenericWriteFile(root->infoHandle, &(root->infoStruct), sizeof(InfoStruct));

    	delete[] Octree::root;
    	GenericCloseFile(Octree::infoHandle);
    	GenericCloseFile(Octree::octreeHandle);
    	GenericCloseFile(Octree::vertexHandle);
    	GenericCloseFile(Octree::faceHandle);
    	//now let's update infoStruct

    }

    //end of expand by 8
    ////////////////////////////////////////////////////////////////////////////////////

    void OctreeLoader::paintPseudoColor()
    {
    	int i, j;
    	PlyVertex* vertexPtr;
    	//int* intPtr;
    	//int temp;
    	//const unsigned int HeadMask=0x00FFFFFF;
    	printf("starts creating pseudo-color data...\n");
    	initialize(false);
    	printf("finishes initializing...\n");
    	for (i=0; i<root->infoStruct.nodeCount; i++)
    	{
    		if (root[i].sonCount==0)
    		{
    			/*
    			if ((vertexPtr=(PlyVertex*)MapViewOfFile(vertexMapHandle, FILE_MAP_WRITE,
    				root[i].vFileAddHigh, root[i].vFileAddLow, root[i].vMapSize))==0)
    			{
    				printf("map vertex failed %d\n", GetLastError());
    				exit(4);
    			}
    			*/
    			vertexPtr=(PlyVertex*)GenericMapViewOfFile(vertexMapHandle, false,
    				root[i].vFileAddHigh, root[i].vFileAddLow, root[i].vMapSize);

    			for (j=0; j<root[i].vNumber+root[i].vRepeatNumber; j++)
    			{
    				//this will have trouble for 64bit integer.
    				memcpy(vertexPtr[j].color, &i, sizeof(UCVector));
    				//intPtr=(int*)(vertexPtr[j].color);
    				//temp=(*intPtr)&HeadMask;
    			}
    			/*
    			if (UnmapViewOfFile(vertexPtr)==0)
    			{
    				printf("unmap failed %d\n", GetLastError());
    				exit(34);
    			}
    			*/
    			GenericUnmapViewOfFile(vertexPtr, root[i].vMapSize);
    		}
    	}
    	printf("finishes creating pseudo color data...\n");
    	uninitialize();
    }

    void OctreeLoader::readInfo()
    {
    	//Octree::readInfo(true);
    	Octree::initialize(true);
    	Octree::infoStruct.display();
    }

    void OctreeLoader::initialize(bool readOnly, bool fakeColor)
    {
    	//octree= new Octree;
    	//DWORD accessRight=readOnly?PAGE_READONLY:PAGE_READWRITE;

    	Octree::initialize(readOnly, fakeColor);
    	root=Octree::root;
    	vertexMapHandle=GenericCreateFileMapping(Octree::vertexHandle, readOnly);
    	faceMapHandle=GenericCreateFileMapping(Octree::faceHandle, readOnly);
    	/*
    	vertexMapHandle=CreateFileMapping(octree->vertexHandle, NULL, accessRight, 0, 0, NULL);
    	if (vertexMapHandle==NULL)
    	{
    		printf("error code=%d\n", GetLastError());
    		exit(2);
    	}

    	faceMapHandle=CreateFileMapping(octree->faceHandle, NULL, accessRight, 0, 0, NULL);
    	if (faceMapHandle==NULL)
    	{
    		printf("error code=%d\n", GetLastError());
    		exit(2);
    	}
    	*/
    }

    void OctreeLoader::uninitialize()
    {
    	Octree::uninitialize(true);
    	GenericCloseFileMapping(vertexMapHandle);
    	GenericCloseFileMapping(faceMapHandle);
    }

    /*
    OctreeLoader::OctreeLoader()
    {
    	if ((mutex=CreateMutex(NULL, FALSE, NULL))==NULL)
    	{
    		printf("create mutex failed\n");
    		exit(89);
    	}

    	if ((loadingThread=CreateThread(NULL, 0, loadingProc, NULL, 0, NULL))==NULL)
    	{
    		printf("create loading thread failed\n");
    		exit(8);
    	}

    	//InitializeCriticalSection(&criticalSection);
    	//printPriority();
    }
    */

    void OctreeLoader::addCandidates(int count, int* array)
    {
    	for (int i=0; i<count; i++)
    	{
    		addCandidate(array[i]);
    	}
    }

    /*
    void OctreeLoader::calculateOctree(int nodeIndex)
    {
    	Octree* ptr=root+nodeIndex;
    	int i;
    	int result=frustum.cubeInsideFrustum(ptr->center[0], ptr->center[1],
    		ptr->center[2], ptr->width/2.0);

    	switch (result)
    	{
    	case 1:
    		//inside
    		nodeSet[nextIndex].insert(nodeIndex);
    		break;
    	case 0:
    		//intersect
    		if (ptr->sonCount==0)
    		{
    			nodeSet[nextIndex].insert(nodeIndex);
    		}
    		else
    		{
    			for (i=0; i<8; i++)
    			{
    				if (ptr->sons[i]!=-1)
    				{
    					calculateOctree(ptr->sons[i]);
    				}
    			}
    		}
    		break;
    	case -1:
    		//outside
    		break;
    	}
    }
    */

    /*
    bool OctreeLoader::testAndAdd(int nodeIndex)
    {
    	Octree*ptr=root+nodeIndex;

    	//we only counts the leaf node data
    	faceCounter+=root[nodeIndex].fNumber;
    	vertexCounter+=root[nodeIndex].vNumber;
    	nodeSet[nextIndex].insert(nodeIndex);
    	return true;
    }
    */

    /*
    bool OctreeLoader::testAndAdd(int nodeIndex)
    {
    	int testing;
    	Octree*ptr=root+nodeIndex;
    	//insert successful
    	testing=frustum.CubeInFrustum(ptr->center[0], ptr->center[1], ptr->center[2],
    		ptr->width/2.0);
    	if (testing==0||testing==1)
    	{
    		//we only counts the leaf node data
    		faceCounter+=root[nodeIndex].fNumber;
    		vertexCounter+=root[nodeIndex].vNumber;
    		nodeSet[nextIndex].insert(nodeIndex);
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    */

    void OctreeLoader::addCandidate(int index)
    {
    	nodeSet[nextIndex].insert(index);

    	mapOctreeNode(index);
    /*
    	if (WaitForSingleObject(mutex, INFINITE)!=WAIT_OBJECT_0)
    	{
    		printf("wait failed %d\n", GetLastError());
    		exit(87);
    	}
    	loadingQueue.push_back(index);
    	//mapOctreeNode(index);
    	if (!ReleaseMutex(mutex))
    	{
    		printf("release error %d\n", GetLastError());
    		exit(8);
    	}
    */
    }

    //assume:node itself passed frustum testing, now put neighbours
    //Please note here: it is possible the neighbour of leaf node is not leaf!!!!!

    /*
    void OctreeLoader::showConservative()
    {
    	int missNode=0, missVertex=0, missTriangle=0;
    	int errorCount=0, i;
    	int totalNode=0, totalVertex=0, totalTriangle=0;
    	if (conservativeMode)
    	{
    		for (i=0; i<root->infoStruct.nodeCount; i++)
    		{
    			if (root[i].sonCount==0)
    			{
    				if (!frustum.canCullCube(root[i].center[0], root[i].center[1],
    					root[i].center[2], root[i].width/2.0))
    				{
    					totalNode++;
    					totalVertex+=root[i].vNumber+root[i].vRepeatNumber;
    					totalTriangle+=root[i].fNumber;
    					if (nodeSet[nextIndex].find(i)==nodeSet[nextIndex].end())
    					{
    						missNode++;
    						missVertex+=root[i].vNumber+root[i].vRepeatNumber;
    						missTriangle+=root[i].fNumber;
    					}
    				}
    				else
    				{
    					if (nodeSet[nextIndex].find(i)!=nodeSet[nextIndex].end())
    					{
    						errorCount++;
    					}
    				}
    			}
    		}

    		if (totalVertex!=0&&totalTriangle!=0&&nodeSet[nextIndex].size()!=0)
    		{
    			printf("missNode=%d[%f], \nmissVertex=%d[%f], \nmissTriangle=%d[%f], \
    				\nerrorCount=%d\n", missNode, 100.0*(float)(missNode)/(float)totalNode,
    				missVertex, 100.0*(float)missVertex/(float)totalVertex, missTriangle,
    				100.0*(float)missTriangle/(float)totalTriangle, errorCount);
    		}
    	}
    }
    */

    void OctreeLoader::unmapUnusedNodes()
    {
    	OctreeNodeSet::iterator it, result, temp;
    	bool canUnmap;
    	if (loadedVertexCounter>capacityFactor*MaxVertexCapacity*ThresholdRate
    		||loadedFaceCounter>capacityFactor*MaxFaceCapacity*ThresholdRate)
    	{
    		printf("begin unmap unused nodes...\n");
    		it=loadedNodeSet.begin();

    		while (it!=loadedNodeSet.end())
    		{
    			canUnmap=true;

    			for (int i=0; i<MaxCoherentFrameNumber; i++)
    			{
    				//printf("loaded size %d, nodeset size %d\n", loadedNodeSet.size(), nodeSet[i].size());
    				if ((result=nodeSet[i].find(*it))!=nodeSet[i].end())
    				{
    					//we find it is used recently
    					canUnmap=false;
    					//printf("find in %d set\n", i);
    					//printf("source %d, target %d\n", *it, *result);
    					break;
    				}
    			}
    			if (canUnmap)
    			{
    				//unmap	we remove
    				/*
    				if (UnmapViewOfFile(root[*it].vAddress)==0)
    				{
    					printf("unmap failed\n");
    					exit(333);
    				}
    				*/
    				GenericUnmapViewOfFile(root[*it].vAddress, root[*it].vMapSize);
    				root[*it].vAddress=NULL;
    				loadedVertexCounter-=(root[*it].vNumber+root[*it].vRepeatNumber);
    				if (loadedVertexCounter==0)
    				{
    					printf("what?\n");
    				}

    				/*
    				if (UnmapViewOfFile(root[*it].fAddress)==0)
    				{
    					printf("unmap failed\n");
    					exit(322);
    				}
    				*/
    				GenericUnmapViewOfFile(root[*it].fAddress, root[*it].fMapSize);

    				root[*it].fAddress=NULL;
    				loadedFaceCounter-=(root[*it].fNumber+root[*it].fRepeatNumber);

    				//in windows, compiler don't complain. but g++ is a nusance
    				//it=loadedNodeSet.erase(it);
    				//in linux we have to change to like this:
    				temp=it;
    				it++;
    				loadedNodeSet.erase(temp);
    			}
    			else
    			{
    				++it;
    			}
    		}
    	}
    }

    /*
    void OctreeLoader::printPriority()
    {
    	int priority;

    	if ((priority=GetThreadPriority(loadingThread))==THREAD_PRIORITY_ERROR_RETURN)
    	{
    		printf("get priority failed\n");
    		exit(8);
    	}

    	switch (priority)
    	{
    	case THREAD_PRIORITY_ABOVE_NORMAL:
    		printf("THREAD_PRIORITY_ABOVE_NORMAL\n");
    		break;
    	case THREAD_PRIORITY_BELOW_NORMAL:
    		printf("THREAD_PRIORITY_BELOW_NORMAL\n");
    		break;
    	case THREAD_PRIORITY_HIGHEST:
    		printf("THREAD_PRIORITY_HIGHEST\n");
    		break;
    	case THREAD_PRIORITY_IDLE:
    		printf("THREAD_PRIORITY_IDLE\n");
    		break;
    	case THREAD_PRIORITY_LOWEST:
    		printf("THREAD_PRIORITY_LOWEST\n");
    		break;
    	case THREAD_PRIORITY_NORMAL:
    		printf("THREAD_PRIORITY_NORMAL\n");
    		break;
    	case THREAD_PRIORITY_TIME_CRITICAL:
    		printf("THREAD_PRIORITY_TIME_CRITICAL\n");
    		break;
    	}
    }
    */

    /*
    	if (fullRenderMode)
    	{
    		for (i=0; i<root->infoStruct.nodeCount; i++)
    		{
    			if (root[i].sonCount==0)
    			{
    				glEnableClientState (GL_VERTEX_ARRAY);
    				glEnableClientState (GL_NORMAL_ARRAY);
    				glEnableClientState (GL_COLOR_ARRAY);
    				mapOctreeNode(i);
    				displayOctreeLeafNode(i);
    				glDisableClientState (GL_VERTEX_ARRAY);
    				glDisableClientState (GL_NORMAL_ARRAY);
    				glDisableClientState (GL_COLOR_ARRAY);
    				/*
    				if (!frustum.canCullCube(root[i].center[0], root[i].center[1],
    					root[i].center[2], root[i].width/2.0))
    				{
    					glEnableClientState (GL_VERTEX_ARRAY);
    					glEnableClientState (GL_NORMAL_ARRAY);
    					glEnableClientState (GL_COLOR_ARRAY);
    					mapOctreeNode(i);
    					displayOctreeLeafNode(i);
    					glDisableClientState (GL_VERTEX_ARRAY);
    					glDisableClientState (GL_NORMAL_ARRAY);
    					glDisableClientState (GL_COLOR_ARRAY);
    					hitCount++;
    				}
    				else
    				{
    					cullCount++;
    				}

    			}
    		}
    		//printf("cullCount=%d, hitCount=%d\n", cullCount, hitCount);
    		return;
    	}
    	*/

    void OctreeLoader::swapBuffer()
    {
    	//int i, cullCount=0, hitCount=0;
    	int i;
    	OctreeNodeSet::iterator it;
    	//printPriority();

    	unmapUnusedNodes();
    	/*
    	//this must be safe
    	if (WaitForSingleObject(mutex, INFINITE)!=WAIT_OBJECT_0)
    	{
    		printf("wait failed %d\n", GetLastError());
    		exit(67);
    	}
    	unmapUnusedNodes();
    	if (!ReleaseMutex(mutex))
    	{
    		printf("release error %d\n", GetLastError());
    		exit(8);
    	}
    	*/
    	//loadingQueue.clear();

    	//waitLoadingThread();

    	//showConservative();

    	//printf("currentset size=%d and nextset size=%d\n", nodeSet[currentIndex].size(), nodeSet[nextIndex].size());

    	//we will unmap only when both previous and current node set is no longer needed

    	glEnableClientState (GL_VERTEX_ARRAY);
    	glEnableClientState (GL_NORMAL_ARRAY);
    	glEnableClientState (GL_COLOR_ARRAY);
    	//printf("nextset size=%d\n", nodeSet[nextIndex].size());
    	for (it=nodeSet[nextIndex].begin(); it!=nodeSet[nextIndex].end(); it++)
    	{
    		displayOctreeLeafNode(*it);
    	}
    	glDisableClientState (GL_VERTEX_ARRAY);
    	glDisableClientState (GL_NORMAL_ARRAY);
    	glDisableClientState (GL_COLOR_ARRAY);

    	currentIndex=nextIndex;
    	nextIndex=(nextIndex+1)%MaxCoherentFrameNumber;
    	nodeSet[nextIndex].clear();

    	//Sleep(0);
    }

    /*
    void OctreeLoader::waitLoadingThread()
    {
    	while (true)
    	{
    		if (WaitForSingleObject(mutex, INFINITE)!=WAIT_OBJECT_0)
    		{
    			printf("wait event failed\n");
    			exit(898);
    		}

    		if (loadingQueue.empty())
    		{
    			if (!ReleaseMutex(mutex))
    			{
    				printf("release mutex failed %d\n", GetLastError());
    				exit(1);
    			}
    			break;
    		}

    		if (!ReleaseMutex(mutex))
    		{
    			printf("releate mutex failed\n");
    			exit(GetLastError());
    		}
    	}
    }
    */

    void OctreeLoader::mapOctreeNode(int index)
    {
    	//EnterCriticalSection(&criticalSection);

    	if (root[index].vAddress==NULL)
    	{
    		/*
    		if ((root[index].vAddress=MapViewOfFile(vertexMapHandle, FILE_MAP_READ,
    			root[index].vFileAddHigh, root[index].vFileAddLow, root[index].vMapSize))==0)
    		{
    			printf("map vertex failed %d\n", GetLastError());
    			exit(4);
    		}
    		*/
    		root[index].vAddress=GenericMapViewOfFile(vertexMapHandle, true, root[index].vFileAddHigh,
    			root[index].vFileAddLow, root[index].vMapSize);

    		loadedVertexCounter+=root[index].vNumber+root[index].vRepeatNumber;
    		loadedNodeSet.insert(index);
    	}
    	/////////////////////////////

    	//////////////////////////////

    	if (root[index].fAddress==NULL)
    	{
    		/*
    		if((root[index].fAddress=MapViewOfFile(faceMapHandle, FILE_MAP_READ,
    			root[index].fFileAddHigh, root[index].fFileAddLow, root[index].fMapSize))==0)
    		{
    			printf("map face failed %d\n", GetLastError());
    			exit(4);
    		}
    		*/
    		root[index].fAddress=GenericMapViewOfFile(faceMapHandle, true, root[index].fFileAddHigh,
    			root[index].fFileAddLow, root[index].fMapSize);

    		loadedFaceCounter+=root[index].fNumber+root[index].fRepeatNumber;
    	}
    	//LeaveCriticalSection(&criticalSection);
    	/*
    	if (ReleaseMutex(mutex)==0)
    	{
    		printf("release failed=%d\n", GetLastError());
    		exit(78);
    	}
    	*/

    	/////////////////////////////
    }

    /*
    OctreeLoader::~OctreeLoader()
    {
    	canTerminate=true;
    	ReleaseMutex(mutex);
    }
    */
    void outputTriangles(char* fileName, int number, PlyVertex* vPtr, UIVector* fPtr)
    {
    	FILE* stream;
    	stream=fopen(fileName, "w");
    	for (int i=0; i<number; i++)
    	{
    		fprintf(stream, "no#%d triangle:\n", i);
    		for (int j=0; j<3; j++)
    		{
    			fprintf(stream, "[%f,%f,%f],", vPtr[fPtr[i][j]].coord[0],
    				vPtr[fPtr[i][j]].coord[1], vPtr[fPtr[i][j]].coord[2]);
    		}
    		fprintf(stream, "\n");
    	}
    	fclose(stream);
    }

    void OctreeLoader::displayOctreeLeafNode(int index)
    {
    	PlyVertex* vPtr;
    	UIVector* fPtr;
    	//static bool doOnce=true;

    	//mapOctreeNode(index);
    	if (root[index].vAddress==NULL ||root[index].fAddress==NULL)
    	{
    		printf("unexpected error\n");
    		exit(87);
    	}

    	vPtr=(PlyVertex*)(root[index].vAddress);
    	fPtr=(UIVector*)(root[index].fAddress);

    	getLastError();
    #ifdef USE_DOUBLE_PRECISION
    	glVertexPointer(3, GL_DOUBLE, sizeof(PlyVertex), &(vPtr->coord));
    #else
    	glVertexPointer(3, GL_FLOAT, sizeof(PlyVertex), &(vPtr->coord));
    #endif

    	glNormalPointer(GL_FLOAT, sizeof(PlyVertex), &(vPtr->normal));
    	glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(PlyVertex), &(vPtr->color));

    	getLastError();

    	glDrawElements(GL_TRIANGLES, (root[index].fNumber+root[index].fRepeatNumber)*3,
    		GL_UNSIGNED_INT, fPtr);

    	getLastError();
    }

    void getLastError()
    {
    	GLenum err;
    	char msg[256];
    	err=glGetError();
    	switch(err)
    	{
    	case GL_NO_ERROR:
    		sprintf(msg, "GL_NO_ERROR");
    		break;
    	case GL_INVALID_ENUM:
    		sprintf(msg, "GL_INVALID_ENUM");
    		break;
    	case GL_INVALID_VALUE:
    		sprintf(msg, "GL_INVALID_VALUE");
    		break;
    	case GL_INVALID_OPERATION:
    		sprintf(msg, "GL_INVALID_OPERATION");
    		break;
    	case GL_STACK_OVERFLOW:
    		sprintf(msg, "GL_STACK_OVERFLOW");
    		break;
    	case GL_STACK_UNDERFLOW:
    		sprintf(msg, "GL_STACK_UNDERFLOW");
    		break;
    	case GL_OUT_OF_MEMORY:
    		sprintf(msg, "GL_OUT_OF_MEMORY");
    		break;
    	}
    	if (err!=GL_NO_ERROR)
    	{
    		printf("error message %s\n", msg);
    		exit(err);
    	}
    }

¡¡

¡¡

file name: renderEngine.cpp

    /***********************************************************************************

    ************************************************************************************/

    #include <GL/glut.h>
    #include "plyModel.h"
    #include "octree.h"
    #include "octreeLoader.h"
    #include "viewCollector.h"
    #include "renderEngine.h"
    #include <sys/stat.h>
    #include <cstdio>
    #include <cmath>

    #ifndef LINUX_VERSION
    #include <windows.h>
    #endif
    //#include "plyloader.h"

    #define RELEASE_VERSION 1

    typedef GLfloat Color[4];
    const VType DefaultPitch=0;
    const VType DefaultYaw=45;
    const VType DefaultRoll=0;
    const Color color[MaxLightNumber] =
    {
    	{ 1.0, 0.0, 0.0 , 1.0},	// RED
    	{ 0.0, 1.0, 0.0 , 1.0},	// GREEN
    	{ 0.0, 0.0, 1.0 , 1.0},	// BLUE
    	{ 1.0, 1.0, 0.0 , 1.0},	// YEWLLOW
    	{ 0.0, 1.0, 1.0 , 1.0},	// MAGENTA
    	{ 1.0, 0.0, 1.0 , 1.0},	// PINK
    	{ 1.0, 1.0, 1.0 , 1.0},	// WHITE
    	{ 0.0, 0.0, 0.0 , 1.0},	// BLACK

    };
    const GLfloat no_mat[] = { 0.0, 0.0, 0.0, 1.0 };
    const GLfloat half_mat[] = { 1.0, 1.0, 1.0, 1.0 };
    const GLfloat light_diffuse[] = {0.9, 0.9, 0.9, 1.0};	//{ 1.0, 1.0, 1.0, 1.0 };
    const GLfloat light_specular[]={1.0, 1.0, 1.0, 1.0};

    const GLfloat light_position[MaxLightNumber][4] =
    {
    	{0.0, 0.0, -5.0, 1.0 },
    	{0.0, 15.0, 0.0, 1.0 },
    	{0.0, -15.0, 0.0, 1.0 },

    	{0.0, 21.0, 0.0, 1.0 },
    	{0.0, -10.0, 0.0, 1.0 },
    	{0.0, -14.0, 0.0, 1.0 },
    	{0.0, -16.0, 0.0, 1.0 },
    	{0.0, -11.0, 0.0, 1.0 }
    };

    const GLfloat light_direction[MaxLightNumber][3] =
    {
    	{0.0, 0.0, 1.0 },
    	{0.0, -1.0, 0.0 },
    	{0.0, -1.0, 0.0 },
    	{0.0, -1.0, 0.0 },
    	{0.0, 1.0, 0.0 },
    	{0.0, 1.0, 0.0 },
    	{0.0, 1.0, 0.0 },
    	{0.0, 1.0, 0.0 }
    };

    const GLfloat fire[] = { 1.0f, 0.6f, 0.2f, 0.8f };

    GLenum lightEnum[MaxLightNumber]=
    {
    	GL_LIGHT0, GL_LIGHT1, GL_LIGHT2,  GL_LIGHT3, GL_LIGHT4, GL_LIGHT5,
    		GL_LIGHT6,GL_LIGHT7
    };

    void Lighting::init()
    {
    	int i;

    	lightEnabled=false;
    	for (i=0; i<currentLightNumber; i++)
    	{
    		glLightfv(lightEnum[i], GL_AMBIENT, no_mat);
    		//glLightfv(lightEnum[i], GL_DIFFUSE, light_diffuse);
    		glLightfv(lightEnum[i], GL_DIFFUSE, color[i]);
    		//glLightfv(lightEnum[i], GL_SPECULAR, light_specular);
    		glLightfv(lightEnum[i], GL_SPECULAR, color[i]);

    		glLightf(lightEnum[i], GL_CONSTANT_ATTENUATION, 1.0);
    		glLightf(lightEnum[i], GL_LINEAR_ATTENUATION, 0.0);
    		glLightf(lightEnum[i], GL_QUADRATIC_ATTENUATION, 0.0);

    		//glLightfv(lightEnum[i], GL_POSITION, light_position[i]);
    		glLightfv(lightEnum[i], GL_POSITION, light_position[i]);
    		glLightfv(lightEnum[i], GL_SPOT_DIRECTION, light_direction[i]);
    		glLightf(lightEnum[i], GL_SPOT_CUTOFF, 40.0);
    	}

    	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color[WHITE]);
    	glMaterialfv(GL_FRONT, GL_SPECULAR, color[WHITE]);
    	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50.0);

    	//glEnable(GL_LIGHT0);
    	glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

    	/*
    	glEnable(GL_COLOR_MATERIAL);

    	for (i=0; i<currentLightNumber; i++)
    	{
    		glEnable(lightEnum[i]);
    	}
    	glEnable(GL_LIGHTING);
    	*/

    }

    void Lighting::update(const Movement& movement)
    {
    	float temp[4];
    	for (int i=0; i<currentLightNumber; i++)
    	{
    		memcpy(temp, light_position[i], sizeof(float)*4);
    		temp[0]+=movement.x;
    		temp[1]+=movement.y;
    		temp[2]+=movement.z;
    		glLightfv(lightEnum[i], GL_POSITION, temp);
    	}
    }

    void Lighting::toggleLighting()
    {
    	int i;
    	if (lightEnabled)
    	{
    		glDisable(GL_COLOR_MATERIAL);
    		glDisable(GL_LIGHTING);
    		for (i=0; i<currentLightNumber; i++)
    		{
    			glDisable(lightEnum[i]);
    		}
    		printf("lighting is disabled\n");
    	}
    	else
    	{
    		glEnable(GL_COLOR_MATERIAL);

    		for (i=0; i<currentLightNumber; i++)
    		{
    			glEnable(lightEnum[i]);
    		}
    		glEnable(GL_LIGHTING);
    		printf("lighting is enabled\n");
    	}
    	lightEnabled=!lightEnabled;
    }

    	//simpleInit();
    	//simpleInit();
    	//glutMainLoop();

    void RenderEngine::init(OctreeLoader* octreeLoader)
    {
    	//createFiles();
    	flatShading=true;
    	wireFrame=false;

    	glShadeModel(GL_FLAT);

    	lighting.currentLightNumber=8;
    	lighting.init();
    	//
    	glEnable(GL_DEPTH_TEST);
    	setupPerspective(octreeLoader);
    	updatePosition();
    }

    //////////////////////////////////////////////////////////
    //from old chopper
    void RenderEngine::keyboardCallback(unsigned char key, int x, int y)
    						// This is the callback procedure for capturing OpenGL Keyboard events.
    {
    	VType fx, fy, fz;
    	Movement temp=movement;
    	switch(key)
    	{
    /*
    	case 'z': //zoom in
    		renderContext.fovy -= 0.5;
    		//fViewVol -= 0.2;
    		break;
    	case 'Z'://zoom out
    		renderContext.fovy += 0.5;
    		//fViewVol += 0.2;
    		break;

    	case 'F': //move camera position forward
    	case 'f':
    		if(abs(radius - 0.1) > 0.0)
    		{
    			radius -= 0.1;
    		}
    		break;

    	case 'b': //move camera position backward
    	case 'B':
    		radius += 0.1;
    		break;
    */

    	case 'v':
    	case 'V':
    		if (wireFrame)
    		{
    			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    		}
    		else
    		{
    			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    		}
    		wireFrame=!wireFrame;
    		break;
    	case 'c':
    	case 'C':
    		initializeMovement(startingPos);
    		break;
    	case 'X' :
    	case 'x' :
    		showMenu();
    		break;

    	case 'n':
    	case 'N':
    		if (flatShading)
    		{
    			glShadeModel(GL_FLAT);
    			printf("flat shading\n");
    		}
    		else
    		{
    			glShadeModel(GL_SMOOTH);
    			printf("smooth shading\n");
    		}
    		flatShading=!flatShading;
    		break;
    	case 'p':
    		if (movement.pitch>-45)
    		{
    			movement.pitch-=angleOffset;
    		}
    		break;
    	case 'P':
    		if (movement.pitch<45)
    		{
    			movement.pitch+=angleOffset;
    		}
    		break;
    	case 'y':
    		movement.yaw+=angleOffset;
    		if (movement.yaw>360)
    		{
    			movement.yaw-=360;
    		}
    		break;
    	case 'Y':
    		movement.yaw-=angleOffset;
    		if (movement.yaw<-360)
    		{
    			movement.yaw+=360;
    		}
    		break;
    	case 'r':
    		if (movement.roll>-45)
    		{
    			movement.roll-=angleOffset;
    		}
    		break;
    	case 'R':
    		if (movement.roll<45)
    		{
    			movement.roll+=angleOffset;
    		}
    		break;
    	case 'U':
    		temp.pitch-=90;
    		calcCoord(positionOffset, temp, fx, fy, fz);
    		movement.x+=fx;
    		movement.y+=fy;
    		movement.z+=fz;
    		break;
    	case 'u':
    		temp.pitch+=90;
    		calcCoord(positionOffset, temp, fx, fy, fz);
    		movement.x+=fx;
    		movement.y+=fy;
    		movement.z+=fz;
    		break;
    	case 'D':
    	case 'd':
    		movement.yaw-=angleOffset;
    		if (movement.yaw<-360)
    		{
    			movement.yaw+=360;
    		}
    		break;
    	case 'a':
    	case 'A':
    		movement.yaw+=angleOffset;
    		if (movement.yaw>360)
    		{
    			movement.yaw-=360;
    		}
    		break;
    	case 's':
    	case 'S':
    		if (movement.pitch>-45)
    		{
    			movement.pitch-=angleOffset;

    		}
    		break;
    	case 'w':
    	case 'W':
    		if (movement.pitch<45)
    		{
    			movement.pitch+=angleOffset;

    		}
    		break;

    //////////////////////////////////////////////////////////////////////////////
    	case 'j':
    	case 'J':

    		movement.yaw+=angleOffset;

    		if (movement.yaw>360)
    		{
    			movement.yaw-=360;
    		}

    		break;

    	case 'l':
    	case 'L':
    		movement.yaw-=angleOffset;

    		if (movement.yaw<-360)
    		{
    			movement.yaw+=360;
    		}

    		break;

    	case 'i':		// Rotate center of projection up
    	case 'I':
    		//theta -= 0.5;
    		calcCoord(movementOffset, movement, fx, fy, fz);

    		movement.x+=fx;
    		movement.y+=fy;
    		movement.z+=fz;
    		break;

    	case 'k':	// Rotate center of projection down
    	case 'K':
    		//theta += 0.5;
    		//movement.z-=1;
    		calcCoord(-movementOffset, movement, fx, fy, fz);
    		movement.x+=fx;
    		movement.y+=fy;
    		movement.z+=fz;
    		break;
    ////////////////////////////////////////////////////////////////
    		///////////////////////////////////////////////////////////

    	case 'e':
    	case 'E':
    		lighting.toggleLighting();
    		break;
    	case 27:				//ESCAPE Code for exiting program.
    		exit(0);
    	}
    	updatePosition();
    	glutPostRedisplay();
    }

    //end of old chopper
    ///////////////////////////////////////////////

    void RenderEngine::initializeMovement(VVector origin)
    {
    	movement.x=origin[0];
    	movement.y=origin[1];
    	movement.z=origin[2];
    	movement.pitch=DefaultPitch;
    	movement.roll=DefaultRoll;
    	movement.yaw=DefaultYaw;
    }

    ///////////////////////////////////////////////////////////////////
    /////////////simple testing

    void RenderEngine::updatePosition()
    {
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    	VType fLook_x=movement.x+
    		sin(deg2rad(movement.yaw))*cos(deg2rad(movement.pitch));
    	VType fLook_y=movement.y+
    		sin(deg2rad(movement.pitch))*cos(deg2rad(movement.roll));
    	VType fLook_z=movement.z+
    		cos(deg2rad(movement.yaw))*cos(deg2rad(movement.pitch));

    	VType fUp_x = sin(deg2rad(movement.roll));
    	VType fUp_y = cos(deg2rad(movement.roll));
    	VType fUp_z = 0;

    	if (lighting.lightEnabled)
    	{
    		lighting.update(movement);
    	}

    	//printf("x=%f, y=%f, z=%f\n", movement.x, movement.y, movement.z);
    	//printf("yaw=%f, pitch=%f, roll=%f\n", movement.yaw, movement.pitch, movement.roll);
    	gluLookAt(movement.x, movement.y, movement.z, fLook_x, fLook_y, fLook_z,
    		fUp_x, fUp_y, fUp_z);

    /////////////////////////////////////////////////////////////
    	drawingContext.center[0]=movement.x;
    	drawingContext.center[1]=movement.y;
    	drawingContext.center[2]=movement.z;
    }

    void RenderEngine::setupPerspective(OctreeLoader* octreeLoader)
    {
    	int i, j;

    	//initLights();
    	//memcpy(startingPos, octreeLoader->root[0].center, sizeof(VVector));
    	memcpy(startingPos, octreeLoader->root[0].center, sizeof(VVector));
    	startingPos[2]-=octreeLoader->root[0].width/10.0;
    	startingPos[0]-=octreeLoader->root[0].width/10.0;
    	initializeMovement(startingPos);
    	/*
    	for (i=0; i<currentLightNumber; i++)
    	{
    		for (j=0; j<3; j++)
    		{
    			light_position[i][j]+=octreeLoader->root[0].center[j];
    		}
    	}
    	*/
    	drawingContext.fovy=1;
    	drawingContext.aspect=1000.0;
    	drawingContext.zFar=drawingContext.zNear=octreeLoader->root[0].width*10;
    	for (i=0; i<=octreeLoader->root[0].infoStruct.maxDepth; i++)
    	{
    		drawingContext.zNear/=2.0;
    	}
    	drawingContext.zNear*=0.1;
    	//positionOffset=movementOffset=drawingContext.zNear *100;
    	positionOffset=movementOffset=drawingContext.zNear *5;
    	angleOffset=5.0;
    	if (drawingContext.zNear==0)
    	{
    		printf("how can this be true?\n");
    		drawingContext.zNear=DefaultNearPlane;
    	}
    	//glDepthFunc(GL_LEQUAL);
    	//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    	//glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

    	//glShadeModel(GL_SMOOTH);
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    	gluPerspective(drawingContext.fovy*60.0f, (VType)scrW/(VType)scrH,
    		drawingContext.zNear, drawingContext.zFar);
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();

    }

    void RenderEngine::showMenu(void)
    {
    	printf("\n\n ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////");
    	printf("\n OpenGL PROGRAM FOR WALK THROUGH POWER PLANT\n");
    	printf("\n April 26, 2007 \n");
    	printf("////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n");
    	printf("\n\n Operations: ");

    	printf("\n Use c/C key to clear viewing parameters and return to default.");
    	printf("\n Use y/Y key for a Yaw rotation.");
    	printf("\n Use p/P key for a pitch rotation.");
    	printf("\n Use U/u/D/d to move chopper upward/downward vertically");
    	printf("\n Use n/N to toggle smooth and flat shading");
    	printf("\n Use arrow key to move forward and rotate");
    	printf("\n Use e/E to toggle lighting");
    	printf("\n Use x/X to show this menu");

    	printf("\n Use b/B to start parallel rendering mode");

    	printf("\n Use esc key to exit gracefully from program.");
    }

    inline VType RenderEngine::deg2rad(VType deg)
    {
    	return deg/360*2*PI;
    }

    inline VType RenderEngine::rad2deg(VType rad)
    {
    	return rad/2.0/PI*360.0;
    }

    void RenderEngine::calcCoord(VType distance, const Movement& position, VType &x, VType &y, VType &z)
    {
    	x=distance*cos(deg2rad(position.pitch))*sin(deg2rad(position.yaw));
    	y=distance*sin(deg2rad(position.pitch))*cos(deg2rad(position.roll));

    	z=distance*cos(deg2rad(position.pitch))*cos(deg2rad(position.yaw));
    }
    ////////////////////////////////////////////////////////////////////////////////

    void Movement::display()
    {
    	printf("x=%f,y=%f,z=%f\n", x, y, z);
    	printf("yaw=%f,pitch=%f,roll=%f\n", yaw, pitch, roll);
    }

¡¡

file name: master.cpp

    /******************************************************************
    author: qingzhe huang
    date: Jul. 31, 2007
    1. This module is a combination of both "Master" and "Displayer". Originally it
    should be separated, at least logically. However, in order to run in more nodes, I
    have to save displayer to combine it with master nodes as displayer is not a heavy
    job.
    2. The most difficult part is "tile" which troubles me for many days. Originally I
    don't really grasp the meaning of "glReadPixel" which is so powerful that allow programer
    to read only "tiles". You see, in both Testers and Renders they are using "tiles"
    to divide screen. However, when sending pixel data, Renders send pixel data according
    to "tiles". Then using "tile" as basic unit, you don't have to worry about "sequence".
    i.e. all "linear" sequence in both "pixel-visible-info" and "pixel-data" are based on
    "tiles". You just move your pointer and finally write back to color buffer in "tiles".
    The big advantage is that in network transmission data buffer needs to be consecutive and
    you don't have to make extra copy if you read and write color buffer in "tiles". This
    may not be a problem at all for somebody. But for me at beginning I read whole color
    buffer in one shot and manually divide "tiles" by index which is really painful experience.
    3. The so-called knapsack is a greedy for NP-complete problem. I don't want to waste
    too much time on it, so make it a simple greedy.

    ******************************************************************/

    #include "config.h"
    #include "master.h"
    #include "dataStructure.h"
    #include "viewCollector.h"
    #include <vector>
    #include <assert.h>
    #include <GL/glut.h>

    #ifndef  LINUX_VERSION
    #include "except.h"
    #endif

    using namespace std;

    char* MasterContext::windowTitle="Master Node";
    extern MPIContext mpiContext;

    int myIntComp(const void* firstInt, const void* secondInt)
    {
    	return ViewCollector::root[*(int*)(firstInt)].fNumber+
    		ViewCollector::root[*(int*)(firstInt)].fRepeatNumber -
    		ViewCollector::root[*(int*)(secondInt)].fNumber-
    		ViewCollector::root[*(int*)(secondInt)].fRepeatNumber;
    }

    void MasterContext::knapsack(int count, int* array, bool doSimple)
    {
    	int i;
    	mpiContext.renderContext->indexCount=count;
    	assert(count<MaxPossibleNodeCollectable);
    	mpiContext.renderShareArray=(int*)(mpiContext.renderContextBuffer+sizeof(RenderContext));
    	mpiContext.testerShareArray=(int*)(mpiContext.renderContextBuffer+sizeof(RenderContext)+sizeof(int)*count);

    	if (!doSimple)
    	{
    		//this is for render

    			//doKnapsack(count, array, RenderCount, mpiContext.renderContext->renderShare, renderShareCount,
    			//	renderVector, mpiContext.renderShareArray);
    			doKnapsack(count, array, RENDER);

    			doKnapsack(count, array, TESTER);
    			//this is for tester
    			//doKnapsack(count, array, TesterCount, mpiContext.renderContext->testerShare, testerShareCount,
    			//	testerVector, mpiContext.testerShareArray);
    	}
    	else
    	{
    		memcpy(mpiContext.renderShareArray, array, sizeof(int)*count);
    		memcpy(mpiContext.testerShareArray, array, sizeof(int)*count);
    		for (i=0; i<RenderCount; i++)
    		{
    			if (i!=RenderCount-1)
    			{
    				mpiContext.renderContext->renderShare[i]=count/RenderCount;
    			}
    			else
    			{
    				mpiContext.renderContext->renderShare[i]=count-(count/RenderCount)*(RenderCount-1);
    			}
    		}
    		for (i=0; i<TesterCount; i++)
    		{
    			if (i!=TesterCount-1)
    			{
    				mpiContext.renderContext->testerShare[i]=count/TesterCount;
    			}
    			else
    			{
    				mpiContext.renderContext->testerShare[i]=count-(count/TesterCount)*(TesterCount-1);
    			}
    		}
    	}
    	mpiContext.renderContextSize=sizeof(RenderContext)+count*2*sizeof(int);

    }

    void MasterContext::doKnapsack(int count, int* array, NodeStatus nodeStatus)
    {
    	int partyNumber;
    	int* shareArray;
    	int* shareCount;
    	vector<int>* shareVector;
    	int* output;

    	int average;
    	int i, j;
    	int offset=1;

    	//int myIntComp(const void* firstInt, const void* secondInt);
    	switch (nodeStatus)
    	{
    	case TESTER:
    		partyNumber=TesterCount;
    		shareArray=mpiContext.renderContext->testerShare;
    		shareCount=testerShareCount;
    		shareVector=testerVector;
    		output=mpiContext.testerShareArray;
    		break;
    	case RENDER:
    		partyNumber=RenderCount;
    		shareArray=mpiContext.renderContext->renderShare;
    		shareCount=renderShareCount;
    		shareVector=renderVector;
    		output=mpiContext.renderShareArray;
    		break;
    	default:
    		printf("error!\n");
    		exit(123);
    	}

    	for (i=0; i<partyNumber; i++)
    	{
    		shareArray[i]=0;
    		shareCount[i]=0;
    		shareVector[i].clear();
    	}
    	average=ViewCollector::faceCounter/partyNumber;
    	//qsort(myVisibleCount, VisibleOctreeIndexCombinationLength, sizeof(int), myIntComp);
    	//qsort(array, count, sizeof(int), myIntComp);

    	j=0;
    	//let's evenly distribute the index set into "RenderHostCount" sets
    	i=count-1;
    	while (i>=0)
    	{
    		if (shareCount[j]<average)
    		{
    			shareVector[j].push_back(array[i]);
    			shareCount[j]+=ViewCollector::root[array[i]].fNumber+
    				ViewCollector::root[array[i]].fRepeatNumber;
    			i--;
    		}
    			//going up
    		j+=offset;
    		if (j==partyNumber)
    		{
    			j=partyNumber-1;
    			offset=-1;
    		}
    		//going down
    		if (j==-1)
    		{
    			j=0;
    			offset=1;
    		}
    	}
    	offset=0;
    	for (i=0; i<partyNumber; i++)
    	{
    		memcpy(output+offset, &(*shareVector[i].begin()), sizeof(int)*shareVector[i].size());
    		shareArray[i]=shareVector[i].size();
    		offset+=shareArray[i];
    	}
    }

    void MasterContext::doKnapsack(int count, int* array, int partyNumber, int* shareArray, int* shareCount,
    				vector<int>* shareVector, int* output)
    {
    	int average;
    	int i, j;
    	int offset=1;

    	int myIntComp(const void* firstInt, const void* secondInt);

    	for (i=0; i<partyNumber; i++)
    	{
    		shareArray[i]=0;
    		shareCount[i]=0;
    		shareVector[i].clear();
    	}
    	average=ViewCollector::faceCounter/partyNumber;
    	//qsort(myVisibleCount, VisibleOctreeIndexCombinationLength, sizeof(int), myIntComp);
    	//qsort(array, count, sizeof(int), myIntComp);

    	j=0;
    	//let's evenly distribute the index set into "RenderHostCount" sets
    	i=count-1;
    	while (i>=0)
    	{
    		if (shareCount[j]<average)
    		{
    			shareVector[j].push_back(array[i]);
    			shareCount[j]+=ViewCollector::root[array[i]].fNumber+
    				ViewCollector::root[array[i]].fRepeatNumber;
    			i--;
    		}
    			//going up
    		j+=offset;
    		if (j==partyNumber)
    		{
    			j=partyNumber-1;
    			offset=-1;
    		}
    		//going down
    		if (j==-1)
    		{
    			j=0;
    			offset=1;
    		}
    	}
    	offset=0;
    	for (i=0; i<partyNumber; i++)
    	{
    		memcpy(output+offset, &(*shareVector[i].begin()), sizeof(int)*shareVector[i].size());
    		shareArray[i]=shareVector[i].size();
    		offset+=shareArray[i];
    	}
    }

    void MasterContext::init()
    {
    	int i;
    	octreeLoader.initialize(true);

    	renderEngine.init(&octreeLoader);

    	viewCollector.initialize(octreeLoader.root);

    	colorBuffer=new ubyte[ColorBufferByteSize];
    	CHECK_NEW(colorBuffer);

    	pixelDataBuffer=new ubyte[ColorBufferByteSize];
    	CHECK_NEW(pixelDataBuffer);

    	visibleTable=new ubyte[ColorBufferSize];
    	CHECK_NEW(visibleTable);

    	compressedVisibleTable=new ubyte[ColorBufferSize*VisibleTableBufferRate];
    	CHECK_NEW(compressedVisibleTable);

    	visibleTableRequests=new MPI_Request[DepthBufferSnapShotNumber];
    	CHECK_NEW(visibleTableRequests);
    	for (i=0; i<DepthBufferSnapShotNumber; i++)
    	{
    		visibleTableRequests[i]=MPI_REQUEST_NULL;
    	}

    	pixelDataRequests=new MPI_Request[RenderCount];
    	CHECK_NEW(pixelDataRequests);
    	for (i=0; i<RenderCount; i++)
    	{
    		pixelDataRequests[i]=MPI_REQUEST_NULL;
    	}

    	renderVector=new vector<int>[RenderCount];
    	CHECK_NEW(renderVector);
    	testerVector=new vector<int>[TesterCount];
    	CHECK_NEW(testerVector);
    	renderShareCount=new int[RenderCount];
    	CHECK_NEW(renderShareCount);
    	testerShareCount=new int[TesterCount];
    	CHECK_NEW(testerShareCount);

    	naiveVisibleTableRequest=MPI_REQUEST_NULL;

    	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    	glDrawBuffer(GL_BACK);
    }

    void MasterContext::uninit()
    {
    	delete[]testerShareCount;
    	delete[]renderShareCount;
    	delete[]testerVector;
    	delete[]renderVector;
    	delete[]pixelDataRequests;
    	delete[]visibleTableRequests;
    	delete[]visibleTable;
    	delete[]pixelDataBuffer;
    	delete[]colorBuffer;
    	octreeLoader.uninitialize();
    }

    void MasterContext::recvVisiblePixelInfo()
    {
    	for (int i=0; i<DepthBufferSnapShotNumber; i++)
    	{
    		visibleTableRequests[i]=MPI_REQUEST_NULL;
    #ifndef USING_COMPRESSION
    		SAFE_CALL(MPI_Irecv(visibleTable+i*DepthBufferAtomSize, DepthBufferAtomSize, MPI_UNSIGNED_CHAR,
    			MPI_ANY_SOURCE, i, MPI_COMM_WORLD, visibleTableRequests+i));
    #else
    		SAFE_CALL(MPI_Irecv(compressedVisibleTable+i*DepthBufferAtomSize*VisibleTableBufferRate, DepthBufferAtomSize*VisibleTableBufferRate,
    			 MPI_UNSIGNED_CHAR, MPI_ANY_SOURCE, i, MPI_COMM_WORLD, visibleTableRequests+i));
    #endif
    	}
    	DEBUG_INFO("displayer receives visible pixel info \n");
    	//	runningStruct[RenderHostCount].arrayIndex, renderContext.frameNumber);
    }

    //let us use wait_any later for optimization
    void MasterContext::testVisiblePixelInfoRecving()
    {
    	int i;
    #ifdef 	USING_COMPRESSION
    	MPI_Status status[DepthBufferSnapShotNumber];
    	int length;

    	SAFE_CALL(MPI_Waitall(DepthBufferSnapShotNumber, visibleTableRequests, status));
    	for (i=0; i<DepthBufferSnapShotNumber; i++)
    	{
    		SAFE_CALL(MPI_Get_count(status+i, MPI_UNSIGNED_CHAR, &length));
    		length=decompressBuffer(compressedVisibleTable+i*DepthBufferAtomSize*VisibleTableBufferRate, length,
    			visibleTable+i*DepthBufferAtomSize, DepthBufferAtomSize);
    		assert(length==DepthBufferAtomSize);
    	}
    #else
    	SAFE_CALL(MPI_Waitall(DepthBufferSnapShotNumber, visibleTableRequests, MPI_STATUSES_IGNORE));
    #endif
    	DEBUG_INFO("displayer finishes visible pixel info receiving\n");
    }

    void MasterContext::displayerPrepareRecvPixelData()
    {
    	int i;
    	for (i=0; i<RenderCount+1; i++)
    	{
    		renderPixelCount[i]=0;
    	}
    	//counting how many pixel from each render, this is purely to save memory
    	//so that in total we only need one color buffer size of buffer to hold all pixel data
    	//from all renders.
    	for (i=0; i<ColorBufferSize; i++)
    	{
    		renderPixelCount[visibleTable[i]]++;
    	}
    }

    void MasterContext::recvPixelData()
    {
    	int i, offset=0;
    	for (i=0; i<RenderCount; i++)
    	{
    		//here we use single size of color buffer to hold all pixel data from all renders
    		renderPixelPtr[i]=pixelDataBuffer+offset;
    		if (renderPixelCount[i]>0)
    		{
    			SAFE_CALL(MPI_Irecv(renderPixelPtr[i], renderPixelCount[i]*4, MPI_UNSIGNED_CHAR,
    				RenderHostStartIndex+i, PIXEL_DATA, MPI_COMM_WORLD, pixelDataRequests+i));
    			DEBUG_INFO("displayer begin recv pixel data\n");
    		}
    		offset+=renderPixelCount[i]*4;
    		//printf("displayer expects to receives pixel data of %d from render#%d \n", renderPixelCount[i], i);
    	}
    	DEBUG_INFO("displayer receives pixel data\n");
    }

    //first let's do the simplest
    void MasterContext::testPixelDataRecving()
    {
    	int i;
    	SAFE_CALL(MPI_Waitall(RenderCount, pixelDataRequests, MPI_STATUSES_IGNORE));
    	for (i=0; i<ColorBufferSize; i++)
    	{
    		if (visibleTable[i]<RenderCount)
    		{
    			//visible
    			memcpy(colorBuffer+i*4, renderPixelPtr[visibleTable[i]], 4);
    			renderPixelPtr[visibleTable[i]]+=4;
    		}
    		else
    		{
    			//set unvisible background to white
    			memset(colorBuffer+i*4, 255, 4);
    		}
    	}
    	//printf("displayer finishes receiving pixel data of frame#%d\n", mpiContext.renderContext->frameNumber);
    }

    void MasterContext::displayCallback()
    {
    	if (!mpiContext.startMPI)
    	{
    		viewCollector.startCollecting(renderEngine.drawingContext.center);
    		knapsack(viewCollector.octreeNodeVector.size(), &(*viewCollector.octreeNodeVector.begin()), false);
    	}
    	mpiContext.sendRenderContext();
    	mpiContext.testRenderContextSending();
    	if (mpiContext.startMPI)
    	{
    		DEBUG_INFO("master starts\n");
    		viewCollector.startCollecting(renderEngine.drawingContext.center);
    		knapsack(viewCollector.octreeNodeVector.size(), &(*viewCollector.octreeNodeVector.begin()), false);
    #ifdef 	USING_NAIVE_DEPTH_BUFFER_EXCHANGE
    		recvNaiveVisiblePixelInfo();
    #else
    		recvVisiblePixelInfo();
    		testVisiblePixelInfoRecving();
    #endif
    		displayerPrepareRecvPixelData();
    		recvPixelData();
    		testPixelDataRecving();
    		outputColorBuffer();
    		DEBUG_INFO("master finishes\n");
    	}
    }

    void MasterContext::outputColorBuffer()
    {
    	int i, j;
    	//save context and setup to write color buffer
    	glMatrixMode(GL_PROJECTION);
    	glPushMatrix();
    	glLoadIdentity();
    	glOrtho(0, ScreenSize, 0, ScreenSize, 0, 100);
    	//glOrtho(-ScreenSize/2, ScreenSize/2, -ScreenSize/2, ScreenSize/2, DefaultNearPlane, 100);
    	glMatrixMode(GL_MODELVIEW);
    	glPushMatrix();
    	glLoadIdentity();

    	//glRasterPos2f(i*DepthBufferAtomWidth, j*DepthBufferAtomWidth);
    	glRasterPos2f(0, 0);
    	for (i=0; i<DepthBufferSnapShotWidth; i++)
    	{
    		for (j=0; j<DepthBufferSnapShotWidth; j++)
    		{
    			//if (j%2==0)
    			glRasterPos2f(i*DepthBufferAtomWidth, j*DepthBufferAtomWidth);
    			glDrawPixels(DepthBufferAtomWidth, DepthBufferAtomWidth,
    				GL_RGBA, GL_UNSIGNED_BYTE, colorBuffer+(i*DepthBufferSnapShotWidth+j)*DepthBufferAtomSize*4);
    		}
    	}
    	glMatrixMode(GL_MODELVIEW);
    	glPopMatrix();
    	glMatrixMode(GL_PROJECTION);
    	glPopMatrix();
    }

    void MasterContext::keyboardCallback(unsigned char key, int x, int y)
    {
    	if (mpiContext.pendingKey!=0)
    	{
    		glutPostRedisplay();
    		return;
    	}
    	else
    	{
    		mpiContext.pendingKey=key;
    	}
    	switch (key)
    	{
    	case 'h':
    	case 'H':
    		viewCollector.hierRenderMode=!viewCollector.hierRenderMode;
    		printf("render mode %s\n", (viewCollector.hierRenderMode)?"hierachical":"flooding");
    		break;
    	case 'f':
    	case 'F':
    		viewCollector.fullRenderMode=!viewCollector.fullRenderMode;
    		printf("full render mode is %s\n", viewCollector.fullRenderMode?"yes":"no");
    		break;

    	case 'm':
    	case 'M':
    		printf("\n****************running info***************************************\n");
    		renderEngine.movement.display();
    		printf("movement offset=%f\n", renderEngine.movementOffset);
    		printf("octree leaf node number=%d\n", viewCollector.octreeNodeVector.size());
    		printf("vertexRendered=%d, faceRendered=%d\n", viewCollector.vertexCounter,
    			viewCollector.faceCounter);
    		printf("nearplane=%f, farplane=%f\n", renderEngine.drawingContext.zNear, renderEngine.drawingContext.zFar);
    		printf("\n****************data info*****************************************\n");
    		octreeLoader.root->infoStruct.display();
    		printf("\n******************************************************************\n");
    		break;

    	case '+':
    		if (octreeLoader.capacityFactor>=MaxCapacity)
    		{
    			octreeLoader.capacityFactor=MaxCapacity;
    		}
    		else
    		{
    			octreeLoader.capacityFactor*=2;
    		}
    		printf("capacityFactor=%d\n", octreeLoader.capacityFactor);
    		break;
    	case '-':
    		octreeLoader.capacityFactor/=2;
    		if (octreeLoader.capacityFactor==0)
    		{
    			octreeLoader.capacityFactor=1;
    		}
    		printf("capacityFactor=%d\n", octreeLoader.capacityFactor);
    		break;
    	default:
    		renderEngine.keyboardCallback(key, x, y);
    		break;

    	}
    	glutPostRedisplay();

    }

    void MasterContext::recvNaiveVisiblePixelInfo()
    {
    #ifdef USING_COMPRESSION
    	MPI_Status status;
    	int length;
    	SAFE_CALL(MPI_Irecv(compressedVisibleTable, VisibleTableSize, MPI_UNSIGNED_CHAR, TesterHostStartIndex,
    		0, MPI_COMM_WORLD, &naiveVisibleTableRequest));
    	SAFE_CALL(MPI_Wait(&naiveVisibleTableRequest, &status));
    	SAFE_CALL(MPI_Get_count(&status,MPI_UNSIGNED_CHAR, &length));
    	length=decompressBuffer(compressedVisibleTable, length, visibleTable, DepthBufferSize);
    	assert(length==DepthBufferSize);
    #else
    	SAFE_CALL(MPI_Irecv(visibleTable, DepthBufferSize, MPI_UNSIGNED_CHAR, TesterHostStartIndex, 0, MPI_COMM_WORLD,
    		&naiveVisibleTableRequest));
    	SAFE_CALL(MPI_Wait(&naiveVisibleTableRequest, MPI_STATUS_IGNORE));
    #endif
    }

¡¡

file name: tester.cpp

    #include "config.h"
    #include "dataStructure.h"
    #include "tester.h"
    #include <cstdlib>
    #include <cstdio>
    #include <mpi.h>
    #include <GL/glut.h>
    #include <assert.h>

    #ifdef DEBUGGING
    #ifdef LINUX_VERSION
    #include <syslog.h>

    #endif
    #endif

    char* TesterContext::windowTitle="Tester Node";
    extern MPIContext mpiContext;
    extern FILE* dumpPtr;
    ////////////////////////////
    //convenient alias
    //float* myRecvDepthBuffer;
    //float* myDepthBuffer;
    //float* myExchangeDepthBuffer;
    //float* myDepthBufferSnapShots;

    void TesterContext::init()
    {
    	int i, j;

    	myTesterIndex=mpiContext.myRank-TesterHostStartIndex;

    	colorBuffer=new ubyte[ColorBufferByteSize];
    	CHECK_NEW(colorBuffer);

    	depthBuffer=new float*[TesterCount];
    	CHECK_NEW(depthBuffer);

    	for (i=0; i<TesterCount; i++)
    	{
    		depthBuffer[i]=new float[DepthBufferSize];
    		CHECK_NEW(depthBuffer[i]);
    	}

    	//myRecvDepthBuffer=depthBuffer[TesterCount];
    	//myDepthBuffer=depthBuffer[myTesterIndex];
    	//the laster one

    	//let's make an independent buffer

    	snapShots=new float*[TesterCount];
    	CHECK_NEW(snapShots);

    	for (i=0; i<TesterCount; i++)
    	{
    		//don't forget the *2 means min and max
    		snapShots[i]=new float[DepthBufferSnapShotNumber*2];
    		CHECK_NEW(snapShots[i]);
    	}

    	visibleTable=new ubyte*[TesterCount];
    	CHECK_NEW(visibleTable);
    	for (i=0; i<TesterCount; i++)
    	{
    		visibleTable[i]=new ubyte[ColorBufferSize];
    		CHECK_NEW(visibleTable[i]);
    	}

    #ifdef USING_COMPRESSION
    	/////////////////////////////
    	//compressed visible table buffer
    	compressedVisibleTable=new ubyte*[TesterCount];
    	CHECK_NEW(compressedVisibleTable);
    	for (i=0; i<TesterCount; i++)
    	{
    		compressedVisibleTable[i]=new ubyte[VisibleTableSize];
    		CHECK_NEW(compressedVisibleTable[i]);
    	}

    	///////////////////////////////
    #endif

    	/////////////////////////////////////////////////////////////////////////////////////////
    	//requests!!
    	//half recv, half send
    	snapShotSendingRequests=new MPI_Request[TesterCount];
    	CHECK_NEW(snapShotSendingRequests);
    	snapShotRecvingRequests=new MPI_Request[TesterCount];
    	CHECK_NEW(snapShotRecvingRequests);
    	for (i=0; i<TesterCount; i++)
    	{
    		snapShotSendingRequests[i]=snapShotRecvingRequests[i]=MPI_REQUEST_NULL;
    	}

    	//because exchange now becomes one-way

    	visibleTableExchangeRequests= new MPI_Request*[TesterCount];
    	CHECK_NEW(visibleTableExchangeRequests);

    	depthBufferExchangeRequests=new MPI_Request*[TesterCount];
    	CHECK_NEW(depthBufferExchangeRequests);
    	for (i=0; i<TesterCount; i++)
    	{
    		depthBufferExchangeRequests[i]=new MPI_Request[DepthBufferSnapShotNumber];
    		CHECK_NEW(depthBufferExchangeRequests[i]);
    		visibleTableExchangeRequests[i]=new MPI_Request[DepthBufferSnapShotNumber];
    		CHECK_NEW(visibleTableExchangeRequests[i]);
    	}

    	visibleTableRenderRequests=new MPI_Request*[RenderCount];//*RenderCount];
    	CHECK_NEW(visibleTableRenderRequests);

    	for (i=0; i<RenderCount; i++)
    	{
    		visibleTableRenderRequests[i]=new MPI_Request[DepthBufferSnapShotNumber];
    		CHECK_NEW(visibleTableRenderRequests[i]);
    		for (j=0; j<DepthBufferSnapShotNumber; j++)
    		{
    			visibleTableRenderRequests[i][j]=MPI_REQUEST_NULL;
    		}
    	}

    	visibleTableDisplayerRequests=new MPI_Request[DepthBufferSnapShotNumber];
    	CHECK_NEW(visibleTableDisplayerRequests);

    	for (i=0; i<DepthBufferSnapShotNumber; i++)
    	{
    		visibleTableDisplayerRequests[i]=MPI_REQUEST_NULL;
    	}

    	octreeLoader.initialize(true, true);
    	renderEngine.init(&octreeLoader);
    	renderEngine.lighting.lightEnabled=false;
    	renderIndexHashTable=new ubyte[Octree::infoStruct.nodeCount];
    	CHECK_NEW(renderIndexHashTable);
    	snapShotCandidate=new int[DepthBufferSnapShotNumber];
    	CHECK_NEW(snapShotCandidate);
    	glPixelStorei(GL_PACK_ALIGNMENT, 1);
    	glReadBuffer(GL_BACK);

    #ifdef 	DEBUGGING
    #ifdef LINUX_VERSION
    	openlog("renderBox", 0,0);
    #endif
    #endif

    #ifdef USING_NAIVE_DEPTH_BUFFER_EXCHANGE
    	naiveDepthBufferExchangeRequests=new MPI_Request[TesterCount];
            CHECK_NEW(naiveDepthBufferExchangeRequests);
            naiveVisibleTableExchangeRequests=new MPI_Request[TesterCount];
            CHECK_NEW(naiveVisibleTableExchangeRequests);
            naiveVisibleTableDisplayerRequests=new MPI_Request[RenderCount+1];
            CHECK_NEW(naiveVisibleTableDisplayerRequests);
            for (i=0; i<TesterCount; i++)
            {
                    naiveDepthBufferExchangeRequests[i]=MPI_REQUEST_NULL;
                    naiveVisibleTableExchangeRequests[i]=MPI_REQUEST_NULL;
            }
    	for (i=0; i<RenderCount+1; i++)
    	{
    		naiveVisibleTableDisplayerRequests[i]=MPI_REQUEST_NULL;
    	}
    #endif

    }

    void TesterContext::createRenderIndexHashTable()
    {
    	int offset=0;
    	for (int i=0; i<RenderCount; i++)
    	{
    		for (int j=0; j<mpiContext.renderContext->renderShare[i]; j++)
    		{
    			renderIndexHashTable[mpiContext.renderShareArray[offset+j]]=i;
    		}
    		offset+=mpiContext.renderContext->renderShare[i];
    	}
    }

    void TesterContext::keyboardCallback(unsigned char key, int x, int y)
    {
    	switch(key)
    	{
    	case 'e':
    	case 'E':
    	case 'n':
    	case 'N':
    		break;
    	case 0:
    		break;
    	default:
    		renderEngine.keyboardCallback(key, x, y);
    		break;
    	}
    	glutPostRedisplay();
    }

    void TesterContext::displayCallback()
    {
    	int offset;
    	mpiContext.recvRenderContext();
    	mpiContext.testRenderContextRecving();
    	createRenderIndexHashTable();
    	////////////////////////////////////////////////
    	keyboardCallback(mpiContext.renderContext->key, 0, 0);
    	offset=0;
    	for (int i=0; i<myTesterIndex; i++)
    	{
    		offset+=mpiContext.renderContext->testerShare[i];
    	}
    	octreeLoader.addCandidates(mpiContext.renderContext->testerShare[myTesterIndex],
    		mpiContext.testerShareArray+offset);
    	octreeLoader.swapBuffer();
    	if (mpiContext.startMPI)
    	{
    		DEBUG_INFO("tester starts...\n");
    		readColorBuffer();
    		readDepthBuffer();
    		processDepthBuffer();
    #ifdef	USING_NAIVE_DEPTH_BUFFER_EXCHANGE
    		naiveExchangeDepthBuffer();
    		DEBUG_INFO("tester finishes\n");
    #else
    		exchangeDepthBufferSnapShot();
    		exchangeDepthBuffer();
    		testDepthBufferExchanging();
    		sendVisiblePixelInfo();
    		testVisiblePixelInfoSending();
    #endif
    	}
    }

    void TesterContext::readColorBuffer()
    {
    	for (int i=0; i<DepthBufferSnapShotWidth; i++)
    	{
    		for (int j=0; j<DepthBufferSnapShotWidth; j++)
    		{
    			glReadPixels(i*DepthBufferAtomWidth, j*DepthBufferAtomWidth, DepthBufferAtomWidth, DepthBufferAtomWidth,
    				GL_RGBA, GL_UNSIGNED_BYTE, colorBuffer+4*(i*DepthBufferSnapShotWidth+j)*DepthBufferAtomSize);
    		}
    	}
    }

    void TesterContext::readDepthBuffer()
    {
    	for (int i=0; i<DepthBufferSnapShotWidth; i++)
    	{
    		for (int j=0; j<DepthBufferSnapShotWidth; j++)
    		{
    			glReadPixels(i*DepthBufferAtomWidth, j*DepthBufferAtomWidth, DepthBufferAtomWidth, DepthBufferAtomWidth,
    				GL_DEPTH_COMPONENT, GL_FLOAT,
    				depthBuffer[myTesterIndex]+(i*DepthBufferSnapShotWidth+j)*DepthBufferAtomSize);
    		}
    	}
    }

    void TesterContext::processDepthBuffer()
    {
    	int i, j, k, index, offset, pos;
    	float* minPtr, *maxPtr, *curPtr;
    	unsigned int* colorPtr;
    	//this must be "little-indian"
    	const unsigned int HeadMask=0x00FFFFFF;
    	for (i=0; i<DepthBufferSnapShotWidth; i++)
    	{
    		for (j=0; j<DepthBufferSnapShotWidth; j++)
    		{
    			index=i*DepthBufferSnapShotWidth+j;
    			minPtr=snapShots[myTesterIndex]+index*2;
    			maxPtr=snapShots[myTesterIndex]+index*2+1;
    			*minPtr=*maxPtr=depthBuffer[myTesterIndex][index*DepthBufferAtomSize];
    			offset=index*DepthBufferAtomSize;
    			for (k=0; k<DepthBufferAtomSize; k++)
    			{
    				curPtr=depthBuffer[myTesterIndex]+ offset+k;
    				if (*curPtr<*minPtr)
    				{
    					*minPtr=*curPtr;
    				}
    				if (*curPtr>*maxPtr)
    				{
    					*maxPtr=*curPtr;
    				}
    				//here we don't restrict ourselves as big-endian or little-endian
    				//cause it is an integer written from "fakecolor.data"
    				colorPtr=(unsigned int*)(colorBuffer+(offset+k)*4);
    				//but here it is still not restricted as little-endian
    				//definitely it is IMPORTANT! but I don't understand why now.
    				pos=(*colorPtr)&HeadMask;//this must be from my VERY old version which uses "alpha" to store index
    				//pos=*colorPtr;//I forget this purpose, but it should be useless

    				if (*curPtr==1)
    				{
    					//invisible
    					visibleTable[myTesterIndex][offset+k]=RenderCount;//invisible Color;
    				}
    				else
    				{
    					//visible
    					visibleTable[myTesterIndex][offset+k]=renderIndexHashTable[pos];
    					//assert(renderIndexHashTable[pos]<RenderCount);
    				}
    			}
    		}
    	}
    }

    void TesterContext::exchangeDepthBufferSnapShot()
    {
    	int source;
    	int i, j;

    	//printf("Tester %d exchange snapshots begins\n", myTesterIndex);

    	for (i=0; i<TesterCount; i++)
    	{
    		if (i!=myTesterIndex)
    		{
    			source=TesterHostStartIndex+i;

    			SAFE_CALL(MPI_Irecv(snapShots[i], DepthBufferSnapShotNumber*2, MPI_FLOAT, source,
    				DEPTH_BUFFER_SNAP_SHOT, MPI_COMM_WORLD, snapShotRecvingRequests+i));

    			SAFE_CALL(MPI_Isend(snapShots[myTesterIndex], DepthBufferSnapShotNumber*2, MPI_FLOAT, source,
    				DEPTH_BUFFER_SNAP_SHOT, MPI_COMM_WORLD, snapShotSendingRequests+i));

    		}
    	}
    	SAFE_CALL(MPI_Waitall(TesterCount, snapShotSendingRequests, MPI_STATUSES_IGNORE));
    	SAFE_CALL(MPI_Waitall(TesterCount, snapShotRecvingRequests, MPI_STATUSES_IGNORE));
    	//printf("Tester %d exchange snapshots finished\n", myTesterIndex);
    }

    //always compare with minPtr
    void TesterContext::exchangeDepthBuffer()
    {
    	int i, j, savedCount=0;
    	float* curPtr, *candPtr, *myPtr;

    	for (i=0; i<DepthBufferSnapShotNumber; i++)
    	{
    		//choose a default for comparison
    		snapShotCandidate[i]=myTesterIndex;
    		candPtr=snapShots[myTesterIndex]+i*2;
    		for (j=0; j<TesterCount; j++)
    		{
    			curPtr=snapShots[j]+i*2;
    			//when equal, we choose by index
    			if (*curPtr<*candPtr||*curPtr==*candPtr&&j<snapShotCandidate[i])
    			{
    				candPtr=curPtr;
    				snapShotCandidate[i]=j;
    			}
    			depthBufferExchangeRequests[j][i]=MPI_REQUEST_NULL;
    			visibleTableExchangeRequests[j][i]=MPI_REQUEST_NULL;
    		}

    		myPtr=snapShots[myTesterIndex]+i*2;
    		if (snapShotCandidate[i]!=myTesterIndex)
    		{
    			candPtr=snapShots[snapShotCandidate[i]]+i*2;
    			//if my smallest is smaller than the biggest of candidate, we need exchange
    			if (myPtr[0]<candPtr[1])
    			{
    				doSendExchangeDepthBuffer(i, snapShotCandidate[i]);
    			}
    		}
    		else
    		{
    			//now we check how many Testers for this particular snapshot is saved
    			for (j=0; j<TesterCount; j++)
    			{
    				if (j!=myTesterIndex)
    				{
    					curPtr=snapShots[j]+i*2;
    					//if its smallest is smaller than our biggest
    					if (curPtr[0]<myPtr[1])
    					{
    						doRecvExchangeDepthBuffer(i, j);
    					}
    					else
    					{
    						//this is purely for testing purpose
    						savedCount++;
    					}
    				}

    			}
    		}

    	}
    #ifdef DEBUGGING
    	//fprintf(dumpPtr, "Tester%d saved depth buffer sending %d of frame %d\n", testerContext.myTesterIndex,
    	//	savedCount, mpiContext.renderContext->frameNumber);
    #ifdef LINUX_VERSION
    	syslog(2,  "Tester%d saved depth buffer sending %d of frame %d\n", testerContext.myTesterIndex,
    		savedCount, mpiContext.renderContext->frameNumber);
    #endif

    #endif

    }

    #ifdef	USING_COMPRESSION
    //here i haven't decide to do a compression for visible table.
    void TesterContext::doSendExchangeDepthBuffer(int snapShotIndex, int targetTester)
    {
    	int length;
    	SAFE_CALL(MPI_Isend(depthBuffer[myTesterIndex]+ snapShotIndex*DepthBufferAtomSize, DepthBufferAtomSize, MPI_FLOAT,
    		targetTester+TesterHostStartIndex, snapShotIndex, MPI_COMM_WORLD,
    		depthBufferExchangeRequests[targetTester]+snapShotIndex));

    	length=compressBuffer(visibleTable[myTesterIndex]+snapShotIndex*DepthBufferAtomSize, DepthBufferAtomSize,
    		compressedVisibleTable[myTesterIndex]+snapShotIndex*DepthBufferAtomSize*VisibleTableBufferRate,
    		DepthBufferAtomSize*VisibleTableBufferRate);

    	SAFE_CALL(MPI_Isend(compressedVisibleTable[myTesterIndex]+
    		snapShotIndex*DepthBufferAtomSize*VisibleTableBufferRate,
    		length,	MPI_UNSIGNED_CHAR, targetTester+TesterHostStartIndex, snapShotIndex+DepthBufferSnapShotNumber,
    		MPI_COMM_WORLD,	visibleTableExchangeRequests[targetTester]+snapShotIndex));
    }
    #else
    void TesterContext::doSendExchangeDepthBuffer(int snapShotIndex, int targetTester)
    {
    	int length;
    	SAFE_CALL(MPI_Isend(depthBuffer[myTesterIndex]+ snapShotIndex*DepthBufferAtomSize, DepthBufferAtomSize, MPI_FLOAT,
    		targetTester+TesterHostStartIndex, snapShotIndex, MPI_COMM_WORLD,
    		depthBufferExchangeRequests[targetTester]+snapShotIndex));

    	SAFE_CALL(MPI_Isend(visibleTable[myTesterIndex]+ snapShotIndex*DepthBufferAtomSize, DepthBufferAtomSize,
    		MPI_UNSIGNED_CHAR, targetTester+TesterHostStartIndex, snapShotIndex+DepthBufferSnapShotNumber, MPI_COMM_WORLD,
    		visibleTableExchangeRequests[targetTester]+snapShotIndex));
    }
    #endif

    #ifdef	USING_COMPRESSION
    void TesterContext::doRecvExchangeDepthBuffer(int snapShotIndex, int sourceTester)
    {
    	SAFE_CALL(MPI_Irecv(depthBuffer[sourceTester]+ snapShotIndex*DepthBufferAtomSize, DepthBufferAtomSize, MPI_FLOAT,
    		sourceTester+TesterHostStartIndex, snapShotIndex, MPI_COMM_WORLD,
    		depthBufferExchangeRequests[sourceTester]+snapShotIndex));
    	SAFE_CALL(MPI_Irecv(compressedVisibleTable[sourceTester]+ snapShotIndex*DepthBufferAtomSize*VisibleTableBufferRate,
    		DepthBufferAtomSize*VisibleTableBufferRate, MPI_UNSIGNED_CHAR, sourceTester+TesterHostStartIndex,
    		snapShotIndex+DepthBufferSnapShotNumber, MPI_COMM_WORLD,
    		visibleTableExchangeRequests[sourceTester]+snapShotIndex));
    }
    #else
    void TesterContext::doRecvExchangeDepthBuffer(int snapShotIndex, int sourceTester)
    {
    	SAFE_CALL(MPI_Irecv(depthBuffer[sourceTester]+ snapShotIndex*DepthBufferAtomSize, DepthBufferAtomSize, MPI_FLOAT,
    		sourceTester+TesterHostStartIndex, snapShotIndex, MPI_COMM_WORLD,
    		depthBufferExchangeRequests[sourceTester]+snapShotIndex));
    	SAFE_CALL(MPI_Irecv(visibleTable[sourceTester]+ snapShotIndex*DepthBufferAtomSize, DepthBufferAtomSize,
    		MPI_UNSIGNED_CHAR, sourceTester+TesterHostStartIndex, snapShotIndex+DepthBufferSnapShotNumber, MPI_COMM_WORLD,
    		visibleTableExchangeRequests[sourceTester]+snapShotIndex));
    }
    #endif

    #ifndef	USING_COMPRESSION
    void TesterContext::testDepthBufferExchanging()
    {
    	int i, j, k;
    	float* myPtr, *curPtr;
    	float* myDepth, *hisDepth;
    	ubyte* myTable, *hisTable;

    	for (i=0; i<TesterCount; i++)
    	{
    		SAFE_CALL(MPI_Waitall(DepthBufferSnapShotNumber, depthBufferExchangeRequests[i], MPI_STATUSES_IGNORE));
    		SAFE_CALL(MPI_Waitall(DepthBufferSnapShotNumber, visibleTableExchangeRequests[i], MPI_STATUSES_IGNORE));
    	}

    	for (i=0; i<DepthBufferSnapShotNumber; i++)
    	{
    		if (snapShotCandidate[i]==myTesterIndex)
    		{
    			//current tester is receiver or candidate
    			myPtr=snapShots[myTesterIndex]+i*2;
    			for (j=0; j<TesterCount; j++)
    			{
    				curPtr=snapShots[j]+i*2;
    				if (curPtr[0]<myPtr[1])
    				{
    					//this means, there is an exchange
    					myDepth=depthBuffer[myTesterIndex]+i*DepthBufferAtomSize;
    					hisDepth=depthBuffer[j]+i*DepthBufferAtomSize;
    					myTable=visibleTable[myTesterIndex]+i*DepthBufferAtomSize;
    					hisTable=visibleTable[j]+i*DepthBufferAtomSize;
    					for (k=0; k<DepthBufferAtomSize; k++)
    					{
    						if (hisDepth[k]<myDepth[k])
    						{
    							myDepth[k]=hisDepth[k];
    							myTable[k]=hisTable[k];
    						}
    					}
    				}
    			}
    		}
    		//nothing because just sending out
    	}
    }

    #else
    void TesterContext::testDepthBufferExchanging()
    {
    	int i, j, k;
    	int length;
    	float* myPtr, *curPtr;
    	float* myDepth, *hisDepth;
    	ubyte* myTable, *hisTable;
    	MPI_Status status[TesterCount][DepthBufferSnapShotNumber];

    	for (i=0; i<TesterCount; i++)
    	{
    		SAFE_CALL(MPI_Waitall(DepthBufferSnapShotNumber, depthBufferExchangeRequests[i], MPI_STATUSES_IGNORE));
    		SAFE_CALL(MPI_Waitall(DepthBufferSnapShotNumber, visibleTableExchangeRequests[i], status[i]));
    	}

    	for (i=0; i<DepthBufferSnapShotNumber; i++)
    	{
    		if (snapShotCandidate[i]==myTesterIndex)
    		{
    			//current tester is receiver or candidate
    			myPtr=snapShots[myTesterIndex]+i*2;
    			for (j=0; j<TesterCount; j++)
    			{
    				if (j==myTesterIndex)
    				{
    					continue;
    				}
    				curPtr=snapShots[j]+i*2;
    				if (curPtr[0]<myPtr[1])
    				{
    					//this means, there is an exchange
    					SAFE_CALL(MPI_Get_count(&status[j][i], MPI_UNSIGNED_CHAR,&length));
    					length=decompressBuffer(compressedVisibleTable[j]+
    						i*DepthBufferAtomSize*VisibleTableBufferRate, length,
    						visibleTable[j]+i*DepthBufferAtomSize, DepthBufferAtomSize);

    					myDepth=depthBuffer[myTesterIndex]+i*DepthBufferAtomSize;
    					hisDepth=depthBuffer[j]+i*DepthBufferAtomSize;
    					myTable=visibleTable[myTesterIndex]+i*DepthBufferAtomSize;
    					hisTable=visibleTable[j]+i*DepthBufferAtomSize;
    					for (k=0; k<DepthBufferAtomSize; k++)
    					{
    						if (hisDepth[k]<myDepth[k])
    						{
    							myDepth[k]=hisDepth[k];
    							myTable[k]=hisTable[k];
    						}
    					}
    				}
    			}
    		}
    		//nothing because just sending out
    	}
    }
    #endif

    void TesterContext::sendVisiblePixelInfo()
    {
    	int i, j;
    	//int counter=0;
    	//for each display tile
    	ubyte* visibleTablePtr;
    	int visibleTableSendingSize;
    	for (i=0; i<DepthBufferSnapShotNumber; i++)
    	{
    		//if current tester is responsible for visiblePixelInfo sending...
    		if (snapShotCandidate[i]==myTesterIndex)
    		{
    			//counter++;
    			visibleTableDisplayerRequests[i]=MPI_REQUEST_NULL;
    			//send to displayer
    			//still we need the pointer for parameter
    			visibleTablePtr=visibleTable[myTesterIndex]+i*DepthBufferAtomSize;
    #ifndef USING_COMPRESSION
    			visibleTableSendingSize=DepthBufferAtomSize;
    #else
    			visibleTableSendingSize=compressBuffer(visibleTablePtr, DepthBufferAtomSize,
    				compressedVisibleTable[myTesterIndex]+i*DepthBufferAtomSize*VisibleTableBufferRate,
    				DepthBufferAtomSize*VisibleTableBufferRate);
    			assert(visibleTableSendingSize!=-1);
    			visibleTablePtr=compressedVisibleTable[myTesterIndex]+i*DepthBufferAtomSize*VisibleTableBufferRate;
    #endif

    			SAFE_CALL(MPI_Isend(visibleTablePtr, visibleTableSendingSize,
    				MPI_UNSIGNED_CHAR, MasterIndex, i, MPI_COMM_WORLD, visibleTableDisplayerRequests+i));
    			for (j=0; j<RenderCount; j++)
    			{
    				visibleTableRenderRequests[j][i]=MPI_REQUEST_NULL;
    				//send to each renderer, with tag of display tile index
    				SAFE_CALL(MPI_Isend(visibleTablePtr, visibleTableSendingSize,
    					MPI_UNSIGNED_CHAR, RenderHostStartIndex+j, i, MPI_COMM_WORLD,
    				visibleTableRenderRequests[j]+i));
    			}

    /*
    			SAFE_CALL(MPI_Isend(visibleTable[myTesterIndex]+i*DepthBufferAtomSize, DepthBufferAtomSize,
    				MPI_UNSIGNED_CHAR, MasterIndex, i, MPI_COMM_WORLD, visibleTableDisplayerRequests+i));
    			for (j=0; j<RenderCount; j++)
    			{
    				visibleTableRenderRequests[j][i]=MPI_REQUEST_NULL;
    				//send to each renderer, with tag of display tile index
    				SAFE_CALL(MPI_Isend(visibleTable[myTesterIndex]+i*DepthBufferAtomSize, DepthBufferAtomSize,
    				MPI_UNSIGNED_CHAR, RenderHostStartIndex+j, i, MPI_COMM_WORLD,
    				visibleTableRenderRequests[j]+i));
    			}
    */
    		}
    	}
    	//printf("Tester%d sends visible pixelInfo of total %d\n", myTesterIndex, counter);
    }

    void TesterContext::testVisiblePixelInfoSending()
    {
    	int i;
    	SAFE_CALL(MPI_Waitall(DepthBufferSnapShotNumber, visibleTableDisplayerRequests, MPI_STATUSES_IGNORE));

    	for (i=0; i<RenderCount; i++)
    	{
    		SAFE_CALL(MPI_Waitall(DepthBufferSnapShotNumber, visibleTableRenderRequests[i], MPI_STATUSES_IGNORE));
    	}
    	//printf("Tester%d finishes sending visible pixelInfo\n", myTesterIndex);
    }

    void TesterContext::naiveExchangeDepthBuffer()
    {
    	int i, j;
    	int candIndex;
    	int dest;
    	int length;
    	MPI_Status status[TesterCount];
    	if (myTesterIndex!=0)
    	{
    		SAFE_CALL(MPI_Isend(depthBuffer[myTesterIndex], DepthBufferSize, MPI_FLOAT,
    			TesterHostStartIndex, myTesterIndex, MPI_COMM_WORLD,
    			naiveDepthBufferExchangeRequests+myTesterIndex));
    #ifdef	USING_COMPRESSION
    		length=compressBuffer(visibleTable[myTesterIndex], DepthBufferSize, compressedVisibleTable[myTesterIndex],
    			VisibleTableSize);
    		//using 0 as tag is enough to distinguish between depth buffer and visible table
    		SAFE_CALL(MPI_Isend(compressedVisibleTable[myTesterIndex], length, MPI_UNSIGNED_CHAR,
    			TesterHostStartIndex, 0, MPI_COMM_WORLD,
    			naiveVisibleTableExchangeRequests+myTesterIndex));
    #else
    		SAFE_CALL(MPI_Isend(visibleTable[myTesterIndex], DepthBufferSize, MPI_UNSIGNED_CHAR,
    			TesterHostStartIndex, 0, MPI_COMM_WORLD,
    			naiveVisibleTableExchangeRequests+myTesterIndex));
    #endif
    		SAFE_CALL(MPI_Wait(naiveDepthBufferExchangeRequests+myTesterIndex, MPI_STATUS_IGNORE));

    		SAFE_CALL(MPI_Wait(naiveVisibleTableExchangeRequests+myTesterIndex, MPI_STATUS_IGNORE));
    	}
    	else
    	{
    #ifdef	USING_COMPRESSION
    		for (i=1; i<TesterCount; i++)
    		{
    			SAFE_CALL(MPI_Irecv(depthBuffer[i], DepthBufferSize, MPI_FLOAT,
    				TesterHostStartIndex+i, i, MPI_COMM_WORLD,
    				naiveDepthBufferExchangeRequests+i));

    			SAFE_CALL(MPI_Irecv(compressedVisibleTable[i], VisibleTableSize, MPI_UNSIGNED_CHAR,
    				TesterHostStartIndex+i, 0, MPI_COMM_WORLD,
    				naiveVisibleTableExchangeRequests+i));
    		}
    		SAFE_CALL(MPI_Waitall(TesterCount, naiveDepthBufferExchangeRequests, MPI_STATUSES_IGNORE));

    		SAFE_CALL(MPI_Waitall(TesterCount, naiveVisibleTableExchangeRequests, status));
    		for (i=1; i<TesterCount; i++)
    		{
    			SAFE_CALL(MPI_Get_count(status+i, MPI_UNSIGNED_CHAR, &length));
    			decompressBuffer(compressedVisibleTable[i], length, visibleTable[i], DepthBufferSize);
    		}

    #else
    		for (i=1; i<TesterCount; i++)
    		{
    			SAFE_CALL(MPI_Irecv(depthBuffer[i], DepthBufferSize, MPI_FLOAT,
    			TesterHostStartIndex+i, i, MPI_COMM_WORLD,
    			naiveDepthBufferExchangeRequests+i));

    			SAFE_CALL(MPI_Irecv(visibleTable[i], DepthBufferSize, MPI_UNSIGNED_CHAR,
    				TesterHostStartIndex+i, 0, MPI_COMM_WORLD,
    				naiveVisibleTableExchangeRequests+i));
    		}
    		SAFE_CALL(MPI_Waitall(TesterCount, naiveDepthBufferExchangeRequests, MPI_STATUSES_IGNORE));
    		SAFE_CALL(MPI_Waitall(TesterCount, naiveVisibleTableExchangeRequests, MPI_STATUSES_IGNORE));
    #endif

    		for (i=0; i<DepthBufferSize; i++)
    		{
    			candIndex=0;
    			for (j=1; j<TesterCount; j++)
    			{
    				if (depthBuffer[j][i]<depthBuffer[candIndex][i])
    				{
    					candIndex=j;
    				}
    			}
    			visibleTable[0][i]=visibleTable[candIndex][i];
    		}

    		for (i=0; i<RenderCount+1; i++)
    		{
    			if (i==RenderCount)
    			{
    				dest=MasterIndex;
    			}
    			else
    			{
    				dest=RenderHostStartIndex+i;
    			}
    #ifdef	USING_COMPRESSION
    			length=compressBuffer(visibleTable[0], DepthBufferSize, compressedVisibleTable[0], VisibleTableSize);
    			SAFE_CALL(MPI_Isend(compressedVisibleTable[0], length, MPI_UNSIGNED_CHAR, dest,
    					0, MPI_COMM_WORLD, naiveVisibleTableDisplayerRequests+i));
    #else
    			SAFE_CALL(MPI_Isend(visibleTable[0], DepthBufferSize, MPI_UNSIGNED_CHAR, dest,
    					0, MPI_COMM_WORLD, naiveVisibleTableDisplayerRequests+i));
    #endif
    		}
    		SAFE_CALL(MPI_Waitall(RenderCount+1, naiveVisibleTableDisplayerRequests, MPI_STATUSES_IGNORE));
    	}
    }

file name: render.cpp

    #include "config.h"
    #include "dataStructure.h"
    #include "render.h"
    #include <mpi.h>
    #include "except.h"
    #include <GL/glut.h>

    char* RendererContext::windowTitle="Renderer Window";

    extern MPIContext mpiContext;

    void RendererContext::init()
    {
    	int i;

    	myRenderIndex=mpiContext.myRank-RenderHostStartIndex;
    	//printf("render %d start initializing...\n", myRenderIndex);
    	////////////////////////////////////////////////
    	//buffer
    	colorBuffer=new ubyte[ColorBufferByteSize];
    	CHECK_NEW(colorBuffer);

    	visibleTable=new ubyte[ColorBufferSize];
    	CHECK_NEW(visibleTable);

    	compressedVisibleTable=new ubyte[ColorBufferSize*VisibleTableBufferRate];
    	CHECK_NEW(compressedVisibleTable);

    	pixelDataBuffer=new ubyte[ColorBufferByteSize];
    	CHECK_NEW(pixelDataBuffer);

    	///////////////////////////////////////////////////////////////////
    	//requests
    	visibleTableRequests=new MPI_Request[DepthBufferSnapShotNumber];
    	CHECK_NEW(visibleTableRequests);

    	for (i=0; i<DepthBufferSnapShotNumber; i++)
    	{
    		visibleTableRequests[i]=MPI_REQUEST_NULL;
    	}

    	pixelDataRequests=new MPI_Request;
    	CHECK_NEW(pixelDataRequests);
    	*pixelDataRequests=MPI_REQUEST_NULL;
    	///////////////////////////////////////////////////////////////////
    	naiveVisibleTableRequest=MPI_REQUEST_NULL;
    	octreeLoader.initialize(true);
    	renderEngine.init(&octreeLoader);
    	glPixelStorei(GL_PACK_ALIGNMENT, 1);
    	glReadBuffer(GL_BACK);

    }

    #ifdef USING_DUMMY_RENDER
    ///////////////////////////////////////
    //dummy render with no render job at all, only for testing
    void RendererContext::displayCallback()
    {
    	mpiContext.recvRenderContext();
    	mpiContext.testRenderContextRecving();

    	if (mpiContext.startMPI)
    	{
    		DEBUG_INFO("render starts..\n");
    #ifdef	USING_NAIVE_DEPTH_BUFFER_EXCHANGE
    		recvNaiveVisiblePixelInfo();
    #else
    		recvVisiblePixelInfo();
    		testVisiblePixelInfoRecving();
    #endif
    		sendPixelData();
    		testPixelDataSending();
    		DEBUG_INFO("render finishes\n");
    	}
    }
    #else
    void RendererContext::displayCallback()
    {
    	int offset=0;
    	mpiContext.recvRenderContext();
    	mpiContext.testRenderContextRecving();

    	renderEngine.keyboardCallback(mpiContext.renderContext->key, 0, 0);

    	for (int i=0; i<myRenderIndex; i++)
    	{
    		offset+=mpiContext.renderContext->renderShare[i];
    	}
    	octreeLoader.addCandidates(mpiContext.renderContext->renderShare[myRenderIndex],
    		mpiContext.renderShareArray+offset);
    	octreeLoader.swapBuffer();
    	if (mpiContext.startMPI)
    	{
    		readColorBuffer();
    #ifdef	USING_NAIVE_DEPTH_BUFFER_EXCHANGE
    		recvNaiveVisiblePixelInfo();
    #else
    		recvVisiblePixelInfo();
    		testVisiblePixelInfoRecving();
    #endif
    		sendPixelData();
    		testPixelDataSending();
    	}
    }
    #endif

    void RendererContext::readColorBuffer()
    {
    	for (int i=0; i<DepthBufferSnapShotWidth; i++)
    	{
    		for (int j=0; j<DepthBufferSnapShotWidth; j++)
    		{
    			glReadPixels(i*DepthBufferAtomWidth, j*DepthBufferAtomWidth, DepthBufferAtomWidth, DepthBufferAtomWidth,
    				GL_RGBA, GL_UNSIGNED_BYTE,
    				colorBuffer+4*(i*DepthBufferSnapShotWidth+j)*DepthBufferAtomSize);
    		}
    	}
    }

    void RendererContext::recvVisiblePixelInfo()
    {
    	for (int i=0; i<DepthBufferSnapShotNumber; i++)
    	{
    		visibleTableRequests[i]=MPI_REQUEST_NULL;
    #ifndef	USING_COMPRESSION
    		SAFE_CALL(MPI_Irecv(visibleTable+i*DepthBufferAtomSize, DepthBufferAtomSize, MPI_UNSIGNED_CHAR,
    			MPI_ANY_SOURCE, i,  MPI_COMM_WORLD, visibleTableRequests+i));
    #else
    		SAFE_CALL(MPI_Irecv(compressedVisibleTable+i*DepthBufferAtomSize*VisibleTableBufferRate, DepthBufferAtomSize*VisibleTableBufferRate,
    			 MPI_UNSIGNED_CHAR, MPI_ANY_SOURCE, i,  MPI_COMM_WORLD, visibleTableRequests+i));
    #endif
    	}
    	DEBUG_INFO("Render receives visible pixel info of frame number \n");
    }

    void RendererContext::testVisiblePixelInfoRecving()
    {
    #ifdef	USING_COMPRESSION
    	MPI_Status status[DepthBufferSnapShotNumber];
    	int i, length;
    	SAFE_CALL(MPI_Waitall(DepthBufferSnapShotNumber, visibleTableRequests, status));
    	for (i=0; i<DepthBufferSnapShotNumber; i++)
    	{
    		SAFE_CALL(MPI_Get_count(status+i, MPI_UNSIGNED_CHAR, &length));
    		length=decompressBuffer(compressedVisibleTable+i*DepthBufferAtomSize*VisibleTableBufferRate, length,
    			visibleTable+i*DepthBufferAtomSize, DepthBufferAtomSize);
    		assert(length==DepthBufferAtomSize);
    	}
    #else
    	SAFE_CALL(MPI_Waitall(DepthBufferSnapShotNumber, visibleTableRequests, MPI_STATUSES_IGNORE));
    #endif
    	DEBUG_INFO("Render finishes pixelInfo of frame receiving\n");
    }

    void RendererContext::sendPixelData()
    {
    	int index=0;
    	for (int i=0; i<ColorBufferSize; i++)
    	{
    		if (visibleTable[i]==myRenderIndex)
    		{
    			memcpy(pixelDataBuffer+index*4, colorBuffer+i*4, 4);
    			index++;
    		}
    	}
    	*pixelDataRequests=MPI_REQUEST_NULL;
    	if (index>0)
    	{
    		SAFE_CALL(MPI_Isend(pixelDataBuffer, index*4, MPI_UNSIGNED_CHAR, MasterIndex, PIXEL_DATA,
    			MPI_COMM_WORLD,	pixelDataRequests));
    	}
    	DEBUG_INFO("Render sends pixel data to displayer\n");
    }

    void RendererContext::testPixelDataSending()
    {
    	SAFE_CALL(MPI_Wait(pixelDataRequests, MPI_STATUS_IGNORE));
    	DEBUG_INFO("Render finished pixel data sending\n");
    }

    void RendererContext::recvNaiveVisiblePixelInfo()
    {
    #ifdef USING_COMPRESSION
            MPI_Status status;
            int length;
            SAFE_CALL(MPI_Irecv(compressedVisibleTable, VisibleTableSize, MPI_UNSIGNED_CHAR, TesterHostStartIndex,
                    0, MPI_COMM_WORLD, &naiveVisibleTableRequest));
            SAFE_CALL(MPI_Wait(&naiveVisibleTableRequest, &status));
            SAFE_CALL(MPI_Get_count(&status,MPI_UNSIGNED_CHAR, &length));
            length=decompressBuffer(compressedVisibleTable, length, visibleTable, DepthBufferSize);
            assert(length==DepthBufferSize);
    #else
            SAFE_CALL(MPI_Irecv(visibleTable, DepthBufferSize, MPI_UNSIGNED_CHAR, TesterHostStartIndex, 0, MPI_COMM_WORLD,
                    &naiveVisibleTableRequest));
            SAFE_CALL(MPI_Wait(&naiveVisibleTableRequest, MPI_STATUS_IGNORE));
    	//DEBUG_INFO("render recv naive visible pixel info\n");
    #endif
    }

¡¡

file name: renderBox.cpp

    //#define TESTING 1

    #include <mpi.h>
    #include "config.h"
    #include "dataStructure.h"
    #include "renderEngine.h"
    #include "tester.h"
    #include "master.h"
    #include "render.h"
    #include <GL/glut.h>

    #ifndef LINUX_VERSION
    #include "except.h"
    #pragma comment(lib, "glut.lib")
    #endif

    unsigned long frameCounter=0;
    unsigned long lastFrameNo=0;
    unsigned long lastTick=0;
    unsigned long currentTick=0;

    void displayCallbackProc();

    void keyboardCallbackProc(unsigned char key, int x, int y);

    extern MPIContext mpiContext;

    extern void getLastError();

    const char*dumpFileName="error.txt";

    FILE* dumpPtr;

    void dump()
    {
    	if (dumpPtr!=NULL)
    	{
    		fclose(dumpPtr);
    	}
    }

    int main(int argc, char** argv)
    {
    	MPI_Init(&argc, &argv);

    	dumpPtr=fopen(dumpFileName, "w");

    	if (dumpPtr==NULL)
    	{
    		printf("open dump file failed\n");
    		exit(1);
    	}

    	if (atexit(dump)!=0)
    	{
    		printf("atexit fails\n");
    		exit(10);
    	}

    	mpiContext.init();

    	glutInit(&argc, argv);
    	getLastError();
    	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_ALPHA);
    	glutInitWindowSize(ScreenSize, ScreenSize);
    #ifdef TESTING
    	glutInitWindowPosition(5+(mpiContext.myRank%4)*ScreenSize, 5+(mpiContext.myRank%4)*ScreenSize);
    #else
    	glutInitWindowPosition(5, 5);
    #endif

    	switch (mpiContext.myNodeStatus)
    	{
    	case MASTER:
    		glutCreateWindow(masterContext.windowTitle);
    		masterContext.init();
    		printf("master rank#%d finishes init\n", mpiContext.myRank);
    		break;
    	case RENDER:
    		glutCreateWindow(rendererContext.windowTitle);
    		rendererContext.init();
    		printf("render rank#%d finishes init\n", mpiContext.myRank);
    		break;
    	case TESTER:
    		glutCreateWindow(testerContext.windowTitle);
    		testerContext.init();
    		printf("tester rank#%d finishes init\n", mpiContext.myRank);
    		break;
    	}
    	glutDisplayFunc(displayCallbackProc);
    	glutKeyboardFunc(keyboardCallbackProc);

    	glutMainLoop();

    	return 0;
    }

    /*
    void displayCallbackProc()
    {
    	glClearColor(1.0, 1.0, 1.0, 1.0);
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	if (frameCounter==0)
    	{
    		lastTick=GenericGetTime();
    		lastFrameNo=0;
    	}

    	switch (mpiContext.myNodeStatus)
    	{
    	case MASTER:
    		masterContext.displayCallback();
    		break;
    	case RENDER:
    		rendererContext.displayCallback();
    		break;
    	case TESTER:
    		testerContext.displayCallback();
    		break;
    	}

    	currentTick=GenericGetTime();
    	if (currentTick-lastTick>=20)
    	{
    		printf("frame#%u:fps=%f\n", frameCounter, (float)(frameCounter-lastFrameNo)/(float)(currentTick-lastTick)*1000.0);
    		lastTick=currentTick;
    		lastFrameNo=frameCounter;
    	}
    	glutSwapBuffers();
    	frameCounter++;
    }
    */

    void displayCallbackProc()
    {
    	glClearColor(1.0, 1.0, 1.0, 1.0);
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    	switch (mpiContext.myNodeStatus)
    	{
    	case MASTER:
    		masterContext.displayCallback();
    		currentTick=GenericGetTime();
    		if (frameCounter!=0)
    		{
    			printf("frame#%u:fps=%f\n", frameCounter, 1000.0/(float)(currentTick-lastTick));
    		}
    		lastTick=currentTick;
    		frameCounter++;
    		break;
    	case RENDER:
    		rendererContext.displayCallback();
    		break;
    	case TESTER:
    		testerContext.displayCallback();
    		break;
    	}

    	glutSwapBuffers();
    #ifdef USING_FORCE_REFRESH
    	glutPostRedisplay();
    #endif
    }

    void keyboardCallbackProc(unsigned char key, int x, int y)
    {
    	switch (mpiContext.myNodeStatus)
    	{
    	case MASTER:
    		masterContext.keyboardCallback(key, x, y);
    		break;
    	case RENDER:
    		break;
    	case TESTER:
    		break;
    	}
    	glutPostRedisplay();
    }

¡¡

///////////////////////////////////////////

//the following file are only needed for preprocessing

file name: rply.h

    #ifndef PLY_H
    #define PLY_H
    /* ----------------------------------------------------------------------
     * RPly library, read/write PLY files
     * Diego Nehab, Princeton University
     * http://www.cs.princeton.edu/~diego/professional/rply
     *
     * This library is distributed under the MIT License. See notice
     * at the end of this file.
     * ---------------------------------------------------------------------- */
    //#define __cplusplus 1
    #ifdef __cplusplus
    extern "C" {
    #endif

    #define RPLY_VERSION   "RPly 1.01"
    #define RPLY_COPYRIGHT "Copyright (C) 2003-2005 Diego Nehab"
    #define RPLY_AUTHORS   "Diego Nehab"

    /* ----------------------------------------------------------------------
     * Types
     * ---------------------------------------------------------------------- */
    /* structures are opaque */
    typedef struct t_ply_ *p_ply;
    typedef struct t_ply_element_ *p_ply_element;
    typedef struct t_ply_property_ *p_ply_property;
    typedef struct t_ply_argument_ *p_ply_argument;

    /* ply format mode type */
    typedef enum e_ply_storage_mode_ {
        PLY_BIG_ENDIAN,
        PLY_LITTLE_ENDIAN,
        PLY_ASCII,
        PLY_DEFAULT      /* has to be the last in enum */
    } e_ply_storage_mode; /* order matches ply_storage_mode_list */

    /* ply data type */
    typedef enum e_ply_type {
        PLY_INT8, PLY_UINT8, PLY_INT16, PLY_UINT16,
        PLY_INT32, PLY_UIN32, PLY_FLOAT32, PLY_FLOAT64,
        PLY_CHAR, PLY_UCHAR, PLY_SHORT, PLY_USHORT,
        PLY_INT, PLY_UINT, PLY_FLOAT, PLY_DOUBLE,
        PLY_LIST    /* has to be the last in enum */
    } e_ply_type;   /* order matches ply_type_list */

    /* ----------------------------------------------------------------------
     * Property reading callback prototype
     *
     * message: error message
     * ---------------------------------------------------------------------- */
    typedef void (*p_ply_error_cb)(const char *message);

    /* ----------------------------------------------------------------------
     * Opens a ply file for reading (fails if file is not a ply file)
     *
     * error_cb: error callback function
     * name: file name
     *
     * Returns 1 if successful, 0 otherwise
     * ---------------------------------------------------------------------- */
    p_ply ply_open(const char *name, p_ply_error_cb error_cb);

    /* ----------------------------------------------------------------------
     * Reads and parses the header of a ply file returned by ply_open
     *
     * ply: handle returned by ply_open
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_read_header(p_ply ply);

    /* ----------------------------------------------------------------------
     * Property reading callback prototype
     *
     * argument: parameters for property being processed when callback is called
     *
     * Returns 1 if should continue processing file, 0 if should abort.
     * ---------------------------------------------------------------------- */
    typedef int (*p_ply_read_cb)(p_ply_argument argument);

    /* ----------------------------------------------------------------------
     * Sets up callbacks for property reading after header was parsed
     *
     * ply: handle returned by ply_open
     * element_name: element where property is
     * property_name: property to associate element with
     * read_cb: function to be called for each property value
     * pdata/idata: user data that will be passed to callback
     *
     * Returns 0 if no element or no property in element, returns the
     * number of element instances otherwise.
     * ---------------------------------------------------------------------- */
    long ply_set_read_cb(p_ply ply, const char *element_name,
            const char *property_name, p_ply_read_cb read_cb,
            void *pdata, long idata);

    /* ----------------------------------------------------------------------
     * Returns information about the element originating a callback
     *
     * argument: handle to argument
     * element: receives a the element handle (if non-null)
     * instance_index: receives the index of the current element instance
     *     (if non-null)
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_get_argument_element(p_ply_argument argument,
            p_ply_element *element, long *instance_index);

    /* ----------------------------------------------------------------------
     * Returns information about the property originating a callback
     *
     * argument: handle to argument
     * property: receives the property handle (if non-null)
     * length: receives the number of values in this property (if non-null)
     * value_index: receives the index of current property value (if non-null)
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_get_argument_property(p_ply_argument argument,
            p_ply_property *property, long *length, long *value_index);

    /* ----------------------------------------------------------------------
     * Returns user data associated with callback
     *
     * pdata: receives a copy of user custom data pointer (if non-null)
     * idata: receives a copy of user custom data integer (if non-null)
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_get_argument_user_data(p_ply_argument argument, void **pdata,
            long *idata);

    /* ----------------------------------------------------------------------
     * Returns the value associated with a callback
     *
     * argument: handle to argument
     *
     * Returns the current data item
     * ---------------------------------------------------------------------- */
    double ply_get_argument_value(p_ply_argument argument);

    /* ----------------------------------------------------------------------
     * Reads all elements and properties calling the callbacks defined with
     * calls to ply_set_read_cb
     *
     * ply: handle returned by ply_open
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_read(p_ply ply);

    /* ----------------------------------------------------------------------
     * Iterates over all elements by returning the next element.
     * Call with NULL to return handle to first element.
     *
     * ply: handle returned by ply_open
     * last: handle of last element returned (NULL for first element)
     *
     * Returns element if successfull or NULL if no more elements
     * ---------------------------------------------------------------------- */
    p_ply_element ply_get_next_element(p_ply ply, p_ply_element last);

    /* ----------------------------------------------------------------------
     * Iterates over all comments by returning the next comment.
     * Call with NULL to return pointer to first comment.
     *
     * ply: handle returned by ply_open
     * last: pointer to last comment returned (NULL for first comment)
     *
     * Returns comment if successfull or NULL if no more comments
     * ---------------------------------------------------------------------- */
    const char *ply_get_next_comment(p_ply ply, const char *last);

    /* ----------------------------------------------------------------------
     * Iterates over all obj_infos by returning the next obj_info.
     * Call with NULL to return pointer to first obj_info.
     *
     * ply: handle returned by ply_open
     * last: pointer to last obj_info returned (NULL for first obj_info)
     *
     * Returns obj_info if successfull or NULL if no more obj_infos
     * ---------------------------------------------------------------------- */
    const char *ply_get_next_obj_info(p_ply ply, const char *last);

    /* ----------------------------------------------------------------------
     * Returns information about an element
     *
     * element: element of interest
     * name: receives a pointer to internal copy of element name (if non-null)
     * ninstances: receives the number of instances of this element (if non-null)
     *
     * Returns 1 if successfull or 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_get_element_info(p_ply_element element, const char** name,
            long *ninstances);

    /* ----------------------------------------------------------------------
     * Iterates over all properties by returning the next property.
     * Call with NULL to return handle to first property.
     *
     * element: handle of element with the properties of interest
     * last: handle of last property returned (NULL for first property)
     *
     * Returns element if successfull or NULL if no more properties
     * ---------------------------------------------------------------------- */
    p_ply_property ply_get_next_property(p_ply_element element,
            p_ply_property last);

    /* ----------------------------------------------------------------------
     * Returns information about a property
     *
     * property: handle to property of interest
     * name: receives a pointer to internal copy of property name (if non-null)
     * type: receives the property type (if non-null)
     * length_type: for list properties, receives the scalar type of
     *     the length field (if non-null)
     * value_type: for list properties, receives the scalar type of the value
     *     fields  (if non-null)
     *
     * Returns 1 if successfull or 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_get_property_info(p_ply_property property, const char** name,
            e_ply_type *type, e_ply_type *length_type, e_ply_type *value_type);

    /* ----------------------------------------------------------------------
     * Creates new ply file
     *
     * name: file name
     * storage_mode: file format mode
     *
     * Returns handle to ply file if successfull, NULL otherwise
     * ---------------------------------------------------------------------- */
    p_ply ply_create(const char *name, e_ply_storage_mode storage_mode,
            p_ply_error_cb error_cb);

    /* ----------------------------------------------------------------------
     * Adds a new element to the ply file created by ply_create
     *
     * ply: handle returned by ply_create
     * name: name of new element
     * ninstances: number of element of this time in file
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_add_element(p_ply ply, const char *name, long ninstances);

    /* ----------------------------------------------------------------------
     * Adds a new property to the last element added by ply_add_element
     *
     * ply: handle returned by ply_create
     * name: name of new property
     * type: property type
     * length_type: scalar type of length field of a list property
     * value_type: scalar type of value fields of a list property
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_add_property(p_ply ply, const char *name, e_ply_type type,
            e_ply_type length_type, e_ply_type value_type);

    /* ----------------------------------------------------------------------
     * Adds a new list property to the last element added by ply_add_element
     *
     * ply: handle returned by ply_create
     * name: name of new property
     * length_type: scalar type of length field of a list property
     * value_type: scalar type of value fields of a list property
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_add_list_property(p_ply ply, const char *name,
            e_ply_type length_type, e_ply_type value_type);

    /* ----------------------------------------------------------------------
     * Adds a new property to the last element added by ply_add_element
     *
     * ply: handle returned by ply_create
     * name: name of new property
     * type: property type
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type);

    /* ----------------------------------------------------------------------
     * Adds a new comment item
     *
     * ply: handle returned by ply_create
     * comment: pointer to string with comment text
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_add_comment(p_ply ply, const char *comment);

    /* ----------------------------------------------------------------------
     * Adds a new obj_info item
     *
     * ply: handle returned by ply_create
     * comment: pointer to string with obj_info data
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_add_obj_info(p_ply ply, const char *obj_info);

    /* ----------------------------------------------------------------------
     * Writes the ply file header after all element and properties have been
     * defined by calls to ply_add_element and ply_add_property
     *
     * ply: handle returned by ply_create
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_write_header(p_ply ply);

    /* ----------------------------------------------------------------------
     * Writes one property value, in the order they should be written to the
     * file. For each element type, write all elements of that type in order.
     * For each element, write all its properties in order. For scalar
     * properties, just write the value. For list properties, write the length
     * and then each of the values.
     *
     * ply: handle returned by ply_create
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_write(p_ply ply, double value);

    /* ----------------------------------------------------------------------
     * Closes a ply file handle. Releases all memory used by handle
     *
     * ply: handle to be closed.
     *
     * Returns 1 if successfull, 0 otherwise
     * ---------------------------------------------------------------------- */
    int ply_close(p_ply ply);

    #ifdef __cplusplus
    }
    #endif

    #endif /* RPLY_H */

    /* ----------------------------------------------------------------------
     * Copyright (C) 2003-2005 Diego Nehab. All rights reserved.
     *
     * Permission is hereby granted, free of charge, to any person obtaining
     * a copy of this software and associated documentation files (the
     * "Software"), to deal in the Software without restriction, including
     * without limitation the rights to use, copy, modify, merge, publish,
     * distribute, sublicense, and/or sell copies of the Software, and to
     * permit persons to whom the Software is furnished to do so, subject to
     * the following conditions:
     *
     * The above copyright notice and this permission notice shall be
     * included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
     * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     * ---------------------------------------------------------------------- */

¡¡

file name: rply.c

    /* ----------------------------------------------------------------------
     * RPly library, read/write PLY files
     * Diego Nehab, Princeton University
     * http://www.cs.princeton.edu/~diego/professional/rply
     *
     * This library is distributed under the MIT License. See notice
     * at the end of this file.
     * ---------------------------------------------------------------------- */
    #include <stdio.h>
    #include <ctype.h>
    #include <assert.h>
    #include <string.h>
    #include <limits.h>
    #include <float.h>
    #include <stdarg.h>
    #include <stdlib.h>
    #include <stddef.h>

    #include "rply.h"

    /* ----------------------------------------------------------------------
     * Constants
     * ---------------------------------------------------------------------- */
    #define WORDSIZE 256
    #define LINESIZE 1024
    #define BUFFERSIZE (8*1024)

    typedef enum e_ply_io_mode_ {
        PLY_READ,
        PLY_WRITE
    } e_ply_io_mode;

    static const char *const ply_storage_mode_list[] = {
        "binary_big_endian", "binary_little_endian", "ascii", NULL
    };     /* order matches e_ply_storage_mode enum */

    static const char *const ply_type_list[] = {
        "int8", "uint8", "int16", "uint16",
        "int32", "uint32", "float32", "float64",
        "char", "uchar", "short", "ushort",
        "int", "uint", "float", "double",
        "list", NULL
    };     /* order matches e_ply_type enum */

    /* ----------------------------------------------------------------------
     * Property reading callback argument
     *
     * element: name of element being processed
     * property: name of property being processed
     * nelements: number of elements of this kind in file
     * instance_index: index current element of this kind being processed
     * length: number of values in current list (or 1 for scalars)
     * value_index: index of current value int this list (or 0 for scalars)
     * value: value of property
     * pdata/idata: user data defined with ply_set_cb
     *
     * Returns handle to ply file if succesful, NULL otherwise.
     * ---------------------------------------------------------------------- */
    typedef struct t_ply_argument_ {
        p_ply_element element;
        long instance_index;
        p_ply_property property;
        long length, value_index;
        double value;
        void *pdata;
        long idata;
    } t_ply_argument;

    /* ----------------------------------------------------------------------
     * Property information
     *
     * name: name of this property
     * type: type of this property (list or type of scalar value)
     * length_type, value_type: type of list property count and values
     * read_cb: function to be called when this property is called
     *
     * Returns 1 if should continue processing file, 0 if should abort.
     * ---------------------------------------------------------------------- */
    typedef struct t_ply_property_ {
        char name[WORDSIZE];
        e_ply_type type, value_type, length_type;
        p_ply_read_cb read_cb;
        void *pdata;
        long idata;
    } t_ply_property;

    /* ----------------------------------------------------------------------
     * Element information
     *
     * name: name of this property
     * ninstances: number of elements of this type in file
     * property: property descriptions for this element
     * nproperty: number of properties in this element
     *
     * Returns 1 if should continue processing file, 0 if should abort.
     * ---------------------------------------------------------------------- */
    typedef struct t_ply_element_ {
        char name[WORDSIZE];
        long ninstances;
        p_ply_property property;
        long nproperties;
    } t_ply_element;

    /* ----------------------------------------------------------------------
     * Input/output driver
     *
     * Depending on file mode, different functions are used to read/write
     * property fields. The drivers make it transparent to read/write in ascii,
     * big endian or little endian cases.
     * ---------------------------------------------------------------------- */
    typedef int (*p_ply_ihandler)(p_ply ply, double *value);
    typedef int (*p_ply_ichunk)(p_ply ply, void *anydata, size_t size);
    typedef struct t_ply_idriver_ {
        p_ply_ihandler ihandler[16];
        p_ply_ichunk ichunk;
        const char *name;
    } t_ply_idriver;
    typedef t_ply_idriver *p_ply_idriver;

    typedef int (*p_ply_ohandler)(p_ply ply, double value);
    typedef int (*p_ply_ochunk)(p_ply ply, void *anydata, size_t size);
    typedef struct t_ply_odriver_ {
        p_ply_ohandler ohandler[16];
        p_ply_ochunk ochunk;
        const char *name;
    } t_ply_odriver;
    typedef t_ply_odriver *p_ply_odriver;

    /* ----------------------------------------------------------------------
     * Ply file handle.
     *
     * io_mode: read or write (from e_ply_io_mode)
     * storage_mode: mode of file associated with handle (from e_ply_storage_mode)
     * element: elements description for this file
     * nelement: number of different elements in file
     * comment: comments for this file
     * ncomments: number of comments in file
     * obj_info: obj_info items for this file
     * nobj_infos: number of obj_info items in file
     * fp: file pointer associated with ply file
     * c: last character read from ply file
     * buffer: last word/chunck of data read from ply file
     * buffer_first, buffer_last: interval of untouched good data in buffer
     * buffer_token: start of parsed token (line or word) in buffer
     * idriver, odriver: input driver used to get property fields from file
     * argument: storage space for callback arguments
     * welement, wproperty: element/property type being written
     * winstance_index: index of instance of current element being written
     * wvalue_index: index of list property value being written
     * wlength: number of values in list property being written
     * error_cb: callback for error messages
     * ---------------------------------------------------------------------- */
    typedef struct t_ply_ {
        e_ply_io_mode io_mode;
        e_ply_storage_mode storage_mode;
        p_ply_element element;
        long nelements;
        char *comment;
        long ncomments;
        char *obj_info;
        long nobj_infos;
        FILE *fp;
        int c;
        char buffer[BUFFERSIZE];
        size_t buffer_first, buffer_token, buffer_last;
        p_ply_idriver idriver;
        p_ply_odriver odriver;
        t_ply_argument argument;
        long welement, wproperty;
        long winstance_index, wvalue_index, wlength;
        p_ply_error_cb error_cb;
    } t_ply;

    /* ----------------------------------------------------------------------
     * I/O functions and drivers
     * ---------------------------------------------------------------------- */
    static t_ply_idriver ply_idriver_ascii;
    static t_ply_idriver ply_idriver_binary;
    static t_ply_idriver ply_idriver_binary_reverse;
    static t_ply_odriver ply_odriver_ascii;
    static t_ply_odriver ply_odriver_binary;
    static t_ply_odriver ply_odriver_binary_reverse;

    static int ply_read_word(p_ply ply);
    static int ply_check_word(p_ply ply);
    static int ply_read_line(p_ply ply);
    static int ply_check_line(p_ply ply);
    static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size);
    static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size);
    static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size);
    static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size);
    static void ply_reverse(void *anydata, size_t size);

    /* ----------------------------------------------------------------------
     * String functions
     * ---------------------------------------------------------------------- */
    static int ply_find_string(const char *item, const char* const list[]);
    static p_ply_element ply_find_element(p_ply ply, const char *name);
    static p_ply_property ply_find_property(p_ply_element element,
            const char *name);

    /* ----------------------------------------------------------------------
     * Header parsing
     * ---------------------------------------------------------------------- */
    static int ply_read_header_format(p_ply ply);
    static int ply_read_header_comment(p_ply ply);
    static int ply_read_header_obj_info(p_ply ply);
    static int ply_read_header_property(p_ply ply);
    static int ply_read_header_element(p_ply ply);

    /* ----------------------------------------------------------------------
     * Error handling
     * ---------------------------------------------------------------------- */
    static void ply_error_cb(const char *message);
    static void ply_error(p_ply ply, const char *fmt, ...);

    /* ----------------------------------------------------------------------
     * Memory allocation and initialization
     * ---------------------------------------------------------------------- */
    static void ply_init(p_ply ply);
    static void ply_element_init(p_ply_element element);
    static void ply_property_init(p_ply_property property);
    static p_ply ply_alloc(void);
    static p_ply_element ply_grow_element(p_ply ply);
    static p_ply_property ply_grow_property(p_ply ply, p_ply_element element);
    static void *ply_grow_array(p_ply ply, void **pointer, long *nmemb, long size);

    /* ----------------------------------------------------------------------
     * Special functions
     * ---------------------------------------------------------------------- */
    static e_ply_storage_mode ply_arch_endian(void);
    static int ply_type_check(void);

    /* ----------------------------------------------------------------------
     * Auxiliary read functions
     * ---------------------------------------------------------------------- */
    static int ply_read_element(p_ply ply, p_ply_element element,
            p_ply_argument argument);
    static int ply_read_property(p_ply ply, p_ply_element element,
            p_ply_property property, p_ply_argument argument);
    static int ply_read_list_property(p_ply ply, p_ply_element element,
            p_ply_property property, p_ply_argument argument);
    static int ply_read_scalar_property(p_ply ply, p_ply_element element,
            p_ply_property property, p_ply_argument argument);

    /* ----------------------------------------------------------------------
     * Buffer support functions
     * ---------------------------------------------------------------------- */
    /* pointers to tokenized word and line in buffer */
    #define BWORD(p) (p->buffer + p->buffer_token)
    #define BLINE(p) (p->buffer + p->buffer_token)

    /* pointer to start of untouched bytes in buffer */
    #define BFIRST(p) (p->buffer + p->buffer_first)

    /* number of bytes untouched in buffer */
    #define BSIZE(p) (p->buffer_last - p->buffer_first)

    /* consumes data from buffer */
    #define BSKIP(p, s) (p->buffer_first += s)

    /* refills the buffer */
    static int BREFILL(p_ply ply) {
        /* move untouched data to beginning of buffer */
        size_t size = BSIZE(ply);
        memmove(ply->buffer, BFIRST(ply), size);
        ply->buffer_last = size;
        ply->buffer_first = ply->buffer_token = 0;
        /* fill remaining with new data */
        size = fread(ply->buffer+size, 1, BUFFERSIZE-size-1, ply->fp);
        /* place sentinel so we can use str* functions with buffer */
        ply->buffer[BUFFERSIZE-1] = '\0';
        /* check if read failed */
        if (size <= 0) return 0;
        /* increase size to account for new data */
        ply->buffer_last += size;
        return 1;
    }

    /* ----------------------------------------------------------------------
     * Exported functions
     * ---------------------------------------------------------------------- */
    /* ----------------------------------------------------------------------
     * Read support functions
     * ---------------------------------------------------------------------- */
    p_ply ply_open(const char *name, p_ply_error_cb error_cb) {
        char magic[5] = "    ";
        FILE *fp = NULL;
        p_ply ply = NULL;
        if (error_cb == NULL) error_cb = ply_error_cb;
        if (!ply_type_check()) {
            error_cb("Incompatible type system");
            return NULL;
        }
        assert(name);
        fp = fopen(name, "rb");
        if (!fp) {
            error_cb("Unable to open file");
            return NULL;
        }
        if (fread(magic, 1, 4, fp) < 4) {
            error_cb("Error reading from file");
            fclose(fp);
            return NULL;
        }
        if (strcmp(magic, "ply\n")) {
            fclose(fp);
            error_cb("Not a PLY file. Expected magic number 'ply\\n'");
            return NULL;
        }
        ply = ply_alloc();
        if (!ply) {
            error_cb("Out of memory");
            fclose(fp);
            return NULL;
        }
        ply->fp = fp;
        ply->io_mode = PLY_READ;
        ply->error_cb = error_cb;
        return ply;
    }

    int ply_read_header(p_ply ply) {
        assert(ply && ply->fp && ply->io_mode == PLY_READ);
        if (!ply_read_word(ply)) return 0;
        /* parse file format */
        if (!ply_read_header_format(ply)) {
            ply_error(ply, "Invalid file format");
            return 0;
        }
        /* parse elements, comments or obj_infos until the end of header */
        while (strcmp(BWORD(ply), "end_header")) {
            if (!ply_read_header_comment(ply) &&
                    !ply_read_header_element(ply) &&
                    !ply_read_header_obj_info(ply)) {
                ply_error(ply, "Unexpected token '%s'", BWORD(ply));
                return 0;
            }
        }
        return 1;
    }

    long ply_set_read_cb(p_ply ply, const char *element_name,
            const char* property_name, p_ply_read_cb read_cb,
            void *pdata, long idata) {
        p_ply_element element = NULL;
        p_ply_property property = NULL;
        assert(ply && element_name && property_name);
        element = ply_find_element(ply, element_name);
        if (!element) return 0;
        property = ply_find_property(element, property_name);
        if (!property) return 0;
        property->read_cb = read_cb;
        property->pdata = pdata;
        property->idata = idata;
        return (int) element->ninstances;
    }

    int ply_read(p_ply ply) {
        long i;
        p_ply_argument argument;
        assert(ply && ply->fp && ply->io_mode == PLY_READ);
        argument = &ply->argument;
        /* for each element type */
        for (i = 0; i < ply->nelements; i++) {
            p_ply_element element = &ply->element[i];
            argument->element = element;
            if (!ply_read_element(ply, element, argument))
                return 0;
        }
        return 1;
    }

    /* ----------------------------------------------------------------------
     * Write support functions
     * ---------------------------------------------------------------------- */
    p_ply ply_create(const char *name, e_ply_storage_mode storage_mode,
            p_ply_error_cb error_cb) {
        FILE *fp = NULL;
        p_ply ply = NULL;
        if (error_cb == NULL) error_cb = ply_error_cb;
        if (!ply_type_check()) {
            error_cb("Incompatible type system");
            return NULL;
        }
        assert(name && storage_mode <= PLY_DEFAULT);
        fp = fopen(name, "wb");
        if (!fp) {
            error_cb("Unable to create file");
            return NULL;
        }
        ply = ply_alloc();
        if (!ply) {
            fclose(fp);
            error_cb("Out of memory");
            return NULL;
        }
        ply->io_mode = PLY_WRITE;
        if (storage_mode == PLY_DEFAULT) storage_mode = ply_arch_endian();
        if (storage_mode == PLY_ASCII) ply->odriver = &ply_odriver_ascii;
        else if (storage_mode == ply_arch_endian())
            ply->odriver = &ply_odriver_binary;
        else ply->odriver = &ply_odriver_binary_reverse;
        ply->storage_mode = storage_mode;
        ply->fp = fp;
        ply->error_cb = error_cb;
        return ply;
    }

    int ply_add_element(p_ply ply, const char *name, long ninstances) {
        p_ply_element element = NULL;
        assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
        assert(name && strlen(name) < WORDSIZE && ninstances >= 0);
        if (strlen(name) >= WORDSIZE || ninstances < 0) {
            ply_error(ply, "Invalid arguments");
            return 0;
        }
        element = ply_grow_element(ply);
        if (!element) return 0;
        strcpy(element->name, name);
        element->ninstances = ninstances;
        return 1;
    }

    int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type) {
        p_ply_element element = NULL;
        p_ply_property property = NULL;
        assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
        assert(name && strlen(name) < WORDSIZE);
        assert(type < PLY_LIST);
        if (strlen(name) >= WORDSIZE || type >= PLY_LIST) {
            ply_error(ply, "Invalid arguments");
            return 0;
        }
        element = &ply->element[ply->nelements-1];
        property = ply_grow_property(ply, element);
        if (!property) return 0;
        strcpy(property->name, name);
        property->type = type;
        return 1;
    }

    int ply_add_list_property(p_ply ply, const char *name,
            e_ply_type length_type, e_ply_type value_type) {
        p_ply_element element = NULL;
        p_ply_property property = NULL;
        assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
        assert(name && strlen(name) < WORDSIZE);
        if (strlen(name) >= WORDSIZE) {
            ply_error(ply, "Invalid arguments");
            return 0;
        }
        assert(length_type < PLY_LIST);
        assert(value_type < PLY_LIST);
        if (length_type >= PLY_LIST || value_type >= PLY_LIST) {
            ply_error(ply, "Invalid arguments");
            return 0;
        }
        element = &ply->element[ply->nelements-1];
        property = ply_grow_property(ply, element);
        if (!property) return 0;
        strcpy(property->name, name);
        property->type = PLY_LIST;
        property->length_type = length_type;
        property->value_type = value_type;
        return 1;
    }

    int ply_add_property(p_ply ply, const char *name, e_ply_type type,
            e_ply_type length_type, e_ply_type value_type) {
        if (type == PLY_LIST)
            return ply_add_list_property(ply, name, length_type, value_type);
        else
            return ply_add_scalar_property(ply, name, type);
    }

    int ply_add_comment(p_ply ply, const char *comment) {
        char *new_comment = NULL;
        assert(ply && comment && strlen(comment) < LINESIZE);
        if (!comment || strlen(comment) >= LINESIZE) {
            ply_error(ply, "Invalid arguments");
            return 0;
        }
        new_comment = (char *) ply_grow_array(ply, (void **) &ply->comment,
                &ply->ncomments, LINESIZE);
        if (!new_comment) return 0;
        strcpy(new_comment, comment);
        return 1;
    }

    int ply_add_obj_info(p_ply ply, const char *obj_info) {
        char *new_obj_info = NULL;
        assert(ply && obj_info && strlen(obj_info) < LINESIZE);
        if (!obj_info || strlen(obj_info) >= LINESIZE) {
            ply_error(ply, "Invalid arguments");
            return 0;
        }
        new_obj_info = (char *) ply_grow_array(ply, (void **) &ply->obj_info,
                &ply->nobj_infos, LINESIZE);
        if (!new_obj_info) return 0;
        strcpy(new_obj_info, obj_info);
        return 1;
    }

    int ply_write_header(p_ply ply) {
        long i, j;
        assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
        assert(ply->element || ply->nelements == 0);
        assert(!ply->element || ply->nelements > 0);
        if (fprintf(ply->fp, "ply\nformat %s 1.0\n",
                    ply_storage_mode_list[ply->storage_mode]) <= 0) goto error;
        for (i = 0; i < ply->ncomments; i++)
            if (fprintf(ply->fp, "comment %s\n", ply->comment + LINESIZE*i) <= 0)
                goto error;
        for (i = 0; i < ply->nobj_infos; i++)
            if (fprintf(ply->fp, "obj_info %s\n", ply->obj_info + LINESIZE*i) <= 0)
                goto error;
        for (i = 0; i < ply->nelements; i++) {
            p_ply_element element = &ply->element[i];
            assert(element->property || element->nproperties == 0);
            assert(!element->property || element->nproperties > 0);
            if (fprintf(ply->fp, "element %s %ld\n", element->name,
                        element->ninstances) <= 0) goto error;
            for (j = 0; j < element->nproperties; j++) {
                p_ply_property property = &element->property[j];
                if (property->type == PLY_LIST) {
                    if (fprintf(ply->fp, "property list %s %s %s\n",
                                ply_type_list[property->length_type],
                                ply_type_list[property->value_type],
                                property->name) <= 0) goto error;
                } else {
                    if (fprintf(ply->fp, "property %s %s\n",
                                ply_type_list[property->type],
                                property->name) <= 0) goto error;
                }
            }
        }
        return fprintf(ply->fp, "end_header\n") > 0;
    error:
        ply_error(ply, "Error writing to file");
        return 0;
    }

    int ply_write(p_ply ply, double value) {
        p_ply_element element = NULL;
        p_ply_property property = NULL;
        int type = -1;
        int breakafter = 0;
        if (ply->welement > ply->nelements) return 0;
        element = &ply->element[ply->welement];
        if (ply->wproperty > element->nproperties) return 0;
        property = &element->property[ply->wproperty];
        if (property->type == PLY_LIST) {
            if (ply->wvalue_index == 0) {
                type = property->length_type;
                ply->wlength = (long) value;
            } else type = property->value_type;
        } else {
            type = property->type;
            ply->wlength = 0;
        }
        if (!ply->odriver->ohandler[type](ply, value)) {
            ply_error(ply, "Failed writing %s of %s %d (%s: %s)",
                        property->name, element->name,
                        ply->winstance_index,
                        ply->odriver->name, ply_type_list[type]);
            return 0;
        }
        ply->wvalue_index++;
        if (ply->wvalue_index > ply->wlength) {
            ply->wvalue_index = 0;
            ply->wproperty++;
        }
        if (ply->wproperty >= element->nproperties) {
            ply->wproperty = 0;
            ply->winstance_index++;
            if (ply->storage_mode == PLY_ASCII) breakafter = 1;
        }
        if (ply->winstance_index >= element->ninstances) {
            ply->winstance_index = 0;
            ply->welement++;
        }
        return !breakafter || putc('\n', ply->fp) > 0;
    }

    int ply_close(p_ply ply) {
        long i;
        assert(ply && ply->fp);
        assert(ply->element || ply->nelements == 0);
        assert(!ply->element || ply->nelements > 0);
        /* write last chunk to file */
        if (ply->io_mode == PLY_WRITE &&
          fwrite(ply->buffer, 1, ply->buffer_last, ply->fp) < ply->buffer_last) {
            ply_error(ply, "Error closing up");
            return 0;
        }
        fclose(ply->fp);
        /* free all memory used by handle */
        if (ply->element) {
            for (i = 0; i < ply->nelements; i++) {
                p_ply_element element = &ply->element[i];
                if (element->property) free(element->property);
            }
            free(ply->element);
        }
        if (ply->obj_info) free(ply->obj_info);
        if (ply->comment) free(ply->comment);
        free(ply);
        return 1;
    }

    /* ----------------------------------------------------------------------
     * Query support functions
     * ---------------------------------------------------------------------- */
    p_ply_element ply_get_next_element(p_ply ply,
            p_ply_element last) {
        assert(ply);
        if (!last) return ply->element;
        last++;
        if (last < ply->element + ply->nelements) return last;
        else return NULL;
    }

    int ply_get_element_info(p_ply_element element, const char** name,
            long *ninstances) {
        assert(element);
        if (name) *name = element->name;
        if (ninstances) *ninstances = (long) element->ninstances;
        return 1;
    }

    p_ply_property ply_get_next_property(p_ply_element element,
            p_ply_property last) {
        assert(element);
        if (!last) return element->property;
        last++;
        if (last < element->property + element->nproperties) return last;
        else return NULL;
    }

    int ply_get_property_info(p_ply_property property, const char** name,
            e_ply_type *type, e_ply_type *length_type, e_ply_type *value_type) {
        assert(property);
        if (name) *name = property->name;
        if (type) *type = property->type;
        if (length_type) *length_type = property->length_type;
        if (value_type) *value_type = property->value_type;
        return 1;

    }

    const char *ply_get_next_comment(p_ply ply, const char *last) {
        assert(ply);
        if (!last) return ply->comment;
        last += LINESIZE;
        if (last < ply->comment + LINESIZE*ply->ncomments) return last;
        else return NULL;
    }

    const char *ply_get_next_obj_info(p_ply ply, const char *last) {
        assert(ply);
        if (!last) return ply->obj_info;
        last += LINESIZE;
        if (last < ply->obj_info + LINESIZE*ply->nobj_infos) return last;
        else return NULL;
    }

    /* ----------------------------------------------------------------------
     * Callback argument support functions
     * ---------------------------------------------------------------------- */
    int ply_get_argument_element(p_ply_argument argument,
            p_ply_element *element, long *instance_index) {
        assert(argument);
        if (!argument) return 0;
        if (element) *element = argument->element;
        if (instance_index) *instance_index = argument->instance_index;
        return 1;
    }

    int ply_get_argument_property(p_ply_argument argument,
            p_ply_property *property, long *length, long *value_index) {
        assert(argument);
        if (!argument) return 0;
        if (property) *property = argument->property;
        if (length) *length = argument->length;
        if (value_index) *value_index = argument->value_index;
        return 1;
    }

    int ply_get_argument_user_data(p_ply_argument argument, void **pdata,
            long *idata) {
        assert(argument);
        if (!argument) return 0;
        if (pdata) *pdata = argument->pdata;
        if (idata) *idata = argument->idata;
        return 1;
    }

    double ply_get_argument_value(p_ply_argument argument) {
        assert(argument);
        if (!argument) return 0.0;
        return argument->value;
    }

    /* ----------------------------------------------------------------------
     * Internal functions
     * ---------------------------------------------------------------------- */
    static int ply_read_list_property(p_ply ply, p_ply_element element,
            p_ply_property property, p_ply_argument argument) {
        int l;
        p_ply_read_cb read_cb = property->read_cb;
        p_ply_ihandler *driver = ply->idriver->ihandler;
        /* get list length */
        p_ply_ihandler handler = driver[property->length_type];
        double length;
        if (!handler(ply, &length)) {
            ply_error(ply, "Error reading '%s' of '%s' number %d",
                    property->name, element->name, argument->instance_index);
            return 0;
        }
        /* invoke callback to pass length in value field */
        argument->length = (long) length;
        argument->value_index = -1;
        argument->value = length;
        if (read_cb && !read_cb(argument)) {
            ply_error(ply, "Aborted by user");
            return 0;
        }
        /* read list values */
        handler = driver[property->value_type];
        /* for each value in list */
        for (l = 0; l < (long) length; l++) {
            /* read value from file */
            argument->value_index = l;
            if (!handler(ply, &argument->value)) {
                ply_error(ply, "Error reading value number %d of '%s' of "
                        "'%s' number %d", l+1, property->name,
                        element->name, argument->instance_index);
                return 0;
            }
            /* invoke callback to pass value */
            if (read_cb && !read_cb(argument)) {
                ply_error(ply, "Aborted by user");
                return 0;
            }
        }
        return 1;
    }

    static int ply_read_scalar_property(p_ply ply, p_ply_element element,
            p_ply_property property, p_ply_argument argument) {
        p_ply_read_cb read_cb = property->read_cb;
        p_ply_ihandler *driver = ply->idriver->ihandler;
        p_ply_ihandler handler = driver[property->type];
        argument->length = 1;
        argument->value_index = 0;
        if (!handler(ply, &argument->value)) {
            ply_error(ply, "Error reading '%s' of '%s' number %d",
                    property->name, element->name, argument->instance_index);
            return 0;
        }
        if (read_cb && !read_cb(argument)) {
            ply_error(ply, "Aborted by user");
            return 0;
        }
        return 1;
    }

    static int ply_read_property(p_ply ply, p_ply_element element,
            p_ply_property property, p_ply_argument argument) {
        if (property->type == PLY_LIST)
            return ply_read_list_property(ply, element, property, argument);
        else
            return ply_read_scalar_property(ply, element, property, argument);
    }

    static int ply_read_element(p_ply ply, p_ply_element element,
            p_ply_argument argument) {
        long j, k;
        /* for each element of this type */
        for (j = 0; j < element->ninstances; j++) {
            argument->instance_index = j;
            /* for each property */
            for (k = 0; k < element->nproperties; k++) {
                p_ply_property property = &element->property[k];
                argument->property = property;
                argument->pdata = property->pdata;
                argument->idata = property->idata;
                if (!ply_read_property(ply, element, property, argument))
                    return 0;
            }
        }
        return 1;
    }

    static int ply_find_string(const char *item, const char* const list[]) {
        int i;
        assert(item && list);
        for (i = 0; list[i]; i++)
            if (!strcmp(list[i], item)) return i;
        return -1;
    }

    static p_ply_element ply_find_element(p_ply ply, const char *name) {
        p_ply_element element;
        int i, nelements;
        assert(ply && name);
        element = ply->element;
        nelements = ply->nelements;
        assert(element || nelements == 0);
        assert(!element || nelements > 0);
        for (i = 0; i < nelements; i++)
            if (!strcmp(element[i].name, name)) return &element[i];
        return NULL;
    }

    static p_ply_property ply_find_property(p_ply_element element,
            const char *name) {
        p_ply_property property;
        int i, nproperties;
        assert(element && name);
        property = element->property;
        nproperties = element->nproperties;
        assert(property || nproperties == 0);
        assert(!property || nproperties > 0);
        for (i = 0; i < nproperties; i++)
            if (!strcmp(property[i].name, name)) return &property[i];
        return NULL;
    }

    static int ply_check_word(p_ply ply) {
        if (strlen(BLINE(ply)) >= WORDSIZE) {
            ply_error(ply, "Word too long");
            return 0;
        }
        return 1;
    }

    static int ply_read_word(p_ply ply) {
        size_t t = 0;
        assert(ply && ply->fp && ply->io_mode == PLY_READ);
        /* skip leading blanks */
        while (1) {
            t = strspn(BFIRST(ply), " \n\r\t");
            /* check if all buffer was made of blanks */
            if (t >= BSIZE(ply)) {
                if (!BREFILL(ply)) {
                    ply_error(ply, "Unexpected end of file");
                    return 0;
                }
            } else break;
        }
        BSKIP(ply, t);
        /* look for a space after the current word */
        t = strcspn(BFIRST(ply), " \n\r\t");
        /* if we didn't reach the end of the buffer, we are done */
        if (t < BSIZE(ply)) {
            ply->buffer_token = ply->buffer_first;
            BSKIP(ply, t);
            *BFIRST(ply) = '\0';
            BSKIP(ply, 1);
            return ply_check_word(ply);
        }
        /* otherwise, try to refill buffer */
        if (!BREFILL(ply)) {
            ply_error(ply, "Unexpected end of file");
            return 0;
        }
        /* keep looking from where we left */
        t += strcspn(BFIRST(ply) + t, " \n\r\t");
        /* check if the token is too large for our buffer */
        if (t >= BSIZE(ply)) {
            ply_error(ply, "Token too large");
            return 0;
        }
        /* we are done */
        ply->buffer_token = ply->buffer_first;
        BSKIP(ply, t);
        *BFIRST(ply) = '\0';
        BSKIP(ply, 1);
        return ply_check_word(ply);
    }

    static int ply_check_line(p_ply ply) {
        if (strlen(BLINE(ply)) >= LINESIZE) {
            ply_error(ply, "Line too long");
            return 0;
        }
        return 1;
    }

    static int ply_read_line(p_ply ply) {
        const char *end = NULL;
        assert(ply && ply->fp && ply->io_mode == PLY_READ);
        /* look for a end of line */
        end = strchr(BFIRST(ply), '\n');
        /* if we didn't reach the end of the buffer, we are done */
        if (end) {
            ply->buffer_token = ply->buffer_first;
            BSKIP(ply, end - BFIRST(ply));
            *BFIRST(ply) = '\0';
            BSKIP(ply, 1);
            return ply_check_line(ply);
        } else {
            end = ply->buffer + BSIZE(ply);
            /* otherwise, try to refill buffer */
            if (!BREFILL(ply)) {
                ply_error(ply, "Unexpected end of file");
                return 0;
            }
        }
        /* keep looking from where we left */
        end = strchr(end, '\n');
        /* check if the token is too large for our buffer */
        if (!end) {
            ply_error(ply, "Token too large");
            return 0;
        }
        /* we are done */
        ply->buffer_token = ply->buffer_first;
        BSKIP(ply, end - BFIRST(ply));
        *BFIRST(ply) = '\0';
        BSKIP(ply, 1);
        return ply_check_line(ply);
    }

    static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size) {
        char *buffer = (char *) anybuffer;
        size_t i = 0;
        assert(ply && ply->fp && ply->io_mode == PLY_READ);
        assert(ply->buffer_first <= ply->buffer_last);
        while (i < size) {
            if (ply->buffer_first < ply->buffer_last) {
                buffer[i] = ply->buffer[ply->buffer_first];
                ply->buffer_first++;
                i++;
            } else {
                ply->buffer_first = 0;
                ply->buffer_last = fread(ply->buffer, 1, BUFFERSIZE, ply->fp);
                if (ply->buffer_last <= 0) return 0;
            }
        }
        return 1;
    }

    static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size) {
        char *buffer = (char *) anybuffer;
        size_t i = 0;
        assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
        assert(ply->buffer_last <= BUFFERSIZE);
        while (i < size) {
            if (ply->buffer_last < BUFFERSIZE) {
                ply->buffer[ply->buffer_last] = buffer[i];
                ply->buffer_last++;
                i++;
            } else {
                ply->buffer_last = 0;
                if (fwrite(ply->buffer, 1, BUFFERSIZE, ply->fp) < BUFFERSIZE)
                    return 0;
            }
        }
        return 1;
    }

    static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size) {
        int ret = 0;
        ply_reverse(anybuffer, size);
        ret = ply_write_chunk(ply, anybuffer, size);
        ply_reverse(anybuffer, size);
        return ret;
    }

    static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size) {
        if (!ply_read_chunk(ply, anybuffer, size)) return 0;
        ply_reverse(anybuffer, size);
        return 1;
    }

    static void ply_reverse(void *anydata, size_t size) {
        char *data = (char *) anydata;
        char temp;
        size_t i;
        for (i = 0; i < size/2; i++) {
            temp = data[i];
            data[i] = data[size-i-1];
            data[size-i-1] = temp;
        }
    }

    static void ply_init(p_ply ply) {
        ply->c = ' ';
        ply->element = NULL;
        ply->nelements = 0;
        ply->comment = NULL;
        ply->ncomments = 0;
        ply->obj_info = NULL;
        ply->nobj_infos = 0;
        ply->idriver = NULL;
        ply->odriver = NULL;
        ply->buffer[0] = '\0';
        ply->buffer_first = ply->buffer_last = ply->buffer_token = 0;
        ply->welement = 0;
        ply->wproperty = 0;
        ply->winstance_index = 0;
        ply->wlength = 0;
        ply->wvalue_index = 0;
    }

    static void ply_element_init(p_ply_element element) {
        element->name[0] = '\0';
        element->ninstances = 0;
        element->property = NULL;
        element->nproperties = 0;
    }

    static void ply_property_init(p_ply_property property) {
        property->name[0] = '\0';
        property->type = -1;
        property->length_type = -1;
        property->value_type = -1;
        property->read_cb = (p_ply_read_cb) NULL;
        property->pdata = NULL;
        property->idata = 0;
    }

    static p_ply ply_alloc(void) {
        p_ply ply = (p_ply) malloc(sizeof(t_ply));
        if (!ply) return NULL;
        ply_init(ply);
        return ply;
    }

    static void *ply_grow_array(p_ply ply, void **pointer,
            long *nmemb, long size) {
        void *temp = *pointer;
        long count = *nmemb + 1;
        if (!temp) temp = malloc(count*size);
        else temp = realloc(temp, count*size);
        if (!temp) {
            ply_error(ply, "Out of memory");
            return NULL;
        }
        *pointer = temp;
        *nmemb = count;
        return (char *) temp + (count-1) * size;
    }

    static p_ply_element ply_grow_element(p_ply ply) {
        p_ply_element element = NULL;
        assert(ply);
        assert(ply->element || ply->nelements == 0);
        assert(!ply->element || ply->nelements > 0);
        element = (p_ply_element) ply_grow_array(ply, (void **) &ply->element,
                &ply->nelements, sizeof(t_ply_element));
        if (!element) return NULL;
        ply_element_init(element);
        return element;
    }

    static p_ply_property ply_grow_property(p_ply ply, p_ply_element element) {
        p_ply_property property = NULL;
        assert(ply);
        assert(element);
        assert(element->property || element->nproperties == 0);
        assert(!element->property || element->nproperties > 0);
        property = (p_ply_property) ply_grow_array(ply,
                (void **) &element->property,
                &element->nproperties, sizeof(t_ply_property));
        if (!property) return NULL;
        ply_property_init(property);
        return property;
    }

    static int ply_read_header_format(p_ply ply) {
        assert(ply && ply->fp && ply->io_mode == PLY_READ);
        if (strcmp(BWORD(ply), "format")) return 0;
        if (!ply_read_word(ply)) return 0;
        ply->storage_mode = ply_find_string(BWORD(ply), ply_storage_mode_list);
        if (ply->storage_mode == (e_ply_storage_mode) (-1)) return 0;
        if (ply->storage_mode == PLY_ASCII) ply->idriver = &ply_idriver_ascii;
        else if (ply->storage_mode == ply_arch_endian())
            ply->idriver = &ply_idriver_binary;
        else ply->idriver = &ply_idriver_binary_reverse;
        if (!ply_read_word(ply)) return 0;
        if (strcmp(BWORD(ply), "1.0")) return 0;
        if (!ply_read_word(ply)) return 0;
        return 1;
    }

    static int ply_read_header_comment(p_ply ply) {
        assert(ply && ply->fp && ply->io_mode == PLY_READ);
        if (strcmp(BWORD(ply), "comment")) return 0;
        if (!ply_read_line(ply)) return 0;
        if (!ply_add_comment(ply, BLINE(ply))) return 0;
        if (!ply_read_word(ply)) return 0;
        return 1;
    }

    static int ply_read_header_obj_info(p_ply ply) {
        assert(ply && ply->fp && ply->io_mode == PLY_READ);
        if (strcmp(BWORD(ply), "obj_info")) return 0;
        if (!ply_read_line(ply)) return 0;
        if (!ply_add_obj_info(ply, BLINE(ply))) return 0;
        if (!ply_read_word(ply)) return 0;
        return 1;
    }

    static int ply_read_header_property(p_ply ply) {
        p_ply_element element = NULL;
        p_ply_property property = NULL;
        /* make sure it is a property */
        if (strcmp(BWORD(ply), "property")) return 0;
        element = &ply->element[ply->nelements-1];
        property = ply_grow_property(ply, element);
        if (!property) return 0;
        /* get property type */
        if (!ply_read_word(ply)) return 0;
        property->type = ply_find_string(BWORD(ply), ply_type_list);
        if (property->type == (e_ply_type) (-1)) return 0;
        if (property->type == PLY_LIST) {
            /* if it's a list, we need the base types */
            if (!ply_read_word(ply)) return 0;
            property->length_type = ply_find_string(BWORD(ply), ply_type_list);
            if (property->length_type == (e_ply_type) (-1)) return 0;
            if (!ply_read_word(ply)) return 0;
            property->value_type = ply_find_string(BWORD(ply), ply_type_list);
            if (property->value_type == (e_ply_type) (-1)) return 0;
        }
        /* get property name */
        if (!ply_read_word(ply)) return 0;
        strcpy(property->name, BWORD(ply));
        if (!ply_read_word(ply)) return 0;
        return 1;
    }

    static int ply_read_header_element(p_ply ply) {
        p_ply_element element = NULL;
        long dummy;
        assert(ply && ply->fp && ply->io_mode == PLY_READ);
        if (strcmp(BWORD(ply), "element")) return 0;
        /* allocate room for new element */
        element = ply_grow_element(ply);
        if (!element) return 0;
        /* get element name */
        if (!ply_read_word(ply)) return 0;
        strcpy(element->name, BWORD(ply));
        /* get number of elements of this type */
        if (!ply_read_word(ply)) return 0;
        if (sscanf(BWORD(ply), "%ld", &dummy) != 1) {
            ply_error(ply, "Expected number got '%s'", BWORD(ply));
            return 0;
        }
        element->ninstances = dummy;
        /* get all properties for this element */
        if (!ply_read_word(ply)) return 0;
        while (ply_read_header_property(ply) ||
            ply_read_header_comment(ply) || ply_read_header_obj_info(ply))
            /* do nothing */;
        return 1;
    }

    static void ply_error_cb(const char *message) {
        fprintf(stderr, "RPly: %s\n", message);
    }

    static void ply_error(p_ply ply, const char *fmt, ...) {
        char buffer[1024];
        va_list ap;
        va_start(ap, fmt);
        vsprintf(buffer, fmt, ap);
        va_end(ap);
        ply->error_cb(buffer);
    }

    static e_ply_storage_mode ply_arch_endian(void) {
        unsigned long i = 1;
        unsigned char *s = (unsigned char *) &i;
        if (*s == 1) return PLY_LITTLE_ENDIAN;
        else return PLY_BIG_ENDIAN;
    }

    static int ply_type_check(void) {
        assert(sizeof(char) == 1);
        assert(sizeof(unsigned char) == 1);
        assert(sizeof(short) == 2);
        assert(sizeof(unsigned short) == 2);
        assert(sizeof(long) == 4);
        assert(sizeof(unsigned long) == 4);
        assert(sizeof(float) == 4);
        assert(sizeof(double) == 8);
        if (sizeof(char) != 1) return 0;
        if (sizeof(unsigned char) != 1) return 0;
        if (sizeof(short) != 2) return 0;
        if (sizeof(unsigned short) != 2) return 0;
        if (sizeof(long) != 4) return 0;
        if (sizeof(unsigned long) != 4) return 0;
        if (sizeof(float) != 4) return 0;
        if (sizeof(double) != 8) return 0;
        return 1;
    }

    /* ----------------------------------------------------------------------
     * Output handlers
     * ---------------------------------------------------------------------- */
    static int oascii_int8(p_ply ply, double value) {
        if (value > CHAR_MAX || value < CHAR_MIN) return 0;
        return fprintf(ply->fp, "%d ", (char) value) > 0;
    }

    static int oascii_uint8(p_ply ply, double value) {
        if (value > UCHAR_MAX || value < 0) return 0;
        return fprintf(ply->fp, "%d ", (unsigned char) value) > 0;
    }

    static int oascii_int16(p_ply ply, double value) {
        if (value > SHRT_MAX || value < SHRT_MIN) return 0;
        return fprintf(ply->fp, "%d ", (short) value) > 0;
    }

    static int oascii_uint16(p_ply ply, double value) {
        if (value > USHRT_MAX || value < 0) return 0;
        return fprintf(ply->fp, "%d ", (unsigned short) value) > 0;
    }

    static int oascii_int32(p_ply ply, double value) {
        if (value > LONG_MAX || value < LONG_MIN) return 0;
        return fprintf(ply->fp, "%d ", (int) value) > 0;
    }

    static int oascii_uint32(p_ply ply, double value) {
        if (value > ULONG_MAX || value < 0) return 0;
        return fprintf(ply->fp, "%d ", (unsigned int) value) > 0;
    }

    static int oascii_float32(p_ply ply, double value) {
        if (value < -FLT_MAX || value > FLT_MAX) return 0;
        return fprintf(ply->fp, "%g ", (float) value) > 0;
    }

    static int oascii_float64(p_ply ply, double value) {
        if (value < -DBL_MAX || value > DBL_MAX) return 0;
        return fprintf(ply->fp, "%g ", value) > 0;
    }

    static int obinary_int8(p_ply ply, double value) {
        char int8 = (char) value;
        if (value > CHAR_MAX || value < CHAR_MIN) return 0;
        return ply->odriver->ochunk(ply, &int8, sizeof(int8));
    }

    static int obinary_uint8(p_ply ply, double value) {
        unsigned char uint8 = (unsigned char) value;
        if (value > UCHAR_MAX || value < 0) return 0;
        return ply->odriver->ochunk(ply, &uint8, sizeof(uint8));
    }

    static int obinary_int16(p_ply ply, double value) {
        short int16 = (short) value;
        if (value > SHRT_MAX || value < SHRT_MIN) return 0;
        return ply->odriver->ochunk(ply, &int16, sizeof(int16));
    }

    static int obinary_uint16(p_ply ply, double value) {
        unsigned short uint16 = (unsigned short) value;
        if (value > USHRT_MAX || value < 0) return 0;
        return ply->odriver->ochunk(ply, &uint16, sizeof(uint16));
    }

    static int obinary_int32(p_ply ply, double value) {
        long int32 = (long) value;
        if (value > LONG_MAX || value < LONG_MIN) return 0;
        return ply->odriver->ochunk(ply, &int32, sizeof(int32));
    }

    static int obinary_uint32(p_ply ply, double value) {
        unsigned long uint32 = (unsigned long) value;
        if (value > ULONG_MAX || value < 0) return 0;
        return ply->odriver->ochunk(ply, &uint32, sizeof(uint32));
    }

    static int obinary_float32(p_ply ply, double value) {
        float float32 = (float) value;
        if (value > FLT_MAX || value < -FLT_MAX) return 0;
        return ply->odriver->ochunk(ply, &float32, sizeof(float32));
    }

    static int obinary_float64(p_ply ply, double value) {
        return ply->odriver->ochunk(ply, &value, sizeof(value));
    }

    /* ----------------------------------------------------------------------
     * Input  handlers
     * ---------------------------------------------------------------------- */
    static int iascii_int8(p_ply ply, double *value) {
        char *end;
        if (!ply_read_word(ply)) return 0;
        *value = strtol(BWORD(ply), &end, 10);
        if (*end || *value > CHAR_MAX || *value < CHAR_MIN) return 0;
        return 1;
    }

    static int iascii_uint8(p_ply ply, double *value) {
        char *end;
        if (!ply_read_word(ply)) return 0;
        *value = strtol(BWORD(ply), &end, 10);
        if (*end || *value > UCHAR_MAX || *value < 0) return 0;
        return 1;
    }

    static int iascii_int16(p_ply ply, double *value) {
        char *end;
        if (!ply_read_word(ply)) return 0;
        *value = strtol(BWORD(ply), &end, 10);
        if (*end || *value > SHRT_MAX || *value < SHRT_MIN) return 0;
        return 1;
    }

    static int iascii_uint16(p_ply ply, double *value) {
        char *end;
        if (!ply_read_word(ply)) return 0;
        *value = strtol(BWORD(ply), &end, 10);
        if (*end || *value > USHRT_MAX || *value < 0) return 0;
        return 1;
    }

    static int iascii_int32(p_ply ply, double *value) {
        char *end;
        if (!ply_read_word(ply)) return 0;
        *value = strtol(BWORD(ply), &end, 10);
        if (*end || *value > LONG_MAX || *value < LONG_MIN) return 0;
        return 1;
    }

    static int iascii_uint32(p_ply ply, double *value) {
        char *end;
        if (!ply_read_word(ply)) return 0;
        *value = strtol(BWORD(ply), &end, 10);
        if (*end || *value < 0) return 0;
        return 1;
    }

    static int iascii_float32(p_ply ply, double *value) {
        char *end;
        if (!ply_read_word(ply)) return 0;
        *value = strtod(BWORD(ply), &end);
        if (*end || *value < -FLT_MAX || *value > FLT_MAX) return 0;
        return 1;
    }

    static int iascii_float64(p_ply ply, double *value) {
        char *end;
        if (!ply_read_word(ply)) return 0;
        *value = strtod(BWORD(ply), &end);
        if (*end || *value < -DBL_MAX || *value > DBL_MAX) return 0;
        return 1;
    }

    static int ibinary_int8(p_ply ply, double *value) {
        char int8;
        if (!ply->idriver->ichunk(ply, &int8, 1)) return 0;
        *value = int8;
        return 1;
    }

    static int ibinary_uint8(p_ply ply, double *value) {
        unsigned char uint8;
        if (!ply->idriver->ichunk(ply, &uint8, 1)) return 0;
        *value = uint8;
        return 1;
    }

    static int ibinary_int16(p_ply ply, double *value) {
        short int16;
        if (!ply->idriver->ichunk(ply, &int16, sizeof(int16))) return 0;
        *value = int16;
        return 1;
    }

    static int ibinary_uint16(p_ply ply, double *value) {
        unsigned short uint16;
        if (!ply->idriver->ichunk(ply, &uint16, sizeof(uint16))) return 0;
        *value = uint16;
        return 1;
    }

    static int ibinary_int32(p_ply ply, double *value) {
        long int32;
        if (!ply->idriver->ichunk(ply, &int32, sizeof(int32))) return 0;
        *value = int32;
        return 1;
    }

    static int ibinary_uint32(p_ply ply, double *value) {
        unsigned long uint32;
        if (!ply->idriver->ichunk(ply, &uint32, sizeof(uint32))) return 0;
        *value = uint32;
        return 1;
    }

    static int ibinary_float32(p_ply ply, double *value) {
        float float32;
        if (!ply->idriver->ichunk(ply, &float32, sizeof(float32))) return 0;
        *value = float32;
        ply_reverse(&float32, sizeof(float32));
        return 1;
    }

    static int ibinary_float64(p_ply ply, double *value) {
        return ply->idriver->ichunk(ply, value, sizeof(double));
    }

    /* ----------------------------------------------------------------------
     * Constants
     * ---------------------------------------------------------------------- */
    static t_ply_idriver ply_idriver_ascii = {
        {   iascii_int8, iascii_uint8, iascii_int16, iascii_uint16,
            iascii_int32, iascii_uint32, iascii_float32, iascii_float64,
            iascii_int8, iascii_uint8, iascii_int16, iascii_uint16,
            iascii_int32, iascii_uint32, iascii_float32, iascii_float64
        }, /* order matches e_ply_type enum */
        NULL,
        "ascii input"
    };

    static t_ply_idriver ply_idriver_binary = {
        {   ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
            ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64,
            ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
            ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64
        }, /* order matches e_ply_type enum */
        ply_read_chunk,
        "binary input"
    };

    static t_ply_idriver ply_idriver_binary_reverse = {
        {   ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
            ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64,
            ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
            ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64
        }, /* order matches e_ply_type enum */
        ply_read_chunk_reverse,
        "reverse binary input"
    };

    static t_ply_odriver ply_odriver_ascii = {
        {   oascii_int8, oascii_uint8, oascii_int16, oascii_uint16,
            oascii_int32, oascii_uint32, oascii_float32, oascii_float64,
            oascii_int8, oascii_uint8, oascii_int16, oascii_uint16,
            oascii_int32, oascii_uint32, oascii_float32, oascii_float64
        }, /* order matches e_ply_type enum */
        NULL,
        "ascii output"
    };

    static t_ply_odriver ply_odriver_binary = {
        {   obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
            obinary_int32, obinary_uint32, obinary_float32, obinary_float64,
            obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
            obinary_int32, obinary_uint32, obinary_float32, obinary_float64
        }, /* order matches e_ply_type enum */
        ply_write_chunk,
        "binary output"
    };

    static t_ply_odriver ply_odriver_binary_reverse = {
        {   obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
            obinary_int32, obinary_uint32, obinary_float32, obinary_float64,
            obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
            obinary_int32, obinary_uint32, obinary_float32, obinary_float64
        }, /* order matches e_ply_type enum */
        ply_write_chunk_reverse,
        "reverse binary output"
    };

    /* ----------------------------------------------------------------------
     * Copyright (C) 2003 Diego Nehab.  All rights reserved.
     *
     * Permission is hereby granted, free of charge, to any person obtaining
     * a copy of this software and associated documentation files (the
     * "Software"), to deal in the Software without restriction, including
     * without limitation the rights to use, copy, modify, merge, publish,
     * distribute, sublicense, and/or sell copies of the Software, and to
     * permit persons to whom the Software is furnished to do so, subject to
     * the following conditions:
     *
     * The above copyright notice and this permission notice shall be
     * included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
     * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     * ---------------------------------------------------------------------- */

¡¡

file name: plyConverter.cpp

    #include <string>
    #include <cstdio>
    #include <vector>
    #include "rply.h"
    #include <cmath>
    #include "plyModel.h"
    #include "octree.h"
    //#include "plyloader.h"

    #ifndef LINUX_VERSION
    #include <windows.h>
    #include <winbase.h>
    #include "except.h"
    #endif

    using namespace std;

    PlyModel ply;

    string fileName;
    VertexVector vertexVector;
    FaceVector faceVector;
    FaceStruct face;

    static int callback(p_ply_argument argument)
    {
        void *pdata;
        /* just pass the value from the input file to the output file */
        ply_get_argument_user_data(argument, &pdata, NULL);
        ply_write((p_ply) pdata, ply_get_argument_value(argument));
        return 1;
    }

    static int setup_callbacks(p_ply iply, p_ply oply)
    {
        p_ply_element element = NULL;
        /* iterate over all elements in input file */
        while ((element = ply_get_next_element(iply, element))) {
            p_ply_property property = NULL;
            long nelems = 0;
            const char *element_name;
            ply_get_element_info(element, &element_name, &nelems);
            /* add this element to output file */
            if (!ply_add_element(oply, element_name, nelems)) return 0;
            /* iterate over all properties of current element */
            while ((property = ply_get_next_property(element, property))) {
                const char *property_name;
                e_ply_type type, length_type, value_type;
                ply_get_property_info(property, &property_name, &type,
                        &length_type, &value_type);
                /* setup input callback for this property */
                if (!ply_set_read_cb(iply, element_name, property_name, callback,
                        oply, 0)) return 0;
                /* add this property to output file */
                if (!ply_add_property(oply, property_name, type, length_type,
                        value_type)) return 0;
            }
        }
        return 1;
    }

    int convert(char* fileName)
    {
        const char *value;
    	p_ply iply, oply;

        iply = ply_open(fileName, NULL);
        if (!iply) return 1;
        if (!ply_read_header(iply)) return 1;

        oply = ply_create(fileName, PLY_LITTLE_ENDIAN, NULL);
        if (!oply) return 1;
        if (!setup_callbacks(iply, oply)) return 1;
        //pass comments and obj_infos from input to output
        value = NULL;
        while ((value = ply_get_next_comment(iply, value)))
            if (!ply_add_comment(oply, value)) return 1;
        value = NULL;
        while ((value = ply_get_next_obj_info(iply, value)))
            if (!ply_add_obj_info(oply, value)) return 1;;
        // write output header
        if (!ply_write_header(oply)) return 1;
        // read input file generating callbacks that pass data to output file /
        if (!ply_read(iply)) return 1;
        // close up, we are done
        if (!ply_close(iply)) return 1;
        if (!ply_close(oply)) return 1;

        return 0;
    }

    VType calcEdge(VVector v1, VVector v2)
    {
    	VType result=0;
    	for (int i=0; i<3; i++)
    	{
    		result+=(v1[i]-v2[i])*(v1[i]-v2[i]);
    	}
    	return sqrt(result);
    }

    void PlyModel::calcLongestEdge(UIVector tri)
    {
    	VType result=0, temp;
    	//float edge[3];
    	//int first, second;
    	for (int i=0; i<3; i++)
    	{
    		temp=calcEdge(vertex[tri[i]].coord, vertex[tri[(i+1)%3]].coord);
    		if (temp>maxEdge)
    		{
    			maxEdge=temp;
    		}
    	}
    }

    bool PlyModel::isValidTriangle(UIVector& myFace)
    {
    	VType result=0, temp;

    	for (int i=0; i<3; i++)
    	{
    		temp=calcEdge(vertex[myFace[i]].coord, vertex[myFace[(i+1)%3]].coord);
    		if (result<temp)
    		{
    			result=temp;
    		}
    	}
    	return result>MinimumEdgeLength;
    }

    /*
    void PlyModel::getVertexFromIndex(HANDLE vertexHandle, int index, PlyVertex& plyVertex)
    {
    	LONGLONG li;
    	long int lo, hi;
    	unsigned long int length;
    	li=Int32x32To64(index, sizeof(PlyVertex));
    	lo=li;
    	hi=li>>32;
    	if (SetFilePointer(vertexHandle, lo, &hi, FILE_BEGIN)==0xFFFFFFFF)
    	{
    		printf("move file pointer failed\n");
    		exit(8);
    	}
    	if (ReadFile(vertexHandle, &plyVertex, sizeof(PlyVertex), &length, NULL)==0)
    	{
    		printf("read vertex failed\n");
    		exit(12);
    	}
    }
    */

    void PlyModel::getTriangleCenter(PlyVertex triangle[3], PlyVertex& center)
    {
    	int i, j;
    	//float totalNormal=0;
    	int tempColor;
    	for (i=0; i<3; i++)
    	{
    		center.color[i]=0;
    		center.coord[i]=0;
    		center.normal[i]=0;
    		tempColor=0;
    		for (j=0; j<3; j++)
    		{
    			center.coord[i]+=triangle[j].coord[i];
    			//center.color[i]+=triangle[j].color[i];
    			tempColor+=triangle[j].color[i];//because it WILL overflow
    			center.normal[i]+=triangle[j].normal[i];
    		}
    		center.color[i]=tempColor/3;
    		center.coord[i]/=3.0;
    		center.normal[i]/=3.0;//not normalized!!!
    		//totalNormal+=center.normal[i]*center.normal[i];
    	}
    	/*
    	totalNormal=sqrtf(totalNormal);
    	for (i=0; i<3; i++)
    	{
    		center.normal[i]/=totalNormal;
    	}
    	*/

    }

    void PlyModel::divideFileByFour(HANDLE vertexHandles[4], HANDLE faceHandles[4])
    {
    	int i, j, k, mySize;

    	int triangleCount[4]={0};

    	UIVector myNewFace;
    	UIVector* myFace;
    	PlyVertex* myVertex;

    	expandModel();

    	mySize=fNumber/4;

    	//for every group
    	for (i=0; i<mySize; i++)
    	{
    		//for every partition
    		for (j=0; j<4; j++)
    		{
    			myFace=triangle+i*4+j;

    			//here let's check if the triangle is a valid one
    			if (isValidTriangle(*myFace))
    			{
    				//then write the triangle and face into its file
    				for (k=0; k<3; k++)
    				{
    					myNewFace[k]=triangleCount[j]*3+k;
    					myVertex=vertex+(*myFace)[k];
    					GenericWriteFile(vertexHandles[j], myVertex, sizeof(PlyVertex));
    				}
    				GenericWriteFile(faceHandles[j], &myNewFace, sizeof(UIVector));

    				triangleCount[j]++;
    			}
    		}
    	}
    	delete[]vertex;
    	delete[]triangle;
    }

    void PlyModel::addToOctree(char* dirName, int expandNumber)
    {
    	vector<string> fileNameVector;
    	GenericRetrieveAllFile(dirName, fileNameVector);

    	for (int i=0; i<fileNameVector.size(); i++)
    	{
    		printf("begin add file %s to octree...\n", fileNameVector[i].c_str());
    		doAddToOctree(fileNameVector[i].c_str(), expandNumber);
    	}

    }

    void PlyModel::addToOctree(char* dirName, VType width, int expandNumber)
    {
    	vector<string> fileNameVector;
    	GenericRetrieveAllFile(dirName, fileNameVector);

    	for (int i=0; i<fileNameVector.size(); i++)
    	{
    		printf("begin add file %s to octree...\n", fileNameVector[i].c_str());
    		doAddToOctree(fileNameVector[i].c_str(), width, expandNumber);
    	}

    }

    VType plyOperations[8][2]=
    {
    	//{1,1},
    	{1,0},
    	{1,-1},
    	{0,1},
    	{0,0},
    	{0,-1},
    	{-1,1},
    	{-1,0},
    	{-1,-1}
    };

    void PlyModel::doAddToOctree(const char* fileName, VType width, int expandNumber)
    {
    	FaceStruct faceStruct;

    	PlyVertex plyVertex;
    	int startIndex, endIndex, expandCount;
    	int i, j, k;

    	VertexVector vertexVector;
    	FaceVector faceVector;

    	doReadFile(fileName);

    	for (i=0; i<fNumber; i++)
    	{
    		memcpy(faceStruct.face, triangle[i], sizeof(UIVector));
    		faceVector.push_back(faceStruct);
    	}

    	for (i=0; i<expandNumber; i++)
    	{
    		vertexVector.clear();
    		for (j=0; j<vNumber; j++)
    		{
    			memcpy(&plyVertex, vertex+j, sizeof(PlyVertex));
    			plyVertex.coord[0]+=width*plyOperations[i][0];
    			plyVertex.coord[2]+=width*plyOperations[i][2];
    			vertexVector.push_back(plyVertex);
    		}
    		Octree::addToOctree(vertexVector, faceVector);

    	}
    }

    void PlyModel::doAddToOctree(const char* fileName, int expandNumber)
    {
    	FaceStruct faceStruct;
    	string stringBuffer;
    	HANDLE vertexHandles[4];
    	HANDLE faceHandles[4];
    	char charBuffer[MAX_TEMP_NAME_LENGTH];
    	int startIndex, endIndex, expandCount;
    	int i, j, k;
    	vector<string> vertexNameVector;
    	vector<string> faceNameVector;
    	vector<HANDLE> vertexHandleVector;
    	vector<HANDLE> faceHandleVector;

    	VertexVector vertexVector;
    	FaceVector faceVector;

    	doReadFile(fileName);

    	vertexHandles[0]=GenericOpenTempFile("temp", charBuffer);
    	stringBuffer.assign(charBuffer);
    	vertexNameVector.push_back(stringBuffer);
    	vertexHandleVector.push_back(vertexHandles[0]);
    	//GenericWriteFile(vertexHandles[0], vertex, sizeof(PlyVertex)*vNumber);

    	faceHandles[0]=GenericOpenTempFile("temp", charBuffer);
    	stringBuffer.assign(charBuffer);
    	faceNameVector.push_back(stringBuffer);
    	faceHandleVector.push_back(faceHandles[0]);

    	//GenericWriteFile(faceHandles[0], triangle, sizeof(UIVector)*fNumber);

    	startIndex=endIndex=0;//this is insane!!!
    	for (i=0; i<expandNumber; i++)
    	{
    		//when i==0, expandCount=1;
    		startIndex=endIndex;
    		endIndex=vertexHandleVector.size();

    		for (j=startIndex; j<endIndex; j++)
    		{
    			//first let create four temp file for preparation
    			for (k=0; k<4; k++)
    			{
    				vertexHandles[k]=GenericOpenTempFile("temp", charBuffer);
    				stringBuffer.assign(charBuffer);
    				vertexNameVector.push_back(stringBuffer);
    				vertexHandleVector.push_back(vertexHandles[k]);

    				faceHandles[k]=GenericOpenTempFile("temp", charBuffer);
    				stringBuffer.assign(charBuffer);
    				faceNameVector.push_back(stringBuffer);
    				faceHandleVector.push_back(faceHandles[k]);
    			}

    			if (i!=0)
    			{
    				vertexVector.clear();
    				faceVector.clear();

    				Octree::readDataFromFile(vertexHandleVector[startIndex+j], vertexVector);
    				Octree::readDataFromFile(faceHandleVector[startIndex+j], faceVector);

    				vNumber=vertexVector.size();
    				vertex=new PlyVertex[vNumber];
    				CHECK_NEW(vertex);
    				memcpy(vertex, &(*vertexVector.begin()), sizeof(PlyVertex)*vNumber);

    				fNumber=faceVector.size();
    				triangle=new UIVector[fNumber];
    				CHECK_NEW(triangle);
    				memcpy(triangle, &(*faceVector.begin()), sizeof(UIVector)*fNumber);
    			}

    			divideFileByFour(vertexHandles, faceHandles);
    			GenericCloseTempFile(vertexHandleVector[j], vertexNameVector[j].c_str());
    			GenericCloseTempFile(faceHandleVector[j], faceNameVector[j].c_str());
    		}
    	}

    	//we need minus one
    	startIndex=endIndex;
    	endIndex=vertexHandleVector.size();

    	for (i=startIndex; i<endIndex; i++)
    	{
    		vertexVector.clear();
    		faceVector.clear();
    		if (expandNumber>0)
    		{
    			Octree::readDataFromFile(vertexHandleVector[i], vertexVector);
    			Octree::readDataFromFile(faceHandleVector[i], faceVector);
    		}
    		else
    		{
    			for (j=0; j<vNumber; j++)
    			{
    				vertexVector.push_back(vertex[j]);
    			}
    			for (j=0; j<fNumber; j++)
    			{
    				memcpy(faceStruct.face, triangle+j, sizeof(UIVector));
    				faceVector.push_back(faceStruct);
    			}
    		}
    		Octree::addToOctree(vertexVector, faceVector);
    		GenericCloseTempFile(vertexHandleVector[i], vertexNameVector[i].c_str());
    		GenericCloseTempFile(faceHandleVector[i], faceNameVector[i].c_str());
    	}
    /*
    	for (i=0; i<vertexHandleVector.size(); i++)
    	{
    		GenericCloseTempFile(vertexHandleVector[i], vertexNameVector[i].c_str());
    		GenericCloseTempFile(faceHandleVector[i], faceNameVector[i].c_str());
    	}
    	*/
    }

    /*
    void PlyModel::retrievePlyData(char* dirName, Octree* octree, int expandNumber, bool toConvert)
    {
    	int i, j;

    	GenericRetrieveAllFile(dirName, fileNameVector);

    	for (i=0; i<fileNameVector.size(); i++)
    	{
    		fileName=fileNameVector[i];
    		if (toConvert)
    		{
    			if (convertFile(fileName.c_str())!=0)
    			{
    				printf("convert file %s failed\n", fileName.c_str());
    				exit(12);
    			}
    		}
    		printf("before reading file %s...\n", fileName.c_str());

    		readFile(fileName.c_str(), expandNumber);

    		vertexVector.clear();
    		faceVector.clear();
    		for (j=0; j<vNumber; j++)
    		{
    			vertexVector.push_back(vertex[j]);
    		}

    		for (j=0; j<fNumber; j++)
    		{
    			sortTriangleIndex(triangle[j]);
    			face=triangle[j];
    			faceVector.push_back(face);
    		}

    		delete[]vertex;
    		delete[]triangle;
    		octree->addToOctree(vertexVector, faceVector);
    	}
    }
    */

    bool PlyModel::analysis(char* buf)
    {
    	char* token;
    	token=strtok(buf, " \n");
    	while (token!=NULL)
    	{
    		if (strcmp(token, "vertex")==0)
    		{
    			token=strtok(NULL, " \n");
    			sscanf(token, "%d", &vNumber);
    			break;
    		}
    		if (strcmp(token, "face")==0)
    		{
    			token=strtok(NULL, " \n");
    			sscanf(token, "%d", &fNumber);
    			break;
    		}
    		if (strcmp(token, "end_header" )==0)
    		{
    			return true;
    		}
    		token=strtok(NULL, " \n");
    	}
    	return false;
    }

    int PlyModel::convertFile(const char* fileName)
    {
    	return convert(const_cast<char*>(fileName));
    }

    int PlyModel::doReadFile(const char* fileName)
    {
    	FILE* file;

    	char buffer[256];
    	int i, j;
    	unsigned char cNumber;
    	UCVector dummy;

    	if ((file=fopen(fileName, "r+b"))==NULL)
    	{
    		printf("fopen failed \n");
    		exit(34);
    	}

    	while (true)
    	{
    		fgets(buffer, 256, file);
    		//i=fread(buffer, 1, 256, file);

    		//vNumber and fNumber is set here
    		if (analysis(buffer))
    		{
    			break;
    		}
    	}
    	//let's do the expanding here

    	vertex=new PlyVertex[vNumber];

    		//each triangle will become four triangles
    	triangle=new UIVector[fNumber];

    	if (vertex==NULL||triangle==NULL)
    	{
    		printf("allocate memory fails\n");
    		exit(13);
    	}

    	for (i=0; i<vNumber; i++)
    	{
    		for (j=0; j<3; j++)
    		{
    			float temp;
    			fread(&temp, sizeof(float), 1, file);
    			vertex[i].coord[j]=temp;
    		}
    		//fread(vertex[i].coord, sizeof(float), 3, file);
    		fread(vertex[i].color, sizeof(unsigned char), 3, file);
    		vertex[i].color[3]=255;
    		fread(vertex[i].normal, sizeof(float), 3, file);
    	}

    	for (i=0; i<fNumber; i++)
    	{
    		if (fread(&cNumber, sizeof(unsigned char), 1, file)!=1)
    		{
    			printf("reading color failed\n");;
    			exit(12);
    		}

    		if (cNumber!=3)
    		{
    			printf("found trouble, some polygon is not triangle\n");
    			exit(14);
    		}
    		if (fread(triangle[i], sizeof(int), cNumber, file)!=cNumber)
    		{
    			printf("reading face index failed\n");;
    			exit(12);
    		}
    		calcLongestEdge(triangle[i]);

    		//discard face color
    		if (fread(dummy, 3, 1, file)!=1)
    		{
    			printf("reading dummy color failed\n");;
    			exit(12);
    		}
    	}
    	//////////////////////////////////////
    	//now let's find vertex by triangle.

    	//int tripleIndex[3];

    	fclose(file);
    	//EXCEPT_CALL(expandModel(expandNumber), fileName);
    	return 0;
    }

    /*
    void PlyModel::outputTriangle(int triangleIndex)
    {
    	for (int i=0; i<3; i++)
    	{
    		printf("\n*******************output vertex%d\n", triangle[triangleIndex][i]);
    		for (int j=0; j<3; j++)
    		{
    			printf("[%f]", vertex[triangle[triangleIndex][i]].coord[j]);
    		}
    		printf("\n");
    	}
    	printf("****************************\n");
    }

    bool PlyModel::equalVertex(int index1, int index2)
    {
    	for (int i=0; i<3; i++)
    	{
    		if (vertex[index1].coord[i]!=vertex[index2].coord[i])
    		{
    			return false;
    		}
    	}
    	return true;
    }

    bool PlyModel::validTriangle(int triangleIndex)
    {
    	for (int i=0; i<3; i++)
    	{
    		if (equalVertex(triangle[triangleIndex][i], triangle[triangleIndex][(i+1)%3]))
    		{
    			printf("ooops\n");
    			exit(1);
    			return false;

    		}
    	}
    	return true;
    }
    */

    void PlyModel::expandModel()
    {

    	int i, j, n, k, l;
    	int vertexSize, faceSize;
    	//PlyVertex oldTripleVertex[3];
    	//PlyVertex newTripleVertex[3];
    	PlyVertex* oldTripleVertex[3];
    	PlyVertex* newTripleVertex[3];
    	UIVector newFace;
    	UIVector tripleFace[3];

    	PlyVertex* newVertexPtr;
    	UIVector* newTrianglePtr;

    	vertexSize=vNumber+3*fNumber;
    	faceSize=fNumber+3*fNumber;

    	newVertexPtr=new PlyVertex[vertexSize];
    	newTrianglePtr=new UIVector[faceSize];

    	CHECK_NEW(newVertexPtr);
    	CHECK_NEW(newTrianglePtr);

    	//reallocate
    	memcpy(newVertexPtr, vertex, sizeof(PlyVertex)*vNumber);
    	memcpy(newTrianglePtr, triangle, sizeof(UIVector)*fNumber);
    	delete[]vertex;
    	delete[]triangle;
    	vertex=newVertexPtr;
    	triangle=newTrianglePtr;

    	for (i=0; i<fNumber; i++)
    	{
    /////////////////////////////////////////////////////////////
    		//printf("original vertex is \n");
    		//outputTriangle(i);

    		for (j=0; j<3; j++)
    		{
    			//memcpy(oldTripleVertex+j, vertex+triangle[i][j], sizeof(PlyVertex));
    			oldTripleVertex[j]=vertex+triangle[i][j];
    			newTripleVertex[j]=vertex+vNumber+i*3+j;
    			//for every triangle, we increase three new vertex, their index starts after previous
    			//newly-added triple vertex of previous triangle, all after original vertex index, vNumber
    			newFace[j]=vNumber+i*3+j;//the three new vertex index is just added from end of old
    		}

    		//tripleFace=triangle+fNumber+expandFactor*i;
    		for (j=0; j<3; j++)
    		{
    			getMiddleVertex(oldTripleVertex[j], oldTripleVertex[(j+1)%3], newTripleVertex[j]);
    			//memcpy(vertex+vNumber+i*3+j, newTripleVertex+j, sizeof(PlyVertex));
    		}

    		for (j=0; j<3; j++)
    		{
    			tripleFace[j][0]=triangle[i][j];
    			tripleFace[j][1]=newFace[j];
    			//this is tricky!!!just think the position of new triangle
    			tripleFace[j][2]=newFace[(j+2)%3];
    		}

    		for (j=0; j<3; j++)
    		{
    			calcLongestEdge(tripleFace[j]);
    		}
    		calcLongestEdge(newFace);

    		//memcpy(triangle[i], tripleFace[0], sizeof(UIVector));
    		//memcpy(triangle[i], newFace, sizeof(UIVector));//replace the old triangle

    		memcpy(triangle[fNumber+i*3], tripleFace, sizeof(UIVector)*3);//replace the old triangle
    		memcpy(triangle[i], newFace, sizeof(UIVector));//replace the old triangle
    	}
    	vNumber=vertexSize;//now updates the vertex number, because each face will generate a new triangle.
    	//fNumber=4*fNumber;//each triangle will become three triangles
    	fNumber=faceSize;
    }

    void PlyModel::getMiddleVertex(PlyVertex* old1, PlyVertex* old2, PlyVertex* result)
    {
    	int i, temp;
    	float total=0.0;
    	for (i=0; i<3; i++)
    	{
    		result->coord[i]=(old1->coord[i]+old2->coord[i])/2.0;
    		temp=((int)(old1->color[i])+(int)(old2->color[i]))/2;
    		result->color[i]=temp;
    		result->normal[i]=(old1->normal[i]+old2->normal[i])/2.0;
    		total+=result->normal[i]*result->normal[i];
    	}
    	result->color[3]=((int)(old1->color[3])+(int)(old2->color[3]))/2;
    	total=sqrtf(total);
    	for (i=0; i<3; i++)
    	{
    		result->normal[i]/=total;
    	}
    }

    VVector tetrahedron[4]=
    {
    	{+1, +1, +1},
    	{-1, -1, +1},
    	{-1, +1, -1},
    	{+1, -1, -1}
    };

    FaceStruct faceStructArray[6]=
    {
    	{0,1,2},
    	{0,1,3},
    	{0,2,3},
    	{1,2,3}
    };

    void myTestData()
    {
    	VertexVector vertexVector;
    	FaceVector faceVector;
    	//FaceStruct faceStruct;
    	PlyVertex plyVertex;
    	int i, j;

    	DWORD timeCount;

    	timeCount=GenericGetTime();
    	Octree::initialize(false);

    	for (i=0; i<4; i++)
    	{
    		memcpy(plyVertex.coord, tetrahedron[i], sizeof(VVector));
    		memcpy(plyVertex.normal, tetrahedron[i], sizeof(VVector));
    		for (j=0; j<4; j++)
    		{
    			plyVertex.color[j]=100;
    			plyVertex.color[i]=255;
    		}
    		vertexVector.push_back(plyVertex);
    	}
    	for (i=0; i<4; i++)
    	{
    		faceVector.push_back(faceStructArray[i]);
    	}

    	Octree::addToOctree(vertexVector, faceVector);

    	Octree::uninitialize(false);
    	timeCount=GenericGetTime()- timeCount;
    	printf("convert file process takes time %d seconds\n", timeCount);
    }

    void mergeData(char* dirName, float width, int expandNumber)
    {
    	DWORD timeCount;
    	timeCount=GenericGetTime();
    	Octree::initialize(false);
    	ply.addToOctree(dirName, width, expandNumber);
    	Octree::uninitialize(false);
    	timeCount=GenericGetTime()- timeCount;
    	printf("convert file process takes time %d seconds\n", timeCount);

    }

    void mergeData(char* dirName, int expandTimes)
    {
    	DWORD timeCount;
    	timeCount=GenericGetTime();
    	Octree::initialize(false);
    	//retrievePlyData(dirName, octree, expandTimes, false);
    	ply.addToOctree(dirName, expandTimes);
    	Octree::uninitialize(false);
    	timeCount=GenericGetTime()- timeCount;
    	printf("convert file process takes time %d seconds\n", timeCount);
    }

    /*
    void retrievePlyData(char* dirName, Octree* octree, int expandNumber, bool toConvert)
    {
    	//DWORD timeCount;
    	//timeCount=GetTickCount();

    	ply.addToOctree(dirName, octree, expandNumber);

    	//loadPlyFile(octree, dirName);

    	//myTestInput(octree);
    	//timeCount=GetTickCount()- timeCount;
    	//printf("convert file process takes time %d seconds\n", timeCount);

    }
    */

    /*
    void convertPlyFile(char* dirName, int expandNumber, bool toConvert)
    {
    	DWORD timeCount;
    	HANDLE handle;
    	unsigned long length;
    	ply.maxEdge=0.0;

    	timeCount=GetTickCount();
    	ply.readDirectory(dirName, expandNumber, toConvert);

    	timeCount=GetTickCount()- timeCount;
    	printf("convert file process takes time %d seconds\n", timeCount/1000);

    	handle=CreateFile("info.data", GENERIC_READ|GENERIC_WRITE, 0, NULL,
    		CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    	if (handle==INVALID_HANDLE_VALUE)
    	{
    		printf("open info file failed\n");
    		exit(89);
    	}

    	if (WriteFile(handle, &ply, sizeof(PlyModel),&length, NULL)==0)
    	{
    		printf("error of writing info and only write %d bytes %d\n", length,  GetLastError());
    		exit(88);
    	}

    	printf("vcount=%d, fcount=%d\n", ply.vertexCount, ply.faceCount);
    	for(int i=0; i<3; i++)
    	{
    		printf("max[%c]=%f, min[%c]=%f\n", 'x'+i, ply.maxCoord[i], 'x'+i, ply.minCoord[i]);
    	}
    	printf("max edge is %f\n", ply.maxEdge);
    	CloseHandle(handle);

    }
    */

¡¡

file name: except.h

    /***********************************************************************************
    file name: except.h
    author: qingzhe huang
    date: Jul. 21, 2007

    Generally speaking, this is used for windows for the Structured Exception Handling(SEH).
    In linux, you can only rely on C++ exception. So, maybe this will be re-write in this
    style.

    ***********************************************************************************/
    #ifndef EXCEPT_H
    #define EXCEPT_H

    #include <stdio.h>
    #ifndef LINUX_VERSION
    #include <windows.h>

    void catchException(DWORD exception);

    #define EXCEPT_CALL(x, y) __try {x;} __except(EXCEPTION_EXECUTE_HANDLER) {printf(y); exit(9);}
    #endif

    /*
    int main()
    {
    	char* ptr;//don't initialize to NULL because sprintf will catch that before exception-catching
    	__try
    	{

    		//myTest(ptr);
    		sprintf(ptr, "impossible%d", 5);
    		printf("never reach here\n");

    	}
    	__except(catchException(GetExceptionCode()))
    	{
    		printf("exception handled\n");
    	}
    	printf("finished");

    	return 0;
    }
    */

    #endif

¡¡

¡¡

file name: except.cpp

    #include "except.h"

    extern FILE* dumpPtr;

    void catchException(DWORD exception)
    {
    	switch (exception)
    	{
    	case EXCEPTION_ACCESS_VIOLATION:
    		fprintf(dumpPtr, "The thread attempted to read from or write to a virtual address for which it \
    			does not have the appropriate access.\n");
    		break;
    	case EXCEPTION_BREAKPOINT:
    		fprintf(dumpPtr, "A breakpoint was encountered.\n");
    		break;
    	case EXCEPTION_DATATYPE_MISALIGNMENT:
    		fprintf(dumpPtr, "The thread attempted to read or write data that is misaligned on hardware that \
    			does not provide alignment. For example, \
    			16-bit values must be aligned on 2-byte boundaries, 32-bit values on 4-byte boundaries,\
    			and so on.\n");
    		break;
    	case EXCEPTION_SINGLE_STEP:
    		fprintf(dumpPtr, "A trace trap or other single-instruction mechanism signaled that one \
    			instruction has been executed.\n");
    		break;
    	case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
    		fprintf(dumpPtr, "The thread attempted to access an array element that is out of bounds,\
    			and the underlying hardware supports bounds checking. \n");
    		break;
    	case EXCEPTION_FLT_DENORMAL_OPERAND:
    		fprintf(dumpPtr, "One of the operands in a floating-point operation is denormal. A denormal \
    			value is one that is too small to represent as a standard floating-point value.\n");
    		break;
    	case EXCEPTION_FLT_DIVIDE_BY_ZERO:
    		fprintf(dumpPtr, "The thread attempted to divide a floating-point value by a floating-point \
    			divisor of zero. \n");
    		break;
    	case EXCEPTION_FLT_INEXACT_RESULT:
    		fprintf(dumpPtr, "The result of a floating-point operation cannot be represented exactly \
    			as a decimal fraction. \n");
    		break;
    	case EXCEPTION_FLT_INVALID_OPERATION:
    		fprintf(dumpPtr, "This exception represents any floating-point exception not included in this list.\n");
    		break;
    	case EXCEPTION_FLT_OVERFLOW:
    		fprintf(dumpPtr, "The exponent of a floating-point operation is greater than the magnitude\
    			allowed by the corresponding type. \n");
    		break;
    	case EXCEPTION_FLT_STACK_CHECK:
    		fprintf(dumpPtr, "The stack overflowed or underflowed as the result of a floating-point operation.\n");
    		break;
    	case EXCEPTION_FLT_UNDERFLOW:
    		fprintf(dumpPtr, "The exponent of a floating-point operation is less than the magnitude allowed\
    			by the corresponding type. \n");
    		break;
    	case EXCEPTION_INT_DIVIDE_BY_ZERO:
    		fprintf(dumpPtr, "The thread attempted to divide an integer value by an integer divisor of zero.\n");
    		break;
    	case EXCEPTION_INT_OVERFLOW:
    		fprintf(dumpPtr, "The result of an integer operation caused a carry out of the most significant\
    			bit of the result. \n");
    		break;
    	case EXCEPTION_PRIV_INSTRUCTION:
    		fprintf(dumpPtr, "The thread attempted to execute an instruction whose operation is not\
    			allowed in the current machine mode. \n");
    		break;
    	case EXCEPTION_NONCONTINUABLE_EXCEPTION:
    		fprintf(dumpPtr, "The thread attempted to continue execution after a noncontinuable \
    			exception occurred. \n");
    		break;
    	}
    	fclose(dumpPtr);
    	dumpPtr=NULL;
    }

¡¡

file name: octreeTool.cpp

    #include "config.h"
    #include "octree.h"
    #include "octreeLoader.h"
    #include "viewCollector.h"
    #include "renderEngine.h"

    #include <vector>
    #include <cstdio>
    #include <string>
    #include <GL/glut.h>
    #include <cmath>

    #ifndef LINUX_VERSION
    #include "windows.h"
    #include "except.h"
    #pragma comment(lib, "glut32.lib")
    int indexBuffer[MaxPossibleNodeCollectable];
    #endif

    using namespace std;

    extern void myTestData();

    void displayCallbackProc();

    void keyboardCallbackProc(unsigned char key, int x, int y);

    void init();

    unsigned long frameCounter=0;
    unsigned long lastFrameNo=0;
    unsigned long lastTick=0;
    unsigned long currentTick=0;

    ViewCollector viewCollector;
    OctreeLoader octreeLoader;
    RenderEngine renderEngine;

    FILE* dumpPtr=NULL;

    void dumpAll()
    {
    	if (dumpPtr!=NULL)
    	{
    		fprintf(dumpPtr, "\n****************begin dump the info*************\n");
    		octreeLoader.root->infoStruct.display(dumpPtr);
    		fprintf(dumpPtr, "\n****************end dump the info*************\n");
    	}
    	fclose(dumpPtr);
    }

    int main(int argc, char** argv)
    {
    	int expandTimes;
    	float width;
    	atexit(dumpAll);

    	dumpPtr=fopen("error.txt", "a+");
    	if (dumpPtr==NULL)
    	{
    		printf("cannot create error output file\n");
    		exit(1);
    	}

    	if (argc==1)
    	{
    		glutInit(&argc, argv);
    		glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_ALPHA);
    		glutInitWindowSize(ScreenSize, ScreenSize);
    		glutInitWindowPosition(5, 5);
    		glutCreateWindow("render tool");
    		init();

    		glutDisplayFunc(displayCallbackProc);
    		glutKeyboardFunc(keyboardCallbackProc);
    		glutMainLoop();
    	}
    	DWORD timeCount;
    	timeCount=GenericGetTime();
    	if (argc==2)
    	{
    		if (strcmp(argv[1], "fake")==0)
    		{
    			printf("\n*************begin create fake color file*****************\n");
    			OctreeLoader::paintPseudoColor();
    		}
    		else
    		{
    			if (strcmp(argv[1], "info")==0)
    			{
    				printf("\n**************now print out info****************\n");
    				OctreeLoader::readInfo();
    			}
    			else
    			{
    				if (strcmp(argv[1], "shift")==0)
    				{
    					printf("\n*************begin shift-expand by 8*****************\n");
    					OctreeLoader::expandBy8();
    				}
    				else
    				{
    					if (strcmp(argv[1], "user")==0)
    					{
    						printf("\n*************begin user input *****************\n");
    						myTestData();
    					}
    					else
    					{
    						printf("\n*************now add directory %s***************\n", argv[1]);
    						mergeData(argv[1]);
    					}
    				}
    			}
    		}
    	}
    	else
    	{
    		if (argc==3)
    		{
    			sscanf(argv[2], "%d", &expandTimes);
    			printf("\n**********add directory %s and expand %s times*********\n", argv[1], argv[2]);
    			mergeData(argv[1], expandTimes);
    		}
    		else
    		{
    			if (argc==4)
    			{
    				if (strcmp(argv[1], "shift")==0)
    				{
    					sscanf(argv[3], "%f", &width);
    					printf("\n*******add directory %s and expand by %s *****\n", argv[1], argv[3]);
    					mergeData(argv[2], width);
    				}
    			}
    			else
    			{
    				if (argc==5)
    				{
    					if (strcmp(argv[1], "shift")==0)
    					{
    						sscanf(argv[3], "%f", &width);
    						sscanf(argv[4], "%d", &expandTimes);
    						mergeData(argv[2], width, expandTimes);
    					}
    				}
    				else
    				{
    					printf("usage:octreetool.exe dirname expandtimes\n");
    				}
    			}
    		}
    	}

    	timeCount=GenericGetTime()- timeCount;
    	printf("file process takes time %d milli seconds\n", timeCount);
    	return 0;
    }

    ubyte* colorBuffer;
    void init()
    {
    	octreeLoader.initialize(true);

    	renderEngine.init(&octreeLoader);
    	renderEngine.lighting.lightEnabled=false;

    	viewCollector.initialize(octreeLoader.root);
    	colorBuffer=new ubyte[ColorBufferByteSize];
    	CHECK_NEW(colorBuffer);
    }

    set<int> myColor;

    /*
    void debugProgram()
    {
    	int result;
    	int* intPtr;
    	bool findIt;
    	glReadBuffer(GL_FRONT);
    	glReadPixels(0, 0, ScreenSize, ScreenSize,  GL_RGBA, GL_UNSIGNED_BYTE, colorBuffer);

    	intPtr=(int*)(viewCollector.octreeNodeVector.begin());
    	for (int i=0; i<ScreenSize; i++)
    	{
    		for (int j=0; j<ScreenSize; j++)
    		{
    			result=0;
    			memcpy(&result, colorBuffer+4*(i*ScreenSize+j), 3);
    			findIt=false;
    			for (int k=0; k<viewCollector.octreeNodeVector.size(); k++)
    			{
    				if (result==intPtr[k])
    				{
    					findIt=true;
    					break;
    				}
    			}
    			if (!findIt)
    			{
    				if (myColor.find(result)==myColor.end())
    				{
    					printf("problem color %d not found\n", result);
    					myColor.insert(result);
    				}
    				//exit(23);
    			}
    			else
    			{
    				printf("find color %d\n", result);
    			}
    		}
    	}
    }

    */

    void displayCallbackProc()
    {
    	//void* temp;

    	glClearColor(1.0, 1.0, 1.0, 1.0);
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    	viewCollector.startCollecting(renderEngine.drawingContext.center);
    	//this is another g++ nusance, it doesn't allow me to do type cast
    	//temp=(void*)(viewCollector.octreeNodeVector.begin());

    	//octreeLoader.addCandidates(viewCollector.octreeNodeVector.size(), (int*)(temp));
    	//octreeLoader.addCandidates(viewCollector.octreeNodeVector.size(), (int*)(viewCollector.octreeNodeVector.begin()));

    	for (int i=0; i<viewCollector.octreeNodeVector.size(); i++)
    	{
    		octreeLoader.addCandidate(viewCollector.octreeNodeVector[i]);
    		//indexBuffer[i]=viewCollector.octreeNodeVector[i];
    	}

    	//octreeLoader.addCandidates(viewCollector.octreeNodeVector.size(), indexBuffer);

    	octreeLoader.swapBuffer();
    	glutSwapBuffers();

            currentTick=GenericGetTime();
            if (frameCounter!=0)
            {
                    printf("frame#%u:fps=%f\n", frameCounter, 1000.0/(float)(currentTick-lastTick));
            }
            lastTick=currentTick;
            frameCounter++;

    }

    void keyboardCallbackProc(unsigned char key, int x, int y)
    {
    	switch (key)
    	{
    	case 'h':
    	case 'H':
    		viewCollector.hierRenderMode=!viewCollector.hierRenderMode;
    		printf("render mode %s\n", (viewCollector.hierRenderMode)?"hierachical":"flooding");
    		break;
    	case 'f':
    	case 'F':
    		viewCollector.fullRenderMode=!viewCollector.fullRenderMode;
    		printf("full render mode is %s\n", viewCollector.fullRenderMode?"yes":"no");
    		break;

    	case 'm':
    	case 'M':
    		renderEngine.movement.display();
    		printf("nodeset size=%d\n", octreeLoader.nodeSet[octreeLoader.currentIndex].size());
    		printf("movement offset=%f\n", renderEngine.movementOffset);
    		printf("vertexLoaded=%d, faceLoaded=%d\n", octreeLoader.loadedVertexCounter,
    			octreeLoader.loadedFaceCounter);
    		printf("vertexRendered=%d, faceRendered=%d\n", viewCollector.vertexCounter,
    			viewCollector.faceCounter);
    		printf("nearplane=%f, farplane=%f\n", renderEngine.drawingContext.zNear, renderEngine.drawingContext.zFar);
    		break;

    	case '+':
    		if (octreeLoader.capacityFactor>=MaxCapacity)
    		{
    			octreeLoader.capacityFactor=MaxCapacity;
    		}
    		else
    		{
    			octreeLoader.capacityFactor*=2;
    		}
    		printf("capacityFactor=%d\n", octreeLoader.capacityFactor);
    		break;
    	case '-':
    		octreeLoader.capacityFactor/=2;
    		if (octreeLoader.capacityFactor==0)
    		{
    			octreeLoader.capacityFactor=1;
    		}
    		printf("capacityFactor=%d\n", octreeLoader.capacityFactor);
    		break;
    	case 'b':
    	case 'B':
    		//debugProgram();
    		break;
    	default:
    		renderEngine.keyboardCallback(key, x, y);
    		break;

    	}
    	glutPostRedisplay();
    }

¡¡

file name: multiple.make (make file for parallel rendering project)

    renderBox.exe: frustum.obj octree.obj octreeLoader.obj renderEngine.obj viewCollector.obj syscallLinux.obj master.obj tester.obj render.obj \
            dataStructure.obj renderBox.cpp
    	@echo "compiling renderBox.exe..."
    	mpiCC -g -lGL -lGLU -lglut -lmpi frustum.obj octree.obj octreeLoader.obj renderEngine.obj viewCollector.obj syscallLinux.obj master.obj \
            tester.obj render.obj dataStructure.obj renderBox.cpp -o ./bin/renderBox.exe
    	rm -f *.obj
    syscallLinux.obj: syscall.h syscallLinux.cpp
    	@echo "syscallLinux.obj"
    	mpiCC -g -c syscallLinux.cpp -o syscallLinux.obj

    frustum.obj: frustum.cpp frustum.h config.h
    	@echo "frustum.obj..."
    	mpiCC -g -c frustum.cpp -o frustum.obj
    octree.obj: octree.h octree.cpp syscall.h config.h
    	@echo "octree.obj..."
    	mpiCC -g -c octree.cpp -o octree.obj
    octreeLoader.obj: octreeLoader.cpp octreeLoader.h frustum.obj
    	@echo "octreeLoader.obj ..."
    	mpiCC -g -c octreeLoader.cpp -o octreeLoader.obj

    viewCollector.obj: viewCollector.h viewCollector.cpp octreeLoader.obj octreeLoader.h
    	@echo "viewCollector.obj..."
    	mpiCC -g -c viewCollector.cpp  -o viewCollector.obj
    renderEngine.obj: renderEngine.cpp renderEngine.h
    	@echo "renderEngine.obj..."
    	mpiCC -g -c renderEngine.cpp -o renderEngine.obj
    master.obj: master.h master.cpp config.h
    	@echo "master.obj..."
    	mpiCC -g -c master.cpp -o master.obj
    tester.obj: tester.cpp tester.h config.h
    	@echo "tester.obj..."
    	mpiCC -g -c tester.cpp -o tester.obj
    render.obj: render.h render.cpp config.h
    	@echo "render.obj..."
    	mpiCC -g -c render.cpp -o render.obj
    dataStructure.obj: dataStructure.cpp dataStructure.h config.h
    	@echo "dataStructure.obj..."
    	mpiCC -g -c dataStructure.cpp -o dataStructure.obj

    file name: single.make (make file for local rendering tool project)

    octreeTool.exe: frustum.obj octree.obj octreeLoader.obj octreeTool.obj plyConverter.obj renderEngine.obj rply.obj viewCollector.obj syscallLinux.obj
    	@echo "compiling octreeTool.exe..."
    	g++ -g -fstack-protector-all -lGL -lGLU -lglut frustum.obj octree.obj octreeLoader.obj octreeTool.obj  plyConverter.obj \
            renderEngine.obj rply.obj viewCollector.obj syscallLinux.obj -o ./bin/octreeTool.exe
    	rm -f *.obj

    syscallLinux.obj: syscall.h syscallLinux.cpp
    	@echo "syscallLinux.obj"
    	g++ -g -c syscallLinux.cpp -o syscallLinux.obj

    frustum.obj: frustum.cpp frustum.h config.h
    	@echo "frustum.obj..."
    	g++ -g -c frustum.cpp -o frustum.obj
    octree.obj: octree.h octree.cpp syscallLinux.obj
    	@echo "octree.obj..."
    	g++ -g -c octree.cpp -o octree.obj
    plyConverter.obj: plyModel.h plyConverter.cpp rply.h syscallLinux.obj
    	@echo "plyConverter.obj..."
    	g++ -c plyConverter.cpp  -o plyConverter.obj
    rply.obj: rply.c rply.h
    	@echo "rply.obj..."
    	gcc -g -c rply.c -o rply.obj
    octreeLoader.obj: octreeLoader.cpp octreeLoader.h frustum.obj
    	@echo "octreeLoader.obj ..."
    	g++ -g -c octreeLoader.cpp -o octreeLoader.obj

    viewCollector.obj: viewCollector.h viewCollector.cpp octreeLoader.obj octreeLoader.h syscallLinux.obj
    	@echo "viewCollector.obj..."
    	g++ -g -c viewCollector.cpp  -o viewCollector.obj
    octreeTool.obj: octreeTool.cpp
    	g++ -g -c octreeTool.cpp -o octreeTool.obj

    renderEngine.obj: renderEngine.cpp renderEngine.h
    	g++ -g -c renderEngine.cpp -o renderEngine.obj

¡¡

¡¡