// ZBUF.c -- ZBuffer saver Image Filter
// Saves current Z-Buffer to a file
// Built from sample NEGATIVE.c on 11/28/95 by Chris "Xenon" Hanson
// Copyright 1995
// last revision Dec 5, 1995
// V1.1 Switched to Kilometer units to be compatible with older
// versions of WCS that only supported K's.
// V1.2 Fixed crashes caused by not checking return code from fa->bufLine --
// sometimes Lightwave doesn't give you an Alpha channel..

/*
 * NEGATIVE.C -- Layout Plugin Post-Process Filter
 *		 Invert color of image after rendering
 *
 * by Allen Hastings and Arnie Cachelin
 * revised by Stuart Ferguson
 * last revision  6/9/95
 */
#include <splug.h>
#include <moni.h>
#include <lwran.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include <string.h>

unsigned char *VersionTag = "\0$VER: ZBUFSaverPlugIn 1.2 (01.30.96)\0";

struct ZBufThing
	{
	// Path and Filename stuff
	char ZFile[255], ZPath[255], ZName[255], ZTemp[255];
	GlobalFunc		*globalstash;
	}; // ZBufThing

struct ILBMHeader
	{
	unsigned char			ChunkID[4];
	unsigned long int		ChunkSize;
	};

struct ZBufferHeader
	{
	unsigned long int		Width, Height;
	unsigned short int	VarType, Compression, Sorting, Units;
	float						Min, Max, Bkgrnd, ScaleFactor, ScaleBase;
	};

// Endian-operations, from WCSV2:Useful.h
       void SimpleEndianFlip64 (            double Source64, double *Dest64);
       void SimpleEndianFlip32F(             float Source32, float  *Dest32);
       void SimpleEndianFlip32U( unsigned long int Source32, unsigned long int *Dest32);
       void SimpleEndianFlip32S(   signed long int Source32, signed long int   *Dest32);
       void SimpleEndianFlip16U(unsigned short int Source16, unsigned short int *Dest16); 
       void SimpleEndianFlip16S(  signed short int Source16, signed short int   *Dest16);

       void SimpleEndianFlip16U(unsigned short int Source16, unsigned short int *Dest16) {(*Dest16) = (unsigned short int)( ((Source16 & 0x00ff) << 8) | ((Source16 & 0xff00) >> 8) );}
       void SimpleEndianFlip16S(  signed short int Source16, signed short int   *Dest16) {(*Dest16) = (  signed short int)( ((Source16 & 0x00ff) << 8) | ((Source16 & 0xff00) >> 8) );}


/*
 * HANDLER -- Instance Methods
 *
 * An instance is a particular set of user-set options for a plug-in
 * operation.  Normally the host would call these callback functions
 * to manage instances, but since the ZBuf saver filter has no
 * options, only a dummy instance needs to be used.  In this case the
 * pointer for all instances will just be a pointer to a string and 
 * the instance handler functions will do nothing.
 */
struct ZBufferHeader ZHd;
struct ILBMHeader ILBM;
static MessageFuncs *Messages;
static struct ZBufThing MyInst;

	XCALL_(LWInstance)
ZBufCreate (
	LWError			*err)
{
	strcpy(MyInst.ZFile, "Pic");
	MyInst.ZName[0] = MyInst.ZTemp[0] = MyInst.ZPath[0] = 0;
	return ((LWInstance) &MyInst);
}

	XCALL_(void)
NilDestroy (
	LWInstance		 inst)
{
}

	XCALL_(LWError)
NilCopy (
	LWInstance		 from,
	LWInstance		 to)
{
	return ((LWInstance) &MyInst);
}

	XCALL_(LWError)
ZBufLoad (
	struct ZBufThing	*ZBT,
	const LWLoadState	*lState)
{
// <<<>>> Could use some work
	return (NULL);
}

	XCALL_(LWError)
ZBufSave (
	struct ZBufThing	*ZBT,
	const LWSaveState	*sState)
{
// <<<>>> Could use some work
	return (NULL);
}



/*
 * PROCESSING
 *
 * The host calls this function to process each rendered frame.  The
 * server has to process the whole frame, line-by-line.
 *
 * Make SURE you copy over the values, even if you don't modify them
 * at ALL.
 *
 * The monitor in the filter access is used to track the progress of
 * the filter for the user and to check for user abort.  If this is
 * used with the pre-release, the monitor is not present in the
 * access struct and is nulled.
 */
	XCALL_(static void)
ZBufProcess (
	struct ZBufThing	*ZBT,
	const FilterAccess	*fa)
	{
	Monitor				*mon;
	float					*Z, ZVal, ZMax, ZMin;
	BufferValue			out[4], *r, *g, *b, *a;
	int					i, j, WriteError = 0;
	FILE 					*ZOut = NULL;
	char					FrameText[5], Chop;
	unsigned long int	BodySize, CalcFrame;
	MessageFuncs		*Messages;
	char ImageError = 0, ZError = 0;

#ifdef LW_PRERELEASE
	mon = NULL;
#else
	mon = fa->monitor;
#endif


	Chop = 0;
	ZMax = 0.0f;
	ZMin = FLT_MAX;

	// Set up for error reporting.
	Messages = (*ZBT->globalstash) ("Info Messages", GFUSE_TRANSIENT);

	strcpy(FrameText, "0000"); // initialize frame number string.
	strcpy(ZBT->ZTemp, ZBT->ZFile);
	
	// Append frame number...
	// This is the easiest portable way I could find to do this.
	CalcFrame = fa->frame;
	if(CalcFrame)
		{
		Chop = (char)(CalcFrame % 10);
		FrameText[3] = Chop + '0';
		
		if(CalcFrame > 9)
			{
			CalcFrame /= 10;
			Chop = (char)(CalcFrame % 10);
			FrameText[2] = Chop + '0';
			
			if(CalcFrame > 9)
				{
				CalcFrame /= 10;
				Chop = (char)(CalcFrame % 10);
				FrameText[1] = Chop + '0';
				
				if(CalcFrame > 9)
					{
					CalcFrame /= 10;
					Chop = (char)(CalcFrame % 10);
					FrameText[0] = Chop + '0';
					} // if
				} // if
			} // if
		} // if
	strcat(ZBT->ZTemp, FrameText);
	strcat(ZBT->ZTemp, ".zb");
	
	BodySize = fa->width * fa->height * 4; // 4 bytes per single-precision FP
	
	ZHd.Width  = fa->width;
	ZHd.Height = fa->height;
	ZHd.VarType = 6; // single precision fp
	ZHd.Compression = ZHd.Sorting = 0; // no compression, near-to-far sort
	// Note: WCS Versions < 2 only support Kilometer units
	ZHd.Units = 3; // 1=cm, 2=meters 3=km
	ZHd.Bkgrnd = FLT_MAX; // <<<>>> Check this empirically...
	ZHd.ScaleFactor = 1.0f; // No scaling
	ZHd.ScaleBase = 0.0f; // n/a unless scaling
	ZHd.Min = 0.0f; // Indicates we don't know what
	ZHd.Max = 0.0f; // the Min or Max values might be
	
	// No need to worry about IFF 16-bit padding, we're always 16-bit aligned
	ILBM.ChunkSize = 12 + 36 + BodySize;
	
	#ifdef _WIN32
	SimpleEndianFlip32U(ZHd.Width, &ZHd.Width);
	SimpleEndianFlip32U(ZHd.Height, &ZHd.Height);
	SimpleEndianFlip16U(ZHd.VarType, &ZHd.VarType);
	SimpleEndianFlip16U(ZHd.Compression, &ZHd.Compression);
	SimpleEndianFlip16U(ZHd.Sorting, &ZHd.Sorting);
	SimpleEndianFlip16U(ZHd.Units, &ZHd.Units);
	SimpleEndianFlip32F(ZHd.Min, &ZHd.Min);
	SimpleEndianFlip32F(ZHd.Max, &ZHd.Max);
	SimpleEndianFlip32F(ZHd.Bkgrnd, &ZHd.Bkgrnd);
	SimpleEndianFlip32F(ZHd.ScaleFactor, &ZHd.ScaleFactor);
	SimpleEndianFlip32F(ZHd.ScaleBase, &ZHd.ScaleBase);
	
	SimpleEndianFlip32U(ILBM.ChunkSize, &ILBM.ChunkSize);
	#endif // _WIN32
	
	// Crucial that we open the file in Binary mode.
	if(ZOut = fopen(ZBT->ZTemp, "wb"))
		{
		// Write header. Note: We don't actually check for errors in writing
		// until we start spewing the ZBOD data. It's marginal at this point.
		strncpy(ILBM.ChunkID, "FORM", 4); // So as not to put in a NULL terminator.
		fwrite(&ILBM, 1, 8, ZOut);        // Write FORM and ChunkSize.
		strncpy(ILBM.ChunkID, "ILBM", 4); // We can embed our ZBOD and ZBUF chunks
		fwrite(&ILBM, 1, 4, ZOut);        // into IFF ILBM images.
		strncpy(ILBM.ChunkID, "ZBUF", 4); // Here's our ZBUF Header chunk.
		ILBM.ChunkSize = 36;              // Size of a ZBUF chunk.
		#ifdef _WIN32                      // May need to flip into Motorola byte-order.
		SimpleEndianFlip32U(ILBM.ChunkSize, &ILBM.ChunkSize);
		#endif // _WIN32
		fwrite(&ILBM, 1, 8, ZOut);        // Write "ZBUF" and ChunkSize.
		fwrite(&ZHd, 1, 36, ZOut);        // Write the actual ZBUF Header.
		strncpy(ILBM.ChunkID, "ZBOD", 4); // Set up ZBOD ID.
		ILBM.ChunkSize = BodySize;        // Set up ZBOD ChunkSize.
		fwrite(&ILBM, 1, 8, ZOut);        // Write "ZBOD" and ChunkSize.
		fflush(ZOut);
		
		
		/*
		 * Scan through the lines in the image and fwrite out the Z stuff.
		 */
		MON_INIT (mon, fa->height);
		for (i = 0; i < fa->height; i++)
			{
			Z = (*fa->fltLine) (LWBUF_DEPTH,	i);
			r = (*fa->bufLine) (LWBUF_RED,	i);
			g = (*fa->bufLine) (LWBUF_GREEN,	i);
			b = (*fa->bufLine) (LWBUF_BLUE,	i);
			a = (*fa->bufLine) (LWBUF_ALPHA,	i);

			if(!(Z && r && g && b))
				{
				if (Messages)
					{
					if(!Z && !ZError)
						{
						(*Messages->info) ("Error:", "Z Buffer unavailable.");
						ZError = 1;
						} // if
					if(!(r && g && b) && !ImageError)
						{
						(*Messages->info) ("Error:", "Image buffers unavailable.");
						ImageError = 1;
						} // if
					} // if
				} // if
			
			for(j = 0; j < fa->width; j++)
				{
				if(r) out[0] = r[j];
				if(g) out[1] = g[j];
				if(b) out[2] = b[j];
				if(a) out[3] = a[j];
				
				// ZBUF Plugin V1.1:
				// WCS Versions < 2 only support Kilometer units,
				// so those users can make use of this plug-in, I'm
				// altering these values to K's. This should have
				// little or no effect on the IEEE-FP precision however.
				if(Z)
					{
					ZVal   = Z[j] / 1000;
					if(ZVal > ZMax)
						ZMax = ZVal;
					if(ZVal < ZMin)
						ZMin = ZVal;
				
					#ifdef _WIN32
					// Endian flop
					SimpleEndianFlip32F(ZVal, &ZVal);
					#endif // _WIN32
					} // if Z
				
				// set values back
				if(r && g && b) (*fa->setRGB)		(j, i, out);
				if(a) (*fa->setAlpha)	(j, i, out[3]);
				
				// Probably not the most efficient way to write the Z data --
				// one value at a time. However, stdio level 3 file buffering
				// helps us a little, and this method is both portable and
				// memory-efficient. (Otherwise we'd sometimes need to allocate
				// an extra Z-line buffer to endian-flip an entire line into
				// before writing).
				if(Z && !WriteError)
					{
					// write Z value to file
					if(fwrite((char *)&ZVal, 1, 4, ZOut) != 4)
						{
						WriteError = 1;
						} // if
					} // if everything still OK
				
				} // for
	
			/*
			 * Step the monitor and check for abort on each line.
			 * This is a good compromise of responsiveness and
			 * input-checking overhead.
			 */
			if (MON_STEP (mon))
				break;
			} // for
		
		// Now rewrite the Max and Min values
		fseek(ZOut, 36, SEEK_SET); // The 36 is just a coincedence...
		
		#ifdef _WIN32
		SimpleEndianFlip32F(ZMin, &ZMin);
		SimpleEndianFlip32F(ZMax, &ZMax);
		#endif // _WIN32
		
		// Write over the dummy values we wrote before.
		// No error checking.
		fwrite((char *)&ZMin, 1, 4, ZOut);
		fwrite((char *)&ZMax, 1, 4, ZOut);
		
		fclose(ZOut);
		ZOut = NULL;

		MON_DONE (mon);
		} // if file opened
	} // ZBufProcess



/*
 * FLAGS
 *
 * The host calls this function to determine what options are needed by
 * this instance of filter.  Specifically, which buffers other than the
 * normal RGBA buffers need to be computed for this filter.
 *
 * We need to get access to the LWBUF_DEPTH ZBuffer, for our saver,
 * so, we'll politely ask for it.
 */
	XCALL_(static unsigned int)
ZBufFlags (
	LWInstance		 inst)
{
	return (LWBUF_DEPTH);
}



/*
 * ACTIVATE -- Main entry point
 *
 * The host calls this function with an uninitialized image filter handler
 * and the server has to fill in the callback fields for the handler
 * functions for this handler class.
 *
 */
	XCALL_(int)
Activate (
	long			 version,
	GlobalFunc		*global,
	ImageFilterHandler	*local,
	void			*serverData)
{
	XCALL_INIT;
	if (version != 1)
		return (AFUNC_BADVERSION);

	local->inst.create  = ZBufCreate;
	local->inst.destroy = NilDestroy;
	local->inst.load    = ZBufLoad;
	local->inst.save    = ZBufSave;
	local->inst.copy    = NilCopy;

	local->process = ZBufProcess;
	local->flags   = ZBufFlags;

	MyInst.globalstash = global;
	
	return (AFUNC_OK);
}

	XCALL_(static int)
Interface (
	long 				version,
	GlobalFunc			*global,
	struct ZBufThing	*ZBT,
	void				*serverData)
{
FileReqFunc			*Request;
int					ReqStatus;
#ifdef _WIN32
MessageFuncs		*Messages;
#endif // _WIN32

Request  = (FileReqFunc *) (*global) ("File Request", GFUSE_TRANSIENT);
if (!Request)
	return (AFUNC_BADGLOBAL);

#ifdef _WIN32
// Post a warning about 8.3 filename conventions
Messages = (*global) ("Info Messages", GFUSE_TRANSIENT);
if (!Messages)
	return (AFUNC_BADGLOBAL);

(*Messages->info) ("Note: Use a 3-4 letter base name if you are", "restricted to 8.3 filenames.");
#endif // _WIN32

ReqStatus = (*Request) ("ZBuf Saver Base File", ZBT->ZName, ZBT->ZPath, ZBT->ZFile, 250);

// No need to pay any attention to ReqStatus in this implementation

	return (AFUNC_OK);
} // Interface



// Globals necessary to declare the class and name of this plugin server.
// Not used in multi-server plug-ins
//char		ServerClass[] = "ImageFilterHandler";
//char		ServerName[]  = "ZBufSaver";

ServerRecord ServerDesc[] =
	{
		{"ImageFilterHandler", "ZBufSaver", Activate},
		{"ImageFilterInterface", "ZBufSaver", Interface},
		{NULL}
	}; // ServerDesc

// Endian Code, from WCSV2:Useful.cpp

void SimpleEndianFlip64(double Source64, double *Dest64)
{
char *SwapBuf, *SrcBuf;

SrcBuf = (char *)&Source64;
SwapBuf = (char *)Dest64; // Trusssssssst me...
SwapBuf[0] = SrcBuf[7]; SwapBuf[7] = SrcBuf[0];
SwapBuf[1] = SrcBuf[6]; SwapBuf[6] = SrcBuf[1];
SwapBuf[2] = SrcBuf[5]; SwapBuf[5] = SrcBuf[2];
SwapBuf[3] = SrcBuf[4]; SwapBuf[4] = SrcBuf[3];

} // SimpleEndianFlip64

void SimpleEndianFlip32F(float Source32, float *Dest32)
{
char *SwapBuf, *SrcBuf;

SrcBuf = (char *)&Source32;
SwapBuf = (char *)Dest32; // Trusssssssst me...
SwapBuf[0] = SrcBuf[3]; SwapBuf[3] = SrcBuf[0];
SwapBuf[1] = SrcBuf[2]; SwapBuf[2] = SrcBuf[1];
} // SimpleEndianFlip32F

void SimpleEndianFlip32U(unsigned long int Source32, unsigned long int *Dest32)
{
char *SwapBuf, *SrcBuf;

SrcBuf = (char *)&Source32;
SwapBuf = (char *)Dest32; // Trusssssssst me...
SwapBuf[0] = SrcBuf[3]; SwapBuf[3] = SrcBuf[0];
SwapBuf[1] = SrcBuf[2]; SwapBuf[2] = SrcBuf[1];
} // SimpleEndianFlip32U

void SimpleEndianFlip32S(signed long int Source32, signed long int *Dest32)
{
char *SwapBuf, *SrcBuf;

SrcBuf = (char *)&Source32;
SwapBuf = (char *)Dest32; // Trusssssssst me...
SwapBuf[0] = SrcBuf[3]; SwapBuf[3] = SrcBuf[0];
SwapBuf[1] = SrcBuf[2]; SwapBuf[2] = SrcBuf[1];
} // SimpleEndianFlip32S
