/**********************************************************************
 *<
	FILE: IHardwareShader.h

	DESCRIPTION: Hardware Shader Extension Interface class

	CREATED BY: Norbert Jeske

	HISTORY:

 *>	Copyright (c) 2000 - 2002, All Rights Reserved.
 **********************************************************************/

#ifndef _HARDWARE_SHADER_H_
#define _HARDWARE_SHADER_H_
#include "maxheap.h"

#define HARDWARE_SHADER_INTERFACE_ID Interface_ID(0x7bbd585b, 0x75bb641c)

#define GFX_MAX_COLORS 2

class Mesh;
class MNMesh;

// Use of the following four classes, TriStrip, MeshData, WireMeshData, and
// MeshFaceData, has been added in 3ds max 4.2.  These classes were updated
// from their original (unused) definitions and use of the IHardwareShader
// class member functions that use these data classes was added in 3ds max 4.2.
// If your plugin utilizes these new mechanisms, be sure that your clients are
// aware that they must run your plugin with 3ds max version 4.2 or higher.
//
// For documentation on the use of these data classes, see the code of the
// HardwareShader plugin samples or contact Discreet.

class TriStrip: public MaxHeapOperators {
public:
	DWORD	rndMode;
	MtlID	mID;
	MtlID	maxID;
	DWORD	smGrp;
	int		clrMode;
	int		texMode;
	DWTab	v;
	DWTab	n;
	DWTab	c[GFX_MAX_COLORS];
	DWTab	tv[GFX_MAX_TEXTURES];

	void AddVertN(DWORD vtx, DWORD nor) { v.Append(1, &vtx, v.Count()/2); n.Append(1, &nor, n.Count()/2); }
	void AddVertNC1(DWORD vtx, DWORD nor, DWORD col) { v.Append(1, &vtx, v.Count()/2); n.Append(1, &nor, n.Count()/2); c[0].Append(1, &col, c[0].Count()/2); }

	void AddVertNT1(DWORD vtx, DWORD nor, DWORD tvtx) {
		int alloc = v.Count()/2+1;
		v.Append(1, &vtx, alloc); n.Append(1, &nor, alloc);
		tv[0].Append(1, &tvtx, alloc);
	}
	void AddVertNC1T1(DWORD vtx, DWORD nor, DWORD col, DWORD tvtx) {
		int alloc = v.Count()/2+1;
		v.Append(1, &vtx, alloc); n.Append(1, &nor, alloc); c[0].Append(1, &col, alloc);
		tv[0].Append(1, &tvtx, alloc);
	}
	void AddVertNT2(DWORD vtx, DWORD nor, DWORD tvtx1, DWORD tvtx2) {
		int alloc = v.Count()/2+1;
		v.Append(1, &vtx, alloc); n.Append(1, &nor, alloc);
		tv[0].Append(1, &tvtx1, alloc); tv[1].Append(1, &tvtx2, alloc);
	}
	void AddVertNC1T2(DWORD vtx, DWORD nor, DWORD col, DWORD tvtx1, DWORD tvtx2) {
		int alloc = v.Count()/2+1;
		v.Append(1, &vtx, alloc); n.Append(1, &nor, alloc); c[0].Append(1, &col, alloc);
		tv[0].Append(1, &tvtx1, alloc); tv[1].Append(1, &tvtx2, alloc);
	}
	void AddVertNT3(DWORD vtx, DWORD nor, DWORD tvtx1, DWORD tvtx2, DWORD tvtx3) {
		int alloc = v.Count()/2+1;
		v.Append(1, &vtx, alloc); n.Append(1, &nor, alloc);
		tv[0].Append(1, &tvtx1, alloc); tv[1].Append(1, &tvtx2, alloc); tv[2].Append(1, &tvtx3, alloc);
	}
	void AddVertNC1T3(DWORD vtx, DWORD nor, DWORD col, DWORD tvtx1, DWORD tvtx2, DWORD tvtx3) {
		int alloc = v.Count()/2+1;
		v.Append(1, &vtx, alloc); n.Append(1, &nor, alloc); c[0].Append(1, &col, alloc);
		tv[0].Append(1, &tvtx1, alloc); tv[1].Append(1, &tvtx2, alloc); tv[2].Append(1, &tvtx3, alloc);
	}
	void AddVertNT4(DWORD vtx, DWORD nor, DWORD tvtx1, DWORD tvtx2, DWORD tvtx3, DWORD tvtx4) {
		int alloc = v.Count()/2+1;
		v.Append(1, &vtx, alloc); n.Append(1, &nor, alloc);
		tv[0].Append(1, &tvtx1, alloc); tv[1].Append(1, &tvtx2, alloc);
		tv[2].Append(1, &tvtx3, alloc); tv[3].Append(1, &tvtx4, alloc);
	}
	void AddVertNC1T4(DWORD vtx, DWORD nor, DWORD col, DWORD tvtx1, DWORD tvtx2, DWORD tvtx3, DWORD tvtx4) {
		int alloc = v.Count()/2+1;
		v.Append(1, &vtx, alloc); n.Append(1, &nor, alloc); c[0].Append(1, &col, alloc);
		tv[0].Append(1, &tvtx1, alloc); tv[1].Append(1, &tvtx2, alloc);
		tv[2].Append(1, &tvtx3, alloc); tv[3].Append(1, &tvtx4, alloc);
	}
};

typedef TriStrip *TriStripPtr;
typedef Tab<TriStripPtr> TriStripTab;

class MeshData: public MaxHeapOperators
{
public:
	DWORD_PTR	id;
	int			numFaces;
	GWFace		*faces;
	int			numStrips;
	int			startStrip;
	int			endStrip;
	TriStripTab	*strips;
	DWORD		mapFlags;
	int			numXYZ;
	Point3		*xyz;
	int			numNor;
	Point3		*nor;
	DWTab		*norIndex;
	Point3		*faceNor;
	int			numRGB[GFX_MAX_COLORS];
	Point3		*rgb[GFX_MAX_COLORS];
	int			numUVW[GFX_MAX_TEXTURES];
	Point3		*uvw[GFX_MAX_TEXTURES];
	int			numMtl;
	Material	*mtlArray;
	int			displayFlags;
	BitArray	*faceSel;
	BitArray	*edgeSel;

	MeshData()
	  : id(NULL),
		numFaces(0),
		faces(NULL),
		numStrips(0),
		startStrip(0),
		endStrip(0),
		strips(NULL),
		mapFlags(0),
		numXYZ(0),
		xyz(NULL),
		numNor(0),
		nor(NULL),
		norIndex(NULL),
		faceNor(NULL),
		numMtl(0),
		mtlArray(NULL),
		displayFlags(0),
		faceSel(NULL),
		edgeSel(NULL)
	{
		int kk;
		for (kk = 0; kk < GFX_MAX_COLORS; kk++) {
			numRGB[kk] = 0;
			rgb[kk] = NULL;
		}
		for (kk = 0; kk < GFX_MAX_TEXTURES; kk++) {
			numUVW[kk] = 0;
			uvw[kk] = NULL;
		}
	}
};

class WireMeshData: public MaxHeapOperators
{
public:
	DWORD_PTR	id;
	int			numFaces;
	GWFace		*faces;
	int			numXYZ;
	Point3		*xyz;
	int			displayFlags;
	BitArray	*faceSel;
	BitArray	*edgeSel;
	int			numMat;
	Material	*mtlArray;

	WireMeshData()
	  : id(NULL),
		numFaces(0),
		faces(NULL),
		numXYZ(0),
		xyz(NULL),
		displayFlags(0),
		faceSel(NULL),
		edgeSel(NULL),
		numMat(0),
		mtlArray(NULL)
	{}
};

class MeshFaceData: public MaxHeapOperators
{
public:
	DWORD_PTR	id;
	int			numFaces;
	GWFace		*faces;
	Point3		*faceNor;
	GWFace		*norFaces;
	DWORD		mapFlags;
	int			displayFlags;
	BitArray	*faceSel;
	BitArray	*edgeSel;
	int			numClrFaces[GFX_MAX_COLORS];
	GWFace		*clrFaces[GFX_MAX_COLORS];
	int			numTexFaces[GFX_MAX_TEXTURES];
	GWFace		*texFaces[GFX_MAX_TEXTURES];
	int			numXYZ;
	Point3		*xyz;
	int			numNor;
	Point3		*nor;
	DWTab		*norIndex;
	int			numRGB[GFX_MAX_COLORS];
	Point3		*rgb[GFX_MAX_COLORS];
	int			numUVW[GFX_MAX_TEXTURES];
	Point3		*uvw[GFX_MAX_TEXTURES];
	int			numMat;
	Material	*mtlArray;

	MeshFaceData()
	  : id(NULL),
		numFaces(0),
		faces(NULL),
		faceNor(NULL),
		norFaces(NULL),
		mapFlags(0),
		displayFlags(0),
		faceSel(NULL),
		edgeSel(NULL),
		numXYZ(0),
		xyz(NULL),
		numNor(0),
		nor(NULL),
		norIndex(NULL),
		numMat(0),
		mtlArray(NULL)
	{
		int kk;
		for (kk = 0; kk < GFX_MAX_COLORS; kk++) {
			numClrFaces[kk] = 0;
			clrFaces[kk] = NULL;
			numRGB[kk] = 0;
			rgb[kk] = NULL;
		}
		for (kk = 0; kk < GFX_MAX_TEXTURES; kk++) {
			numTexFaces[kk] = 0;
			texFaces[kk] = NULL;
			numUVW[kk] = 0;
			uvw[kk] = NULL;
		}
	}
};

// End of 3ds max 4.2 Extension


class IHardwareShader : public BaseInterface
{
public:
	virtual Interface_ID	GetID() { return HARDWARE_SHADER_INTERFACE_ID; }
	virtual Interface_ID	GetVSID() = 0;
	virtual Interface_ID	GetPSID() = 0;

	// Interface Lifetime
	virtual LifetimeType	LifetimeControl() { return noRelease; }

	// Start to draw object
	virtual void	StartObject(Mesh *mesh) = 0;
	virtual void	StartObject(MNMesh *mnmesh) = 0;

	// Initialize HardwareShader first time or frame-by-frame
	virtual void	InitializeShaders(Mesh *mesh, BaseInterface *pbvs, Material *mtlArray, int numMtl, GFX_ESCAPE_FN fn) = 0;
	virtual void	InitializeShaders(MNMesh *mnmesh, BaseInterface *pbvs, Material *mtlArray, int numMtl, GFX_ESCAPE_FN fn) = 0;

	// Try to draw as tristrips?
	virtual bool	CanTryStrips() = 0;

	// Retrive the current rendering mode
	virtual DWORD	GetRndMode() = 0;

	// Retrieve a Material
	virtual Material	*GetMaterial(int numMat) = 0;

	// Set a Material
    virtual void	SetMaterial(const Material &m, int index=0) = 0;

	// Retrieve number of passes for specified Material in Material array
	virtual int		GetNumMultiPass(int numMtl) = 0;

	// Set the number of the current pass
	virtual void	SetNumMultiPass(int numPass) = 0;

	// Draw 3D mesh as TriStrips
	virtual void	DrawMeshStrips(MeshData *data, GFX_ESCAPE_FN fn) = 0;

	// Draw 3D mesh as wireframe
	virtual void	DrawWireMesh(WireMeshData *data, GFX_ESCAPE_FN fn) = 0;

	// Draw 3D lines
	virtual void	StartLines(WireMeshData *data) = 0;
	virtual void	AddLine(DWORD *vert, int vis) = 0;
	virtual void	DrawLines() = 0;
	virtual void	EndLines(GFX_ESCAPE_FN fn) = 0;

	// Draw 3D triangles
	virtual void	StartTriangles(MeshFaceData *data) = 0;
	virtual void	AddTriangle(DWORD index, int *edgeVis) = 0;
	virtual void	DrawTriangles() = 0;
	virtual void	EndTriangles(GFX_ESCAPE_FN fn) = 0;

	// End of drawing object
	virtual void	EndObject(Mesh *mesh) = 0;
	virtual void	EndObject(MNMesh *mnmesh) = 0;
};

class IVertexShader: public MaxHeapOperators
{
public:
	// Load VertexShader instructions, create any additional per vertex data on
	// a per node basis.  VertexShader instructions should be loaded once and
	// shared among all the nodes using this VertexShader.  Additional per
	// vertex data should be (re)created only when the associated node data
	// changes.  In addition, if there is sufficient memory, VertexBuffers can
	// be created and updated only when node data changes in order to improve
	// rendering performance.
	virtual HRESULT Initialize(Mesh *mesh, INode *node) = 0;
	virtual HRESULT Initialize(MNMesh *mnmesh, INode *node) = 0;
};

#endif
